标签

标签 是目标的标识符。完整规范形式的典型标签 如下所示:

@@myrepo//my/app/main:app_binary

标签的第一部分是代码库名称 @@myrepo。双@ 语法表示这是规范代码库 名称,在工作区内是唯一的。无论标签出现在哪个上下文中,带有规范代码库名称的标签都能明确标识目标 。

规范代码库名称通常是看起来像 @@rules_java++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 是软件包中目标的名称。规则的名称 是规则声明中 name 属性的值,该声明位于 BUILD 文件中;文件的名称是其相对于包含 BUILD 文件的目录的路径名。

目标名称必须完全由以下字符集中的字符组成:azAZ09,以及标点符号 !%-@^_"#$&'()*-+,;<=>?[]{|}~/.

文件名必须是普通形式的相对路径名,这意味着它们既不能以斜杠开头或结尾(例如,/foofoo/ 是禁止使用),也不能包含多个连续的斜杠作为路径分隔符(例如,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 会强制执行以下操作:

  • 软件包名称中允许使用的字符包括小写字母 az、大写字母 AZ、数字 09、字符 ! \"#$%&'()*+,-.;<=>?@[]^_`{|}(是的,其中包含一个空格字符!),当然还有正斜杠 /(因为它是目录分隔符)。
  • 软件包名称不得以正斜杠字符 / 开头或结尾。
  • 软件包名称不得包含子字符串 //。这没有意义,因为对应的目录路径是什么?
  • 软件包名称不得包含子字符串 /.//..//.../ 等。之所以进行此强制执行,是为了避免在逻辑软件包名称和物理目录名称之间进行转换时出现混淆,因为路径字符串中的点字符具有语义含义。

在实际应用中:

  • 对于目录结构对其模块 系统(例如 Java)非常重要的语言,选择在该语言中 有效的标识符作为目录名称非常重要。例如,不要以数字开头,并避免使用特殊字符,尤其是下划线和连字符。
  • 虽然 Bazel 支持工作区根软件包中的目标(例如 //:foo),但最好将该软件包留空,以便所有有意义的软件包 都具有描述性名称。

规则

规则指定了输入和输出之间的关系,以及 构建输出的步骤。规则可以有多种不同的 类型(有时称为规则类),它们会生成已编译的 可执行文件和库、测试可执行文件以及构建百科全书中介绍的其他受支持的 输出。

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 规则,规则名称决定了构建生成的可执行文件的名称。

这种针对目标的有向无环图称为目标图构建依赖关系图,是 Bazel 查询工具运行的领域。

目标 BUILD 文件