利用平台进行构建

Bazel 对建模 平台工具链提供了完善的支持。要将此功能与实际项目集成,需要代码所有者、规则维护人员和核心 Bazel 开发者之间的密切合作。

本页总结了平台的目的,并展示了如何使用平台进行构建。

要点: Bazel 的平台和工具链 API 可用,但在所有语言规则、select() 和其他旧版引用更新之前,这些 API 无法在所有位置正常运行。这项工作仍在进行中。最终,所有构建都将基于平台。 请阅读下文,了解您的构建适合哪些平台。

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

背景

引入了平台工具链,目的是为了标准化软件 项目如何定位不同的机器,以及如何使用正确的语言工具进行构建。

这是 Bazel 中相对较新的功能。其灵感来源于语言维护人员已经以临时、不兼容的方式执行此操作。例如,C++ 规则使用 --cpu--crosstool_top 来设置构建的目标 CPU 和 C++ 工具链。这两者都无法正确模拟“平台”。过去尝试这样做会导致构建笨拙且不准确。 这些标志也不会控制 Java 编译,Java 编译通过 --java_toolchain 演变出了自己的独立接口。

Bazel 适用于大型、多语言、多平台项目。这需要对这些概念提供更规范的支持,包括鼓励语言和项目互操作性的清晰 API。这些新 API 就是为此而设计的。

迁移

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

例如,Bazel 的 C++ 规则支持平台。但 Apple 规则 不支持。您的 C++ 项目可能不关心 Apple。 但其他项目可能会关心。因此,目前尚不适合为所有 C++ 构建全局启用平台。

本页的其余部分介绍了此迁移顺序,以及您的项目如何以及何时适合迁移。

目标

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

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

这意味着:

  1. 您的项目使用的规则可以从 //:myplatform 推断出正确的工具链。
  2. 项目的依赖项使用的规则可以从 //:myplatform 推断出正确的工具链。
  3. 依赖于您的项目支持 //:myplatform或者 您的 项目支持旧版 API(如 --crosstool_top)。
  4. //:myplatform 引用了 [通用声明][Common Platform Declaration]{: .external} 的 CPUOS 和其他支持自动跨项目 兼容性的通用概念。
  5. 所有相关项目的 select()s 都了解//:myplatform暗含的机器属性。
  6. //:myplatform 在清晰、可重用的位置定义:如果平台是您的项目独有的,则在项目的代码库中定义;否则,在所有可能使用此平台的项目都可以找到的位置定义。

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

我是否应该使用平台?

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

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

价值

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

费用

  • 尚不支持平台的依赖项目可能无法自动与您的项目协同工作。
  • 为了让它们协同工作,可能需要额外的临时维护
  • 新旧 API 共存需要更仔细的用户指南,以避免混淆。
  • 常见属性的规范定义仍在不断发展,可能需要额外的初始贡献。OSCPU
  • 特定于语言的工具链的规范定义仍在不断发展,可能需要额外的初始贡献。

API 审核

platformconstraint_value 目标集合:

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

A 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")。其 提供程序 将 此信息传递给需要使用这些工具进行构建的规则。

工具链声明了它们可以 定位 的机器的 constraint_value (target_compatible_with = ["@platforms//os:linux"]),以及它们的工具可以在其上 运行 的机器 (exec_compatible_with = ["@platforms//os:mac"])。

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

可用工具链集可以在 WORKSPACE 中使用 register_toolchains 注册,也可以在 命令行中使用 --extra_toolchains 注册。

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

状态

目前,不同语言对平台的支持情况各不相同。Bazel 的所有主要规则都在迁移到平台。但此过程需要时间。这主要是因为以下三个原因:

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

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

    平台定义也是必需的(除非您为 Bazel 运行的同一机器构建)。一般来说,项目应定义自己的平台。

  3. 必须迁移现有项目。select()转换 也必须 迁移。这是最大的挑战。对于多语言项目来说,这尤其具有挑战性(如果所有语言都无法读取 --platforms,则可能会失败)。

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

常见平台属性

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

例如,如果 MyApp 具有对 constraint_value @myapp//cpus:armselect(),而 SomeCommonLib 具有对 @commonlib//constraints:armselect(),则这些 select() 会使用不兼容的 条件触发其“arm”模式。

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

默认平台

一般来说,项目所有者应定义明确的 平台来描述他们想要构建的 机器类型。然后,使用 --platforms 触发这些平台。

如果未设置 --platforms,Bazel 会默认使用表示 本地构建机器的 platform。这是在 @local_config_platform//:host 自动生成的,因此无需显式定义。它将本地机器的 OSCPU 映射到 constraint_values 中声明的 @platforms

C++

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

这意味着您可以使用以下内容配置 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 规则使用平台来选择工具链。

此模式默认处于停用状态。但迁移工作进展顺利。

Apple

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

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

其他语言

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

select()

项目可以对 constraint_value目标进行select(),但不能对完整 平台进行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 转换会更改 构建图的部分标志。如果您的项目使用的转换设置了 --cpu--crossstool_top 或其他旧版标志,则读取 --platforms 的规则将看不到这些更改。

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

如何立即使用平台

如果您只是想构建或交叉编译项目,则应遵循项目的官方文档。语言和项目维护人员可以自行决定如何以及何时与平台集成,以及集成可以提供哪些价值。

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

  1. 为项目的语言(如果有 )启用“使用平台”标志,并执行所需的任何测试,以查看您关心的项目是否正常运行。

  2. 如果您关心的项目仍然依赖于旧版标志(如 --cpu--crosstool_top),请将这些标志与 --platforms 一起使用:

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

    这会产生一些维护费用(您必须手动确保设置匹配)。但在没有恶意 转换的情况下,这应该可以正常运行。

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

平台映射

平台映射是一种临时 API,可让平台驱动的逻辑和旧版驱动的逻辑在后者的废弃窗口期间共存于同一构建中。

平台映射是将 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

另请参阅