代码库规则

报告问题 查看源代码

本页面介绍了如何定义代码库规则,并提供了更多详情示例。

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

代码库规则定义

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

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

属性

属性是传递给 Repo 规则调用的参数。如果 Repo 规则是通过调用 repository_rule 来定义的,那么 Repo 规则接受的属性架构是使用 attrs 参数指定的。以下示例将 urlsha256 属性定义为字符串:

http_archive = repository_rule(
    implementation=_impl,
    local=True,
    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 需要某个代码库中的目标时,例如当其他代码库中的另一个目标依赖于该目标,或者命令行中提到该目标时,系统会执行 Repo 规则的实现函数。然后,实现函数需要在文件系统中创建代码库。这称为“提取”代码库。

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

  • 传递给 Repo 规则调用的属性。
  • 包含 Repo 规则实现的 Starlark 代码。
  • 传递给 repository_ctxgetenv() 方法或使用 repository_ruleenviron 属性声明的任何环境变量的值。这些环境变量的值可以在命令行中使用 --repo_env 标志进行硬连接。
  • 传递给 read()execute() 以及 repository_ctx 的类似方法(由标签引用,例如 //mypkg:label.txt,而不是 mypkg/label.txt)的任何文件的内容
  • 执行 bazel fetch --force 时。

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

  • 如果设置了 configure 标志,则仅当将 --configure 参数传递给仓库时,才会在 bazel fetch 上重新提取仓库(如果未设置该属性,此命令不会导致重新提取)
  • 如果设置了 local 标志,除了上述情况之外,当 Bazel 服务器重启时,系统也会重新获取代码库。

重启实现函数

在提取代码库时,如果缺少实现函数请求的依赖项,则可以重启实现函数。在这种情况下,实现函数的执行将停止,缺少的依赖项会得到解析,而函数会在依赖项得到解决后重新执行。为了避免不必要的重启(因为网络访问可能必须重复而代价高昂),系统会预提取标签参数,前提是所有标签参数都可以解析为现有文件。请注意,解析仅在函数执行期间构造的字符串或标签的路径仍可能会导致重启。

强制重新提取外部代码库

有时,在不更改其定义或依赖项的情况下,外部代码库可能会过时。例如,代码库提取来源可能跟随第三方代码库的特定分支,并且该分支上会提供新的提交。在这种情况下,您可以通过调用 bazel fetch --force --all 让 bazel 无条件地重新提取所有外部代码库。

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

示例

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

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

  • rules_jvm_external 默认创建一个名为 @maven 的外部代码库,为传递依赖项树中的每个 Maven 工件生成构建目标。