1"""Rule to support Bazel in copying its output files to the dist dir outside of 2the standard Bazel output user root. 3""" 4 5load("@bazel_skylib//rules:copy_file.bzl", "copy_file") 6load("//build/bazel_common_rules/exec/impl:embedded_exec.bzl", "embedded_exec") 7 8def _label_list_to_manifest(lst): 9 """Convert the outputs of a label list to manifest content.""" 10 all_dist_files = [] 11 for f in lst: 12 all_dist_files += f[DefaultInfo].files.to_list() 13 return all_dist_files, "\n".join([dist_file.short_path for dist_file in all_dist_files]) 14 15def _generate_dist_manifest_impl(ctx): 16 if ctx.attr.archives: 17 # buildifier: disable=print 18 print("archives is deprecated. Please file a bug if you are using it.") 19 20 # Create a manifest of dist files to differentiate them from other runfiles. 21 dist_manifest = ctx.actions.declare_file(ctx.attr.name + "_dist_manifest.txt") 22 all_dist_files, dist_manifest_content = _label_list_to_manifest(ctx.attr.data) 23 ctx.actions.write( 24 output = dist_manifest, 25 content = dist_manifest_content, 26 ) 27 28 dist_archives_manifest = ctx.actions.declare_file(ctx.attr.name + "_dist_archives_manifest.txt") 29 all_dist_archives, dist_archives_manifest_content = _label_list_to_manifest(ctx.attr.archives) 30 ctx.actions.write( 31 output = dist_archives_manifest, 32 content = dist_archives_manifest_content, 33 ) 34 35 # Create the runfiles object. 36 runfiles = ctx.runfiles(files = all_dist_files + all_dist_archives + [ 37 dist_manifest, 38 dist_archives_manifest, 39 ]) 40 41 return [DefaultInfo(runfiles = runfiles)] 42 43_generate_dist_manifest = rule( 44 implementation = _generate_dist_manifest_impl, 45 doc = """Generate a manifest of files to be dist to a directory.""", 46 attrs = { 47 "data": attr.label_list( 48 allow_files = True, 49 doc = """Files or targets to copy to the dist dir. 50 51In the case of targets, the rule copies the list of `files` from the target's DefaultInfo provider. 52""", 53 ), 54 "archives": attr.label_list( 55 allow_files = [".tar.gz", ".tar"], 56 doc = """Files or targets to be extracted to the dist dir. 57 58In the case of targets, the rule copies the list of `files` from the target's DefaultInfo provider. 59""", 60 ), 61 }, 62) 63 64def copy_to_dist_dir( 65 name, 66 data = None, 67 archives = None, 68 flat = None, 69 prefix = None, 70 strip_components = 0, 71 archive_prefix = None, 72 dist_dir = None, 73 wipe_dist_dir = None, 74 allow_duplicate_filenames = None, 75 mode_overrides = None, 76 log = None, 77 testonly = False, 78 **kwargs): 79 """A dist rule to copy files out of Bazel's output directory into a custom location. 80 81 Example: 82 ``` 83 bazel run //path/to/my:dist_target -- --dist_dir=/tmp/dist 84 ``` 85 86 Run `bazel run //path/to/my:dist_target -- --help` for explanations of 87 options. 88 89 Args: 90 name: name of this rule 91 data: A list of labels, whose outputs are copied to `--dist_dir`. 92 archives: **DEPRECATED**. A list of labels, whose outputs are treated as tarballs and 93 extracted to `--dist_dir`. 94 95 Deprecated: 96 97 This is deprecated due to inactive usage. If you are using it, please file 98 a bug. 99 flat: If true, `--flat` is provided to the script by default. Flatten the distribution 100 directory. 101 strip_components: If specified, `--strip_components <prefix>` is provided to the script. Strip 102 leading components from the existing copied file paths before applying --prefix 103 (if specified). 104 prefix: If specified, `--prefix <prefix>` is provided to the script by default. Path prefix 105 to apply within dist_dir for copied files. 106 archive_prefix: **DEPRECATED**. If specified, `--archive_prefix <prefix>` is provided to the script by 107 default. Path prefix to apply within dist_dir for extracted archives. 108 109 Deprecated: 110 111 This is deprecated due to inactive usage. If you are using it, please file 112 a bug. 113 dist_dir: If specified, `--dist_dir <dist_dir>` is provided to the script by default. 114 115 In particular, if this is a relative path, it is interpreted as a relative path 116 under workspace root when the target is executed with `bazel run`. 117 118 By default, the script will overwrite any files of the same name in `dist_dir`, but preserve 119 any other contents there. This can be overridden with `wipe_dist_dir`. 120 121 See details by running the target with `--help`. 122 wipe_dist_dir: If true, and `dist_dir` already exists, `dist_dir` will be removed prior to 123 copying. 124 allow_duplicate_filenames: If true, duplicate filenames from different sources will be allowed to 125 be copied to the same `dist_dir` (with subsequent sources overwriting previous sources). 126 127 With this option enabled, order matters. The final source of the file listed in `data` will be the 128 final version copied. 129 130 Use of this option is discouraged. Preferably, the input `data` targets would not include labels 131 which produce a duplicate filename. This option is available as a last resort. 132 mode_overrides: Map of glob patterns to octal permissions. If the file path being copied matches the 133 glob pattern, the corresponding permissions will be set in `dist_dir`. Full file paths are used for 134 matching even if `flat = True`. Paths are relative to the workspace root. 135 136 Order matters; the overrides will be stepped through in the order given for each file. To prevent 137 buildifier from sorting the list, use the `# do not sort` magic line. For example: 138 ``` 139 mode_overrides = { 140 # do not sort 141 "**/*.sh": 755, 142 "**/hello_world": 755, 143 "restricted_dir/**": 600, 144 "common/kernel_aarch64/vmlinux": 755, 145 "**/*": 644, 146 }, 147 ``` 148 149 If no `mode_overrides` are provided, the default Bazel output permissions are preserved. 150 log: If specified, `--log <log>` is provided to the script by default. This sets the 151 default log level of the script. 152 153 testonly: If enabled, testonly will also be set on the internal targets 154 for Bazel analysis to succeed due to the nature of testonly enforcement 155 on reverse dependencies. 156 157 See `dist.py` for allowed values and the default value. 158 **kwargs: Additional attributes to the internal rule, e.g. 159 [`visibility`](https://docs.bazel.build/versions/main/visibility.html). 160 161 These additional attributes are only passed to the underlying embedded_exec rule. 162 """ 163 164 unhandled_attrs = [] 165 unsupported_attrs = [] 166 167 default_args = [] 168 if flat: 169 default_args.append("--flat") 170 if strip_components != None: 171 if strip_components < 0: 172 fail("strip_components must greater than 0, but is %s" % strip_components) 173 default_args += ["--strip_components", str(strip_components)] 174 if strip_components: 175 unhandled_attrs.append("strip_components") 176 if prefix != None: 177 default_args += ["--prefix", prefix] 178 unhandled_attrs.append("archive_prefix") 179 if archive_prefix != None: 180 default_args += ["--archive_prefix", archive_prefix] 181 unsupported_attrs.append("archive_prefix") 182 if dist_dir != None: 183 default_args += ["--dist_dir", dist_dir] 184 if wipe_dist_dir: 185 default_args.append("--wipe_dist_dir") 186 unsupported_attrs.append("wipe_dist_dir") 187 if allow_duplicate_filenames: 188 default_args.append("--allow_duplicate_filenames") 189 unsupported_attrs.append("allow_duplicate_filenames") 190 if mode_overrides != None: 191 for (pattern, mode) in mode_overrides.items(): 192 default_args += ["--mode_override", pattern, str(mode)] 193 unhandled_attrs.append("mode_overrides") 194 if log != None: 195 default_args += ["--log", log] 196 197 # Separate flags from BUILD with flags from command line 198 default_args.append("CMDLINE_FLAGS_SENTINEL") 199 200 _generate_dist_manifest( 201 name = name + "_dist_manifest", 202 data = data, 203 archives = archives, 204 testonly = testonly, 205 ) 206 207 copy_file( 208 name = name + "_dist_tool", 209 src = "//build/bazel_common_rules/dist:dist.py", 210 out = name + "_dist.py", 211 ) 212 213 # The dist py_binary tool must be colocated in the same package as the 214 # dist_manifest so that the runfiles directory is the same, and that the 215 # dist_manifest is in the data runfiles of the dist tool. 216 native.py_binary( 217 name = name + "_internal", 218 main = name + "_dist.py", 219 srcs = [name + "_dist.py"], 220 python_version = "PY3", 221 visibility = ["//visibility:public"], 222 data = [name + "_dist_manifest"], 223 testonly = testonly, 224 args = default_args, 225 ) 226 227 # buildifier: disable=print 228 print(""" 229WARNING: copy_to_dist_dir() is deprecated. Use pkg_install() instead. 230 231Suggested edit: 232 233load("@rules_pkg//pkg:install.bzl", "pkg_install") 234load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") 235pkg_files( 236 name = {name_files}, 237 srcs = {data}, 238 strip_prefix = {strip_prefix}, 239 visibility = ["//visibility:private"], 240) 241pkg_install( 242 name = {name}, 243 srcs = [{colon_name_files}], 244 destdir = {dist_dir}, 245){unhandled_attrs}{unsupported_attrs}""".format( 246 name = repr(name), 247 name_files = repr(name + "_files"), 248 colon_name_files = repr(":" + name + "_files"), 249 dist_dir = repr(dist_dir), 250 strip_prefix = "strip_prefix.files_only()" if flat else "None", 251 data = repr(data), 252 unhandled_attrs = "" if not unhandled_attrs else "\nThe following attributes are not converted; read the API reference of rules_pkg " + 253 "for an alternative: {}".format(repr(unhandled_attrs)), 254 unsupported_attrs = "" if not unsupported_attrs else "\nThe following attributes may not be supported by rules_pkg: {}".format(repr(unsupported_attrs)), 255 )) 256 257 embedded_exec( 258 name = name, 259 actual = name + "_internal", 260 testonly = testonly, 261 **kwargs 262 ) 263