本页面面向编写自定义 build 和测试规则的 Bazel 用户,旨在帮助他们了解远程执行环境中的 Bazel 规则要求。
远程执行功能可让 Bazel 在单独的平台(例如数据中心)上执行操作。Bazel 使用 gRPC 协议进行远程执行。您可以尝试使用 bazel-buildfarm 进行远程执行,这是一个旨在提供分布式远程执行平台的开源项目。
本页面在提及不同环境类型或平台时使用以下术语:
- 宿主平台 - Bazel 运行的平台。
- 执行平台 - Bazel 操作的运行位置。
- 目标平台 - 构建输出(和某些操作)的运行位置。
概览
为远程执行配置 Bazel 构建时,您必须遵循本页面中所述的准则,以确保构建能够顺利地远程执行。这是由于远程执行的性质所致,具体如下:
隔离的 build 操作。构建工具不会保留状态,并且依赖关系不会在它们之间泄露。
多种多样的执行环境。本地 build 配置并不总是适合远程执行环境。
本页介绍了为远程执行实现自定义 Bazel build 和测试规则时可能会出现的问题,以及如何避免这些问题。它涵盖以下主题:
通过工具链规则调用 build 工具
Bazel 工具链规则是一种配置提供程序,用于告知 build 规则要使用哪些 build 工具(例如编译器和链接器),以及如何使用规则创建者定义的参数来配置这些工具。借助工具链规则,构建规则和测试规则可以以可预测的预配置方式调用与远程执行兼容的构建工具。例如,使用工具链规则,而不是通过 PATH
、JAVA_HOME
或其他可能未在远程执行环境中设置为等效值(或根本未设置)的本地变量来调用 build 工具。
目前,Bazel 构建和测试规则的工具链规则适用于 Scala、Rust 和 Go,并且正在为其他语言和工具(例如 bash)开发新的工具链规则。如果您的规则所使用的工具没有对应的工具链规则,请考虑创建工具链规则。
管理隐式依赖项
如果构建工具可以跨构建操作访问依赖项,那么当远程执行这些操作时,这些操作将会失败,因为每个远程构建操作都是与其他操作分开执行的。某些 build 工具会在 build 操作之间保留状态,并访问未明确包含在工具调用中的依赖项,这会导致远程执行的 build 操作失败。
例如,当 Bazel 指示有状态编译器在本地构建 foo 时,编译器会保留对 foo 的 build 输出的引用。然后,当 Bazel 指示编译器构建依赖于 foo 的 bar 时,如果在 BUILD 文件中未明确声明该依赖项以将其纳入编译器调用中,只要同一编译器实例执行这两个操作(这在本地执行中很常见),该操作就会成功执行。不过,由于在远程执行场景中,每个 build 操作都会执行单独的编译器实例,因此编译器状态和 bar 对 foo 的隐式依赖关系会丢失,并且 build 会失败。
为了帮助检测和消除这些依赖项问题,Bazel 0.14.1 提供了本地 Docker 沙盒,该沙盒对依赖项的限制与远程执行相同。通过识别和解决与依赖项相关的构建错误,使用沙盒为远程执行准备构建。如需了解详情,请参阅使用 Docker 沙盒排查 Bazel 远程执行问题。
管理依赖于平台的二进制文件
通常,在宿主平台上构建的二进制文件无法在任意远程执行平台上安全执行,因为可能存在不匹配的依赖项。例如,随 Bazel 提供的 SingleJar 二进制文件以宿主平台为目标平台。 不过,对于远程执行,SingleJar 必须在构建代码的过程中进行编译,以便它以远程执行平台为目标平台。(请参阅目标选择逻辑。)
请勿将 build 所需的 build 工具的二进制文件与源代码一起交付,除非您确信这些二进制文件可在您的执行平台上安全运行。请改为执行以下操作之一:
交付或外部引用该工具的源代码,以便可以针对远程执行平台构建该工具。
如果该工具足够稳定,请将其预安装到远程执行环境(例如工具链容器)中,并使用工具链规则在 build 中运行该工具。
管理配置样式的工作区规则
Bazel 的 WORKSPACE
规则可用于探测构建所需的工具和库的宿主平台,对于本地构建,该平台也是 Bazel 的执行平台。如果 build 明确依赖于本地 build 工具和制品,那么在远程执行期间,如果远程执行平台与宿主平台不完全相同,build 将会失败。
由 WORKSPACE
规则执行的以下操作与远程执行不兼容:
构建二进制文件。在
WORKSPACE
规则中执行编译操作会导致二进制文件与远程执行平台不兼容(如果与主机平台不同)。安装
pip
软件包。通过WORKSPACE
规则安装的pip
软件包要求其依赖项预安装在宿主平台上。如果专门为宿主平台构建的此类软件包与远程执行平台不同,则会与远程执行平台不兼容。与本地工具或制品建立符号链接。通过
WORKSPACE
规则创建的指向主机平台上安装的工具或库的符号链接会导致构建在远程执行平台上失败,因为 Bazel 将无法找到这些工具或库。请改用标准 build 操作创建符号链接,以便从 Bazel 的runfiles
树访问符号链接的工具和库。请勿使用repository_ctx.symlink
将目标文件符号链接到外部代码库目录之外。使宿主平台发生突变。避免在 Bazel
runfiles
树之外创建文件、创建环境变量以及执行类似操作,因为这些操作在远程执行平台上可能会出现意外行为。
为了帮助您找到潜在的非密封行为,您可以使用 Workspace 规则日志。
如果外部依赖项执行依赖于宿主平台的特定操作,您应将这些操作拆分到 WORKSPACE
和 build 规则之间,如下所示:
平台检查和依赖项枚举。这些操作可通过
WORKSPACE
规则在本地安全执行,该规则可以检查已安装的库、下载必须构建的软件包,并准备编译所需制品。对于远程执行,这些规则还必须支持使用预先检查的制品来提供通常在主机平台检查期间获得的信息。预先检查过的制品可让 Bazel 像描述本地依赖项一样描述依赖项。为此,请使用条件语句或--override_repository
标志。生成或编译特定于目标的制品和平台突变。 这些操作必须通过常规 build 规则执行。生成外部依赖项的目标特定制品的 action 必须在 build 期间执行。
为了更轻松地生成用于远程执行的预检查制品,您可以使用 WORKSPACE
规则来发出生成的文件。您可以在每个新的执行环境(例如每个工具链容器内)中运行这些规则,并将远程执行 build 的输出检入源代码库以供参考。
例如,对于 TensorFlow 的 cuda
和 python
规则,WORKSPACE
规则会生成以下 BUILD files
。对于本地执行,系统会使用通过检查宿主环境生成的文件。
对于远程执行,环境变量上的条件语句允许规则使用已签入代码库的文件。
BUILD
文件声明了可在本地和远程运行的 genrules
,并执行之前通过 repository_ctx.symlink
完成的必要处理,如此处所示。