"""Rule and corresponding provider that joins a label pointing to a TreeArtifact with a path nested within that directory """ load("//lib:utils.bzl", _to_label = "to_label") DirectoryPathInfo = provider( doc = "Joins a label pointing to a TreeArtifact with a path nested within that directory.", fields = { "directory": "a TreeArtifact (ctx.actions.declare_directory)", "path": "path relative to the directory", }, ) def _directory_path(ctx): if not ctx.file.directory.is_directory: msg = "Expected directory to be a TreeArtifact (ctx.actions.declare_directory) but {} is either a source file or does not exist.".format(ctx.file.directory) fail(msg) return [DirectoryPathInfo(path = ctx.attr.path, directory = ctx.file.directory)] directory_path = rule( doc = """Provide DirectoryPathInfo to reference some path within a directory. Otherwise there is no way to give a Bazel label for it.""", implementation = _directory_path, attrs = { "directory": attr.label( doc = "a TreeArtifact (ctx.actions.declare_directory)", mandatory = True, allow_single_file = True, ), "path": attr.string( doc = "path relative to the directory", mandatory = True, ), }, provides = [DirectoryPathInfo], ) def make_directory_path(name, directory, path, **kwargs): """Helper function to generate a directory_path target and return its label. Args: name: unique name for the generated `directory_path` target directory: `directory` attribute passed to generated `directory_path` target path: `path` attribute passed to generated `directory_path` target **kwargs: parameters to pass to generated `output_files` target Returns: The label `name` """ directory_path( name = name, directory = directory, path = path, **kwargs ) return _to_label(name) def make_directory_paths(name, dict, **kwargs): """Helper function to convert a dict of directory to path mappings to directory_path targets and labels. For example, ``` make_directory_paths("my_name", { "//directory/artifact:target_1": "file/path", "//directory/artifact:target_2": ["file/path1", "file/path2"], }) ``` generates the targets, ``` directory_path( name = "my_name_0", directory = "//directory/artifact:target_1", path = "file/path" ) directory_path( name = "my_name_1", directory = "//directory/artifact:target_2", path = "file/path1" ) directory_path( name = "my_name_2", directory = "//directory/artifact:target_2", path = "file/path2" ) ``` and the list of targets is returned, ``` [ "my_name_0", "my_name_1", "my_name_2", ] ``` Args: name: The target name to use for the generated targets & labels. The names are generated as zero-indexed `name + "_" + i` dict: The dictionary of directory keys to path or path list values. **kwargs: additional parameters to pass to each generated target Returns: The label of the generated `directory_path` targets named `name + "_" + i` """ labels = [] pairs = [] for directory, val in dict.items(): if type(val) == "list": for path in val: pairs.append((directory, path)) elif type(val) == "string": pairs.append((directory, val)) else: fail("Value must be a list or string") for i, pair in enumerate(pairs): directory, path = pair labels.append(make_directory_path( "%s_%d" % (name, i), directory, path, **kwargs )) return labels