代码库规则

报告问题 查看源代码

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

外部代码库是一种规则,只能在 WORKSPACE 文件中使用,并在 Bazel 的加载阶段启用非封闭操作。每条外部代码库规则会创建自己的工作区,其中包含自己的 BUILD 文件和工件。它们可用于依赖第三方库(例如 Maven 封装的库),但也可用于生成 Bazel 运行所在的主机专用的 BUILD 文件。

代码库规则创建

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

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

规则会在您明确构建时加载,如果它是构建的依赖项,则会加载。在本例中,Bazel 将执行其 implementation 函数。此函数介绍如何创建代码库、其内容和 BUILD 文件。

属性

特性是一个规则参数,例如 urlsha256。定义存储库规则时,您必须列出特性及其类型。

local_repository = repository_rule(
    implementation=_impl,
    local=True,
    attrs={"path": attr.string(mandatory=True)})

如需访问特性,请使用 repository_ctx.attr.<attribute_name>

所有 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 标志以硬性方式连接这些环境变量(但此标志会使 build 的每项操作失效)。
  • 传递给 read()execute() 以及 repository_ctx 的类似方法(通过标签引用)的任何文件的内容(例如,//mypkg:label.txt,而不是 mypkg/label.txt
  • 执行 bazel sync 时。

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

  • 如果设置了 configure 标志,则仅当将 --configure 参数传递给 bazel sync 时,系统才会在 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 工件生成构建目标。