Bazel的扩展代码

Bazel的扩展代码都是写在.bzl文件中,然后通过load()函数在WORKSPACE文件或者BUILD文件中调用。比如:

load("//build_tools/rules:maprule.bzl", "maprule")

可以加载的扩展有:rule、函数、常量。在.bzl文件中_开头的符号是私有的,不能被别的文件load。目前所有的.bzl文件都是可见的,不需要额外的export_files操作。
.bzl文件都是由Skylark语言所写的,这是一个类Python的语言,不过有些地方与python稍有不同。我们无需研究特别深入,不过这个文档Skylark简介推荐看下。

Macro和Rule

这是bazel的扩展中非常重要的两个概念,这里会做个大体的介绍,下面有细节介绍。

一个macro是指一个rule的实例化,也就是说在macro中会调用一些rule来做事情。当一个BUILD文件里面很多内容具有重复性和复杂性的时候,就可以使用macro来做代码的重复使用的简化。

rule比macro要强大的多,它可以深入到bazel的内部并且完全控制正在发生的事情。比如可以传递信息到其他的rule中。

总的来说:如果你想重复使用简单的逻辑,那么就使用macro。如果macro变得非常复杂,那么就推荐将其变成一个rule。比如对一个新的编程语言的支持,就是通过rule来实现的。不过rule只是给高级用户使用的,期待绝大多数的用户永远不会需要写一个rule,而只是load和call已经存在的rule就够了。

Macro细节

创建macro

macro没有别的功能,只是用来做封装和方便代码重用。正如其名,这其实是个宏,只在bazel的load阶段完成之前存在,load完成之后macro就不村子了,bazel只会看到这些宏所创建的rules。

native rules(也就是不需要load()语句加载的rules)可以通过native 模块来调用,比如:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

如果需要在macro中知道package的名字(在BUILD中调用这个宏的package),可以在macro中参考使用native.package_name()

举例

  • 创建rule的macro

empty.bzl: # 在这个文件中定义macro创建一个rule

def _impl(ctx):
  print("This rule does nothing")

empty = rule(implementation=_impl)

extension.bzl: # 在这个文件中调用

# Loading the rule. The rule doesn't have to be in a separate file.
load("//pkg:empty.bzl", "empty")

def macro(name, visibility=None):
  # Creating the rule.
  empty(name = name, visibility = visibility)

BUILD: # 在BUILD中调用

load("//pkg:extension.bzl", "macro")

macro(name = "myrule")
  • 创建native rule的macro

extension.bzl: # 定义macro,且实例化一个genrule

def macro(name, visibility=None):
  # Creating a native genrule.
  native.genrule(
      name = name,
      outs = [name + ".txt"],
      cmd = "echo hello > $@",
      visibility = visibility,
  )

BUILD:  # 调用macro

load("//pkg:extension.bzl", "macro")

macro(name = "myrule")
  • 创建多个rule

def _impl(ctx):
  return struct([...],
                # When instrumenting this rule, again hide implementation from
                # users.
                instrumented_files(
                  source_attributes = ["srcs", "csrcs"],
                  dependency_attributes = ["deps", "cdeps"]))

# This rule is private and can only be accessed from the current file.
_cc_and_something_else_binary = rule(implementation=_impl)


# This macro is public, it's the public interface to instantiate the rule.
def cc_and_something_else_binary(name, srcs, deps, csrcs, cdeps):
   cc_binary_name = "%s.cc_binary" % name

   native.cc_binary(
      name = cc_binary_name,
      srcs = csrcs,
      deps = cdeps,
      visibility = ["//visibility:private"]
  )

  _cc_and_something_else_binary(
    name = name,
    srcs = srcs,
    deps = deps,
    # A label attribute so that this depends on the internal rule.
    cc_binary = cc_binary_name,
    # Redundant labels attributes so that the rule with this target name knows
    # about everything it would know about if cc_and_something_else_binary
    # were an actual rule instead of a macro.
    csrcs = csrcs,
    cdeps = cdeps)

Debugging

bazel query –output=build //my/path:all可以看到BUILD文件在展开macro,globs,loops之后的模样。

Errors

如果需要报错,可以使用fail函数:

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

上述内容的一个样例:

假设BUILD内之前之前有这么一个genrule():

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

现在想在BUILD中重复使用上面的rule:

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

可以看到需要在path/generator.bzl定义如下:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location generator) %s > $@" % arg,
    tools = ["//test:generator"],
    visibility = visibility,
  )

接下来如果想要知道这个宏最后展开内容是什么,可以:

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//test:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location generator) some_arg > $@",
)

转载需保留链接来源:VCBeta.CN » Bazel的扩展代码

赞 (3)