代码库规则

报告问题 查看源代码

本页面介绍了如何创建代码库规则,并提供了相关示例了解详情。

外部代码库是只能在 WORKSPACE 文件中使用的规则,它可在 Bazel 的加载阶段启用非封闭操作。每个外部代码库规则都会创建自己的工作区,以及自己的 BUILD 文件和工件。它们可用于依赖于第三方库(例如 Maven 打包库),也可用于生成特定于运行 Bazel 的主机的 BUILD 文件。

创建仓库规则

.bzl 文件中,使用 repository_rule 函数创建新的仓库规则并将其存储在全局变量中。

自定义代码库规则的使用方式与原生代码库规则相同。它有一个强制性的 name 属性,其 build 文件中存在的每个目标都可以称为 @<name>//package:target,其中 <name>name 属性的值。

规则会在您显式构建规则或它是构建的依赖项时加载。在这种情况下,Bazel 将执行其 implementation 函数。此函数描述了如何创建代码库、其内容和 BUILD 文件。

属性

属性是以字典形式传递给 attrs 规则参数的规则参数。当您定义仓库规则时,系统会列出属性及其类型。以下示例将 urlsha256 属性定义为字符串:

local_repository = 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(就像构建规则一样)和 repo_mapping。您可以使用 repository_ctx.name 访问代码库规则的名称。repo_mapping 的含义与原生代码库规则 local_repositorynew_local_repository 的含义相同。

如果属性名称以 _ 开头,则该属性为私有属性,用户无法设置。

实现函数

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

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

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

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

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

何时执行实现函数?

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

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

  • 传递给 WORKSPACE 文件中代码库声明的参数。
  • 构成存储库实现的 Starlark 代码。
  • 使用 repository_ruleenviron 属性声明的任何环境变量的值。您可以使用 --action_env 标志在命令行上硬连接这些环境变量的值(但此标志会使构建的所有操作失效)。
  • 传递给 read()execute() 以及被标签引用的 repository_ctx 的类似方法的任何文件的内容(例如,//mypkg:label.txt,而不是 mypkg/label.txt
  • 执行 bazel sync 时。

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

  • 如果设置了 configure 标志,则仅当 --configure 参数传递给代码库时,系统才会在 bazel sync 上重新提取该代码库(如果未设置该属性,此命令不会导致重新提取)
  • 如果设置了 local 标志,除了上述情况之外,当 Bazel 服务器重启或任何影响代码库声明的文件(例如 WORKSPACE 文件或其加载的文件)发生更改时,系统也会重新提取代码库,无论这些更改是否导致代码库或其代码的声明发生变化。

    在这些情况下,系统不会重新提取非本地代码库。这是因为这些代码库会被假定为与网络通信或具有高昂的成本。

重启实现函数

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

强制重新提取外部代码库

有时,外部代码库可能会在不对其定义或依赖项进行任何更改的情况下过时。例如,提取源代码的代码库可能遵循第三方代码库的特定分支,并且该分支上提供了新的提交内容。在这种情况下,您可以通过调用 bazel sync 要求 bazel 无条件重新提取所有外部代码库。

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

示例

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

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

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