报告问题 查看来源 每晚 · 7.2。 · 7.1敬上 · 7.0 · 6.5 条 · 6.4

本页介绍了使用宏的基础知识,并介绍了典型用例, 调试和惯例。

宏是从 BUILD 文件中调用的函数,可以实例化规则。 宏主要用于现有规则的封装和代码重复使用 和其他宏。在 加载阶段,那么宏已不存在, 而 Bazel 只能看到一组具体的实例化规则。

用法

宏的典型用例是重复使用规则。

例如,BUILD 文件中的 genrule 会使用 带有硬编码的 some_arg 参数的 //:generator

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

如果要生成更多具有不同参数的文件, 将此代码提取到宏函数中。我们不妨将宏 file_generator 命名为 具有 namearg 参数。将 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 文件中使用,而不能用于 WORKSPACEBUILD 文件。

宏的标签解析

由于宏在加载阶段进行评估, 系统会对宏中出现的标签字符串(如 "//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 和 Genrule foo_gen

  • 在大多数情况下,可选参数的默认值应为 NoneNone 可以直接传递给原生规则,原生规则的处理方式与 您并未传入任何参数。因此,您无需替换 并使用 0False[] 来实现此目的。相反,宏应延迟 因为其默认值可能比较复杂, 。此外,明确设为默认值的参数 看起来与访问时从未设置(或设置为 None)的样式不同 查询语言或构建系统内部机制

  • 宏应该有一个可选的 visibility 参数。