bazelbazel-extra-action

How can you run the executables of other rules within a Bazel rule?


Say I have a custom rule, my_object. It looks like:

my_object(
  name = "foo",
  deps = [
    //services/image-A:push,
    //services/image-B:push,
  ]
)

Where the labels in deps are rules_docker's container_push rules.

I want to be able to bazel run //:foo and have it push the Docker images within the deps list. How do I do this?

This seems to be a specific case of just generally wanting to run the executables of other rules within the executable of a custom rule.


Solution

  • The thing to do here is to have my_object output an executable that executes the other executables.

    Consider this example:

    def _impl1(ctx):
      ctx.actions.write(
        output = ctx.outputs.executable,
        is_executable = True,
        content = "echo %s 123" % ctx.label.name)
      return DefaultInfo(executable = ctx.outputs.executable)
    
    
    exec_rule1 = rule(
      implementation = _impl1,
      executable = True,
    )
    
    
    def _impl2(ctx):
    
      executable_paths = []
      runfiles = ctx.runfiles()
      for dep in ctx.attr.deps:
        # the "./" is needed if the executable is in the current directory
        # (i.e. in the workspace root)
        executable_paths.append("./" + dep.files_to_run.executable.short_path)
        # collect the runfiles of the other executables so their own runfiles
        # will be available when the top-level executable runs
        runfiles = runfiles.merge(dep.default_runfiles)
    
      ctx.actions.write(
        output = ctx.outputs.executable,
        is_executable = True,
        content = "\n".join(executable_paths))
    
      return DefaultInfo(
        executable = ctx.outputs.executable,
        runfiles = runfiles)
    
    
    exec_rule2 = rule(
      implementation = _impl2,
      executable = True,
      attrs = {
        "deps": attr.label_list(),
      },
    )
    

    BUILD.bazel:

    load(":defs.bzl", "exec_rule1", "exec_rule2")
    
    exec_rule1(name = "foo")
    exec_rule1(name = "bar")
    exec_rule2(name = "baz", deps = [":foo", ":bar"])
    

    and then running it:

    $ bazel run //:baz
    INFO: Analyzed target //:baz (4 packages loaded, 19 targets configured).
    INFO: Found 1 target...
    Target //:baz up-to-date:
      bazel-bin/baz
    INFO: Elapsed time: 0.211s, Critical Path: 0.01s
    INFO: 0 processes.
    INFO: Build completed successfully, 6 total actions
    INFO: Build completed successfully, 6 total actions
    foo 123
    bar 123