我使用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无法在分析阶段使用。
答案 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",
)