目录
目录结构
一个代码库是一个目录,设为代码根目录'/',根目录下可以有多个子目录,子目录中可以继续嵌套子目录。
在根目录下要有一个workspace文件,根目录和每个代码子目录下得有BUILD文件。 这种有BUILD的文件目录称作一个包。
核心概念 workspace、packages、targets
一个工作空间,多个包,多个目标。
workspace 工作空间
工作空间就是代码根目录,目录里必须有个WORKSPACE文件。可以是空文件,或者在文件里包含一个外部依赖的信息。 根目录下同样会有一些软链接,链接到编译产物。
如果子目录中还有workspace文件,那么bazel会忽略此目录。
同时文件名可以是WORKSPACE.bazel,且优先级高很多。
Repositories 代码仓库
代码以多个仓库的形式组织。包含WORKSPACE文件的目录是主仓库根目录,也叫@。其他外部仓库是按一定规则定义在WORKSPACE文件中。
WORKSPACE规则:
外部范仓库有自己的WORKSPACE文件,这都被bazel忽略。
Packages 包
包是代码仓库里组织代码的基本单元。 包是一些相关文件的集合加上一些特定的依赖规则。包是在workspace目录下的一个包含有BUILD或者BUILD.bazel的子目录。
src/my/app/BUILD
src/my/app/app.cc
src/my/app/data/input.txt
src/my/app/tests/BUILD
src/my/app/tests/test.cc
my/app和my/app/tests是包。my/app/data不是包,是my/app包的子目录。
Targets 目标
包是容器,其中的元素叫目标。目标分为三类:files(文件),rules(规则), package groups(包组)。
files(文件)进一步分为Source files(源文件)和derived files(派生文件)。源文件是人写的,派生文件是构建工具根据源文件和特定规则生成的。
rules(规则)指定了一组输入文件和输出文件之间的关系,包括必要的步骤。规则的输出只能是生成的文件,但是规则的输出既可以是源文件,也能是生成的文件。这就导致一个规则的输出成为另一个规则的输入,并允许构建成规则链。
规则的输入是源文件还是派生文件根本不重要,重要的是文件内容。对于一些复杂的文件,可以用规则生成,也不影响依赖此文件的其他规则。
规则的输入也可能包含其他规则。具体的关系可能很复杂,且和语言和规则相关。比如一个c++的库A的规则可能依赖另一个c++库B的规则 作为输入。这种依赖达到的效果是在编译器B的头文件对A可用;链接期B的符号对A可用;运行期B的数据对A可用。
不变的是所有规则产生的文件只属于规则自己所在的包,不可能给其他包产生文件。一个规则的输入来自其他的包的情况也不常见。
Package Groups是一组包的集合,目的是用来限制可访问范围。用package_group
函数来定义。有两个属性,一个是一系列的包名,一个是自己的名字。唯一允许的引用方式是通过规则的visibility属性,或者package 函数的default_visibility
属性。包组不产生或者依赖文件。
Labels(标签)
所有的目标属于一个包。目标的名字叫label,简洁的标签如下:
@myrepo//my/app/main:app_binary
如果标签是指向的目标在自己的仓库,就可能省略@myrepo,写成
//my/app/main:app_binary
每个标签由两部分构成,包名(/my/app/main)和目标名(app_binary)。一个标签唯一确定一个目标。冒号后边的名称也能省略,省略后目标名就和包名的最后一部分相同。如下两个标签是等价的。
//my/app
//my/app:app
标签总是以//开头,包名从不以//开头。因此,my/app是一个包,他包含//my/app这个标签。
在BUIL文件中,包名和冒号都可以被省略。因此,包my/app中的BUILD文件,//my/app:BUILD中的标签可以如下:
//my/app:app
//my/app
:app
app
此包下的文件的标签也能简写为:
generate.cc
testdata/input.txt
但是其他包里,或者命令里,这些目标都要写全名://my/app:genereate.cc.相对标签不能用在其他包里,也不能用相对标签表示子包中的标签。
以@//开头的标签代表着主仓库,即使在外部仓库中这么写。因此在外部仓库中@//a/b/c和//a/b/c是不同的。前者代表主仓库,后者代表外部仓库自己。所以在主仓库中写规则的时候要注意,此主仓库将来会不会作为另一个仓库的外部仓库使用。
Rules(规则)
规则指定了输入和输出之间的关系和构建输出的步骤。规则有很多种,能编译生成可执行文件和库,测试用的可执行文件。
规则有一个name(名字),可以任意指定,但是最好指定为生成文件的名字。名字在*_binary和*_test规则中就是可执行文件的名字。
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
deps = [
"//absl/base",
"//absl/strings",
],
)
每个规则有一组属性。上述srcs就是规则cc_binary的属性名,属性值是一个由任意个源文件标签构成的的列表。deps也是属性名,值是一个标签列表。srcs和outs最终形成以目标为结点的有向无环图,称为目标图或者目标依赖图。
BUILD files(BUILD文件)
上文对包,目标,标签,构建依赖图进行了抽象的描述,这一节我们看一下定义一个包的具体语法。
每个包有一个BUILD文件,内容是一个简单的程序。 是用命令式的语言Starlark写成的。被解析为一系列有序的语句。通常来说顺序不重要,但是变量必须要使用前定义。大多数情况下只用关心声明了哪些规则,当规则执行完成时生产了哪些值。例如cc_library这个规则执行后会在目标图中生成一个新的目标,这个目标后续就能用标签来引用了。为了鼓励代码和数据清晰分离,BUILD文件中不能用函数定义。
加载插件
load("//foo/bar:file.bzl", "some_library")
load("//foo/bar:file.bzl", library_alias = "some_library")
load(":my_rules.bzl", "some_rule", nice_alias = "some_other_rule")
规则分类
*_binary
*_test
*_library
外部依赖
在WORKSPACE文件里添加如下代码。
依赖外部bazel项目
local_repository
, git_repository
or http_archive
local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
)
依赖非bazel项目
需要给这个项目写个BUILD文件,这样就能在BUILD文件中用了:@coworkers_project//:some-lib
new_local_repository(
name = "coworkers_project",
path = "/path/to/coworkers-project",
build_file = "coworker.BUILD",
)
build_file specifies a BUILD file to overlay on the existing project, for example:
cc_library(
name = "some-lib",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
依赖外部包
主要用于Maven,rules_jvm_external
.
获取依赖
bazel build时会获取。此外bazel fetch, bazel sync也能获取。
影子依赖
myproject/WORKSPACE
workspace(name = "myproject")
local_repository(
name = "A",
path = "../A",
)
local_repository(
name = "B",
path = "../B",
)
A/WORKSPACE
workspace(name = "A")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner",
urls = ["https://github.com/testrunner/v1.zip"],
sha256 = "...",
)
B/WORKSPACE
workspace(name = "B")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner",
urls = ["https://github.com/testrunner/v2.zip"],
sha256 = "..."
)
Both dependencies A and B depend on testrunner, but they depend on different versions of testrunner. There is no reason for these test runners to not peacefully coexist within myproject, however they will clash with each other since they have the same name. To declare both dependencies, update myproject/WORKSPACE:
workspace(name = "myproject")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "testrunner-v1",
urls = ["https://github.com/testrunner/v1.zip"],
sha256 = "..."
)
http_archive(
name = "testrunner-v2",
urls = ["https://github.com/testrunner/v2.zip"],
sha256 = "..."
)
local_repository(
name = "A",
path = "../A",
repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
name = "B",
path = "../B",
repo_mapping = {"@testrunner" : "@testrunner-v2"}
)
在命令行中覆盖仓库
覆盖@foo
--override_repository=foo=/path/to/local/foo
离线构建
--nofetch
本文翻译自官网:
https://docs.bazel.build/versions/4.1.0/build-ref.html
https://docs.bazel.build/versions/4.1.0/external.html#shadowing-dependencies