bazelrulesstarlark

Chain expand_template and run in one bazel rule


I am trying to write a custom rule, where I first generate a file from a template, then pass this file to a script to generate some c++ headers that are the output of my rule.

def _msg_library_impl(ctx):
  # For each target in deps, print its label and files.
  for source in enumerate(ctx.attr.srcs):
    print("File = " + str(source))
  out_header = ctx.actions.declare_file("some_header.hpp")
  out_arguments = ctx.actions.declare_file("arguments.json")
  ctx.actions.expand_template(
        template = ctx.file._arguments_file,
        output = out_arguments,
        substitutions = {
            "{output_dir}": out_header.dirname,
            "{idl_tuples}": out_header.path,
        },
  )
  args = ctx.actions.args()
  args.add("--arguments-file")
  args.add(out_arguments)
  ctx.actions.run(
      outputs = [out_header],
      progress_message = "Generating headers '{}'".format(out_header.short_path),
      executable = ctx.executable._generator,
      arguments = [args],
  )
  return [
    CcInfo(compilation_context=cc_common.create_compilation_context(
        includes=depset([out_header.dirname]),
        headers=depset([out_header])))
  ]



msg_library = rule(
    implementation = _msg_library_impl,
    output_to_genfiles = True,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        "outs": attr.output_list(),
        "_arguments_file": attr.label(
            allow_single_file = [".json"],
            default = Label("//examples/generation_rule:arguments_template.json"),
        ),
        "_generator": attr.label(
            default = Label("//examples/generation_rule:generator"),
            executable = True,
            cfg = "exec"
        ),
    },
)

Here, generator is a python library that, given an input file provided to srcs and an arguments file generates headers.

The issue that I am facing is that it seems that the expand_template doesn't actually run before run is called, so the generated file is nowhere to be found. What am I doing wrong here? Did I misunderstand how things work?


Solution

  • You need to indicate the file is an input to the action, in addition to passing its path in the arguments. Change the ctx.actions.run to:

      ctx.actions.run(
          outputs = [out_header],
          inputs = [out_arguments],
          progress_message = "Generating headers '{}'".format(out_header.short_path),
          executable = ctx.executable._generator,
          arguments = [args],
      )