У меня есть правило Bazel, которое создает выходной файл и может при желании иметь зависимость от аналогичной цели, например:
load(":node.bzl", "node")
node(
name = "one",
src = "one.txt",
)
node(
name = "two",
src = "two.txt",
dep = "one",
)
Теперь, если я вызываю bazel build :two
, я хочу, чтобы он build: сначала один, а затем: два, каждая из которых создает выходной файл. Казалось бы, это так же просто, как написать файл node.bzl, например
def _node_impl(ctx):
out_file = ctx.actions.declare_file(ctx.attr.name)
print("ok running: out_file={} src={}".format(out_file.path, ctx.file.src.path))
ctx.actions.run_shell(
outputs = [out_file],
inputs = [ctx.file.src],
command = ("echo {} >{}".format(ctx.file.src.path, out_file.path)),
)
print("done running")
return [
DefaultInfo(
files = depset([out_file]),
),
]
node = rule(
implementation = _node_impl,
attrs = {
"src": attr.label(
mandatory = True,
allow_single_file = True,
),
"dep": attr.label(
),
},
)
, но, увы, это создаст только файл «два», а не зависимый «один»:
INFO: Starting clean (this may take a while). Consider using --async if the clean takes more than several minutes.
bazel build :two
DEBUG: nok/node.bzl:3:12: ok running: out_file=bazel-out/k8-fastbuild/bin/one src=one.txt
DEBUG: nok/node.bzl:9:12: done running
DEBUG: nok/node.bzl:3:12: ok running: out_file=bazel-out/k8-fastbuild/bin/two src=two.txt
DEBUG: nok/node.bzl:9:12: done running
INFO: Analyzed target //:two (4 packages loaded, 9 targets configured).
INFO: Found 1 target...
Target //:two up-to-date:
bazel-bin/two
INFO: Elapsed time: 0.491s, Critical Path: 0.05s
INFO: 1 process: 1 linux-sandbox.
INFO: Build completed successfully, 2 total actions
Обратите внимание, что вывод отладки подтверждает, что он видит зависимость при построении графа зависимостей, когда дело доходит до выполнения зависимой цели, он решает пропустить ее.
Теперь я ' Мы добились этого, добавив поставщика, который добавляет транзитивную зависимость к депонированию выходных файлов, но разве bazel не должен делать это автоматически? Вот мое решение:
NodeProv = provider(fields = ["out_file"])
def _node_impl(ctx):
out_file = ctx.actions.declare_file(ctx.attr.name)
print("ok running: out_file={} src={}".format(out_file.path, ctx.file.src.path))
ctx.actions.run_shell(
outputs = [out_file],
inputs = [ctx.file.src],
command = ("echo {} >{}".format(ctx.file.src.path, out_file.path)),
)
print("done running")
dep_file = [ctx.attr.dep[NodeProv].out_file] if ctx.attr.dep else []
return [
DefaultInfo(
files = depset([out_file], transitive = [depset(dep_file)]),
),
NodeProv(
out_file = out_file
),
]
node = rule(
implementation = _node_impl,
attrs = {
"src": attr.label(
mandatory = True,
allow_single_file = True,
),
"dep": attr.label(
providers = [(NodeProv)],
),
},
)
Это действительно создает обе цели:
INFO: Analyzed target //:two (4 packages loaded, 9 targets configured).
INFO: Found 1 target...
Target //:two up-to-date:
bazel-bin/one
bazel-bin/two
INFO: Elapsed time: 0.532s, Critical Path: 0.07s
INFO: 2 processes: 2 linux-sandbox.
INFO: Build completed successfully, 3 total actions
Что думает коллективный коллективный разум, это рекомендуемый способ решения этой проблемы или есть лучший подход ?