load("@bazel_skylib//:lib.bzl", "shell")

_content_prefix = """#!/bin/bash

set -euo pipefail

"""

def _multirun_impl(ctx):
    transitive_depsets = []
    content = [_content_prefix]

    for command in ctx.attr.commands:
        if command.files_to_run == None:
            fail("%s is not executable" % command.label, attr = "commands")
        exe = command.files_to_run.executable
        if exe == None:
            fail("%s does not have an executable file" % command.label, attr = "commands")

        transitive_depsets.append(command.default_runfiles.files)
        content.append("echo Running %s\n./%s\n" % (shell.quote(str(command.label)), shell.quote(exe.short_path)))

    out_file = ctx.actions.declare_file(ctx.label.name + ".bash")
    ctx.actions.write(
        output = out_file,
        content = "".join(content),
        is_executable = True,
    )
    runfiles = ctx.runfiles(
        transitive_files = depset([], transitive = transitive_depsets),
    )
    return [DefaultInfo(
        files = depset([out_file]),
        runfiles = runfiles,
        executable = out_file,
    )]

_multirun = rule(
    implementation = _multirun_impl,
    attrs = {
        "commands": attr.label_list(
            allow_empty = True,  # this is explicitly allowed - generated invocations may need to run 0 targets
            mandatory = True,
            allow_files = True,
            doc = "Targets to run in specified order",
            cfg = "host",
        ),
    },
    executable = True,
)

def multirun(name, **kwargs):
    tags = kwargs.get("tags", [])
    if "manual" not in tags:
        tags.append("manual")
        kwargs["tags"] = tags
    _multirun(
        name = name,
        **kwargs
    )

load("@bazel_skylib//:lib.bzl", "shell")

_content_prefix = """#!/bin/bash

set -euo pipefail

"""

def _multirun_impl(ctx):
    transitive_depsets = []
    content = [_content_prefix]

    for command in ctx.attr.commands:
        info = command[DefaultInfo]
        if info.files_to_run == None:
            fail("%s is not executable" % command.label, attr = "commands")
        exe = info.files_to_run.executable
        if exe == None:
            fail("%s does not have an executable file" % command.label, attr = "commands")

        transitive_depsets.append(info.default_runfiles.files)
        content.append("echo Running %s\n./%s\n" % (shell.quote(str(command.label)), shell.quote(exe.short_path)))

    out_file = ctx.actions.declare_file(ctx.label.name + ".bash")
    ctx.actions.write(
        output = out_file,
        content = "".join(content),
        is_executable = True,
    )
    runfiles = ctx.runfiles(
        transitive_files = depset([], transitive = transitive_depsets),
    )
    return [DefaultInfo(
        files = depset([out_file]),
        runfiles = runfiles,
        executable = out_file,
    )]

_multirun = rule(
    implementation = _multirun_impl,
    attrs = {
        "commands": attr.label_list(
            allow_empty = True,  # this is explicitly allowed - generated invocations may need to run 0 targets
            mandatory = True,
            allow_files = True,
            doc = "Targets to run in specified order",
            cfg = "host",
        ),
    },
    executable = True,
)

def multirun(name, **kwargs):
    tags = kwargs.get("tags", [])
    if "manual" not in tags:
        tags.append("manual")
        kwargs["tags"] = tags
    _multirun(
        name = name,
        **kwargs
    )

Access attributes via the DefaultInfo provider