为远程执行调整 Bazel 规则

报告问题 查看源代码

本页面适用于编写自定义构建和测试规则并希望了解远程执行上下文中 Bazel 规则要求的 Bazel 用户。

远程执行让 Bazel 能够在单独的平台(例如数据中心)上执行操作。Bazel 使用 gRPC 协议进行远程执行。您可以尝试使用 bazel-buildfarm 进行远程执行,这是一个开源项目,旨在提供分布式远程执行平台。

在指代不同的环境类型或平台时,本页面使用以下术语:

  • 托管平台 - 运行 Bazel 的位置。
  • 执行平台 - 运行 Bazel 操作的地方。
  • 目标平台 - 构建输出(和某些操作)的运行位置。

概览

在配置 Bazel 构建以进行远程执行时,您必须遵循本页面中所述的指南,以确保构建远程执行时不会出现错误。这取决于远程执行的性质,即:

  • 隔离构建操作。构建工具不会保留状态,并且依赖项之间不会泄露。

  • 丰富多样的执行环境。本地构建配置并不总是适合远程执行环境。

本页面介绍为远程执行实现自定义 Bazel 构建和测试规则时可能出现的问题,以及如何避免这些问题。其中包括以下主题:

通过工具链规则调用构建工具

Bazel 工具链规则是一个配置提供程序,用于告知构建规则要使用哪些构建工具(例如编译器和链接器),以及如何使用规则创建者定义的参数对其进行配置。借助工具链规则,构建和测试规则能够以与远程执行兼容的可预测、预配置方式调用构建工具。例如,使用工具链规则,而不是通过 PATHJAVA_HOME 或其他在远程执行环境中可能未设置为等效值(或根本未设为)的局部变量来调用构建工具。

目前,适用于 ScalaRustGo 的 Bazel 构建和测试规则的工具链规则,其他语言和工具(如 bash)的新工具链规则正在制定中。如果您的规则使用的工具不存在工具链规则,请考虑创建工具链规则

管理隐式依赖项

如果构建工具可以跨构建操作访问依赖项,则这些操作在远程执行时会失败,因为每个远程构建操作都是与其他操作分开执行的。一些构建工具会在工具调用中未明确包含的构建操作和访问依赖项之间保留状态,这会导致远程执行的构建操作失败。

例如,当 Bazel 指示有状态编译器在本地构建 foo 时,编译器会保留对 foo 的构建输出的引用。然后,当 Bazel 指示编译器构建依赖于 foobar 时,如果未在 BUILD 文件中明确声明该依赖项以将其纳入到编译器调用中,只要针对这两项操作都执行了同一编译器实例(这通常是本地执行的典型情况),该操作便会成功执行。不过,在远程执行场景中,每个构建操作都会执行一个单独的编译器实例,因此编译器状态和 barfoo 的隐式依赖项将会丢失,并且构建将失败。

为了帮助检测并消除这些依赖项问题,Bazel 0.14.1 提供了本地 Docker 沙盒,该沙盒对依赖项的限制与远程执行相同。使用沙盒识别和解决与依赖项相关的构建错误,为远程执行准备 build。如需了解详情,请参阅对使用 Docker Sandbox 的 Bazel Remote Execution 进行问题排查

管理依赖于平台的二进制文件

通常,由于依赖项可能不匹配,在主机平台上构建的二进制文件无法在任意远程执行平台上安全地执行。例如,与 Bazel 一起提供的 SingleJar 二进制文件以主机平台为目标。 不过,对于远程执行,必须在构建代码的过程中对 SingleJar 进行编译,使其以远程执行平台为目标。(请参阅目标选择逻辑。)

请勿随源代码一起提供 build 所需的构建工具的二进制文件,除非您确定它们会在您的执行平台中安全运行。请改为执行以下操作之一:

  • 发布或外部引用该工具的源代码,以便针对远程执行平台构建源代码。

  • 如果该工具足够稳定,请将其预安装到远程执行环境(例如,工具链容器)中,然后使用工具链规则在 build 中运行该工具。

管理配置式 WORKSPACE 规则

Bazel 的 WORKSPACE 规则可用于探测主机平台,以获取构建所需的工具和库。对于本地构建,这也是 Bazel 的执行平台。如果构建明确依赖于本地构建工具和工件,并且远程执行平台与主机平台不同,则构建会在远程执行期间失败。

WORKSPACE 规则执行的以下操作与远程执行不兼容:

  • 构建二进制文件 -执行 WORKSPACE 规则中的编译操作会导致二进制文件与远程执行平台不兼容(如果与主机平台不同)。

  • 安装 pip 软件包。通过 WORKSPACE 规则安装的 pip 软件包要求在主机平台上预安装其依赖项。专门为主机平台构建的此类软件包如果与主机平台不同,它将与远程执行平台不兼容。

  • 与本地工具或工件建立符号链接。通过 WORKSPACE 规则创建的主机平台上安装的工具或库的符号链接会导致构建在远程执行平台上失败,因为 Bazel 无法找到这些工具或库。而是应该使用标准构建操作来创建符号链接,以便从 Bazel 的 runfiles 树访问符号链接的工具和库。请勿使用 repository_ctx.symlink 对外部代码库目录之外的目标文件进行符号链接。

  • 更改主机平台。请避免在 Bazel runfiles 树之外创建文件、创建环境变量以及执行类似操作,因为它们可能会在远程执行平台上出现意外行为。

为了帮助您发现潜在的非封闭行为,您可以使用工作区规则日志

如果外部依赖项根据主机平台执行特定操作,您应按如下方式在 WORKSPACE 和构建规则之间拆分这些操作:

  • 平台检查和依赖项枚举。这些操作可通过 WORKSPACE 规则在本地执行,这些规则可以检查已安装的库、下载必须构建的软件包,以及准备编译所需的工件。对于远程执行,这些规则还必须支持使用预先检查的工件来提供在主机平台检查期间通常可以获得的信息。借助预先选中的工件,Bazel 能够像描述本地依赖项一样描述依赖项。为此,请使用条件语句或 --override_repository 标志。

  • 生成或编译特定于目标的工件和平台变更。 这些操作必须通过常规构建规则执行。为外部依赖项生成目标专用工件的操作必须在构建期间执行。

为了更轻松地生成预检查的工件以进行远程执行,您可以使用 WORKSPACE 规则来发出生成的文件。您可以在每个新的执行环境中(例如在每个工具链容器内)运行这些规则,并在源代码库中检查远程执行构建的输出以供参考。

例如,对于 Tensorflow 针对 cudapython 的规则,WORKSPACE 规则会生成以下 BUILD files。对于本地执行,将使用通过检查主机环境生成的文件。对于远程执行,环境变量上的条件语句允许规则使用已签入代码库的文件。

BUILD 文件声明可以在本地和远程运行的 genrules,并执行之前通过 repository_ctx.symlink 完成的必要处理(如此处所示)。