利用平台进行构建

报告问题 查看源代码

Bazel 为建模平台工具链提供复杂的支持。要将此功能与实际项目集成,代码所有者、规则维护人员和核心 Bazel 开发者之间需要认真协作。

本页总结了平台的用途,并展示了如何使用这些平台构建应用。

tl;dr::Bazel 的平台和工具链 API 可用,但在所有语言规则、select() 和其他旧版引用都更新之前,并非在所有位置都可用。这项工作仍在进行中。最终所有 build 都将基于平台。 请阅读下文,了解您的 build 的适用区域。

如需查看更正式的文档,请参阅:

背景

我们引入了平台工具链,以对软件项目如何针对不同机器进行标准化处理,以及如何使用合适的语言工具进行构建。

这是相对新近的 Bazel 添加功能。语言维护者已经在以不兼容的临时方式开展这项工作,这一观察结果深受启发例如,C++ 规则使用 --cpu--crosstool_top 来设置 build 的目标 CPU 和 C++ 工具链。它们都没有正确建模“平台”。历史上一直尝试这样做的做法会导致构建尴尬且不准确。这些标志也不控制 Java 编译,因为 Java 编译已经通过 --java_toolchain 演化了自己的独立接口。

Bazel 适用于大型、多语言和多平台项目。这就需要对这些概念的更多原则性支持,包括鼓励语言和项目互操作性的明确 API。这正是这些新 API 的用途

迁移

平台和工具链 API 仅在项目实际使用它们时才有效。这并非易事,因为项目的规则逻辑、工具链、依赖项和 select() 必须支持它们。这需要执行谨慎的迁移序列,以确保所有项目及其依赖项正常运行。

例如,Bazel 的 C++ 规则支持平台。但 Apple 规则则不然。您的 C++ 项目可能不关注 Apple。但其他系统可能。因此,针对所有 C++ build 全局启用平台尚不安全。

本页面的其余部分介绍了此迁移序列,以及项目适合的方式和时机。

目标

当所有项目都使用以下格式构建时,Bazel 的平台迁移完成:

bazel build //:myproject --platforms=//:myplatform

这意味着:

  1. 项目使用的规则可以从 //:myplatform 推断出正确的工具链。
  2. 项目的依赖项使用的规则可以从 //:myplatform 推断出正确的工具链。
  3. 您所依赖的项目支持 //:myplatform您的项目支持旧版 API(如 --crosstool_top)。
  4. //:myplatform 引用了 CPUOS 的 [通用声明][通用平台声明]{: .external},以及支持自动跨项目兼容性的其他通用概念。
  5. 所有相关项目的 select() 均理解 //:myplatform 隐含的机器属性。
  6. //:myplatform 在一个清晰且可重复使用的位置进行定义:如果平台对您的项目而言是唯一的,则可以在项目的代码库中定义;否则,可能使用此平台的所有项目都可以在某个位置找到。

实现此目标后,旧 API 将被移除。然后,这将是项目选择平台和工具链的标准方式。

我是否应该使用平台?

如果您只想构建或交叉编译项目,则应遵循该项目的官方文档。

如果您是项目、语言或工具链维护人员,您最终会希望支持新 API。是等到全球迁移完成还是提前选择启用,取决于您的具体价值 / 费用需求:

价值

  • 您可以 select() 或选择您关注的属性上的工具链,而不是 --cpu 等硬编码标志。例如,多个 CPU 可以支持同一指令集
  • 更正确的 build。如果您在上例中使用 --cpu 执行 select(),然后添加支持同一指令集的新 CPU,则 select() 无法识别新 CPU。但平台上的 select() 仍然是准确的。
  • 更简单的用户体验。所有项目都理解:--platforms=//:myplatform。无需在命令行上使用多个特定于语言的标志。
  • 语言设计更简单。所有语言共用一个通用 API,用于定义工具链、使用工具链以及为平台选择正确的工具链。
  • 如果目标与目标平台不兼容,则可以在构建和测试阶段跳过目标。

费用

  • 尚不支持平台的依赖项目可能不会自动与您的项目配合使用。
  • 使它们正常运行可能需要额外的临时维护
  • 新旧 API 共存需要更仔细的用户指导,以避免混淆。
  • 常见属性(如 OSCPU)的规范定义仍在不断完善中,可能需要额外的初始贡献。
  • 特定于语言的工具链的规范定义仍在发展中,可能需要额外的初始贡献。

API 审核

platform 是一系列 constraint_value 目标

platform(
    name = "myplatform",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:arm",
    ],
)

constraint_value 是一种机器属性。同一“种类”的值将划分到共同的 constraint_setting 下:

constraint_setting(name = "os")
constraint_value(
    name = "linux",
    constraint_setting = ":os",
)
constraint_value(
    name = "mac",
    constraint_setting = ":os",
)

toolchainStarlark 规则。其属性声明了语言的工具(例如 compiler = "//mytoolchain:custom_gcc")。其提供程序会将此信息传递给需要使用这些工具进行构建的规则。

工具链会声明它们可以作为目标平台 (target_compatible_with = ["@platforms//os:linux"]) 的机器的 constraint_value 以及可以运行其工具的机器 (exec_compatible_with = ["@platforms//os:mac"])。

构建 $ bazel build //:myproject --platforms=//:myplatform 时,Bazel 会自动选择可以在构建机器上运行的工具链,并为 //:myplatform 构建二进制文件。这称为工具链解析。

您可以使用 register_toolchainsWORKSPACE 中注册这组可用工具链,也可以使用 --extra_toolchains 在命令行中注册。

如需深入了解,请点击此处

状态

当前平台支持因语言而异。Bazel 的所有主要规则都迁移到了平台但此过程需要一些时间。这主要有三个原因:

  1. 必须更新规则逻辑,以便从新的工具链 API (ctx.toolchains) 获取工具信息,并停止读取 --cpu--crosstool_top 等旧版设置。这种方法相对简单。

  2. 工具链维护人员必须定义工具链,并使其可供用户访问(在 GitHub 代码库和 WORKSPACE 条目中)。这在技术上很简单,但必须巧妙地组织起来,以保持轻松的用户体验。

    平台定义也是必要的(除非您是针对运行 Bazel 的同一台机器进行构建)。通常,项目应定义自己的平台。

  3. 您必须迁移现有项目。此外,还必须迁移 select()过渡。这是最大的挑战。这对于多语言项目而言尤其具有挑战性(如果所有语言都无法读取 --platforms,则可能会失败)。

如果要设计新的规则集,您必须从一开始就支持平台。这会自动使您的规则与其他规则和项目兼容,并且随着平台 API 变得越来越普及,规则的价值也会越来越大。

通用平台属性

各项目通用的平台属性(如 OSCPU)应在标准的集中位置声明。这有利于实现跨项目和跨语言兼容性。

例如,如果 MyAppconstraint_value @myapp//cpus:arm 上有 select(),而 SomeCommonLib@commonlib//constraints:arm 上具有 select(),它们会使用不兼容的条件触发其“arm”模式。

全局通用属性在 @platforms 代码库中声明(因此,上述示例的规范标签为 @platforms//cpu:arm)。语言通用属性应在其相应语言的代码库中声明。

默认平台

通常,项目所有者应定义明确的平台,以描述他们要针对哪些类型的机器构建应用。然后,您可以使用 --platforms 触发这些事件。

如果未设置 --platforms,则 Bazel 默认使用表示本地构建机器的 platform。该代码是在 @local_config_platform//:host 中自动生成的,因此无需明确定义。它会将本地计算机的 OSCPU@platforms 中声明的 constraint_value 进行映射。

C++

在设置 --incompatible_enable_cc_toolchain_resolution 时,Bazel 的 C++ 规则使用平台选择工具链 (#7260)。

这意味着您可以用以下代码配置 C++ 项目:

bazel build //:my_cpp_project --platforms=//:myplatform

而不是旧版:

bazel build //:my_cpp_project` --cpu=... --crosstool_top=...  --compiler=...

如果您的项目是纯 C++ 项目,非 C++ 项目并不依赖于该项目,那么只要您的 select转换兼容,您就可以安全地使用平台。如需更多指导,请参阅 #7260配置 C++ 工具链

此模式默认处于停用状态。这是因为 Apple 项目仍然使用 --cpu--crosstool_top 配置 C++ 依赖项(示例)。这取决于迁移到平台的 Apple 规则。

Java

Bazel 的 Java 规则使用平台。

这取代了旧版标志 --java_toolchain--host_java_toolchain--javabase--host_javabase

如需了解如何使用配置标志,请参阅 Bazel 和 Java 手册。 如需了解详情,请参阅设计文档

如果您仍在使用旧版标志,请按照问题 7849 中的迁移流程操作。

Android

在您设置 --incompatible_enable_android_toolchain_resolution 时,Bazel 的 Android 规则会使用平台来选择工具链。

默认情况下,此功能处于停用状态。不过,迁移工作正在进行之中。

苹果

Bazel 的 Apple 规则尚不支持用于选择 Apple 工具链的平台。

它们也不支持启用平台的 C++ 依赖项,因为它们使用旧版 --crosstool_top 来设置 C++ 工具链。在迁移之前,您可以将 Apple 项目与支持平台功能的 C++ 与平台映射示例)混合使用。

其他语言

如果要为新语言设计规则,请使用平台来选择语言的工具链。有关详细的演示,请参阅工具链文档

select()

项目可以在 constraint_value 目标上执行 select(),但不能完成平台。这是有意为之,以便 select() 支持尽可能多的机器。除非有更具体的理由,否则包含特定于 ARM 的源代码的库应支持所有ARM 提供支持的机器。

如需选择一个或多个 constraint_value,请使用以下命令:

config_setting(
    name = "is_arm",
    constraint_values = [
        "@platforms//cpu:arm",
    ],
)

这相当于传统上在 --cpu 上进行选择:

config_setting(
    name = "is_arm",
    values = {
        "cpu": "arm",
    },
)

如需了解更多详情,请点击此处

--cpu--crosstool_top 等平台上的 select 无法识别 --platforms。将项目迁移到平台时,您必须将其转换为 constraint_values,或使用平台映射在迁移窗口期内支持这两种样式。

转场效果

Starlark 过渡会更改 build 图的各个部分。如果您的项目使用设置了 --cpu--crossstool_top 或其他旧版标志的转换,则读取 --platforms 的规则将不会看到这些更改。

将项目迁移到平台时,您必须将 return { "//command_line_option:cpu": "arm" } 等更改转换为 return { "//command_line_option:platforms": "//:my_arm_platform" },或使用平台映射在迁移期内支持这两种样式。

当前如何使用平台

如果您只想构建或交叉编译项目,则应遵循该项目的官方文档。与平台集成的方式和时机以及集成的价值由语言和项目维护人员决定。

如果您是项目、语言或工具链维护人员,并且您的 build 默认不使用平台,您有三种选择(除了等待全局迁移之外):

  1. 切换您项目所用语言对应的“使用平台”标志(如果有),然后进行所需的测试,看看您关注的项目是否有效。

  2. 如果您关注的项目仍依赖于 --cpu--crosstool_top 等旧版标志,请将这些标志与 --platforms 结合使用:

    bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...
    

    这会有一些维护费用(您必须手动确保设置一致)。但是,这在没有反行为过渡的情况下应该有效。

  3. 编写平台映射以支持这两种样式,将--cpu样式设置映射到相应的平台,反之亦然。

平台映射

平台映射是一种临时 API,可让平台驱动的逻辑和旧版驱动的逻辑共存于同一 build 中,直到前者的废弃期结束。

平台映射是 platform() 与一组相应的旧版标志(或反过来)的映射。例如:

platforms:
  # Maps "--platforms=//platforms:ios" to "--cpu=ios_x86_64 --apple_platform_type=ios".
  //platforms:ios
    --cpu=ios_x86_64
    --apple_platform_type=ios

flags:
  # Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --cpu=ios_x86_64
  --apple_platform_type=ios
    //platforms:ios

  # Maps "--cpu=darwin --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin
  --apple_platform_type=macos
    //platforms:macos

Bazel 使用此方式来保证所有设置(包括基于平台的设置和旧版设置)在整个构建过程中始终如一地应用,包括通过过渡

默认情况下,Bazel 会从工作区根目录中的 platform_mappings 文件中读取映射。您还可以设置 --platform_mappings=//:my_custom_mapping

如需了解完整的详细信息,请参阅此处

问题

如需常规支持或有关迁移时间表的问题,请联系 bazel-discuss@googlegroups.com 或相应规则的所有者。

如需讨论平台/工具链 API 的设计和演变,请与 bazel-dev@googlegroups.com 联系。

另请参阅