Bazel 支持外部依赖项,即构建中使用的并非来自工作区的源文件(文本和二进制文件)。例如,它们可能是托管在 GitHub 代码库中的规则集、Maven 工件或本地机器上当前工作区之外的目录。
从 Bazel 6.0 开始,您可以通过两种方式使用 Bazel 管理外部依赖项:传统的以代码库为中心的 WORKSPACE
系统,以及较新的以模块为中心的 MODULE.bazel
系统(代号为 Bzlmod,使用标志 --enable_bzlmod
启用)。这两种系统可以搭配使用,但在未来的 Bazel 版本中,Bzlmod 将取代 WORKSPACE
系统。如需了解如何进行迁移,请参阅 Bzlmod 迁移指南。
本文档将介绍与 Bazel 中的外部依赖项管理相关的概念,然后再按顺序详细讨论这两个系统。
概念
代码库
一个包含 WORKSPACE
或 WORKSPACE.bazel
文件的目录,其中包含要在 Bazel 构建中使用的源文件。通常缩写为“repo”。
主代码库
运行当前 Bazel 命令的代码库。
工作区
所有 Bazel 命令共享的环境都在同一个主代码库中运行。
请注意,过去“仓库”和“工作区”的概念已被混淆;术语“工作区”通常用于指代主仓库,有时甚至用作“仓库”的同义词。
规范代码库名称
可供代码库寻址的规范名称。在工作区环境中,每个代码库都有一个规范名称。可以使用标签 @@canonical_name//pac/kage:target
(注意双 @
)来指定代码库中规范名称为 canonical_name
的目标。
主代码库始终将空字符串用作规范名称。
明显的代码库名称
代码库可在其他某个代码库的上下文中被寻址的名称。可以将其视为代码库的“昵称”:在代码库 alice
的上下文中,规范名称为 michael
的代码库可能具有表观名称 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 项目,每个版本都会发布有关其依赖的其他模块的元数据。模块必须在其代码库根目录的 WORKSPACE
文件旁边有一个 MODULE.bazel
文件。此文件是模块的清单,用于声明模块的名称、版本、依赖项列表以及其他信息。以下是一个基本示例:
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 模块构建的依赖关系图。
Bzlmod 上的外部链接
- bazelbuild/examples 中的 Bzlmod 用法示例
- Bazel 外部依赖项大改(原始 Bzlmod 设计文档)
- 2021 年 BazelCon 大会上关于 Bzlmod 的演讲
- 有关 Bzlmod 的 Bazel 社区日演讲
使用 WORKSPACE
定义代码库
过去,您可以通过在 WORKSPACE
(或 WORKSPACE.bazel
)文件中定义代码库来管理外部依赖项。此文件的语法与 BUILD
文件类似,使用的是代码库规则而不是构建规则。
以下代码段是在 WORKSPACE
文件中使用 http_archive
代码库规则的示例:
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
系统的缺点
自推出 WORKSPACE
系统以来的几年里,用户报告了许多痛点,包括:
- Bazel 不会评估任何依赖项的
WORKSPACE
文件,因此除了直接依赖项之外,还必须在主代码库的WORKSPACE
文件中定义所有传递依赖项。 - 为了解决此问题,项目采用了“deps.bzl”模式,该模式定义了一个宏,而该宏又定义了多个代码库,并要求用户在其
WORKSPACE
文件中调用此宏。- 这有其自身的问题:宏无法
load
其他.bzl
文件,因此这些项目必须在此“deps”宏中定义其传递依赖项,或者通过让用户调用多个分层“deps”宏来解决此问题。 - Bazel 依序评估
WORKSPACE
文件。此外,依赖项是使用http_archive
和网址指定的,不含任何版本信息。这意味着,在使用菱形依赖项的情况下,没有可靠的方法来执行版本解析(A
依赖于B
和C
;B
和C
依赖于不同版本的D
)。
- 这有其自身的问题:宏无法
由于 WORKSPACE 的缺点,Bzlmod 将在未来的 Bazel 版本中替换旧版 WORKSPACE 系统。如需了解如何迁移到 Bzlmod,请参阅 Bzlmod 迁移指南。