平台

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。
报告问题 查看源代码

Bazel 可以使用许多不同版本的构建工具(例如链接器和编译器)在各种硬件、操作系统和系统配置上构建和测试代码。为了帮助管理这种复杂性,Bazel 有一个“限制”和“平台”的概念。约束是指构建或生产环境可能存在差异的维度,例如 CPU 架构、是否存在 GPU 或系统安装的编译器版本。平台是这些限制条件的命名选项集合,表示在某些环境中可用的特定资源。

将环境建模为平台有助于 Bazel 自动为构建操作选择适当的工具链。平台也可以与 config_setting 规则结合使用,以写入可配置的属性

Bazel 认识到平台可能会提供的三种角色:

  • 主机 - Bazel 自身运行的平台。
  • Execution - 一个构建工具,在该平台上,构建工具执行构建操作以生成中间和最终输出。
  • 目标 - 最终输出在其中驻留和执行的平台。

Bazel 支持以下有关平台的构建场景:

  • 单平台 build(默认)- 主机平台、执行平台和目标平台相同。例如,在 Ubuntu 上构建在 Intel x64 CPU 上运行的 Linux 可执行文件。

  • 交叉编译 build - 主机平台和执行平台相同,但目标平台不同。例如,在 macOS Pro 上运行的 macOS 上构建 iOS 应用。

  • 多平台构建 - 托管平台、执行平台和目标平台都是不同的。

定义限制条件和平台

平台可使用 BUILD 文件中的 constraint_settingconstraint_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 架构上运行 glibc 版本 2.25 的任何 Linux 操作系统。(如需详细了解 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

跳过不兼容的目标

针对特定目标平台进行构建时,通常会希望跳过永远无法在该平台上运行的目标。例如,在使用 //... 的 Linux 计算机上进行构建时,您的 Windows 设备驱动程序可能会生成大量编译器错误。使用 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 进行构建,并且与所有其他 Windows 不兼容。不兼容是传递性的。间接依赖于不兼容目标的所有目标本身均被视为不兼容。

目标何时被跳过?

当目标不兼容,并包含在目标模式扩展中时,系统会跳过目标。例如,以下两个调用会跳过在目标模式扩展中找到的所有不兼容目标。

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

如果使用 --expand_test_suites 在命令行中指定 test_suitetest_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"],
    }),
)

上述可解读为:

  1. 针对 macOS 时,该目标没有任何限制。
  2. 以 Linux 为目标平台时,该目标没有任何限制。
  3. 否则,目标将具有 @platforms//:incompatible 限制条件。由于 @platforms//:incompatible 不属于任何平台,因此该目标会被视为不兼容。

如需提高约束条件的可读性,请使用 skylibselects.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 检测不兼容的目标

您可以使用 bazel cqueryStarlark 输出格式中的 IncompatiblePlatformProvider 区分不兼容的目标与兼容的目标。

这可用于过滤掉不兼容的目标。以下示例只会输出兼容目标的标签。系统不会输出不兼容的目标。

$ cat example.cquery

def format(target):
  if "IncompatiblePlatformProvider" not in providers(target):
    return target.label
  return ""


$ bazel cquery //... --output=starlark --starlark:file=example.cquery

已知问题

不兼容的目标会忽略公开范围限制