过滤源文件以获取自定义规则

时间:2018-11-12 20:49:38

标签: bazel

我使用bazel宏在源文件的子集上运行python测试。与此类似:

def report(name, srcs):
    source_labels = [file for file in srcs if file.startswith("a")]
    if len(source_labels) == 0:
        return;

    source_filenames = ["$(location %s)" % x for x in source_labels]

    native.py_test(
        name = name + "_report",
        srcs = ["report_tool"],
        data = source_labels,
        main = "report_tool.py",
        args = source_filenames,
    )

report("foo", ["foo.hpp", "afoo.hpp"])

这很好,直到我的一个源文件开始使用select并出现错误:

File "/home/david/foo/report.bzl", line 47, in report
    [file for file in srcs if file.startswith("a")]
type 'select' is not iterable

我试图将代码移至bazel规则,但随后出现另一个错误,即py_test无法在分析阶段使用。

1 个答案:

答案 0 :(得分:3)

select引起错误的原因是在加载阶段对宏进行了评估,而在分析阶段才对selects进行了评估(请参见Extension Overview)。 / p>

类似地,py_test不能用于规则实现中,因为规则实现是在分析阶段进行评估的,而py_test则需要在加载阶段进行加载。

一种解决方法是创建一个单独的Starlark规则,该规则将获取标签列表,并仅创建一个带有标签中每个文件名的文件。然后py_test将该文件作为数据并从那里加载其他文件。像这样:

def report(name, srcs):

  file_locations_label = "_" + name + "_file_locations"
  _generate_file_locations(
    name = file_locations_label,
    labels = srcs
  )

  native.py_test(
      name = name + "_report",
      srcs = ["report_tool.py"],
      data = srcs + [file_locations_label],
      main = "report_tool.py",
      args = ["$(location %s)" % file_locations_label]
  )


def _generate_file_locations_impl(ctx):
  paths = []
  for l in ctx.attr.labels:
    f = l.files.to_list()[0]
    if f.basename.startswith("a"):
      paths.append(f.short_path)
  ctx.actions.write(ctx.outputs.file_paths, "\n".join(paths))
  return DefaultInfo(runfiles = ctx.runfiles(files = [ctx.outputs.file_paths]))

_generate_file_locations = rule(
  implementation = _generate_file_locations_impl,
  attrs = { "labels": attr.label_list(allow_files = True) },
  outputs = { "file_paths": "%{name}_files" },
)

这有一个缺点:因为py_test必须依赖所有源,所以即使更改的唯一文件是被忽略的文件,py_test也将重新运行。 (如果这是一个严重的缺点,那么至少有一种方法可以解决,即也使_generate_file_locations过滤文件,并使py_test仅依赖于_generate_file_locations。这可以通过{{3 }})

更新:

由于测试报告工具来自外部存储库,无法轻松修改,因此这是另一种可能会更好的方法。 Starlark规则本身可以是使用报告工具作为测试可执行文件的测试规则,而不是像上面那样创建创建params文件(包含要处理的路径的文件)的规则,

def _report_test_impl(ctx):
  filtered_srcs = []
  for f in ctx.attr.srcs:
    f = f.files.to_list()[0]
    if f.basename.startswith("a"):
      filtered_srcs.append(f)

  report_tool = ctx.attr._report_test_tool
  ctx.actions.write(
      output = ctx.outputs.executable,
      content = "{report_tool} {paths}".format(
          report_tool = report_tool.files_to_run.executable.short_path,
          paths = " ".join([f.short_path for f in filtered_srcs]))
  )

  runfiles = ctx.runfiles(files = filtered_srcs).merge(
      report_tool.default_runfiles)
  return DefaultInfo(runfiles = runfiles)

report_test = rule(
  implementation = _report_test_impl,
  attrs = {
      "srcs": attr.label_list(allow_files = True),
      "_report_test_tool": attr.label(default="//:report_test_tool"),
  },
  test = True,
)

这要求测试报告工具在某个地方py_binary处,以便上面的测试规则可以依赖它:

py_binary(
    name = "report_test_tool",
    srcs = ["report_tool.py"],
    main = "report_tool.py",
)