sqlserver如何定义一个静态变量_[Bazel]自定义规则实现将多个静态库合并为一个动态库或静态库...

f2b9bf7a8674181e83f09cd24cf69079.png
  • 1 前言

  • 2 自定义规则实现

    • 2.1 规则功能

    • 2.2 实现规则的理论基础

    • 2.3 规则代码实现

  • 3 总结

  • 4 参考资料

1 前言

为了实现如标题所述的将多个静态库合并为一个动态库,内置的 Bazel 规则是没有这个功能的,Bazel C/C++ 相关的内置规则有:

  • cc_binary :生成可执行文件
  • cc_import :允许用户导入预编译的 C/C++ 库,包括动态库、静态库
  • cc_library :生成动/静态库
  • cc_proto_library :从 .proto 文件生成 C++ 代码
  • fdo_prefetch_hints :表示位于工作区中或位于指定绝对路径的 FDO 预取提示配置文件
  • fdo_profile :表示工作区中或位于指定绝对路径的 FDO 配置文件
  • cc_test :测试 C/C++ 样例
  • cc_toolchain :表示一个 C++ 工具链
  • cc_toolchain_suite :表示 C++ 工具链的集合

而我们知道规则(Rule)定义了 Bazel 对输入执行的一系列操作,以生成一组输出。例如 cc_binary 规则可能:

  • 输入(Inputs):获取一组 .cpp 文件
  • 动作(Action):基于输入运行 g++
  • 输出(Output):返回一个可执行文件

从 Bazel 的角度来看,g++ 和标准 C++ 库也是这个规则的输入。作为规则编写人员,你不仅必须考虑用户提供的规则输入,还必须考虑执行操作(Actions)所需的所有工具和库。比如我们手动的将多个静态库(libA.a、libB.a、libC.a)合并为一个动态库(libcombined.so):

$ gcc -shared -fPIC -Wl,--whole-archive libA.a libB.a libC.a -Wl,--no-whole-archive -Wl,-soname -o libcombined.so

注:-Wl,option 后面接的选项最终会作为链接器 ld 的参数,即上面的命令最终还调用了 ld 命令。而 -Wl,--whole-archive {xxx} -Wl,--no-whole-archive 所包围的库表示将 {xxx} 库列表中所有 .o 中的符号都链接进来,这样会导致链接不必要的代码进来,从而导致生成的库会相对很大。目前还没有找到相关办法是否可以做到只链接进上层模块库所调用到的函数。

在编写规则中我们就需要获取当前的编译器,我们不能直接使用固定的路径,比如 Linux 下 /usr/bin/gcc,因为可能是交叉编译器,路径就不一样了。另外我们还需要传入 gcc 将多个静态库合并成一个动态库的相关参数、待合成的静态库列表、最后要生成的动态库名称和路径。这样就是一个比较完善的自定义规则了。

2 自定义规则实现

2.1 规则功能

  • 将多个静态库合并成一个动态库
  • 将多个静态库合并成一个静态库
  • 可以设置生成库的名称和生成路径
  • 静态库作为规则依赖

2.2 实现规则的理论基础

将多个静态库合并成一个动态库:

$ gcc -shared -fPIC -Wl,--whole-archive libA.a libB.a libC.a -Wl,--no-whole-archive  -Wl,-soname -o libcombined.so

将多个静态库合并成一个静态库:

方式一:

$ cd temp
$ ar x libA.a
$ ar x libB.a
$ ar x libC.a
$ ar rc libcombined.a *.o

用这种方式无法指定库的输出目录。笨方法就是,将每个待合并的静态库都拷贝到目标目录里去,然后一一 ar -x 操作,然后再到目标目录里操作 ar rc。这就涉及到了中间文件的产生,有一个很重要的点就是中间文件的产生只能在当前 Bazel 包中创建。中间文件的创建我们可以使用 File actions.declare_file(filename, *, sibling=None) 声明然后结合 Action 去真实创建。

方式二(需安装libtool):

# MacOS系统
$ libtool -static -o libcombined.a libA.a libB.a libC.a

在 Unix-like 系统上:

$ sudo apt-get install libtool-bin
# 生成的libcombined.a ar -x 解压出来是 libA.a libB.a libC.a ,而不是 *.o 文件。
$ libtool --mode=link gcc -o libcombined.a libA.a libB.a libC.a
# 这样可以指定生成路径,但是 *.o 的生成还是需要 ar -x 来生成
$ libtool --mode=link gcc -o libcombined.a *.o

另外我们需要规则具有参数输入功能,参数输入类型定义可以详见:https://docs.bazel.build/versions/3.4.0/skylark/lib/attr.html ,比如定义一个决定是否合成动态库或静态库的布尔参数(genstatic),以及带依赖项配置(deps):

my_cc_combine = rule(
    implementation = _combine_impl,
    attrs = {
        "genstatic" : attr.bool(default = False),
        "deps": attr.label_list(),
    }
)

Action 描述了如何从一组输入生成一组输出,例如 “在 hello.c 上运行 gcc 并获取 hello.o”。创建操作(Action)时,Bazel 不会立即运行命令。它将其注册在依赖关系图中,因为一个 Action 可以依赖于另一个 Action 的输出(例如,在 C 语言中,必须在编译后调用链接器)。在执行阶段,Bazel 会决定必须以何种顺序运行哪些操作。所有创建 Action 的函数都定义在 ctx.actions 中:

  • ctx.actions.run :运行一个可执行文件
  • ctx.actions.run_shell :运行一个脚本命令
  • ctx.actions.write :将一个字符串写入文件
  • ctx.actions.expand_template :从模板文件中创建一个文件

因此我们可以通过创建一个运行脚本命令的 Action 来运行上面所述的打包命令,即使用 ctx.actions.run_shell 函数。

如前言中讲到的,如果是交叉编译器呢? 那我们还需要在规则中获取到当前编译器的信息,包括 gccldar 工具。需要在规则中传入当前编译器信息:

my_cc_combine = rule(
    implementation = _combine_impl,
    attrs = {
        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
        "genstatic" : attr.bool(default = False),
        "deps": attr.label_list(),
    }
)

然后在 _combine_impl 中通过 load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") 中的 find_cpp_toolchain(ctx) 获取当前编译器信息。

还有一个比较重要的问题就是,如果依赖还有依赖呢? 比如 libA.a 依赖了 libD.alibE.a,那我们还需要将 libD.alibE.a 也合并到 libcombined.so 中。这种依赖也分为两种,一种是 libD.a 是外部已经编译好的静态库,而 libE.a 是有 cc_library 规则编译出来的静态库。那如何能够把这两种方式的库都最后合并到 libcombined.so 呢?

depset 是一种专门的数据结构,支持有效的合并操作,并定义了遍历顺序。通常用于从 rules 和 aspects 的传递依赖中积累数据。depset 的成员必须是可散列的(hashable),并且所有元素都是相同类型。具体的其他特性和用法这里就不展开了,我们只需要知道这种数据结构保存了 rules 里目标的依赖关系信息。Depsets 可能包含重复的值,但是使用 to_list() 成员函数可以获取一个没有重复项的元素列表,遍历所以成员。

我们在 _combine_impl 中可以用 ctx.attr.deps 获得当前目标的依赖列表,每个元素的组成为,即包含一个目标和目标的三个信息体,目标里结构具体可以参考官方文档并获取相关信息,比如用 {Target}.files.to_list() 可以获取 Target 直接生成的一组文件列表,意思就是比如 A 目标,直接生成的就是 libA.a。目标 A 的依赖目标 E 信息在 CcInfo 结构体内,这里先不展开如何获取了,这里只做个提示:

x = dep_target[CcInfo].linking_context.linker_inputs.to_list()
for linker_in in x:
    # ]bazel-out/k8-fastbuild/bin]libA/_objs/A/liba.pic.o], pic_static_library=File:[[]bazel-out/k8-fastbuild/bin]libA/libA.a, alwayslink=false)>, ], userLinkFlags=[], nonCodeInputs=[])>
    for linker_in_lib in linker_in.libraries:
        # 
        # 
        internal_link_lib = linker_in_lib.pic_static_library
        # 
        external_link_lib = linker_in_lib.static_library
        

2.3 规则代码实现

my_cc_combine.bzl:

load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")

def _combine_impl(ctx):
    cc_toolchain = find_cpp_toolchain(ctx)    

    target_list = []
    for dep_target in ctx.attr.deps:        
        # CcInfo, InstrumentedFilesInfo, OutputGroupInfo      
        cc_info_linker_inputs = dep_target[CcInfo].linking_context.linker_inputs

        target_dirname_list = []
        for linker_in in cc_info_linker_inputs.to_list():            
            for linker_in_lib in linker_in.libraries:                
                if linker_in_lib.pic_static_library != None:
                    target_list += [linker_in_lib.pic_static_library]                    
                if linker_in_lib.static_library != None:
                    target_list += [linker_in_lib.static_library]
    
    output = ctx.outputs.output
    if ctx.attr.genstatic:
        cp_command  = ""       
        processed_list = []
        processed_path_list = []
        for dep in target_list:
            cp_command += "cp -a " + dep.path + " " + output.dirname + "/ && "
            processed = ctx.actions.declare_file(dep.basename)
            processed_list += [processed]
            processed_path_list += [dep.path]
        cp_command += "echo 'starting to run shell'"
        processed_path_list += [output.path]
  
        ctx.actions.run_shell(
            outputs = processed_list,
            inputs = target_list,
            command = cp_command,
        )

        command = "cd {} && ar -x {} {}".format(
                output.dirname,
                " && ar -x ".join([dep.basename for dep in target_list]),
                " && ar -rc libauto.a *.o"
            )
        print("command = ", command)
        ctx.actions.run_shell(
            outputs = [output],
            inputs = processed_list,
            command = command,
        )
    else:
        command = "export PATH=$PATH:{} && {} -shared -fPIC -Wl,--whole-archive {} -Wl,--no-whole-archive -Wl,-soname -o {}".format(
            cc_toolchain.ld_executable,
            cc_toolchain.compiler_executable,
            " ".join([dep.path for dep in target_list]),
            output.path)
        print("command = ", command)
        ctx.actions.run_shell(
            outputs = [output],
            inputs = target_list,
            command = command,
        )

my_cc_combine = rule(
    implementation = _combine_impl,
    attrs = {
        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
        "genstatic" : attr.bool(default = False),
        "deps": attr.label_list(),
        "output": attr.output()
    },
)

BUILD 文件中调用我们创建的规则示例:

load(":my_cc_combine.bzl", "my_cc_combine")

my_cc_combine(
    name = "hello_combined",
    # 这里将所有的静态库合并成一个静态库
    genstatic = True,
    output = "libcombined.a",
    deps = ["//libA:A", "//libB:B", "//libC:C"]

3 总结

至此自定义规则实现完成,中间遇到了一些麻烦,不过最终都解决了,因为 Bazel 的中文社区目前为止并不是很完善,可以说中文资料大都是概念性介绍和简单入门,很多内容都需要参考官方文档或者去 https://groups.google.com/forum/#!forum/bazel-discuss 提问题,有 Bazel bug 的话就只有去 https://github.com/bazelbuild/bazel/issues 提 issue 了。最后在实现自定义规则中将多个静态库合并为一个动态库示例中,这里有几个点我们需要注意下:

  • 在实现我们中间文件的拷贝过程中,如果最后没有实现输出 output Action,那么中间文件也不会产生,这在我调试过程中带给了我一阵疑惑
  • 另外创建的中间文件因为是拷贝过程,实际生成的中间文件,Bazel 已经做了处理,居然是软链接到沙箱(sandbox)源文件,这中间的原理我暂未弄清楚,或许就是沙箱优化
  • 对于交叉编译器,我们必须使用 find_cpp_toolchain(ctx),而不是直接使用 /usr/bin/gcc 等工具链
  • 这里实现自定义规则,我们只使用了 action.run_shell。其他的比如还可以编写测试规则(类名需以_test结尾)、actions.write(适合小文件生成)、actions.expand_template(用模板生成文件)、用 aspect 从依赖中搜集信息等等规则的具体用法

4 参考资料

  • https://docs.bazel.build/versions/3.4.0/skylark/rules.html
  • https://docs.bazel.build/versions/3.4.0/skylark/lib/actions.html
  • https://docs.bazel.build/versions/3.4.0/skylark/tutorial-creating-a-macro.html
  • https://docs.bazel.build/versions/3.4.0/skylark/depsets.html
  • https://docs.bazel.build/versions/3.4.0/skylark/lib/Target.html
  • https://docs.bazel.build/versions/3.4.0/skylark/lib/attr.html
  • https://docs.bazel.build/versions/3.4.0/rules.html
  • https://docs.bazel.build/versions/3.4.0/skylark/lib/ctx.html
  • https://docs.bazel.build/versions/3.4.0/be/c-cpp.html
  • https://sourceware.org/binutils/docs/ld/Options.html
aa6048e95c60cd277aaede691ea00cf9.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329616.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(转)base64编码(严格说来,base64不算作加解密算法)

【README】 1.本文转自: Java base64加密解密 - xuwc - 博客园参考: https://www.cnblogs.com/luguo3000/p/3940197.html https://blog.csdn.net/jiahao1186/article/detailhttps://www.cnblogs.com/xuwc/p/14058183.htmlhttps://zh.wikipedia.org/wik…

JavaWeb的web.xml标签元素(一)

普通信息标签 &#xff08;1&#xff09;display-name 定义了WEB应用的名字,在servletContextConfig display-name* XML语法&#xff1a;<display-name>AppName</display-name> &#xff08;2&#xff09;description 可以使用description元素来提供有关部署描述符…

python3多线程第三方库_Python3 多线程

Python3 多线程多线程类似于同时执行多个不同程序&#xff0c;多线程运行有如下优点&#xff1a;使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面可以更加吸引人&#xff0c;比如用户点击了一个按钮去触发某些事件的处理&#xff0c;可以弹出一个进度条来显示…

采用MiniProfiler监控EF与.NET MVC项目

今天来说说EF与MVC项目的性能检测和监控&#xff0c;相对本篇文章&#xff0c;更推荐大家使用另一个类似组件 NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC MiniProfiler是Stack Ove…

(转)公钥,私钥和数字签名这样最好理解

转自&#xff1a; 公钥&#xff0c;私钥和数字签名这样最好理解_21aspnet的博客-CSDN博客_公钥签名还是私钥签名一、公钥加密 假设一下&#xff0c;我找了两个数字&#xff0c;一个是1&#xff0c;一个是2。我喜欢2这个数字&#xff0c;就保留起来&#xff0c;不告诉你们(私钥…

JavaWeb的web.xml标签元素(二)

JavaWeb的web.xml标签元素&#xff08;一&#xff09; 九、session-config为Web应用中的javax.servlet.http.HttpSession对象定义参数 session-config-session-timeout?session-timeout元素用来指定默认的会话超时时间间隔&#xff0c;以分钟为单位。该元素值必须为整数。如果…

登录系统 提示框_实物资产管理软件操作手册(职员和系统用户)

一、职员和系统用户“职员和系统用户”菜单提供了对公司人员信息进行维护的功能&#xff0c;可以自定义职员编码、 姓名、用户名等相关职员信息。注意&#xff1a;职员编码、用户名需唯一&#xff0c;且用户名一旦保存则不可修改。根据要求可以将“用户类型”分为“职员”和“系…

HoloLens开发手记-凝视 Gaze

凝视 Gaze 在全息应用中&#xff0c;凝视是第一种输入形式&#xff0c;被用于定位物体&#xff08;功能和PC的光标指针类似&#xff09;。凝视告诉你用户正在看向世界中的位置&#xff0c;让你能够确定他们的意图。在现实世界中&#xff0c;你通常会盯着你打算与之交互的物体。…

摘要算法与加密(以MD5算法为例)

【README】 部分内容总结自&#xff1a; 摘要与加密的区别&#xff08;以MD5算法为例&#xff09; - 掘金https://juejin.cn/post/6844903561478799368 【1】摘要算法与加密区别 【1.1】摘要算法&#xff08;不可逆&#xff09; 1&#xff09;摘要算法&#xff1a; 说白了…

Oracle入门(三A)之sqlplus

转载自 sqlplus /nolog是什么意思sqlplus命令格式如下&#xff1a; 用法: SQLPLUS [ [<option>] [<logon>] [<start>] ] 其中 <option> :: -H | -V | [ [-M <o>] [-R <n>] [-S] ] <登录> :: <用户名>[/<口令>][<con…

.Net Core下如何管理配置文件

一、前言 根据该issues来看&#xff0c;System.Configuration在.net core中已经不存在了&#xff0c;那么取而代之的是由Microsoft.Extensions.Cnfiguration.XXX一系列的类库提供&#xff0c;对应的开源地址为点击这里。 从当前开源的代码来看&#xff0c;在.net core下提供了…

python短视频自动制作_Python 带你一键生成朋友圈超火的九宫格短视频

1. 场景如果你经常刷抖音和微信朋友圈&#xff0c;一定发现了最近九宫格短视频很火&#xff01;​从朋友圈九宫格图片&#xff0c;到九宫格视频&#xff0c;相比传统的图片视频&#xff0c;前者似乎更有个性和逼格除了传统的剪辑软件可以实现&#xff0c;是否有其他更加快捷方便…

3-系统总线

【README】 1.本文总结自B站 《计算机组成原理&#xff08;哈工大刘宏伟&#xff09;》的视频讲解&#xff0c;非常棒&#xff0c;墙裂推荐&#xff1b; 2.补充&#xff1a;冯洛伊曼计算机由5大部分组成&#xff1a; 1. 运算器2. 控制器3. 存储器4. 输入设备5. …

HashMap中傻傻分不清楚的那些概念

转载自 HashMap中傻傻分不清楚的那些概念 很多人在通过阅读源码的方式学习Java&#xff0c;这是个很好的方式。而JDK的源码自然是首选。在JDK的众多类中&#xff0c;我觉得HashMap及其相关的类是设计的比较好的。很多人读过HashMap的代码&#xff0c;不知道你们有没有和我一样&…

HoloLens开发手记-硬件细节 Hardware Detail

微软HoloLens是世界第一款完全无线缆的全息计算机。通过在新方式上赋予用户的全息体验&#xff0c;HoloLens重新定义了个人计算&#xff08;Personal Computing&#xff09;。为了将3D全息图形固定到你周围的真实世界中&#xff0c;HoloLens融合了最先进的光学元件和传感器。 设…

super构造方法为什么给子类赋值_【Java学习 | Javase】super

整理自&#xff1a;动力节点基础讲义super概述 严格来说&#xff0c;super其实并不是一个引用&#xff0c;它只是一个关键字&#xff0c;super代表了当前对象中从父类继承过来的那部分特征。换句话说&#xff0c;super其实是this的一部分&#xff0c;从父类继承过来的属性和方法…

4-存储器

【README】 1.本文总结自B站 《计算机组成原理&#xff08;哈工大刘宏伟&#xff09;》的视频讲解&#xff0c;非常棒&#xff0c;墙裂推荐&#xff1b; 【1】概述 【1.1】存储器分类 1&#xff09;按存储介质分类 1&#xff0c; 半导体存储器&#xff0c;分为 TTL&#…

坑爹的日志无法按天切割问题

转载自 坑爹的日志无法按天切割问题问题背景 线上某个新管理型系统出现了日志无法按天切割生成日志文件的问题&#xff0c;所有的日志都在一个日志文件里面&#xff0c;只有每次重启的时候才会重新生成文件。 这个管理系统使用的是 Spring Boot Logback 框架&#xff0c;查看了…

Asp.net 面向接口框架之应用程序上下文作用域组件

在团队中推广面向接口开发两年左右,成果总体来说我还是挺满意的,使用面向接口开发的模块使用Unity容器配置的功能非常稳定,便于共享迁移(另一个项目使用只需要复制配置和调用接口即可)也很好扩展(操作的数据库、表、资源等都可以配置)。 但是由于当时开发的匆忙(边开发边应用),…

投票源码程序_[内附完整源码和文档] 基于JSP实现的影视创作论坛系统

摘 要随着时代的发展&#xff0c;互联网的出现&#xff0c;给传统影视行业带来的最大便利就是&#xff0c;方便了影视从业人员以及爱好者的交流和互动&#xff0c;而为用户提供一个书写影评&#xff0c;阅读影评以及回复影评的平台&#xff0c;以影评为载体来使用户感受影评、解…