本页介绍了使用宏的基础知识,并介绍了典型用例, 调试和惯例。
宏是从 BUILD
文件中调用的函数,可以实例化规则。
宏主要用于现有规则的封装和代码重复使用
和其他宏。在
加载阶段,那么宏已不存在,
而 Bazel 只能看到一组具体的实例化规则。
用法
宏的典型用例是重复使用规则。
例如,BUILD
文件中的 genrule 会使用
带有硬编码的 some_arg
参数的 //:generator
:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
如果要生成更多具有不同参数的文件,
将此代码提取到宏函数中。我们不妨将宏 file_generator
命名为
具有 name
和 arg
参数。将 Genrule 替换为以下代码:
load("//path:generator.bzl", "file_generator")
file_generator(
name = "file",
arg = "some_arg",
)
file_generator(
name = "file-two",
arg = "some_arg_two",
)
file_generator(
name = "file-three",
arg = "some_arg_three",
)
在这里,您可以从位于以下位置的 .bzl
文件中加载 file_generator
符号:
在 //path
软件包中。通过将宏函数定义放在单独的
.bzl
文件,请确保 BUILD
文件整洁有序且声明性强,即 .bzl
文件。
最后,在 path/generator.bzl
中,将该宏的定义写入到
封装原始 Genrule 定义并进行参数化:
def file_generator(name, arg, visibility=None):
native.genrule(
name = name,
outs = [name + ".txt"],
cmd = "$(location //:generator) %s > $@" % arg,
tools = ["//:generator"],
visibility = visibility,
)
您还可以使用宏将规则链接在一起。此示例展示了 genrule,其中 Genrule 使用前一个 genrule 的输出作为输入:
def chained_genrules(name, visibility=None):
native.genrule(
name = name + "-one",
outs = [name + ".one"],
cmd = "$(location :tool-one) $@",
tools = [":tool-one"],
visibility = ["//visibility:private"],
)
native.genrule(
name = name + "-two",
srcs = [name + ".one"],
outs = [name + ".two"],
cmd = "$(location :tool-two) $< $@",
tools = [":tool-two"],
visibility = visibility,
)
该示例仅为第二代规则分配了可见性值。这样, 宏作者可以隐藏中间规则的输出,以免受到依赖 工作区中的其他目标。
扩展宏
如果您想研究宏的作用,请使用带有query
--output=build
以查看展开的表单:
$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
name = "file",
tools = ["//:generator"],
outs = ["//test:file.txt"],
cmd = "$(location //:generator) some_arg > $@",
)
实例化原生规则
原生规则(即不需要 load()
语句的规则)可以
从 native 模块实例化:
def my_macro(name, visibility=None):
native.cc_library(
name = name,
srcs = ["main.cc"],
visibility = visibility,
)
如果您需要知道软件包名称(例如,哪个 BUILD
文件正在调用
宏),请使用函数 native.package_name()。
请注意,native
只能在 .bzl
文件中使用,而不能用于 WORKSPACE
或
BUILD
个文件。
宏的标签解析
由于宏在加载阶段进行评估,
系统会对宏中出现的标签字符串(如 "//foo:bar"
)进行解释
是相对于使用宏的 BUILD
文件而言的,而不是相对于
.bzl
文件中定义了令牌。一般情况下,我们并不希望出现
其他代码库中使用的宏,例如由于
是已发布的 Starlark 规则集的一部分。
要获得与 Starlark 规则相同的行为,请使用
Label
构造函数:
# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
native.cc_library(
name = name,
deps = deps + select({
# Due to the use of Label, this label is resolved within @my_ruleset,
# regardless of its site of use.
Label("//config:needs_foo"): [
# Due to the use of Label, this label will resolve to the correct target
# even if the canonical name of @dep_of_my_ruleset should be different
# in the main workspace, such as due to repo mappings.
Label("@dep_of_my_ruleset//tools:foo"),
],
"//conditions:default": [],
}),
**kwargs,
)
调试
bazel query --output=build //my/path:all
会向您展示BUILD
文件是如何生成的。 负责完成评估所有宏、glob、循环都会扩展。认识 限制:select
表达式当前未显示在输出中。您可以根据
generator_function
(其函数 或generator_name
(宏的名称属性):bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'
如需了解
foo
规则在BUILD
文件中生成的确切位置, 可以试试以下技巧。在BUILD
顶部附近插入此行 文件:cc_library(name = "foo")
。运行 Bazel。在以下情况下,您将获得例外情况 创建了规则“foo
”(由于名称冲突),该规则会显示 全栈轨迹。您还可以使用 print 进行调试。它会显示 在加载阶段以
DEBUG
日志行的形式显示消息。极少数 要么移除print
调用,要么将其设为以debugging
参数(默认为False
),然后再将代码提交到
错误
如果要抛出错误,请使用 fail 函数。
向用户清楚说明出了什么问题以及如何修复 BUILD
文件。
无法捕获错误。
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
惯例
符合以下条件的所有公共函数(不以下划线开头的函数): 实例化规则必须具有
name
参数。此参数不应是 可选属性(不提供默认值)。公共函数应使用 Python 后面的文档字符串 惯例。
在
BUILD
文件中,宏的name
参数必须是关键字 参数(不是位置参数)。由宏生成的规则的
name
属性应包含名称 作为前缀。例如,macro(name = "foo")
可以生成cc_library
foo
和 Genrulefoo_gen
。在大多数情况下,可选参数的默认值应为
None
。None
可以直接传递给原生规则,这些规则的处理方式与 您并未传入任何参数。因此,您无需替换 并使用0
、False
或[]
来实现此目的。相反,宏应延迟 因为其默认值可能比较复杂, 。此外,明确设为默认值的参数 看起来与访问时从未设置(或设置为None
)的样式不同 查询语言或构建系统内部机制宏应该有一个可选的
visibility
参数。