def _self_extract_binary(ctx):
    """Implementation for the self_extract_binary rule."""

    # This is a bit complex for stripping out timestamps
    zip_artifact = ctx.actions.declare_file(ctx.label.name + ".zip")
    touch_empty_files = [
        "mkdir -p $(dirname ${tmpdir}/%s); touch ${tmpdir}/%s" % (f, f)
        for f in ctx.attr.empty_files
    ]
    cp_resources = [
        ("mkdir -p $(dirname ${tmpdir}/%s)\n" % r.short_path +
         "cp %s ${tmpdir}/%s" % (r.path, r.short_path))
        for r in ctx.files.resources
    ]
    cp_flatten_resources = [
        "cp %s ${tmpdir}/%s" % (r.path, r.basename)
        for r in ctx.files.flatten_resources
    ]
    ctx.actions.run_shell(
        inputs = ctx.files.resources + ctx.files.flatten_resources,
        outputs = [zip_artifact],
        command = "\n".join([
            "tmpdir=$(mktemp -d ${TMPDIR:-/tmp}/tmp.XXXXXXXX)",
            "trap \"rm -fr ${tmpdir}\" EXIT",
        ] + touch_empty_files + cp_resources + cp_flatten_resources + [
            "find ${tmpdir} -exec touch -t 198001010000.00 '{}' ';'",
            "(d=${PWD}; cd ${tmpdir}; zip -rq ${d}/%s *)" % zip_artifact.path,
        ]),
        mnemonic = "ZipBin",
    )
    ctx.actions.run_shell(
        inputs = [ctx.file.launcher, zip_artifact],
        outputs = [ctx.outputs.executable],
        command = "\n".join([
            "cat %s %s > %s" % (
                ctx.file.launcher.path,
                zip_artifact.path,
                ctx.outputs.executable.path,
            ),
            "zip -qA %s" % ctx.outputs.executable.path,
        ]),
        mnemonic = "BuildSelfExtractable",
    )

self_extract_binary = rule(
    _self_extract_binary,
    attrs = {
        "launcher": attr.label(
            mandatory = True,
            allow_single_file = True,
        ),
        "empty_files": attr.string_list(default = []),
        "resources": attr.label_list(
            default = [],
            allow_files = True,
        ),
        "flatten_resources": attr.label_list(
            default = [],
            allow_files = True,
        ),
    },
    executable = True,
)
