本页介绍了如何创建代码库规则,并提供了示例以供详细了解。
外部代码库是一种只能在 WORKSPACE
文件中使用的规则,可在 Bazel 的加载阶段启用非封闭操作。每个外部代码库规则都会创建自己的工作区,其中包含自己的 BUILD
文件和制品。它们可用于依赖第三方库(例如 Maven 打包的库),也可用于生成特定于运行 Bazel 的主机的 BUILD
文件。
创建代码库规则
在 .bzl
文件中,使用 repository_rule 函数创建一个新的代码库规则,并将其存储在全局变量中。
自定义代码库规则可以像原生代码库规则一样使用。它具有强制性 name
属性,并且其 build 文件中的每个目标都可以称为 @<name>//package:target
,其中 <name>
是 name
属性的值。
当您明确构建规则时,或者当规则是 build 的依赖项时,系统会加载该规则。在这种情况下,Bazel 将执行其 implementation
函数。此函数描述了如何创建代码库、其内容和 BUILD
文件。
属性
属性是作为字典传递给 attrs
规则实参的规则实参。定义代码库规则时,系统会列出属性及其类型。以下示例将 url
和 sha256
属性定义为字符串:
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_repository
和 new_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
的getenv()
方法或使用repository_rule
的environ
属性声明的任何环境变量的值。这些环境变量的值可以通过--repo_env
标志在命令行中硬编码。 - 传递给
repository_ctx
的read()
、execute()
和类似方法(由标签引用,例如//mypkg:label.txt
,但不是mypkg/label.txt
)的任何文件的内容 - 执行
bazel sync
时。
repository_rule
有两个参数用于控制何时重新提取代码库:
- 如果设置了
configure
标志,则仅在bazel sync
上将--configure
参数传递给该标志时,才会重新提取相应代码库(如果未设置该属性,此命令不会导致重新提取) 如果设置了
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 目标。