Bazel是什么

bazel是一个工程编译工具。

Bazel的特点

它适合构建和测试拥有庞大代码库的项目,用(多种)需要编译的语言写的项目,在多平台上部署的项目,及有大量测试的工程的项目。

bazel可以成倍的提高构建速度,因为它只重新编译需要重新编译的文件,所以,对于没有发生变化的文件,它是不会对其进行重新编译的。当然为了能够让bazel很好的识别哪些文件是增量文件,在BUILD文件中,每个库,测试程序,二进制文件必须明确完整地指定直接依赖。当修改源代码文件后,Bazel使用这个依赖信息就可以知道哪些必须重新构建,哪些任务可以并行执行。这意味者所有的构建都是增量形式的并能够每次都生成相同的结果。这样一来,也就意味着它的两个特点,第一,它会比较占用缓存。也就是说它会在第一次构建工程的时候,将所有代码编译并缓存起来,后面在构建的时候,只重新编译那些有变化的文件即可。第二就是,他不适合去处理解释性语言的过程,比如python,作为解释性语言,每次的构建,都会重新加载所有文件,这样以来就无法发挥bazel的优势了。

bazel本身是支持多语言的,如:java, c, c++, 也支持拓展为任意的编程语言。而且它同样支持多平台,同一套工具和同样的BUILD文件可以用来构建不同架构和不同平台的软件。

ok,接下看看,bazel的具体概念和使用吧~

Bazel的概念:

    workspace 工作空间:每个工作空间目录都有一个名为WORKSPACE的文本文件,该文件可能为空,或者可能包含对构建输出所需的外部依赖项外部依赖项的引用。简单来说,每一个Bazel项目就对应着一个名为WORKSPACE的文本文件,WORKSPACE的文本文件所在的文件夹,就是bazel项目的工作空间。所以工作空间的另一个特点就是:它(WORKSPACE的文本文件所在的文件夹)包括了单个或多个项目所有源文件。

在构建这个工作空间的时候,需要先到包括了单个或多个项目所有源文件的文件夹里,执行命令:

$touch WORKSPACE
$bazel info workspace

这时候我们应该可以看到输出当前路径,这说明workspace创建成功(注意这里的WORKSPACE文件本身还是空的)。

package 程序包:每个程序包中包含一个BUILD文件,此文件中描述了此工具包的生成构建方式。简单来说就是,工程中,我们往往会将具有一定功能的模块进行封装,然后就会形成一个具备一定功能包,那么这个包说白了,就是由源码文件组成而来的具备一定功能的包。ok,对于bazel的package程序包,就是指刚才说的功能包,唯一的区别就是,需要在这个工具包的里面添加一个BUILD文件。

target 目标:package程序包里的每一个元素都称之为目标。所以,目标分为两类,一个就是源文件,一个就是BUILD文件了。由于BUILD文件里写的东西,叫规则,因此,也有人将目标的2个分类,分为源文件和规则。除此之外,还有一类,叫包组,但它们的数量要少得多,包组就相当于包的嵌套。

Bazel的具体使用:

    通常就是修改各个BUILD文件,为新的包添加BUILD文件,同时将新建的BUILD文件添加到外层BUILD文件的依赖里。

看看,BUILD文件(示例最外层构建的BUILD):

android_binary是规则的一种,name, mainfest, deps都是规则的属性。

name属性绑定的就是,这个规则对外的名称。

android_binary(name = "app",manifest = "AndroidManifest.xml",deps = ["//src/main/java/com/example/bazel:greeter_activity"],
)

中间层BUILD文件(注意最后一行中添加的对最内层BUILD的依赖的添加):

deps = ["//src/main/java/com/example/bazel/util:util"]

最内层BUILD文件:

最后回到工作目录下执行命令:

bazel build //src/main:app

这里分析下他的命令~

bazel的构建命令为:bazel  build  包目录:规则名称

注意,包目录的目录起始是从工作目录开始算的,而不是从整个的工程的根目录开始算的。

如上目录的目录结构为:

注意这里最后一个目录于中间目录的变化,这是因为util是后加的,所以,也就有了util依赖添加到中间那个BUILD的那一幕了。

现在,来扯扯 target 的编写(BUILD文件的编写):

一个普通的BUILD是这样写的:

package(default_visibility = ["//src:__subpackages__"],
)android_library(name = "greeter_activity",srcs = ["Greeter.java","MainActivity.java",],manifest = "AndroidManifest.xml",resource_files = glob(["res/**"]),
)

那么如果,在使用命令, bazel build //src/main:app 的时候调用 greeter_activity 的时候,这个对象有其他依赖,怎么办呢,通常就可以在这个规则里面添加依赖属性(deps),依赖是个列表对象。

package(default_visibility = ["//src:__subpackages__"],
)android_library(name = "greeter_activity",srcs = ["Greeter.java","MainActivity.java",],manifest = "AndroidManifest.xml",resource_files = glob(["res/**"]),deps = ["//src/main/java/com/example/bazel/util:util"],
)

注意这里的deps里面的内容,是给的绝对路径。

当然,也有一种情况,在同一个BUILD文件里,有法则之间存在互相引用的情况,又该当如何?

这里举个例子:

这也是一个BUILD文件:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

请注意,红色的deps,这里由于是在同一个文件中的引用,所以就直接省略了文件夹的绝对路径这一段。(这里特别注意:依赖的引用都是直接写BUILD文件做在的文件夹的路径就行了,然后给 ‘:’ , 加具体target的name属性的值就行

再举个例子:

现有工程目录为此:

└──stage3
   ├── main
   │   ├── BUILD
   │   ├── hello-world.cc
   │   ├── hello-greet.cc
   │   └── hello-greet.h
   ├── lib
   │   ├── BUILD
   │   ├── hello-time.cc
   │   └── hello-time.h
   └── WORKSPACE

然后,我们看下main下的BUILD:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
":hello-greet",
"//lib:hello-time",
    ],
)

这下应该足够形象了吧。

接着看下lib下的BUILD:

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

ok, 这里迎来了 target 的另一个属性:visibility

visibility属性是target显式可见属性,因为默认情况下,targets只对同一个BUILD文件里的其他targets可见,所以添加这个属性,绑定一个列表,你想让那些包可以访问这个targe,那么就将其添加到这个列表里就可以了。

顺便介绍下其他属性:

name属性是target被编译成静态库后叫什么名字。

srcs属性是target下有哪些源文件需要被编译。它是一个列表。

hdrs属性是有哪些头文件是public。

这里再看一种BUILD:

load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application")objc_library(name = "universal_lib",srcs = ["universal/main.m","universal/AppDelegate.m","universal/ViewController.m",],hdrs = ["universal/AppDelegate.h","universal/ViewController.h",],enable_modules = 1,
)

load("//foo/bar:file.bzl", "some_library")

上面的语句加载foo/bar/file.bzl并添加其中定义的符号some_libraray到当前环境中

load属性是load语句可以用来加载规则、函数、常量(字符串、列表等)。有点import的意思。

load语句必须出现在顶级作用域,不能出现在函数中。第一个参数说明扩展的位置,你可以为导入的符号设置别名。

这里的 load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application") 是为了告诉bazel,需要用build_bazel_rulers_apple这个库里的那个规则来编译这个target.

这里是不是有点奇怪,为啥有的 load(...) 里用的@xxx,有的就直接 //xxx.

这里加个小插曲:

Bazel允许依赖其它项目中定义的目标,这些来自其它项目的依赖叫做“外部依赖“。当前工作空间的WORKSPACE文件声明从何处下载外部依赖的源码。

外部依赖可以有自己的1-N个BUILD文件,其中定义自己的目标。当前项目可以使用这些目标。例如下面的两个项目结构:

/home/user/project1/WORKSPACEBUILDsrcs/...project2/WORKSPACEBUILDmy-libs/

如果project1需要依赖定义在project2/BUILD中的目标:foo,则可以在其WORKSPACE中声明一个存储库(repository),名字为project2,位于/home/user/project2。然后,可以在BUILD中通过标签@project2//:foo引用目标foo。

所以,以@来引用的包,就属于外部依赖包。

举个例子:

bazel项目可以使用local_repository、git_repository或者http_archive这几个规则来引用外部依赖包。

引用本地Bazel项目的例子:

在目录my_project/WORKSPACE下local_repository(name = "coworkers_project",path = "/path/to/coworkers-project",
)

在BUILD中,引用coworkers_project中的目标//foo:bar时,使用标签@coworkers_project//foo:bar

非bazel的项目,可以使用new_local_repository、new_git_repository或者new_http_archive这几个规则来引用。你需要自己编写BUILD文件来构建这些项目。

在WORKSPACE中:new_local_repository(name = "coworkers_project",path = "/path/to/coworkers-project",build_file = "coworker.BUILD",
)
在BUILD中:cc_library(name = "some-lib",srcs = glob(["**"]),visibility = ["//visibility:public"],
) 

在BUILD文件中,使用标签@coworkers_project//:some-lib引用上面的库。

在这里不难发现,我们都是直接引用规则,那么规则如何自定义呢?

存储库规则用于定义外部存储库。外部存储库是一种规则,这种规则只能用在WORKSPACE文件中,可以在Bazel加载阶段启用非封闭性( non-hermetic,所谓封闭是指自包含,不依赖于外部环境)操作。每个外部存储库都创建自己的WORKSPACE,具有自己的BUILD文件和构件。

外部存储库可以用来:

  1. 加载第三方依赖,例如Maven打包的库
  2. 为运行构件的主机生成特化的BUILD文件

在bzl文件中,调用repository_rule函数可以创建一个存储库规则,你需要将其存放在全局变量中:

local_repository = repository_rule(# 实现函数implementation=_impl,local=True,# 属性列表attrs={"path": attr.string(mandatory=True)}) 

每个存储库规则都必须提供实现函数,其中包含在Bazel加载阶段需要执行的严格的逻辑。该函数具有唯一的入参repository_ctx:

def _impl(repository_ctx):# 你可以通过repository_ctx访问属性值、调用非密封性函数(例如查找、执行二进制文件,创建或下载文件到存储库)repository_ctx.symlink(repository_ctx.attr.path, "") 

引用存储库中规则时,可以使用 @REPO_NAMAE//package:target这样的标签。

这里加个小总结:

其实我们不管是在WORKSPACE中还是BUILD中,往往会使用很多规则,这些规则有些是内建的可以直接用,但是有些可能就需要我们自己去创建,去定义。但是有一点是肯定的,不管是内建还是自定义,都是需要一个实现函数的。一般的规则都是给个变量名就可以代表规则,然后绑定规则函数即可 rule(...),这样一来就可以创建一个规则,rule函数中必须绑定一个实现函数,该函数又必须有唯一参数ctx,而对于外部引用规则,又有一些小的区别,他的规则函数是repository_rule(...),同样的他也需要一个实现函数,实现函数就是自定义一个函数即可,但是其必须拥有唯一参数repository_ctx。东西总体来说和一般的rule规则差不多,当然这里选择介绍了引用第三方的规则。当然,正如上面所演示的,repository_rule(...)是有一些属性的,这些属性并不是随便定义的,也是有一定的规则遵守的,这里可以参考:绿色记忆:Bazel学习笔记中的ctx部分。

再来一个更详细的说明:

在xxx.bzl文件里有如下内容:

load("//third_party/mkl:build_defs.bzl", "mkl_repository")
load("//third_party:repo.bzl", "tf_http_archive")mkl_repository(name = "mkl_linux",build_file = clean_dep("//third_party/mkl:mkl.BUILD"),sha256 = "a936d6b277a33d2a027a024ea8e65df62bd2e162c7ca52c48486ed9d5dc27160",strip_prefix = "mklml_lnx_2019.0.5.20190502",urls = ["https://storage.googleapis.com/mirror.tensorflow.org/github.com/intel/mkl-dnn/releases/download/v0.20-rc/mklml_lnx_2019.0.5.20190502.tgz","https://github.com/intel/mkl-dnn/releases/download/v0.20-rc/mklml_lnx_2019.0.5.20190502.tgz",],
)

mkl_repository 在文件//third_party/mkl:build_defs.bzl 中定义:

_TF_MKL_ROOT = "TF_MKL_ROOT"def _enable_local_mkl(repository_ctx):return _TF_MKL_ROOT in repository_ctx.os.environdef _mkl_autoconf_impl(repository_ctx):"""Implementation of the local_mkl_autoconf repository rule."""if _enable_local_mkl(repository_ctx):# Symlink lib and include local folders.mkl_root = repository_ctx.os.environ[_TF_MKL_ROOT]mkl_lib_path = "%s/lib" % mkl_rootrepository_ctx.symlink(mkl_lib_path, "lib")mkl_include_path = "%s/include" % mkl_rootrepository_ctx.symlink(mkl_include_path, "include")mkl_license_path = "%s/license.txt" % mkl_rootrepository_ctx.symlink(mkl_license_path, "license.txt")else:# setup remote mkl repository.repository_ctx.download_and_extract(repository_ctx.attr.urls,sha256 = repository_ctx.attr.sha256,stripPrefix = repository_ctx.attr.strip_prefix,)# Also setup BUILD file.repository_ctx.symlink(repository_ctx.attr.build_file, "BUILD")mkl_repository = repository_rule(implementation = _mkl_autoconf_impl,environ = [_TF_MKL_ROOT,],attrs = {"build_file": attr.label(),"urls": attr.string_list(default = []),"sha256": attr.string(default = ""),"strip_prefix": attr.string(default = ""),},
)

可以看出在这里定义了repository_rule, attrs 里面定义了build_file (这里这个文件主要是 mkl.BUILD 文件,在mkl.BUILD文件内定义了相关的操作library)。 定义attrs ,就可以在implementation 的函数内部使用repository_ctx.attr.<attribute_name> 进行获取。而 repository_rule 的name,可以通过repository_ctx.name 得到。

这里需要注意,在xxx.bzl文件里,使用规则 mkl_repository 时,是使用了许多参数的,那么这些参数,就需要在定义规则 mkl_repository 时,有所体现,就必须声明这些参数的性质,所以,就需要在 repository_rule 里使用 attrs 参数,对  mkl_repository 中除了 name 参数以外的参数做出声明。

关于repository_ctx参数的使用,详见:repository_ctx - Bazel main

关于 attrs 都有哪些参数,详见:attr - Bazel 3.4.0

ok书接上文,进一步看一个BUILD:

load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application")objc_library(name = "universal_lib",srcs = ["universal/main.m","universal/AppDelegate.m","universal/ViewController.m",],hdrs = ["universal/AppDelegate.h","universal/ViewController.h",],enable_modules = 1,
)ios_application(name = "universal",bundle_id = "com.sunxxxx.universal",families = ["iphone","ipad",],minimum_os_version = "9.0",infoplists = [":universal/Info.plist"],visibility = ["//visibility:public"],deps = [":universal_lib"],
)

bundle_id属性是app的唯一标识符。

familier属性是支持iphone还是ipad还是都支持。

munimum_os_version属性是最低支持的系统版本。

infoplists属性是info.plist文件路径。

copts属性是指定编译选项,如 "-I<include-paths>"。

data属性是依赖的一种,不属于源码,不影响目标如何构建,但是目标在运行时可能依赖之。

关于BUILD的一些属性先扯这么多。

接下来,看看BUILD里的一些规则:

filegroup

为一组目标指定一个名字,你可以从其它规则中方便的引用这组目标。

Bazel鼓励使用filegroup,而不是直接引用目录。Bazel构建系统不能完全了解目录中文件的变化情况,因而文件发生变化时,可能不会进行重新构建。而使用filegroup,即使联用glob,目录中所有文件仍然能够被构建系统正确的监控。

filegroup(
    name = "exported_testdata",
    srcs = glob([
        "testdata/*.dat",
        "testdata/logs/**/*.log",
    ]),
)

要引用filegroup,只需要使用标签:

cc_library(
    name = "my_library",
    srcs = ["foo.cc"],
    data = [
        "//my_package:exported_testdata",
        "//my_package:mygroup",
    ],
)

test_suite

定义一组测试用例,给出一个有意义的名称,便于在特定时机  —— 例如迁入代码、执行压力测试 —— 时执行这些测试用例。

# 匹配当前包中所有small测试

test_suite(

name = "small_tests",

tags = ["small"],

)

# 匹配不包含flaky标记的测试

test_suite(

name = "non_flaky_test",

tags = ["-flaky"],

)

# 指定测试列表

test_suite(

name = "smoke_tests",

tests = [

"system_unittest",

"public_api_unittest",

],

)

alias

为规则设置一个别名:

filegroup(

name = "data",

srcs = ["data.txt"],

)

# 定义别名

alias(

name = "other",

actual = ":data",

)

config_setting

通过匹配以Bazel标记或平台约束来表达的“配置状态”,config_setting能够触发可配置的属性。

下面这个例子,匹配针对ARM平台的构建:

1

2

3

4

config_setting(

name = "arm_build",

values = {"cpu": "arm"},

)

下面的例子,匹配任何定义了宏FOO=bar的针对X86平台的调试(-c dbg)构建:

1

2

3

4

5

6

7

8

config_setting(

name = "x86_debug_build",

values = {

"cpu": "x86",

"compilation_mode": "dbg",

"define": "FOO=bar"

},

)

下面的库,通过select来声明可配置属性:

1

2

3

4

5

6

7

8

9

10

11

12

cc_binary(

name = "mybinary",

srcs = ["main.cc"],

deps = select({

# 如果config_settings arm_build匹配正在进行的构建,则依赖arm_lib这个目标

":arm_build": [":arm_lib"],

# 如果config_settings x86_debug_build匹配正在进行的构建,则依赖x86_devdbg_lib

":x86_debug_build": [":x86_devdbg_lib"],

# 默认情况下,依赖generic_lib

"//conditions:default": [":generic_lib"],

}),

)

select声明就像一个选项,就是说这里的依赖不会同时加载,而是匹配到了哪个,才加载哪个。

上面的描述不够形象,看这里:

cc_library(name = "multiplatform_lib",srcs = select({":x86_mode": ["x86_impl.cc"],":arm_mode": ["arm_impl.cc"]})
)
config_setting(name = "x86_mode",values = { "cpu": "x86" }
)
config_setting(name = "arm_mode",values = { "cpu": "arm" }
)

在属性值的设置中使用select()可以根据输入的config_setting的值来进行匹配。比如

bazel build :multiplatform_lib --cpu=arm

在执行bazel命令的时候,可以附带参数,那么这里的参数就由config_setting而来。换个说法,config_setting就是用来搞bazel命令里的参数的。

glob:

glob(include, exclude=[], exclude_directories=1)

Glob是一个辅助函数,用于在任何位置获得想要的文件列表。可以使用*通配符以及目录分隔符/,另外**表示递归通配符只能在目录分隔符/之间使用,比如"x/**/*.java" is valid, but “test**/testdata.xml” and “**.java” are both invalid. No other wildcards are supported.
select:

select(
    {conditionA: valuesA, conditionB: valuesB, ...},
    no_match_error = "custom message"
)

这也是一个辅助函数,可以使得rule的属性被配置,选取的方式通过读Bazel的命令行flag。

genrule

一般性的规则 —— 使用用户指定的Bash命令,生成一个或多个文件。使用genrule理论上可以实现任何构建行为,例如压缩JavaScript代码。但是在执行C++、Java等构建任务时,最好使用相应的专用规则,更加简单。

不要使用genrule来运行测试,如果需要一般性的测试规则,可以考虑使用sh_test。

genrule在一个Bash shell环境下执行,当任意一个命令或管道失败(set -e -o pipefail),整个规则就失败。你不应该在genrule中访问网络。

genrule(
    name = "foo",
    # 不需要输入
    srcs = [],
    # 生成一个foo.h
    outs = ["foo.h"],
    # 运行当前规则所在包下的一个Perl脚本
    cmd = "./$(location create_foo.pl) > \"$@\"",
    tools = ["create_foo.pl"],
)

再扯一个bazel的目录布局:

workspace-name>/                          # 工作空间根目录bazel-my-project => <...my-project>     # execRoot的符号链接,所有构建动作在此目录下执行bazel-out => <...bin>                   # outputPath的符号链接bazel-bin => <...bin>                   # 最近一次写入的二进制目录的符号链接,即$(BINDIR)bazel-genfiles => <...genfiles>         # 最近一次写入的genfiles目录的符号链接,即$(GENDIR)/home/user/.cache/bazel/                  # outputRoot,所有工作空间的Bazel输出的根目录_bazel_$USER/                           # outputUserRoot,当前用户的Bazel输出的根目录install/fba9a2c87ee9589d72889caf082f1029/   # installBase,Bazel安装清单的哈希值_embedded_binaries/               # 第一次运行时从Bazel可执行文件的数据段解开的可执行文件或脚本7ffd56a6e4cb724ea575aba15733d113/     # outputBase,某个工作空间根目录的哈希值action_cache/                       # Action cache目录层次action_outs/                        # Action output目录command.log                         # 最近一次Bazel命令的stdout/stderr输出external/                           # 远程存储库被下载、链接到此目录server/                             # Bazel服务器将所有服务器有关的文件存放在此jvm.out                           # Bazel服务器的调试输出execroot/                           # 所有Bazel Action的工作目录<workspace-name>/                 # Bazel构建的工作树_bin/                           # 助手工具链接或者拷贝到此bazel-out/                      # outputPath,构建的实际输出目录local_linux-fastbuild/        # 每个独特的BuildConfiguration实例对应一个子目录bin/                        # 单个构建配置二进制输出目录,$(BINDIR)foo/bar/_objs/baz/        # 命名为//foo/bar:baz的cc_*规则的Object文件所在目录foo/bar/baz1.o          # //foo/bar:baz1.cc对应的Object文件other_package/other.o   # //other_package:other.cc对应的Object文件foo/bar/baz               # //foo/bar:baz这一cc_binary生成的构件foo/bar/baz.runfiles/     # //foo/bar:baz生成的二进制构件的runfiles目录MANIFEST<workspace-name>/...genfiles/                   # 单个构建配置生成的源文件目录,$(GENDIR)testlogs/                   # Bazel的内部测试运行器将日志文件存放在此include/                    # 按需生成的include符号链接树,符号链接bazel-include指向这里host/                         # 本机的BuildConfiguration<packages>/                       # 构建引用的包,对于此包来说,它就像一个正常的WORKSPACE 

这个可以了解一下。

接下来扯个别的, 叫 .bzl 文件:

其实,.bzl 文件是bazel的配置文件,这里就很惊喜了,来瞅瞅。

Bazel配置文件使用Starlark(原先叫Skylark)语言,具有短小、简单、线程安全的特点。

这种语言的语法和Python很类似,Starlark是Python2/Python3的子集。所以,他的写法与python非常类似。

这里特别说明下两者之间的不同之处:

不支持的特性 说明
隐含字符串连接 需要明确使用 + 操作符
链式比较操作符 例如:1 < x < 5
class 使用struct函数
import 使用load语句
is 使用==代替
以下关键字:while、yield、try、raise、except、finally 、global、nonlocal
以下数据类型:float、set
生成器、生成器表达式
lambda以及嵌套函数
绝大多数内置函数、方法

Starlark支持的数据类型包括:None、bool、dict、function、int、list、string,以及两种Bazel特有的类型:depset、struct。

给个例子看看:

# 定义一个数字
number = 18# 定义一个字典
people = {"Alice": 22,"Bob": 40,"Charlie": 55,"Dave": 14,
}names = ", ".join(people.keys())# 定义一个函数
def greet(name):"""Return a greeting."""return "Hello {}!".format(name)
# 调用函数
greeting = greet(names)def fizz_buzz(n):"""Print Fizz Buzz numbers from 1 to n."""# 循环结构for i in range(1, n + 1):s = ""# 分支结构if i % 3 == 0:s += "Fizz"if i % 5 == 0:s += "Buzz"print(s if s else i)

另外,具体在写BUILD的时候,可以设定变量。

COPTS = ["-DVERSION=5"]
 
cc_library(
  name = "foo",
  copts = COPTS,
  srcs = ["foo.cc"],
)
 
cc_library(
  name = "bar",
  copts = COPTS,
  srcs = ["bar.cc"],
  deps = [":foo"],
)

但是,如果要声明跨越多个BUILD文件共享的变量,必须把变量放入.bzl文件中,然后通过load加载bzl文件。

以上内容为对bazel的一些使用

====================================================================

一下内容为bazel在python工程中的使用

一般情况下,我们需要在WORKSPACE中添加pip的依赖项。这里需要使用 pip_install(..) 方法。通过这个方法他将创建包含第三方库的中央外部存储库。

load("@rules_python//python:pip.bzl", "pip_install")# Create a central external repo, @my_deps, that contains Bazel targets for all the
# third-party packages specified in the requirements.txt file.
pip_install(name = "my_deps",requirements = "//path/to:requirements.txt",
)

使用pip相关的包,如果要从储存库中提取相关的包,则必须借助标签 py_library(..) 表示引用内容,同时,还需要使用中央仓库//:requirements.bzl文件中定义的函数requirement(),来将 pip 包名称映射到标签。

load("@my_deps//:requirements.bzl", "requirement")py_library(name = "mylib",srcs = ["mylib.py"],deps = [":myotherlib",requirement("some_pip_dep"),requirement("another_pip_dep"),]
)

这里提一句,文中的 rules_python 是bazel的一个第三方库,在上面的用法中,相当于requirements.txt 中的依赖包会下载到外部储存库,该库的标签就是我们给的name即上述的my_deps,同时也会生成一个 requirements.bzl 文件,该文件中会有 requirement方法。

同样的,rules_python这个第三方库中,还有个叫 pip_import 的方法。用法类似:

load("@rules_python//python:pip.bzl", "pip_import")

pip_import(
    name = "py2_common_deps",
    requirements = "//:support/bazel/external/py2/requirements.txt",
)

load("@py2_buildapi_deps//:requirements.bzl", "requirement")

py_library(
    name = "django",
    deps = [requirement("django")],
)

这里注意,经过py_library处理出来的东西,是二进制文件。

他的实际用法:

这是一个BUILD文件:

package(default_visibility = ["//visibility:public"])load("//support/bazel/rules/deploy:deploy.bzl", "fab_deploy_service", "py2_service")
load("@py2_buildapi_deps//:requirements.bzl", "requirement")py_library(name = "django",deps = [requirement("django")],
)py2_service(name = "buildapi",srcs = glob(["**/*.py"]),bin_path = ".binary.runfiles",data = ["restapi/templates/doc.xhtml","restapi/templates/general_help.xhtml","static/error-404.html","static/index.html",],imports = ["."],main = "manage.py",port = "80",deps = [":django","//lib/build/utils:global_config","//support/bazel/external/py2:django-vhealth-check","//support/bazel/external/py2:gunicorn","//support/bazel/external/py2:httplib2","//support/bazel/external/py2:netaddr","//support/bazel/external/py2:progressbar","//support/bazel/external/py2:psycopg2","//support/bazel/external/py2:python-dateutil","//support/bazel/external/py2:pyyaml","//support/bazel/external/py2:requests","//support/bazel/external/py2:simplejson","//support/bazel/external/py2:six",],
)

当然,对于py_library(..) 的用法也可以打包来用:

py_library(name = "flake8",deps = [requirement("configparser"),requirement("contextlib2"),requirement("enum34"),requirement("flake8"),requirement("functools32"),requirement("importlib-metadata"),requirement("mccabe"),requirement("pycodestyle"),requirement("pyflakes"),requirement("pathlib2"),requirement("scandir"),requirement("six"),requirement("typing"),requirement("zipp"),],
)

总体来说,bazel的用法不是很难,只是相关的资料实在太少了,我们对bazel的许多应用实在是太陌生了,如果想了解bazel的语法还是要多看官方文档,只不过官方文档,缺乏实例,确实有时候看了,还是不知道怎么用。Bazel overview - Bazel 4.2.0

bazel简介 | 万广鲁

Bazel 构建工具介绍_tomcat的专栏-CSDN博客_bazel是什么

Google软件构建工具Bazel FAQ - 生栋 - 博客园

Bazel入门(3. 简单Demo) - 知乎

绿色记忆:Bazel学习笔记  详细的介绍了bazel的一些信息

bazel 工具函数_TH_NUM的博客-CSDN博客  一些配置的信息

bazel 链接_[Bazel]自定义规则实现将多个静态库合并为一个动态库或静态库_weixin_39612733的博客-CSDN博客https://docs.bazel.build/versions/4.2.2/    这是bazel官方文档

hello:bazel相关推荐

  1. Tool之Bazel:Bazel的简介、安装、使用方法之详细攻略

    Tool之Bazel:Bazel的简介.安装.使用方法之详细攻略 目录 Bazel的简介 Bazel的安装 Bazel的使用方法 Bazel的简介 Bazel:Build and test softw ...

  2. Bazel入门教程:编译C++项目

    文章目录 Bazel入门教程:编译C++项目 安装bazel 编译C++项目 建立工作区(workspace) 理解BUILD文件 使用Bazel编译项目 1. 编译你的第一个Bazel项目 2. 查 ...

  3. Bazel 与 gtest:构建一个最简单的测试驱动开发环境

    Bazel 介绍 Google 自家的构建工具,相比 CMake 的优势,主要是多语言构建和相比 Make 语法有更好的可读性.最著名的使用 Bazel 的项目大概是 Tensorflow 吧.目前生 ...

  4. bazel源码编译Tensorflow

    因为研究需求,要从Tensorflow源码编译libtensorflow_cc.so和libtensorflow_framwork.so两个库,工具是bazel. 编译硬件需求:GCC4.8以上,ba ...

  5. bazel编译tensorflow 生成libtensorflow_inference.so 和 libandroid_tensorflow_inference_java.jar

    bazel build -c opt //tensorflow/contrib/android:libtensorflow_inference.so --crosstool_top=//externa ...

  6. Bazel发布Beta版本,增加对Groovy、Rust和Scala语言的支持

    Bazel是Googe旗下的一款构建系统工具,六个月前Google将其开源,目前取得里程碑式的进展,发布了其第一个beta版本,增加了对数种语言和技术的支持. \\ Bazel是Google用来构建自 ...

  7. 深度学习利器: TensorFlow系统架构及高性能程序设计

    2015年11月9日谷歌开源了人工智能平台TensorFlow,同时成为2015年最受关注的开源项目之一.经历了从v0.1到v0.12的12个版本迭代后,谷歌于2017年2月15日发布了TensorF ...

  8. 深度学习指南:在iOS平台上使用TensorFlow

    在利用深度学习网络进行预测性分析之前,我们首先需要对其加以训练.目前市面上存在着大量能够用于神经网络训练的工具,但TensorFlow无疑是其中极为重要的首选方案之一. 大家可以利用TensorFlo ...

  9. 谷歌翻译api_翻译:TensorFlow on Android APP 示例

    本文翻译自原文: Android TensorFlow Machine Learning Example​blog.mindorks.com 翻译部分借助谷歌或有道翻译帮助,再经过我人工修改完成,其中 ...

最新文章

  1. 使用 Shiro 设计基于用户、角色、权限的通用权限管理系统
  2. python-全栈开发-前方高能-内置函数
  3. 2 Oracle用户和表空间
  4. 文件操作03 - 零基础入门学习C语言62
  5. oracle 批量生成约束,关于sql脚本导入Oracle时重复生成check约束的问题解决
  6. mysql必知必会pdf脚本之家,sql必知必会pdf 下载
  7. python snap7 plc_python-snap7-master
  8. HWSD土壤数据库介绍
  9. [解疑][TI]TI毫米波雷达系列(五):恒虚警算法(CFAR)原理
  10. ADB投屏_手机无需安装客户端的【安卓投屏】
  11. 计算机now函数,玩转NOW函数 日期时间随心变
  12. 谷歌浏览器收藏栏不见了解决办法
  13. 【Practical】决策系统与粗糙集
  14. 贪心法 第1关:找零钱
  15. java小游戏-飞翔的小鸟
  16. Unity3D-游戏场景优化之遮挡剔除(Occlusion Culling)的使用
  17. 物联网安全与隐私保护之物联网安全体系
  18. 中国高校计算机大赛--网络技术挑战赛(C4-Network Technology Challenge)
  19. Mac 终于有显示隐藏文件的快捷键了
  20. 行波进位加法器C语言,行波进位加法器.doc

热门文章

  1. “猫虎狗”如何破解汽车后市场的新能源难题
  2. Ansys仿真TDR
  3. 闻泰科技收购安世半导体基本完成 进入上报证监会流程
  4. uniapp-微信小程序-图片转base64
  5. CSS字体样式有哪些
  6. 制造服务行业需要项目管理软件吗?
  7. python获取时间————前一天后一天前一小时后一小时前一分钟后一分钟前一秒后一秒
  8. linux 信号灯的PV操作
  9. 为知笔记中MathJax中使用多行公式
  10. android jni 发送短信,android5.0以上版本如何直接发送短信?