外部依赖项概览

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。
报告问题 查看源代码

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

从 Bazel 6.0 开始,您可以通过以下两种方法使用 Bazel 管理外部依赖项:传统的以代码库为中心的 WORKSPACE 系统(以代码为中心的新版 MODULE.bazel 系统(代号为 Bzlmod,并使用 --enable_bzlmod 标志启用)。这两个系统可以一起使用,但 Bzlmod 即将取代 Bazel 系统的版本。

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

概念

代码库

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

主代码库

当前 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 是新的外部依赖项子系统,并不直接使用 Repo 定义。相反,它会从模块构建依赖项图,基于图运行扩展程序,并相应地定义代码库。

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)。

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

使用 WORKSPACE 定义代码库

过去,您可以通过在 WORKSPACE(或 WORKSPACE.bazel)文件中定义代码库来管理外部依赖项。此文件采用的语法与 BUILD 文件类似,它使用 Repo 规则,而非 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 系统推出以来的几年里,用户报告了许多痛点,包括:

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