Bazel 支持外部依赖项,即您的 build 中并非来自您的工作区的源文件(文本和二进制文件)。例如,它们可以是托管在 GitHub 代码库中的规则集、Maven 工件,也可以是本地机器上当前工作区之外的目录。
从 Bazel 6.0 开始,您可以通过两种方式管理使用 Bazel 的外部依赖项:传统的、侧重于代码库的 WORKSPACE
系统和较新的基于模块的 MODULE.bazel
系统(代号为 Bzlmod,使用 --enable_bzlmod
标记启用)。这两个系统可以一起使用,但在未来的 Bazel 迁移说明中,Bzlmod 将会取代 系统 1 的 WORKSPACE
系统,
本文档将依次介绍 Bazel 中外部依赖项管理的概念,按顺序详细介绍这两个系统。
概念
仓库
包含 WORKSPACE
或 WORKSPACE.bazel
文件的目录,其中包含要在 Bazel 构建中使用的源文件。通常简称为 repo。
主代码库
运行当前 Bazel 命令的代码库。
工作区
所有 Bazel 命令共享的环境在同一主代码库中运行。
请注意,过去“代码库”和“工作区”的概念一直令人困惑;“工作区”这一术语通常用于指代主代码库,有时甚至用作“代码库”的同义词。
规范代码库名称
代码库可处理的规范名称。在工作区环境中,每个代码库都有一个规范名称。规范名称为 canonical_name
的代码库中的目标可以通过标签 @@canonical_name//pac/kage:target
(请注意双 @
)进行处理。
主代码库始终包含空字符串作为规范名称。
明显的代码库名称
代码库在其他特定代码库中的上下文中可以使用的名称。
这可以被视为代码库的“别名”:具有规范名称 michael
的代码库可能在代码库 alice
的上下文中具有明显的名称 mike
,但在代码库 bob
中可能具有明显的名称 mickey
。在这种情况下,可以在 alice
上下文中通过 @mike//pac/kage:target
标签处理 michael
内的目标(请注意单个 @
)。
相反,这可以理解为“代码库映射”:每个代码库都保留从“明显的代码库名称”到“规范代码库名称”的映射。
代码库规则
存储库定义的架构,用于告诉 Bazel 如何具体化存储库。例如,它可以是“从特定网址下载 zip 归档文件并将其提取”,或者“提取某些 Maven 工件并将其用作 java_import
目标”,或者直接“对本地目录进行符号链接”。通过调用具有适当数量的参数的代码库规则,可以为每个代码库定义代码库。
如需详细了解如何编写自己的代码库规则,请参阅代码库规则。
到目前为止,最常见的 Repo 规则是 http_archive
(用于从网址下载归档文件并将其提取)和 local_repository
(用于对已属于 Bazel 代码库的本地目录进行符号化解析)。
提取代码库
通过运行关联的代码库规则,使本地磁盘支持代码库的操作。在提取工作区之前,工作区中定义的代码库不可用。
通常,Bazel 仅在需要从代码库中获取某些内容时才会提取代码库,并且尚未进行提取。如果之前已经提取了代码库,则 Bazel 仅在其定义发生变化时才重新提取。
目录布局
提取后,您可以在输出基础的子目录 external
中找到其代码库,并在其规范名称下找到它。
您可以运行以下命令来查看规范名称为 canonical_name
的代码库内容:
ls $(bazel info output_base)/external/ canonical_name
使用 Bzlmod 管理外部依赖项
Bzlmod 是新的外部依赖项子系统,不直接使用 repo 定义。而是从模块构建依赖关系图,在该图之上运行扩展,并相应地定义代码库。
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 Central Registry。注册表提供依赖项的 MODULE.bazel
文件,Bazel 有助于在执行版本解析之前发现整个传递依赖关系图。
解析版本后,为每个模块选择一个版本,Bazel 会再次咨询注册表,了解如何为每个模块定义代码库(在大多数情况下,使用 http_archive
进行定义)。
模块还可以指定称为“标记”的自定义数据,在模块解析后模块扩展会使用这些数据来定义其他代码库。这些扩展程序具有与 Repo 规则类似的功能,能够执行文件 I/O 和发送网络请求等操作。除此之外,它们让 Bazel 能够与其他软件包管理系统进行交互,同时遵循基于 Bazel 模块构建的依赖关系图。
Bzlmod 上的外部链接
- bazelbuild/examples 中的 Bzlmod 用法示例
- Bazel 外部依赖项改进(原始 Bzlmod 设计文档)
- BzlCon 2021 年演讲
- 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
文件,因此这些项目必须在此“依赖项”宏中定义其传递依赖项,或者让用户可通过调用多个分层的“依赖项”宏来解决此问题。 - Bazel 会依序评估
WORKSPACE
文件。此外,依赖项是使用网址指定的http_archive
,不带任何版本信息。这意味着在菱形依赖项的情况下,没有可靠的方式执行版本解析(A
依赖于B
和C
;B
和C
均依赖于不同的D
版本)。
- 它有自己的问题:宏无法
由于 WORKSPACE 的缺点,Bzlmod 将在未来的 Bazel 版本中取代旧版 WORKSPACE 系统。请阅读 Bzlmod 迁移指南,了解如何迁移到 Bzlmod。