标签是目标的标识符。完整规范化形式的典型标签如下所示:
@@myrepo//my/app/main:app_binary
标签的第一部分是代码库名称 @@myrepo
。双@
语法表示这是一个规范代码库名称,在工作区中是唯一的。具有规范代码库名称的标签可以明确地标识目标,无论它们出现在哪个上下文中。
规范的代码库名称通常是一个奇怪的字符串,类似于 @@rules_java~7.1.0~toolchains~local_jdk
。较为常见的是具有明显的代码库名称的标签,如下所示:
@myrepo//my/app/main:app_binary
唯一的区别是代码库名称以一个 @
(而不是两个)为前缀。它指的是表观名称为 myrepo
的代码库,此名称可能会因此标签所在的上下文而异。
在典型情况下,标签表示使用它的同一仓库,仓库名称部分可以省略。因此,在 @@myrepo
中,第一个标签通常写为
//my/app/main:app_binary
该标签的第二部分是非限定软件包名称 my/app/main
,即相对于代码库根目录的软件包路径。代码库名称和非限定软件包名称共同构成完全限定软件包名称 @@myrepo//my/app/main
。如果标签引用的是其使用的同一软件包,则可以省略软件包名称(以及可选的冒号)。因此,在 @@myrepo//my/app/main
中,此标签可以采用以下任一方式编写:
app_binary
:app_binary
按照惯例,文件会省略英文冒号,但会按照规则保留英文冒号,但这并不重要。
冒号后面的标签部分 app_binary
是非限定目标名称。当它与软件包路径的最后一个组成部分匹配时,该组成部分以及冒号可以省略。因此,以下两个标签是等效的:
//my/app/lib
//my/app/lib:lib
软件包子目录中的文件目标名称是文件相对于软件包根目录(包含 BUILD
文件的目录)的路径。因此,此文件位于代码库的 my/app/main/testdata
子目录中:
//my/app/main:testdata/input.txt
//my/app
和 @@some_repo//my/app
等字符串具有两种含义,具体取决于它们的使用情境:当 Bazel 需要标签时,它们分别表示 //my/app:app
和 @@some_repo//my/app:app
。但是,当 Bazel 需要软件包时(例如在 package_group
规范中),它们会引用包含该标签的软件包。
BUILD
文件中的一个常见错误是使用 //my/app
引用软件包,或引用软件包中的所有目标,这并非如此。请注意,它等同于 //my/app:app
,因此它会在当前代码库的 my/app
软件包中命名 app
目标。
不过,在 package_group
的规范或 .bzl
文件中建议使用 //my/app
来引用软件包,因为它明确地说明软件包名称是绝对的且位于工作区的顶层目录中。
相对标签不能用于引用其他软件包中的目标;在这种情况下,必须始终指定代码库标识符和软件包名称。例如,如果源代码树同时包含软件包 my/app
和软件包 my/app/testdata
(这两个目录都有自己的 BUILD
文件),则后一个软件包会包含一个名为 testdepot.zip
的文件。您可以通过以下两种方式在 //my/app:BUILD
中引用此文件(一种错误,一种正确):
错误 - testdata
是不同的软件包,因此您无法使用相对路径
testdata/testdepot.zip
正确 - 请参阅 testdata
及其完整路径
//my/app/testdata:testdepot.zip
以 @@//
开头的标签是对主代码库的引用,即使从外部仓库中也会正常工作。因此,从外部代码库引用时,@@//a/b/c
与 //a/b/c
不同。前者是指主仓库,而后者是指在外部仓库本身中查找 //a/b/c
。在主代码库中编写引用主代码库中的目标的规则时,此功能尤为重要,并且将用于外部仓库。
如需了解引用目标的不同方式,请参阅目标模式。
标签的词汇规范
标签语法不建议使用对 shell 具有特殊含义的元字符。这有助于避免无意中引出问题,并可让您更轻松地构建操作标签的工具和脚本(例如 Bazel 查询语言)。
下面详细说明了允许使用的目标名称。
目标名称 - package-name:target-name
target-name
是软件包中目标的名称。规则的名称是 BUILD
文件中规则声明中的 name
属性的值;文件的名称是相对于包含 BUILD
文件的目录的路径名。
目标名称必须完全由从 a
-z
、A
-Z
、0
-9
集合中提取的字符以及标点符号 !%-@^_"#$&'()*-+,;<=>?[]{|}~/.
组成。
文件名必须是普通形式的相对路径名,这意味着它们不得以斜杠开头或结尾(例如禁止使用 /foo
和 foo/
),也不得包含多个连续斜杠作为路径分隔符(例如 foo//bar
)。同样,禁止高级引用 (..
) 和当前目录引用 (./
)。
错误 - 请勿使用 ..
引用其他软件包中的文件
正确 - 使用 //package-name:filename
虽然通常在文件目标的名称中使用 /
,但请避免在规则名称中使用 /
。特别是在使用标签的简写形式时,可能会让读者感到困惑。标签 //foo/bar/wiz
始终是 //foo/bar/wiz:wiz
的简写形式,即使没有此类软件包 foo/bar/wiz
,它也绝不会引用 //foo:bar/wiz
,即使该目标存在也是如此。
不过,在某些情况下,使用斜杠非常方便,有时甚至很有必要。例如,某些规则的名称必须与其主源文件的名称一致,该文件可能位于软件包的子目录中。
软件包名称 - //package-name:target-name
软件包的名称是指包含其 BUILD
文件的目录的名称,相对于所在代码库的顶级目录。例如:my/app
。
在技术层面,Bazel 会强制执行以下政策:
- 软件包名称中允许使用的字符包括小写字母
a
到z
、大写字母A
到Z
、数字0
到9
、字符! \"#$%&'()*+,-.;<=>?@[]^_`{|}
(是的,其中包含空格字符!),当然还有正斜杠/
(因为它是目录分隔符)。 - 软件包名称不得以正斜杠字符
/
开头或结尾。 - 软件包名称不得包含子字符串
//
。这没有意义 - 相应的目录路径是什么? - 软件包名称不得包含子字符串
/./
、/../
或/.../
等。根据路径字符串中点字符的语义含义,此强制执行是为了避免在逻辑软件包名称与物理目录名称之间进行转换时出现混淆。
从实际的层面来说:
- 如果语言的目录结构对其模块系统很重要(例如 Java),请务必选择可作为该语言的有效标识符的目录名称。例如,不要以前导数字开头,并且避免使用特殊字符,尤其是下划线和连字符。
- 尽管 Bazel 支持工作区根软件包中的目标(例如
//:foo
),但最好将该软件包留空,以便所有有意义的软件包都具有描述性名称。
规则
规则指定了输入和输出之间的关系以及构建输出的步骤。规则可以是多种不同类型中的一种(有时称为规则类),这些类型会生成经过编译的可执行文件和库、测试可执行文件以及其他受支持的输出,如 Build Encyclopedia 中所述。
BUILD
文件通过调用规则来声明目标。
在以下示例中,我们会看到使用 cc_binary
规则的目标 my_app
声明。
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
deps = [
"//absl/base",
"//absl/strings",
],
)
每个规则调用都有一个 name
属性(必须是有效的目标名称),用于在 BUILD
文件的软件包中声明一个目标。
每条规则都有一组属性;适用于给定规则的属性,以及每个属性的重要性和语义都是规则种类的函数;如需查看规则及其对应属性的列表,请参阅构建百科全书。每个属性都有一个名称和类型。属性可以具有的一些常见类型包括整数、标签、标签列表、字符串、字符串列表、输出标签、输出标签列表。但不需要在每条规则中指定所有属性。因此,属性构成了从键(名称)到可选的类型化值的字典。
许多规则中存在的 srcs
属性具有类型“标签列表”;其值(如果存在)是标签列表,每个标签都是该规则的输入的目标名称。
在某些情况下,规则种类的名称可能有点任意,而规则生成的文件的名称更有趣,这在 genrule 中也是如此。如需了解详情,请参阅一般规则:genrule。
在其他情况下,名称非常重要:例如,对于 *_binary
和 *_test
规则,规则名称决定了 build 生成的可执行文件的名称。
这个目标上的有向无环图称为目标图或构建依赖关系图,是运行 Bazel 查询工具的网域。
目标 | build 文件 |