Bazel 标签

Bazel 标签是对 BUILD.bazel 文件中定义的源文件和项的字符串引用。其一般格式为:

@<repository_name>//<package_name>:<target_name>

其中:

  • @<repository_name>// 表示已命名的 Bazel 工作区的目录。

    为方便起见,对于当前工作区(包含当前 BUILD.bazel 文件的那个工作区),此命令可简写为 //。另请注意,即使在外部代码库中使用,@// 也用于指定项目的根工作区。

  • <package_name> 是软件包的目录路径,相对于工作区目录。例如,在标签 //src:main.cc 或 //src/lib:foo 中,软件包名称分别为 src 和 src/lib。

    此值可以为空,例如 //:BUILD.bazel 指向当前工作区顶级目录中的 build 文件。

  • 对于源文件,<target_name> 是相对于其父软件包目录的文件路径,可能包含子目录部分。例如,对于 //src:main.cc 或 //src:extra/extra.cc,目标名称分别为 main.cc 和 extra/extra.cc。

  • 对于其他项,<target_name> 对应于 BUILD.bazel 文件中定义的项(build 制品、build 设置、配置条件等)。

    按照惯例,其 name 属性不应包含目录分隔符,除非在极少数情况下,以避免与来源混淆。

    对于来自其他 build 系统(例如 GN 使用“Targets”“Configs”“Toolchains”和“Pools”来指定不同的事物)的开发者来说,这可能会造成混淆。

还支持使用标签的缩短表达式:

  • 如果标签以代码库名称开头,但不包含冒号,则表示这是一个软件包路径,指向具有相同名称的项。例如,//src/foo 等同于 //src/foo:foo。

  • 如果标签以冒号开头,则表示相对于当前软件包的名称。例如,src/foo/BUILD.bazel 中显示的“:bar”和“:extra/bar.cc”分别等同于 //src/foo:bar 和 //src/foo:extra/bar.cc。

  • 如果标签没有代码库名称且不含英文冒号,则始终是相对于当前软件包的名称,即使它包含目录分隔符也是如此。例如,src/foo/BUILD.bazel 中的“bar/bar.cc”始终是指 //src/foo:bar/bar.cc。

    请注意,这与 //src/foo/bar:bar.cc 不同

相对标签和软件包所有权

由于每个源文件只能属于一个软件包,因此相对标签可能无效。例如,在如下所示的项目中:

/home/user/project/
    WORKSPACE.bazel
    src/
        BUILD.bazel
        main.cc
        extra/
            extra.cc
        lib/
            BUILD.bazel
            foo.cc
            foo.h

foo.cc 文件属于软件包 src/lib,因此其标签 必须为 //src/lib:foo.cc。

在 src/BUILD.bazel 中使用 src:lib/foo.cc 等标签是错误的:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "lib/foo.cc",       # Error: Label '//src:lib/foo.cc' is invalid because 'src/lib' is a subpackage
    "lib/foo.h"         # Error: Label '//src:lib/foo.h' is invalid because 'src/lib' is a subpackage
    "main.cc",
  ],
)

从其他软件包访问源文件

默认情况下,给定软件包的源文件无法从其他软件包访问,并且相对软件包标签无效,如下所示:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "lib:foo.cc",    # Error: invalid label 'lib:foo.cc': absolute label must begin with '@' or '//'
    "lib:foo.h"      # Error: invalid label 'lib:foo.h': absolute label must begin with '@' or '//'
    "main.cc",
  ],
)

即使使用正确的绝对标签,也会发生错误:

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "//src/lib:foo.cc",    # Error: no such target '//src/lib:foo.cc': target 'foo.h' not declared in package 'src/lib'
    "//src/lib:foo.h"      # Error: no such target '//src/lib:foo.h': target 'foo.h' not declared in package 'src/lib'
    "main.cc",
  ],
)

可以通过 export_files() 授予对跨软件包边界的文件进行直接访问的权限:

# From src/lib/BUILD.bazel
export_files([
  "foo.cc" ,
  "foo.h" ,
])

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "//src/lib:foo.cc",    # OK
    "//src/lib:foo.h"      # OK
    "main.cc",
  ],
)

来自其他软件包的目标访问权限

在 BUILD.bazel 文件中定义的非源文件的带标签项不需要导出,但其 visibility 属性必须允许在自己的软件包之外使用:

# From src/lib/BUILD.bazel
cc_library(
  name = " lib" ,
  srcs = [ " foo.cc"  ],
  hdrs = [ " foo.h"  ],
  visibility = [ " //visibility:public" ],  # Anyone can reference this directly!
)

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "main.cc",
  ],
  deps = [ "lib" ],   # OK!
)

默认情况下,项仅对同一软件包中的其他项可见。 您可以使用 package() 指令更改软件包中定义的所有项的默认可见性,从而更改此设置:

# From src/lib/BUILD.bazel

# Ensure that all items defined in this file are visible to anyone
package(default_visibility = ["//visibility:public"])

cc_library(
  name = " lib" ,
  srcs = [ "foo.cc" ],
  hdrs = [ "foo.h" ],
)

# From src/BUILD.bazel
cc_binary(
  name = "program",
  srcs = [
    "extra/extra.cc",
    "main.cc",
  ],
  deps = [ "lib" ],   # OK!
)

有关虚拟软件包的警告

避免在项目中创建具有以下名称的顶级目录:

  • conditions
  • command_line_option
  • external
  • visibility

因为 Bazel 在 BUILD.bazel 文件中的标签内使用了许多硬编码的“虚拟软件包”。例如:

  //visibility:public
  //conditions:default
  //command_line_option:copt

external 的情况略有不同:它不会出现在 BUILD.bazel 文件中,但会在内部用于管理外部代码库。当用作项目目录时,这会使 Bazel 感到困惑

规范代码库名称

自 Bazel 6.0 起,标签中的代码库名称也可以以 @@ 开头。

启用可选的 BzlMod 功能后,这些标签会用作外部代码库的替代但唯一标签名称,这在项目中使用了复杂的传递依赖项树时非常重要。

例如,@@com_acme_anvil.1.0.3 可能是工作区目录(在项目自己的 BUILD.bazel 文件中由 @anvil 标识,在外部代码库中(例如在 @foo//:BUILD.bazel 内)由 @acme_anvil 标识)的规范名称。这三个标签都将指向同一目录的内容。

规范的代码库名称不会显示在 BUILD.bazel 文件中,但会在分析阶段(执行查看标签值的 Starlark 函数时)或查看 Bazel 查询结果时显示。