外部依赖项概览

Bazel 支持 外部依赖项,即构建中使用的源文件(文本和二进制文件),这些文件并非来自您的工作区。例如,它们可以是托管在 GitHub 代码库中的 规则集、Maven 工件,也可以是本地机器上当前工作区之外的 目录。

从 Bazel 6.0 开始,您可以通过两种方式使用 Bazel 管理外部依赖项: 传统的、以代码库为中心的 WORKSPACE 系统,以及 较新的、以模块为中心的 MODULE.bazel 系统(代号为 Bzlmod, 并通过 --enable_bzlmod 标志启用)。这两个系统可以 一起使用,但 Bzlmod 将在未来的 Bazel 版本中取代 WORKSPACE 系统,请查看 Bzlmod 迁移指南,了解如何 迁移。

本文档将先介绍 Bazel 中外部依赖项管理的相关概念 ,然后再详细介绍这两个系统。

概念

代码库

包含 WORKSPACEWORKSPACE.bazel 文件的目录,其中包含要在 Bazel 构建中使用的源文件。通常简称为代码库

主代码库

运行当前 Bazel 命令的代码库。

工作区

在同一主代码库中运行的所有 Bazel 命令共享的环境。

请注意,从历史上看,“代码库”和“工作区”这两个概念一直混为一谈 ;“工作区”一词通常用于指代主 代码库,有时甚至用作“代码库”的同义词。

规范代码库名称

代码库可寻址的规范名称。在工作区的上下文中,每个代码库都有一个规范名称。规范名称为 canonical_name 的代码库中的目标可通过标签 @@canonical_name//pac/kage:target 进行寻址(请注意双 @)。

主代码库的规范名称始终为空字符串。

显示的代码库名称

在某个其他代码库的上下文中,代码库可寻址的名称。 这可以被视为代码库的“昵称”:规范名称为 michael的代码库在代码库 alice的上下文中可能具有显示名称mike,但在代码库 bob的上下文中可能具有显示名称mickey。在这种情况下,michael 中的目标可在 alice 的上下文中通过标签 @mike//pac/kage:target 进行寻址(请注意单个 @)。

相反,这可以理解为代码库映射:每个代码库 都维护从“显示的代码库名称”到“规范代码库名称”的映射。

代码库规则

代码库定义的架构,用于告知 Bazel 如何实现代码库。例如,它可以是“从某个网址下载 zip 归档 并将其解压缩”,也可以是“提取某个 Maven 工件并将其作为 java_import 目标提供”,或者只是“符号链接本地目录”。每个代码库都是通过使用适当数量的实参调用代码库规则来 定义 的。

如需详细了解如何编写自己的代码库规则,请参阅代码库规则

到目前为止,最常见的代码库规则是 http_archive(用于从网址下载归档并将其解压缩)和 local_repository(用于符号链接已是 Bazel 代码库的本地目录)。

提取代码库

通过运行与代码库关联的 代码库规则,使代码库在本地磁盘上可用的操作。在提取之前,工作区中定义的代码库在本地磁盘上不可用 。

通常,Bazel 仅在需要代码库中的某些内容且尚未提取代码库时才会提取代码库, 如果代码库之前已提取 ,则 Bazel 仅在代码库的定义发生更改时才会重新提取。

目录布局

提取后,可以在输出基目录的子目录 external 中找到该代码库,其名称为规范名称。

您可以运行以下命令来查看规范名称为 canonical_name的代码库的内容:

ls $(bazel info output_base)/external/ canonical_name 

使用 Bzlmod 管理外部依赖项

Bzlmod 是新的外部依赖项子系统,不直接使用代码库 定义。相反,它会从 模块 构建依赖关系图,在该图上运行 扩展程序 ,并相应地定义代码库。

Bazel 模块 是一个 Bazel 项目,可以有多个 版本,每个版本都会发布有关其所依赖的其他模块的元数据 。模块必须在其代码库根目录中有一个 MODULE.bazel 文件,该文件位于 WORKSPACE 文件旁边。此文件是模块的清单,用于声明其名称、 版本、依赖项列表以及其他信息。以下是一个基本 示例:

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

模块必须仅列出其直接依赖项,Bzlmod 会在 Bazel 注册表中查找这些依赖项(默认情况下为 Bazel 中央 注册表)。该注册表提供了 依赖项的 MODULE.bazel 文件,这让 Bazel 能够在执行版本解析之前发现整个 传递依赖关系图。

在版本解析(为每个模块选择一个版本)之后, Bazel 会再次查询注册表,以了解如何为每个模块定义代码库 (在大多数情况下,使用 http_archive)。

模块还可以指定称为 标记 的自定义数据片段,这些标记在模块解析后由 模块扩展程序 使用,以定义其他代码库。这些扩展程序具有类似于代码库 规则的功能,使它们能够执行文件 I/O 和发送网络 请求等操作。除其他事项外,它们还允许 Bazel 与其他软件包 管理系统进行交互,同时遵守由 Bazel 模块构建的依赖关系图。

使用 WORKSPACE 定义代码库

从历史上看,您可以通过在 WORKSPACE(或 WORKSPACE.bazel)文件中定义代码库来管理外部依赖项。此文件的语法与 BUILD 文件类似,采用的是代码库规则而不是构建规则。

以下代码段是在 http_archive 代码库规则的 WORKSPACE 文件中使用示例:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "foo",
    urls = ["https://example.com/foo.zip"],
    sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)

该代码段定义了一个规范名称为 foo 的代码库。在 WORKSPACE 系统中,默认情况下,代码库的规范名称也是其对 所有其他代码库的显示名称。

WORKSPACE 系统的缺点

WORKSPACE 系统推出以来,用户报告了 许多痛点,包括:

  • Bazel 不会评估任何依赖项的 WORKSPACE 文件,因此除了直接依赖项之外,所有 传递依赖项都必须在主 代码库的 WORKSPACE 文件中定义。
  • 为了解决此问题,项目采用了 "deps.bzl" 模式,在该模式中 项目定义了一个宏,该宏又定义了多个代码库,并要求用户在其 WORKSPACE 文件中调用此宏。
    • 这有其自身的问题:宏无法 load 其他 .bzl 文件,因此 这些项目必须在此 “deps”宏中定义其传递依赖项,或者通过让用户调用多个 分层的“deps”宏来解决此问题。
    • Bazel 会按顺序评估 WORKSPACE 文件。此外, 依赖项是使用 http_archive 指定的,带有网址,没有任何 版本信息。这意味着,在菱形依赖项(A 依赖于 BCBC 都依赖于不同版本的 D)的情况下,无法可靠地执行 版本解析。

由于 WORKSPACE 的缺点,Bzlmod 将在未来的 Bazel 版本中取代旧版 WORKSPACE 系统。请阅读Bzlmod 迁移 指南,了解如何迁移到 Bzlmod。