代码库规则

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

外部代码库是一种只能 在 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 都有隐式定义的属性(就像 build 规则一样)。这两个隐式属性是 name(与 build 规则相同)和 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_ctx's getenv() 方法或使用 environ 属性声明的任何环境变量的值 repository_rule。这些环境变量的值 可以使用 --repo_env标志在命令行中硬编码。
  • 传递给 repository_ctxread()execute() 和类似方法的任何文件的内容,这些文件由标签引用(例如,//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 工件生成 build 目标。