Bazel 支持 外部依赖项,即构建中使用的源文件(文本和二进制文件),这些文件并非来自您的工作区。例如,它们可以是托管在 GitHub 代码库中的 规则集、Maven 工件,也可以是本地机器上当前工作区之外的 目录。
从 Bazel 6.0 开始,您可以通过两种方式使用 Bazel 管理外部依赖项:
传统的、以代码库为中心的 WORKSPACE 系统,以及
较新的、以模块为中心的 MODULE.bazel 系统(代号为 Bzlmod,
并通过 --enable_bzlmod 标志启用)。这两个系统可以
一起使用,但 Bzlmod 将在未来的 Bazel
版本中取代 WORKSPACE 系统,请查看 Bzlmod 迁移指南,了解如何
迁移。
本文档将先介绍 Bazel 中外部依赖项管理的相关概念 ,然后再详细介绍这两个系统。
概念
代码库
包含 WORKSPACE 或 WORKSPACE.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 模块构建的依赖关系图。
Bzlmod 上的外部链接
- bazelbuild/examples 中的 Bzlmod 用法示例
- Bazel 外部依赖项大修 (原始 Bzlmod 设计文档)
- BazelCon 2021 上关于 Bzlmod 的演讲
- Bazel Community Day 上关于 Bzlmod 的演讲
使用 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依赖于B和C;B和C都依赖于不同版本的D)的情况下,无法可靠地执行 版本解析。
- 这有其自身的问题:宏无法
由于 WORKSPACE 的缺点,Bzlmod 将在未来的 Bazel 版本中取代旧版 WORKSPACE 系统。请阅读Bzlmod 迁移 指南,了解如何迁移到 Bzlmod。