代码库规则

报告问题 查看来源 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

本页介绍了如何定义代码库规则,并提供了示例以供详细了解。

外部代码库是一个目录树,包含可在 Bazel build 中使用的源文件,通过运行相应的 repo 规则按需生成。Repo 可以通过多种方式定义,但最终,每个 repo 都是通过调用 repo 规则来定义的,就像 build 目标是通过调用 build 规则来定义的一样。它们可用于依赖第三方库(例如 Maven 打包的库),也可用于生成特定于运行 Bazel 的主机的 BUILD 文件。

代码库规则定义

.bzl 文件中,使用 repository_rule 函数定义新的代码库规则,并将其存储在全局变量中。定义了 repo 规则后,可以将其作为函数调用来定义 repo。此调用通常从模块扩展实现函数内部执行。

repo 规则定义的两个主要组成部分是其属性架构和实现函数。属性架构决定了传递给库规则调用的属性的名称和类型,并且当需要提取库时,系统会运行实现函数。

属性

属性是传递给 repo 规则调用的实参。在通过调用 repository_rule 定义 repo 规则时,使用 attrs 实参指定 repo 规则接受的属性的架构。以下示例将 urlsha256 属性定义为字符串:

http_archive = repository_rule(
    implementation=_impl,
    attrs={
        "url": attr.string(mandatory=True),
        "sha256": attr.string(mandatory=True),
    }
)

如需访问实现函数中的属性,请使用 repository_ctx.attr.<attribute_name>

def _impl(repository_ctx):
    url = repository_ctx.attr.url
    checksum = repository_ctx.attr.sha256

所有 repository_rule 都具有隐式定义的属性 name。这是一个表现得有点神奇的字符串属性:当指定为仓库规则调用的输入时,它会采用明显的仓库名称;但当使用 repository_ctx.attr.name 从仓库规则的实现函数中读取时,它会返回规范仓库名称。

实现函数

每个 repo 规则都需要一个 implementation 函数。它包含规则的实际逻辑,并且严格在加载阶段执行。

该函数只有一个输入参数,即 repository_ctx。该函数会返回 None,表示在给定指定参数的情况下,相应规则是可重现的;或者返回一个字典,其中包含一组参数,这些参数可将相应规则转换为可重现的规则,从而生成相同的代码库。例如,对于跟踪 Git 代码库的规则,这意味着返回特定的提交标识符,而不是最初指定的浮动分支。

输入参数 repository_ctx 可用于访问属性值,以及非密封函数(查找二进制文件、执行二进制文件、在代码库中创建文件或从互联网下载文件)。如需了解更多背景信息,请参阅 API 文档。示例:

def _impl(repository_ctx):
  repository_ctx.symlink(repository_ctx.attr.path, "")

local_repository = repository_rule(
    implementation=_impl,
    ...)

实现函数何时执行?

当 Bazel 需要相应代码库中的目标时(例如,当另一个代码库中的另一个目标依赖于该目标时,或者当该目标在命令行中被提及时),系统会执行代码库规则的实现函数。然后,实现函数应在文件系统中创建相应代码库。这称为“提取”代码库。

与常规目标不同,当发生会导致代码库发生更改的更改时,系统不一定会重新提取代码库。这是因为有些内容 Bazel 无法检测到其更改,或者每次构建时检测这些内容会导致过多的开销(例如,从网络中提取的内容)。因此,只有在以下情况发生变化时,系统才会重新提取代码库:

  • 传递给 repo 规则调用的属性。
  • 包含代码库规则实现的 Starlark 代码。
  • 传递给 repository_ctxgetenv() 方法或使用 repository_ruleenviron 属性声明的任何环境变量的值。这些环境变量的值可以通过 --repo_env 标志在命令行中硬编码。
  • 在 repo 规则的实现函数中watch任何路径的存在性、内容和类型。
    • repository_ctx 的某些其他方法(带有 watch 参数,例如 read()execute()extract())也会导致路径被监控。
    • 同样,repository_ctx.watch_treepath.readdir 可能会导致以其他方式监控路径。
  • 执行 bazel fetch --force 时。

repository_rule 有两个参数用于控制何时重新提取代码库:

  • 如果设置了 configure 标志,则会在 bazel fetch --force --configure 时重新提取代码库(不会重新提取非 configure 代码库)。
  • 如果设置了 local 标志,除了上述情况外,当 Bazel 服务器重启时,系统也会重新提取代码库。

强制重新提取外部代码库

有时,外部代码库可能会过时,即使其定义或依赖项没有任何变化也是如此。例如,提取源代码的 repo 可能会跟踪第三方代码库的特定分支,并且该分支上提供了新的提交。在这种情况下,您可以通过调用 bazel fetch --force --all 要求 bazel 无条件重新提取所有外部代码库。

此外,某些 repo 规则会检查本地机器,如果本地机器已升级,这些规则可能会过时。在此处,您可以要求 Bazel 仅重新提取 repository_rule 定义设置了 configure 属性的外部代码库,使用 bazel fetch --force --configure

示例

  • C++ 自动配置的工具链:它使用代码库规则,通过查找本地 C++ 编译器、环境和 C++ 编译器支持的标志,自动为 Bazel 创建 C++ 配置文件。

  • Go 代码库使用多个 repository_rule 来定义使用 Go 规则所需的依赖项列表。

  • rules_jvm_external 默认情况下会创建一个名为 @maven 的外部代码库,该代码库会为传递依赖树中的每个 Maven 制品生成 build 目标。