Bazel 可以使用许多不同版本的构建工具(例如 链接器和编译器)在各种硬件、操作系统和 系统配置上构建和测试代码。为了帮助管理这种复杂性,Bazel 引入了 “限制条件”和“平台”的概念。限制条件是指构建或 生产环境可能不同的维度,例如 CPU 架构、是否存在 GPU 或系统安装的编译器的版本。平台是指针对这些限制条件的一组 命名选择,表示某些环境中可用的特定 资源。
将环境建模为平台有助于 Bazel 自动为构建操作选择 合适的 工具链 。平台还可以与 config_setting 规则结合使用,以编写可配置的属性。
Bazel 识别平台可能充当的以下三种角色:
- 主机 - Bazel 本身运行的平台。
- 执行 - 构建工具在其中执行构建操作以 生成中间输出和最终输出的平台。
- 目标 - 最终输出所在并执行的平台。
Bazel 支持以下与平台相关的构建场景:
单平台构建 (默认)- 主机平台、执行平台和目标平台 相同。例如,在运行于 Intel x64 CPU 的 Ubuntu 上构建 Linux 可执行文件。
交叉编译构建 - 主机平台和执行平台相同,但 目标平台不同。例如,在运行于 MacBook Pro 的 macOS 上构建 iOS 应用 。
多平台构建 - 主机平台、执行平台和目标平台都 不同。
定义限制条件和平台
平台可能选择的空间是通过在 BUILD 文件中使用
constraint_setting 和
constraint_value 规则来定义的。constraint_setting 会创建一个新维度,而
constraint_value 会为给定维度创建一个新值;它们共同
有效地定义了枚举及其可能的值。例如,以下
BUILD文件代码段引入了系统 glibc 版本的限制条件
并提供了两个可能的值。
constraint_setting(name = "glibc_version")
constraint_value(
name = "glibc_2_25",
constraint_setting = ":glibc_version",
)
constraint_value(
name = "glibc_2_26",
constraint_setting = ":glibc_version",
)
限制条件及其值可以在工作区中的不同软件包中定义。它们通过标签引用,并受常规可见性 控制的约束。如果可见性允许,您可以为现有限制条件设置定义自己的值,从而扩展该设置 。
platform 规则引入了一个新平台,其中包含
某些限制条件值的选择。以下代码会创建一个名为 linux_x86 的平台,并说明它描述的是在 x86_64 架构上运行 Linux 操作系统且 glibc 版本为 2.25 的任何环境。(如需详细了解 Bazel 的内置限制条件,请参阅下文。)
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
":glibc_2_25",
],
)
通常有用的限制条件和平台
为了保持生态系统的一致性,Bazel 团队维护了一个代码库,其中包含 最常用的 CPU 架构和操作系统的 限制条件定义。这些定义都位于 https://github.com/bazelbuild/platforms 中。
Bazel 附带以下特殊平台定义:
@local_config_platform//:host。这是自动检测到的主机平台值 -
表示 Bazel 运行的系统自动检测到的平台。
为构建指定平台
您可以使用以下 命令行标志为构建指定主机平台和目标平台:
--host_platform- 默认为@bazel_tools//platforms:host_platform--platforms- 默认为@bazel_tools//platforms:target_platform
跳过不兼容的目标
在为特定目标平台构建时,通常需要跳过
永远无法在该平台上运行的目标。例如,您的 Windows 设备
驱动程序在
Linux 机器上使用 //... 构建时,可能会生成大量编译器错误。使用
target_compatible_with
属性告知 Bazel 您的代码具有哪些目标平台限制条件。
此属性最简单的用法是将目标限制为单个平台。
对于不满足所有
限制条件的任何平台,都不会构建该目标。以下示例将 win_driver_lib.cc 限制为 64 位
Windows。
cc_library(
name = "win_driver_lib",
srcs = ["win_driver_lib.cc"],
target_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
],
)
:win_driver_lib 仅与使用 64 位 Windows 进行构建兼容,
与其他所有内容都不兼容。不兼容是可传递的。任何以传递方式依赖于不兼容目标的其他目标本身也被视为
不兼容。
何时跳过目标?
当目标被视为不兼容,并且作为目标模式扩展的一部分包含在 构建中时,系统会跳过这些目标。例如,以下两个 调用会跳过在目标模式扩展中找到的任何不兼容目标。
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
如果使用
--expand_test_suites 在命令行中指定了 test_suite,则系统也会跳过 test_suite 中的不兼容测试。换句话说,命令行中的 test_suite 目标的行为类似于 :all 和
...。使用 --noexpand_test_suites 可防止扩展,并导致
test_suite 目标也变得不兼容。
在命令行中明确指定不兼容的目标会导致 错误消息和构建失败。
$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully
更具表现力的限制条件
为了更灵活地表达限制条件,请使用任何平台都不满足的
@platforms//:incompatible
constraint_value
。
将 select() 与
@platforms//:incompatible 结合使用,以表达更复杂的限制。例如,使用它来实现基本的 OR 逻辑。以下代码将库标记为
与 macOS 和 Linux 兼容,但与其他平台不兼容。
cc_library(
name = "unixish_lib",
srcs = ["unixish_lib.cc"],
target_compatible_with = select({
"@platforms//os:osx": [],
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
上述内容可以解释如下:
- 以 macOS 为目标时,目标没有限制条件。
- 以 Linux 为目标时,目标没有限制条件。
- 否则,目标具有
@platforms//:incompatible限制条件。由于@platforms//:incompatible不属于任何平台,因此目标被视为 不兼容。
为了使您的限制条件更具可读性,请使用
skylib's
selects.with_or()。
您可以使用类似的方式表达反向兼容性。以下示例 描述了一个与除 ARM 之外的所有内容都兼容的库。
cc_library(
name = "non_arm_lib",
srcs = ["non_arm_lib.cc"],
target_compatible_with = select({
"@platforms//cpu:arm": ["@platforms//:incompatible"],
"//conditions:default": [],
],
)
使用 bazel cquery 检测不兼容的目标
您可以使用
IncompatiblePlatformProvider
在 bazel cquery's Starlark 输出
格式中区分不兼容的目标和兼容的目标。
这可用于过滤掉不兼容的目标。以下示例将 仅输出兼容目标的标签。不兼容的目标不会 输出。
$ cat example.cquery
def format(target):
if "IncompatiblePlatformProvider" not in providers(target):
return target.label
return ""
$ bazel cquery //... --output=starlark --starlark:file=example.cquery