Bazel 构建配置

构建配置是一组设置(比如 cpu = "arm64"),其中每项设置都会为 Bazel 知道的已知配置变量分配一个配置值。这些值会影响 Bazel 的构建方式

build 配置可以看作是将变量名称(字符串键)映射到配置值(布尔值、整数、字符串、字符串列表、标签或标签列表)的不可变字典

它们仅在 Bazel 分析阶段才有意义,并且通过其内容的唯一哈希在内部进行标识。它们没有标签,不能直接显示在 Bazel 构建文件或 Starlark 代码中。

每个 build 配置实例在 Bazel output_base 下都有自己的输出目录,即:

${output_base}/execroot/<workspace_name>/bazel-out/<config_dir>/bin/

在 build 配置上下文中评估的目标的 build 工件存储在其中。<config_dir> 值取决于 build 配置的内容。其计算是一个 Bazel 实现细节,实际上可能会因 Bazel 版本而异。

在最简单的情况下,<config_dir> 将类似于 ${cpu}-${compilation_mode}(例如 k8-fastbuild1aarch64-dbg),但复杂性可能会变得非常高。

下面是一些小型示例 Bazel 项目的 build 配置的一些 config_dir 值示例:

  • k8-fastbuild
  • k8-fastbuild-ST-6f89e1bee3ea
  • k8-fastbuild-ST-bd2abcc18995
  • k8-fastbuild-ST-ec22d3eeb595
  • k8-opt
  • k8-opt-exec-2B5CBBC6
  • k8-opt-exec-2B5CBBC6-ST-5934bb4653e4

请注意,上面的十六进制值与配置的 ID(即其唯一哈希值)无关。

通用 build 配置

默认情况下,Bazel 管理着两种配置:

  • 默认配置,也称为目标配置,有歧义,因为它对应于必须在最终“目标”系统上运行的构建工件。

    默认情况下,其设置与主机系统匹配,但可以使用命令行选项进行更改(请参阅下文)。

  • 主机配置,对应于必须在运行 Bazel 的机器上运行的代码。此项包含的设置与默认配置相同,交叉编译(例如,使用 --cpu=<name> 进行编译)除外。

此外,还有:

  • exec 配置,对应于在构建操作期间运行的代码。在实践中,这包含与主机相同的设置,但使用远程构建器时除外。

Bazel 项目还可以定义额外的配置来适应某些用例,例如在一次构建调用中针对多个目标架构进行交叉编译。

原生配置变量

bazel build 命令支持大量用于更改原生配置变量(也称为原生设置)的命令行选项,例如:

  • --cpu=<name>--host_cpu=<name>

    分别更改为默认配置或主机配置生成的代码的 CPU 架构。例如:

    bazel build --cpu=arm64 --host_cpu=x86_64 ...
    
  • -c <mode>--compilation_mode=<mode>--host_compilation_mode=<mode>

    其中,<mode> 是“fastbuild”“dbg”或“opt”之一,用于描述一组编译器链接器标志。例如:

    bazel build -c opt --host_compilation_mode=dbg ...
    
  • --crosstool=<file_path>--host_crosstool=<file_path>

    指向一个 CROSSTOOL 文件,该文件用于向 Bazel 描述自定义 C++ 工具链,以便编译和关联目标或主机二进制文件。

大量原生配置变量(和相关的命令行标志)。例如,Bazel 5.2.0 支持超过 340 种(包括 83 种实验性)。

自定义配置标志(已废弃)

您可以定义自定义配置变量(Bazel 文档中名称混乱的自定义配置标志),这些变量将在命令行中使用“--define <name>=<value>”设置,其中 <name> 是任意 build 配置变量名称,<value> 记录为任意字符串(系统绝不会解释该值)。

您可以使用特殊构造在 BUILD.bazel 文件中测试这些名称,例如:

# This represents a named configuration condition which will be True whenever
# `--define foo=bar` is used in the `bazel build` command-line.
config_setting(
  name = "is_foo_bar",
  define_flags = {
    "foo": "bar",
  }
)

用户定义的 build 设置

为了克服自定义 --define 标志的限制,Bazel 项目现在可以定义符合以下条件的 build 配置变量:

  • 使用标签(提供工作区和软件包范围,以及可用性检查)命名。

  • 可以具有特定类型,类型包括布尔值、整数、字符串、字符串列表、标签和标签列表。

  • 始终提供默认值。

您可以在命令行中使用“--<label>=<value>”进行相关设置,如下所示:

# Set the build setting named enable_logs defined in
# $WORKSPACE/my_settings/BUILD.bazel to True.
#
# Note that the double-slash must follow the double caret directly
# without spaces between them.
bazel build --//my_settings:enable_logs=True …

它与必须出现在文件 $PROJECT/my_settings/BUILD.bazel 中的定义匹配,例如:

# From my_settings/BUILD.bazel
load("@bazel_skylib//rules/common_settings.bzl", "bool_flag")

bool_flag(
  name = "enable_logs",
  default_value = False
)

构建设置还可以在外部仓库中定义,如下所示:

# Set the build setting named enable_logs defined in the config/BUILD.bazel
# file of the @project_settings external repository.
bazel build --@project_settings//config:enable_logs=True …

存储用户配置值

设置配置值的参数可存储在项目工作区目录顶部的 .bazelrc 文件中,而不是直接通过命令行传递,如下所示:

# from $PROJECT/.bazelrc
build --host_copt=-O3 -s
build --copt=-O1 -g -Wall
build --//my_settings:enable_logs=True

调用 bazel build <target> 时,.bazelrc 提供的选项会自动包含在内,因此生成的命令等同于:

bazel build --host_copy="-O3 -s" --copt="-O1 -g -Wall" --//my_settings:enable_logs=True <target>

您还可以使用自定义 name 对多个定义进行分组,如下所示:

# from $PROJECT/.bazelrc
build:conf_x64 --cpu=x86_64
build:conf_x64 --copt=-mavx2

build:conf_arm64 --cpu=aarch64
build:conf_arm64 --copt=-marmv8.1-a+simd

然后使用 --config=<name> 选择一个组中的所有选项,如下所示:

bazel build --config=conf_x64 <target>

这相当于:

bazel build --cpu=x86_64 --copt=-mavx2 <target>

您也可以将 --config 与其他选项结合使用,例如:

bazel build --config=conf_arm64 --copy="-DENABLE_ASSERTS=1" ...

===

重要提示:尽管名称是 --config,但它不会创建新的 build 配置。这只是对用户的命令行选项进行分组的便捷方式。每个 <name> 都直接以递归方式扩展为其

规范元素,然后被 Bazel 完全忽略。

配置转换(快速概览)

除了标准目标、主机和执行配置之外,Bazel 还支持使用转换的概念创建额外的构建配置。

过渡是一项高级功能,我们在这里不会详述,但请务必了解:

  • 从概念上来讲,build 配置只是将配置变量名称映射到配置值的字典。E.g.:

    {
       "copt": "-O2 -g",
       "cpu": "x86_64",
       "compiler": "gcc",
       "//my_settings:enable_logs": True,
       …
    }
    
  • 过渡提供了一个函数,该函数将当前 build 配置作为输入,并返回新的 build 配置作为输出。

  • 转换函数只能修改字典中的值,也就是说,它无法在输入中添加或移除键。

  • 转换函数会在分析阶段(稍后介绍)下在特定条件下调用,但无法由用户通过命令行或 .bazelrc 文件直接触发。

检查 build 配置

运行 Bazel 构建命令后,您可以查看需要的所有配置。使用 bazel config 命令输出所有配置及其 config_dir 值的列表,例如:

$ bazel clean
$ bazel build //src:program
$ bazel config
Available configurations:
20222e2616387f00ae3814d79f82a650829d1aea962d558edda5cf54c459f4a2 k8-fastbuild
30f5a27e1bd054836d97b800bdfe61c6d1cffccdf6b82c9963e94020e3852026 k8-fastbuild-ST-bd2abcc18995
34e198ac81dafe34b82cf62466cc84f5b1af6ef8dce44b9283b3152a6635f64a k8-fastbuild-ST-6f89e1bee3ea
aaa26904110821b8e0f87aabfffcfb8e5003a5620aee74de3d7ddea9a654c0f6 k8-opt (host)
d6b1a93e6619d845f29ee752e338ec2636e7fbf4c88e50168602cc21f5ae6f13 k8-opt-exec-2B5CBBC6-ST-5934bb4653e4 (exec)

注意:配置信息会一直累积,直到下一次 bazel 清理完毕。

使用 bazel config <id> 将配置转储到 stdout,以检查其值。警告:输出内容很长。

$ bazel config 20222e2616387f00ae3814d79f82a650829d1aea962d558edda5cf54c459f4a2 | wc -l
Loading:
INFO: Displaying config with id 20222e2616387f00ae3814d79f82a650829d1aea962d558edda5cf54c459f4a2
436

此处为实际输出的链接。

请注意,实际上,每个配置都是一组字典,每个字典涵盖一个特定主题。这些 fragment 称为 fragment,只有几个 fragment 可以直接从 Starlark 访问:

$ bazel config 20222e2616387f00ae3814d79f82a650829d1aea962d558edda5cf54c459f4a2 | grep FragmentOptions
FragmentOptions com.google.devtools.build.lib.analysis.PlatformOptions {
FragmentOptions com.google.devtools.build.lib.analysis.ShellConfiguration$Options {
FragmentOptions com.google.devtools.build.lib.analysis.config.CoreOptions {
FragmentOptions com.google.devtools.build.lib.analysis.test.CoverageConfiguration$CoverageOptions {
FragmentOptions com.google.devtools.build.lib.analysis.test.TestConfiguration$TestOptions {
FragmentOptions com.google.devtools.build.lib.bazel.rules.BazelRuleClassProvider$StrictActionEnvOptions {
FragmentOptions com.google.devtools.build.lib.bazel.rules.python.BazelPythonConfiguration$Options {
FragmentOptions com.google.devtools.build.lib.rules.android.AndroidConfiguration$Options {

  1. k8 是现在的 x86_64 CPU 架构的旧命名惯例。