本页介绍了如何定义代码库规则,并提供了示例以供您详细了解。
外部代码库是一个目录树,其中包含可在 Bazel 构建中使用的源文件,这些文件是通过运行相应的代码库规则按需生成的。可以通过多种方式定义代码库,但最终,每个代码库都是通过调用代码库规则来定义的,就像通过调用 build 规则来定义 build 目标一样。它们可用于依赖第三方库(例如 Maven 打包的库),也可用于生成特定于 Bazel 运行主机的 BUILD
文件。
代码库规则定义
在 .bzl
文件中,使用 repository_rule 函数定义新的代码库规则,并将其存储在全局变量中。定义代码库规则后,您可以将其作为函数调用来定义代码库。此调用通常在模块扩展实现函数内执行。
代码库规则定义的两个主要组件是其属性架构和实现函数。属性架构决定了传递给代码库规则调用的属性的名称和类型,并且在需要提取代码库时运行实现函数。
属性
属性是传递给代码库规则调用的参数。使用 repository_rule
调用定义代码库规则时,可使用 attrs
参数指定代码库规则接受的属性的架构。以下示例将 url
和 sha256
属性定义为字符串:
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
。这是一个字符串属性,其行为有点神奇:当被指定为 Repo 规则调用的输入时,它采用明显的代码库名称;但使用 repository_ctx.attr.name
从代码库规则的实现函数中读取时,它会返回规范化代码库名称。
实现函数
每条代码库规则都需要一个 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 无法检测到对某些元素所做的更改,或者会导致每次构建时产生过多开销(例如,从网络中提取的内容)。因此,只有在以下某项发生变化时,系统才会重新提取代码库:
- 传递给代码库规则调用的属性。
- 包含代码库规则实现的 Starlark 代码。
- 传递给
repository_ctx
的getenv()
方法或使用repository_rule
的environ
属性声明的任何环境变量的值。您可以使用--repo_env
标志在命令行中对这些环境变量的值进行硬编码。 - 在代码库规则的实现函数中被
watch
的所有路径的存在性、内容和类型。- 某些带有
watch
参数的repository_ctx
方法(例如read()
、execute()
和extract()
)也可能会导致路径被监控。 - 同样,
repository_ctx.watch_tree
和path.readdir
也会导致以其他方式监视路径。
- 某些带有
- 执行
bazel fetch --force
时。
repository_rule
有两个参数用于控制何时重新提取代码库:
- 如果设置了
configure
标志,系统会在bazel fetch --force --configure
上重新提取代码库(系统不会重新提取非configure
代码库)。 - 如果设置了
local
标志,除了上述情况外,在 Bazel 服务器重启时,系统还会重新提取代码库。
强制重新提取外部代码库
有时,外部代码库可能会在其定义或依赖项没有任何更改的情况下过时。例如,提取源代码的代码库可能会跟随第三方代码库的特定分支,并且该分支上有新的提交。在这种情况下,您可以通过调用 bazel fetch --force --all
要求 bazel 无条件地重新提取所有外部代码库。
此外,某些代码库规则会检查本地机器,如果本地机器已升级,这些规则可能会过时。在这里,您可以要求 Bazel 仅重新提取 repository_rule
定义设置了 configure
属性的外部代码库,请使用 bazel fetch --force
--configure
。
示例
C++ 自动配置的工具链:它使用代码库规则查找本地 C++ 编译器、环境以及 C++ 编译器支持的标志,以便为 Bazel 自动创建 C++ 配置文件。
Go 代码库使用多个
repository_rule
来定义使用 Go 规则所需的依赖项列表。默认情况下,rules_jvm_external 会创建一个名为
@maven
的外部仓库,该仓库会为传递依赖项树中的每个 Maven 工件生成构建目标。