Bazel 支持 外部依赖项,即构建中使用的源文件(文本和二进制文件),这些文件并非来自您的工作区。例如,它们可以是托管在 GitHub 代码库中的 规则集、Maven 工件,也可以是本地机器上当前工作区之外的 目录。
本文档首先概述了该系统,然后更详细地介绍了一些 概念。
系统概览
Bazel 的外部依赖项系统基于 Bazel 模块(每个模块都是一个版本化的 Bazel 项目)和 代码库(或 repo,即包含 源文件的目录树)运行。
Bazel 从根模块(即您正在处理的项目)开始。
与所有模块一样,它需要在目录根目录下有一个 MODULE.bazel 文件,
用于声明其基本元数据和直接依赖项。以下是一个基本
示例:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")
然后,Bazel 会在
_Bazel 注册表_ (默认情况下为 Bazel Central
Registry)中查找所有传递依赖项模块。该注册表提供
依赖项的 MODULE.bazel 文件,这让 Bazel 能够在执行版本解析之前发现整个
传递依赖项图。
在版本解析(为每个模块选择一个版本)之后, Bazel 会再次查询注册表,以了解如何为每个模块定义代码库 ,即如何提取每个依赖项模块的来源。大多数情况下,这些只是从互联网下载并提取的归档文件。
模块还可以指定称为 标记 的自定义数据,这些数据在模块解析后由 模块扩展程序 使用,以定义其他代码库。这些扩展程序可以执行文件 I/O 和发送网络请求等操作。除其他事项外,它们还允许 Bazel 与其他软件包管理系统进行交互 ,同时遵守由 Bazel 模块构建的依赖项图 。
这三种代码库(主代码库,即您正在处理的源树;表示传递依赖项模块的代码库;以及由模块扩展程序创建的代码库)共同构成了 工作区 。外部代码库(非主代码库)是按需提取的,例如,当 BUILD 文件中的标签(如 @repo//pkg:target)引用它们时。
优势
Bazel 的外部依赖项系统具有广泛的优势。
自动处理依赖项
- 确定性版本解析:Bazel 采用确定性 MVS版本解析算法, 可最大限度地减少冲突并解决菱形依赖项问题。
- 简化依赖项管理:
MODULE.bazel仅声明直接 依赖项,而传递依赖项会自动解析, 从而更清楚地了解项目的依赖项。 - 严格的依赖项可见性: 仅直接依赖项可见,确保正确性和 可预测性。
生态系统集成
- Bazel Central Registry:一个集中式 代码库,用于发现和管理作为 Bazel 模块的常见依赖项。
- 采用非 Bazel 项目:当非 Bazel 项目(通常是 C++ 库)适用于 Bazel 并可在 BCR 中使用时,它会简化整个社区的集成,并消除自定义 BUILD 文件的重复工作和冲突。
- 与特定于语言的软件包管理器统一集成:规则集
简化了与非 Bazel
依赖项的外部软件包管理器的集成,包括:
- rules_jvm_external 适用于 Maven 的,
- rules_python 适用于 PyPi 的
- bazel-gazelle 适用于 Go 模块,
- rules_rust 适用于 Cargo。
高级功能
- 模块扩展程序:
use_repo_rule和模块 扩展程序功能允许灵活使用自定义代码库规则和 解析逻辑来引入任何非 Bazel 依赖项。 bazel mod命令:该子命令提供了 强大的方式来检查外部依赖项。您可以准确了解外部依赖项的定义方式和来源。- 供应商模式:预提取您 需要的确切外部依赖项,以便进行离线构建。
- 锁定文件:锁定文件可提高构建的可重现性并 加快依赖项解析速度。
- (即将推出)BCR 出处 证明: 通过确保依赖项的出处经过验证,加强供应链安全性。
概念
本部分详细介绍了与外部依赖项相关的概念。
模块
一个 Bazel 项目,可以有多个版本,每个版本都可以有 依赖于其他模块。
在本地 Bazel 工作区中,模块由代码库表示。
如需了解详情,请参阅 Bazel 模块。
代码库
一个目录树,其根目录中有一个边界标记文件,其中包含可在 Bazel 构建中使用的源 文件。通常简称为 repo 。
代码库边界标记文件可以是 MODULE.bazel(表示此代码库
代表 Bazel 模块)、REPO.bazel(见下文),或者在
旧版上下文中,可以是 WORKSPACE 或 WORKSPACE.bazel。任何代码库边界标记文件
都将表示代码库的边界;多个此类文件可以共存于一个
目录中。
主代码库
当前 Bazel 命令正在运行的代码库。
主代码库的根目录也称为 工作区根目录。
工作区
在同一主代码库中运行的所有 Bazel 命令共享的环境。它 包含主代码库和所有已定义的外部代码库的集合。
请注意,从历史上看,“代码库”和“工作区”这两个概念一直混为一谈 ;“工作区”一词通常用于指代主 代码库,有时甚至用作“代码库”的同义词。
规范代码库名称
代码库始终可寻址的名称。在工作区的上下文中,每个代码库都有一个规范名称。规范名称为 canonical_name 的代码库中的目标可以通过标签
@@canonical_name//package:target(注意双 @)寻址。
主代码库始终以空字符串作为规范名称。
显式代码库名称
在某个其他
代码库的上下文中,代码库可寻址的名称。这可以被视为代码库的“昵称”:规范
名称 michael 的代码库在代码库
alice 的上下文中可能具有显式名称 mike,但在代码库
bob 的上下文中可能具有显式名称 mickey。在这种情况下,michael 中的目标可以通过标签
@mike//package:target 在 alice 的上下文中寻址(注意单个 @)。
相反,这可以理解为代码库映射:每个代码库 都维护从“显式代码库名称”到“规范代码库名称”的映射。
代码库规则
代码库定义的架构,用于告知 Bazel 如何实现代码库。例如,它可以是“从某个网址下载 zip 归档文件
并将其解压缩”,也可以是“提取某个 Maven 工件并使其作为
java_import 目标可用”,或者只是“符号链接本地目录”。每个代码库都是通过使用适当数量的实参调用代码库规则来
定义 的。
如需详细了解如何编写自己的代码库规则,请参阅代码库规则。
到目前为止,最常见的代码库规则是
http_archive(从网址下载归档文件并将其解压缩)和
local_repository(符号链接已是 Bazel 代码库的本地目录)。
提取代码库
通过运行与代码库关联的 代码库规则,使代码库在本地磁盘上可用的操作。在提取之前,工作区中定义的代码库在本地磁盘上不可用 。
通常,Bazel 仅在需要从代码库中获取某些内容时才提取代码库, 且尚未提取代码库。如果代码库之前已提取 ,则 Bazel 仅在代码库的定义发生更改时才重新提取它。
fetch 命令可用于启动代码库、
目标或执行任何构建所需的所有代码库的预提取。此功能
支持使用 --nofetch 选项进行离线构建。
--fetch 选项用于管理网络访问权限。其默认值为 true。
但是,当设置为 false (--nofetch) 时,该命令将使用依赖项的任何缓存
版本,如果不存在任何缓存版本,该命令将导致
失败。
如需详细了解如何控制提取,请参阅提取选项。
目录布局
提取后,可以在
输出库中的子目录 external 下找到该代码库,其名称为规范名称。
您可以运行以下命令来查看规范名称为
canonical_name的代码库的内容:
ls $(bazel info output_base)/external/ canonical_name REPO.bazel 文件
REPO.bazel 文件用于标记构成代码库的目录树的最顶层
边界。它不需要包含任何内容即可作为代码库边界文件;不过,它也可用于为代码库内的所有构建目标指定一些常见属性。
REPO.bazel 文件的语法与 BUILD 文件类似,但不支持
load 语句。repo() 函数采用与 package()
函数 在 BUILD 文件中的实参相同;而 package()
为软件包内的所有构建目标指定常见属性,repo()
则类似地为代码库内的所有构建目标指定常见属性。
例如,您可以通过以下 REPO.bazel 文件为代码库中的所有目标指定通用许可:
repo(
default_package_metadata = ["//:my_license"],
)
旧版 WORKSPACE 系统
在较旧的 Bazel 版本(9.0 之前)中,外部依赖项是通过
在 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 系统的缺点
在 WORKSPACE 系统推出后的几年里,用户报告了许多
痛点,包括:
- Bazel 不会评估任何依赖项的
WORKSPACE文件,因此除了直接依赖项之外,所有 传递依赖项都必须在主 代码库的WORKSPACE文件中定义。 - 为了解决此问题,项目采用了 "deps.bzl" 模式,在该模式中
它们定义了一个宏,该宏又定义了多个代码库,并要求用户
在其
WORKSPACE文件中调用此宏。- 这有其自身的问题:宏无法
load其他.bzl文件,因此 这些项目必须在此 “deps”宏中定义其传递依赖项,或者通过让用户调用多个 分层“deps”宏来解决此问题。 - Bazel 会按顺序评估
WORKSPACE文件。此外, 依赖项是使用http_archive指定的,带有网址,没有任何 版本信息。这意味着,在菱形依赖项(A依赖于B和C;B和C都依赖于不同版本的D)的情况下,无法可靠地执行 版本解析。
- 这有其自身的问题:宏无法
由于 WORKSPACE 的缺点,新的基于模块的系统(代号为 “Bzlmod”)在 Bazel 6 和 9 之间逐渐取代了旧版 WORKSPACE 系统。 如需了解如何迁移 到 Bzlmod,请参阅Bzlmod 迁移指南。