Search code examples
bazel

How do I wrap all files from a Bazel filegroup into a directory?


How can I wrap all files of a filegroup in a directory and create a new target out of that?

My use case is that I have a filegroup inside test-data/BUILD.bazel but the test runner expects the files inside a directory called test-data.

filegroup(
    name = "render-test-files",
    srcs = glob([
        "integration/**",
        "tests/**",
        "linux-gcc8-release/**",
        "expectations/platform-all/**",
        "ignores/**",
    ], allow_empty = False)
    visibility = [
        "//render-test:__subpackages__",
    ],
)

I cannot use a genrule because it requires all output files to be listed explicitly.


Solution

  • To collect files into a directory and have that considered a build artifact, you can write a custom rule returning TreeArtifact using declare_directory action. In a very naive/trivial form (not portable, not checking if sources contain any files, ...) something like:

    def _impl(ctx):
        outdir = ctx.actions.declare_directory("{}.dir".format(ctx.attr.name))
        args = ctx.actions.args()
        args.add(outdir.path)
        args.add_all(ctx.files.srcs)
        ctx.actions.run_shell(
            outputs = [outdir],
            inputs = ctx.files.srcs,
            arguments = [args],
            command = """
    outdir="$1";
    shift;
    cp "$@" "${outdir}"
                    """,
        )
        return [
            DefaultInfo(files = depset([outdir])),
        ]
    
    dirrule = rule(
        _impl,
        attrs = {
            "srcs": attr.label_list(
                allow_files = True,
                mandatory = True,
            ),
        },
    )
    
    

    Which could then be used in a BUILD file with mydir now being directory containing copies of file0[12]:

    load(":dirrule.bzl", "dirrule")
    
    dirrule(
        name = "mydir",
        srcs = [
            "//myfiles:file01",
            "//myfiles:file02",
        ],
    )
    

    I would personally rather try to adapt the receiving tool to actually handle list of inputs... or if impossible (beyond my control), probably rather wrap its use (call) from (I presume already the case anyways) custom rule which would take that list and populate the tree (preferably symlink even if the tool isn't way broken) in shape it expects.