为远程执行调整 Bazel 规则

本页面面向编写自定义构建和测试规则的 Bazel 用户 他们希望了解在 远程执行环境中 Bazel 规则的要求。

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

本页面在提及不同 环境类型或 平台时使用以下术语:

  • 宿主平台 - Bazel 在此平台上运行。
  • 执行平台 - Bazel 操作在此平台上运行。
  • 目标平台 - 构建输出(和一些操作)在此平台上运行。

概览

为远程执行配置 Bazel 构建时,您必须遵循本页面中所述的 准则,以确保构建在远程执行时不会出错 。这是因为远程执行的性质,即:

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

  • 多样化的执行环境。本地构建配置并不总是 适用于远程执行环境。

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

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

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

目前,Bazel 构建和测试规则的工具链规则适用于 ScalaRustGo, 并且正在为其他语言和工具(例如 bash)开发新的工具链规则。 如果您的规则使用的工具没有工具链规则,请考虑 创建工具链规则

管理隐式依赖项

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

例如,当 Bazel 指示有状态编译器在本地构建 foo 时, 编译器会保留对 foo 的构建输出的引用。然后,当 Bazel 指示编译器构建依赖于 foobar 时,如果未在 BUILD 文件中明确声明该依赖项以包含在编译器调用中,则只要同一编译器实例执行这两个操作(这对于本地执行来说很常见),该操作就会成功执行。但是,由于在远程执行场景中,每个构建操作都会执行单独的编译器实例,因此编译器状态和 bar 对 foo 的隐式依赖项将会丢失,并且构建将会失败。

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

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

通常,在宿主平台上构建的二进制文件无法在任意远程执行平台上安全执行,因为依赖项可能不匹配。例如,Bazel 附带的 SingleJar 二进制文件以宿主平台为目标。 但是,对于远程执行,SingleJar 必须作为构建代码过程的一部分进行编译 以便以远程执行平台为目标。(请参阅 目标选择逻辑。)

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

  • 提供或外部引用该工具的源代码,以便可以为远程执行平台构建该工具。

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

管理配置样式的 WORKSPACE 规则

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

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

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

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

  • 符号链接到本地工具或工件。通过 WORKSPACE 规则创建的指向安装在宿主平台上的工具或库的符号链接会导致构建在远程执行平台上失败,因为 Bazel 将无法找到它们。请改为使用标准构建操作创建符号链接,以便可以从 Bazel 的 runfiles 树访问符号链接的工具和库。请勿使用 repository_ctx.symlink 将目标文件符号链接到外部代码库目录之外。

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

为了帮助查找潜在的非封闭行为,您可以使用 Workspace 规则日志

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

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

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

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

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

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