• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Rule to support Bazel in copying its output files to the dist dir outside of
2# the standard Bazel output user root.
3
4load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
5
6def _label_list_to_manifest(lst):
7    """Convert the outputs of a label list to manifest content."""
8    all_dist_files = []
9    for f in lst:
10        all_dist_files += f[DefaultInfo].files.to_list()
11    return all_dist_files, "\n".join([dist_file.short_path for dist_file in all_dist_files])
12
13def _generate_dist_manifest_impl(ctx):
14    # Create a manifest of dist files to differentiate them from other runfiles.
15    dist_manifest = ctx.actions.declare_file(ctx.attr.name + "_dist_manifest.txt")
16    all_dist_files, dist_manifest_content = _label_list_to_manifest(ctx.attr.data)
17    ctx.actions.write(
18        output = dist_manifest,
19        content = dist_manifest_content,
20    )
21
22    dist_archives_manifest = ctx.actions.declare_file(ctx.attr.name + "_dist_archives_manifest.txt")
23    all_dist_archives, dist_archives_manifest_content = _label_list_to_manifest(ctx.attr.archives)
24    ctx.actions.write(
25        output = dist_archives_manifest,
26        content = dist_archives_manifest_content,
27    )
28
29    # Create the runfiles object.
30    runfiles = ctx.runfiles(files = all_dist_files + all_dist_archives + [
31        dist_manifest,
32        dist_archives_manifest,
33    ])
34
35    return [DefaultInfo(runfiles = runfiles)]
36
37_generate_dist_manifest = rule(
38    implementation = _generate_dist_manifest_impl,
39    doc = """Generate a manifest of files to be dist to a directory.""",
40    attrs = {
41        "data": attr.label_list(
42            allow_files = True,
43            doc = """Files or targets to copy to the dist dir.
44
45In the case of targets, the rule copies the list of `files` from the target's DefaultInfo provider.
46""",
47        ),
48        "archives": attr.label_list(
49            allow_files = [".tar.gz", ".tar"],
50            doc = """Files or targets to be extracted to the dist dir.
51
52In the case of targets, the rule copies the list of `files` from the target's DefaultInfo provider.
53""",
54        ),
55    },
56)
57
58def copy_to_dist_dir(
59        name,
60        data = None,
61        archives = None,
62        flat = None,
63        prefix = None,
64        archive_prefix = None,
65        dist_dir = None):
66    """A dist rule to copy files out of Bazel's output directory into a custom location.
67
68    Example:
69    ```
70    bazel run //path/to/my:dist_target -- --dist_dir=/tmp/dist
71    ```
72
73    Run `bazel run //path/to/my:dist_target -- --help` for explanations of
74    options.
75
76    Args:
77        name: name of this rule
78        data: A list of labels, whose outputs are copied to `--dist_dir`.
79        archives: A list of labels, whose outputs are treated as tarballs and
80          extracted to `--dist_dir`.
81        flat: If true, `--flat` is provided to the script by default. Flatten the distribution
82          directory.
83        prefix: If specified, `--prefix <prefix>` is provided to the script by default. Path prefix
84          to apply within dist_dir for copied files.
85        archive_prefix: If specified, `--archive_prefix <prefix>` is provided to the script by
86          default. Path prefix to apply within dist_dir for extracted archives.
87        dist_dir: If specified, `--dist_dir <dist_dir>` is provided to the script by default.
88
89          In particular, if this is a relative path, it is interpreted as a relative path
90          under workspace root when the target is executed with `bazel run`.
91          See details by running the target with `--help`.
92    """
93
94    default_args = []
95    if flat:
96        default_args.append("--flat")
97    if prefix != None:
98        default_args += ["--prefix", prefix]
99    if archive_prefix != None:
100        default_args += ["--archive_prefix", archive_prefix]
101    if dist_dir != None:
102        default_args += ["--dist_dir", dist_dir]
103
104    _generate_dist_manifest(
105        name = name + "_dist_manifest",
106        data = data,
107        archives = archives,
108    )
109
110    copy_file(
111        name = name + "_dist_tool",
112        src = "//build/bazel_common_rules/dist:dist.py",
113        out = name + "_dist.py",
114    )
115
116    # The dist py_binary tool must be colocated in the same package as the
117    # dist_manifest so that the runfiles directory is the same, and that the
118    # dist_manifest is in the data runfiles of the dist tool.
119    native.py_binary(
120        name = name,
121        main = name + "_dist.py",
122        srcs = [name + "_dist.py"],
123        python_version = "PY3",
124        visibility = ["//visibility:public"],
125        data = [name + "_dist_manifest"],
126        args = default_args,
127    )
128