xref: /aosp_15_r20/build/bazel_common_rules/dist/dist.py (revision 7887bec861e78e44e4e86ae7a52515235a00b778)
1*7887bec8SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*7887bec8SAndroid Build Coastguard Worker#
3*7887bec8SAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project
4*7887bec8SAndroid Build Coastguard Worker#
5*7887bec8SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*7887bec8SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*7887bec8SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*7887bec8SAndroid Build Coastguard Worker#
9*7887bec8SAndroid Build Coastguard Worker#   http://www.apache.org/licenses/LICENSE-2.0
10*7887bec8SAndroid Build Coastguard Worker#
11*7887bec8SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*7887bec8SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*7887bec8SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*7887bec8SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*7887bec8SAndroid Build Coastguard Worker# limitations under the License.
16*7887bec8SAndroid Build Coastguard Worker"""A script to copy outputs from Bazel rules to a user specified dist directory.
17*7887bec8SAndroid Build Coastguard Worker
18*7887bec8SAndroid Build Coastguard WorkerThis script is only meant to be executed with `bazel run`. `bazel build <this
19*7887bec8SAndroid Build Coastguard Workerscript>` doesn't actually copy the files, you'd have to `bazel run` a
20*7887bec8SAndroid Build Coastguard Workercopy_to_dist_dir target.
21*7887bec8SAndroid Build Coastguard Worker
22*7887bec8SAndroid Build Coastguard WorkerThis script copies files from Bazel's output tree into a directory specified by
23*7887bec8SAndroid Build Coastguard Workerthe user. It does not check if the dist dir already contains the file, and will
24*7887bec8SAndroid Build Coastguard Workersimply overwrite it.
25*7887bec8SAndroid Build Coastguard Worker
26*7887bec8SAndroid Build Coastguard WorkerOne approach is to wipe the dist dir every time this script runs, but that may
27*7887bec8SAndroid Build Coastguard Workerbe overly destructive and best left to an explicit rm -rf call outside of this
28*7887bec8SAndroid Build Coastguard Workerscript.
29*7887bec8SAndroid Build Coastguard Worker
30*7887bec8SAndroid Build Coastguard WorkerAnother approach is to error out if the file being copied already exist in the
31*7887bec8SAndroid Build Coastguard Workerdist dir, or perform some kind of content hash checking.
32*7887bec8SAndroid Build Coastguard Worker"""
33*7887bec8SAndroid Build Coastguard Worker
34*7887bec8SAndroid Build Coastguard Workerimport argparse
35*7887bec8SAndroid Build Coastguard Workerimport collections
36*7887bec8SAndroid Build Coastguard Workerimport fnmatch
37*7887bec8SAndroid Build Coastguard Workerimport glob
38*7887bec8SAndroid Build Coastguard Workerimport logging
39*7887bec8SAndroid Build Coastguard Workerimport os
40*7887bec8SAndroid Build Coastguard Workerimport pathlib
41*7887bec8SAndroid Build Coastguard Workerimport shutil
42*7887bec8SAndroid Build Coastguard Workerimport sys
43*7887bec8SAndroid Build Coastguard Workerimport tarfile
44*7887bec8SAndroid Build Coastguard Workerimport textwrap
45*7887bec8SAndroid Build Coastguard Worker
46*7887bec8SAndroid Build Coastguard Worker
47*7887bec8SAndroid Build Coastguard Worker_CMDLINE_FLAGS_SENTINEL = "CMDLINE_FLAGS_SENTINEL"
48*7887bec8SAndroid Build Coastguard Worker
49*7887bec8SAndroid Build Coastguard Worker# Arguments that should not be specified in the command line, but only
50*7887bec8SAndroid Build Coastguard Worker# in BUILD files.
51*7887bec8SAndroid Build Coastguard Worker_DEPRECATED_CMDLINE_OPTIONS = {
52*7887bec8SAndroid Build Coastguard Worker    "--dist_dir": "Use --destdir instead.",
53*7887bec8SAndroid Build Coastguard Worker    "--log": "Use -q instead.",
54*7887bec8SAndroid Build Coastguard Worker    "--archive_prefix": "",
55*7887bec8SAndroid Build Coastguard Worker    "--flat": "Specify it in the BUILD file (e.g. copy_to_dist_dir(flat=True))",
56*7887bec8SAndroid Build Coastguard Worker    "--strip_components": "Specify it in the BUILD file "
57*7887bec8SAndroid Build Coastguard Worker                          "(e.g. copy_to_dist_dir(strip_components=1))",
58*7887bec8SAndroid Build Coastguard Worker    "--prefix": "Specify it in the BUILD file "
59*7887bec8SAndroid Build Coastguard Worker                "(e.g. copy_to_dist_dir(prefix='prefix'))",
60*7887bec8SAndroid Build Coastguard Worker    "--wipe_dist_dir": "Specify it in the BUILD file "
61*7887bec8SAndroid Build Coastguard Worker                       "(e.g. copy_to_dist_dir(wipe_dist_dir=True))",
62*7887bec8SAndroid Build Coastguard Worker    "--allow_duplicate_filenames":
63*7887bec8SAndroid Build Coastguard Worker        "Specify it in the BUILD file "
64*7887bec8SAndroid Build Coastguard Worker        "(e.g. copy_to_dist_dir(allow_duplicate_filenames=True))",
65*7887bec8SAndroid Build Coastguard Worker    "--mode_override":
66*7887bec8SAndroid Build Coastguard Worker        "Specify it in the BUILD file "
67*7887bec8SAndroid Build Coastguard Worker        "(e.g. copy_to_dist_dir(mode_overrides=[('*.sh', '755')]))",
68*7887bec8SAndroid Build Coastguard Worker}
69*7887bec8SAndroid Build Coastguard Worker
70*7887bec8SAndroid Build Coastguard Worker
71*7887bec8SAndroid Build Coastguard Workerdef copy_with_modes(src, dst, mode_overrides):
72*7887bec8SAndroid Build Coastguard Worker    mode_override = None
73*7887bec8SAndroid Build Coastguard Worker    for (pattern, mode) in mode_overrides:
74*7887bec8SAndroid Build Coastguard Worker        if fnmatch.fnmatch(src, pattern):
75*7887bec8SAndroid Build Coastguard Worker            mode_override = mode
76*7887bec8SAndroid Build Coastguard Worker            break
77*7887bec8SAndroid Build Coastguard Worker
78*7887bec8SAndroid Build Coastguard Worker    # Remove destination file that may be write-protected
79*7887bec8SAndroid Build Coastguard Worker    pathlib.Path(dst).unlink(missing_ok=True)
80*7887bec8SAndroid Build Coastguard Worker
81*7887bec8SAndroid Build Coastguard Worker    # Copy the file with copy2 to preserve whatever permissions are set on src
82*7887bec8SAndroid Build Coastguard Worker    shutil.copy2(os.path.abspath(src), dst, follow_symlinks=True)
83*7887bec8SAndroid Build Coastguard Worker
84*7887bec8SAndroid Build Coastguard Worker    if mode_override:
85*7887bec8SAndroid Build Coastguard Worker        os.chmod(dst, mode_override)
86*7887bec8SAndroid Build Coastguard Worker
87*7887bec8SAndroid Build Coastguard Worker
88*7887bec8SAndroid Build Coastguard Workerdef ensure_unique_filenames(files):
89*7887bec8SAndroid Build Coastguard Worker    basename_to_srcs_map = collections.defaultdict(list)
90*7887bec8SAndroid Build Coastguard Worker    for f in files:
91*7887bec8SAndroid Build Coastguard Worker        basename_to_srcs_map[os.path.basename(f)].append(f)
92*7887bec8SAndroid Build Coastguard Worker
93*7887bec8SAndroid Build Coastguard Worker    duplicates_exist = False
94*7887bec8SAndroid Build Coastguard Worker    for (basename, srcs) in basename_to_srcs_map.items():
95*7887bec8SAndroid Build Coastguard Worker        if len(srcs) > 1:
96*7887bec8SAndroid Build Coastguard Worker            duplicates_exist = True
97*7887bec8SAndroid Build Coastguard Worker            logging.error('Destination filename "%s" has multiple possible sources: %s',
98*7887bec8SAndroid Build Coastguard Worker                         basename, srcs)
99*7887bec8SAndroid Build Coastguard Worker
100*7887bec8SAndroid Build Coastguard Worker    if duplicates_exist:
101*7887bec8SAndroid Build Coastguard Worker        sys.exit(1)
102*7887bec8SAndroid Build Coastguard Worker
103*7887bec8SAndroid Build Coastguard Worker
104*7887bec8SAndroid Build Coastguard Workerdef files_to_dist(pattern):
105*7887bec8SAndroid Build Coastguard Worker    # Assume that dist.bzl is in the same package as dist.py
106*7887bec8SAndroid Build Coastguard Worker    runfiles_directory = os.path.dirname(__file__)
107*7887bec8SAndroid Build Coastguard Worker    dist_manifests = glob.glob(
108*7887bec8SAndroid Build Coastguard Worker        os.path.join(runfiles_directory, pattern))
109*7887bec8SAndroid Build Coastguard Worker    if not dist_manifests:
110*7887bec8SAndroid Build Coastguard Worker        logging.warning("Could not find a file with pattern %s"
111*7887bec8SAndroid Build Coastguard Worker                        " in the runfiles directory: %s", pattern, runfiles_directory)
112*7887bec8SAndroid Build Coastguard Worker    files_to_dist = []
113*7887bec8SAndroid Build Coastguard Worker    for dist_manifest in dist_manifests:
114*7887bec8SAndroid Build Coastguard Worker        with open(dist_manifest, "r") as f:
115*7887bec8SAndroid Build Coastguard Worker            files_to_dist += [line.strip() for line in f]
116*7887bec8SAndroid Build Coastguard Worker    return files_to_dist
117*7887bec8SAndroid Build Coastguard Worker
118*7887bec8SAndroid Build Coastguard Worker
119*7887bec8SAndroid Build Coastguard Workerdef copy_files_to_dist_dir(files, archives, mode_overrides, dist_dir, flat, prefix,
120*7887bec8SAndroid Build Coastguard Worker    strip_components, archive_prefix, wipe_dist_dir, allow_duplicate_filenames, **ignored):
121*7887bec8SAndroid Build Coastguard Worker
122*7887bec8SAndroid Build Coastguard Worker    if flat and not allow_duplicate_filenames:
123*7887bec8SAndroid Build Coastguard Worker        ensure_unique_filenames(files)
124*7887bec8SAndroid Build Coastguard Worker
125*7887bec8SAndroid Build Coastguard Worker    if wipe_dist_dir and os.path.exists(dist_dir):
126*7887bec8SAndroid Build Coastguard Worker        shutil.rmtree(dist_dir)
127*7887bec8SAndroid Build Coastguard Worker
128*7887bec8SAndroid Build Coastguard Worker    logging.info("Copying to %s", dist_dir)
129*7887bec8SAndroid Build Coastguard Worker
130*7887bec8SAndroid Build Coastguard Worker    for src in files:
131*7887bec8SAndroid Build Coastguard Worker        if flat:
132*7887bec8SAndroid Build Coastguard Worker            src_relpath = os.path.basename(src)
133*7887bec8SAndroid Build Coastguard Worker        elif strip_components > 0:
134*7887bec8SAndroid Build Coastguard Worker            src_relpath = src.split('/', strip_components)[-1]
135*7887bec8SAndroid Build Coastguard Worker        else:
136*7887bec8SAndroid Build Coastguard Worker            src_relpath = src
137*7887bec8SAndroid Build Coastguard Worker
138*7887bec8SAndroid Build Coastguard Worker        src_relpath = os.path.join(prefix, src_relpath)
139*7887bec8SAndroid Build Coastguard Worker
140*7887bec8SAndroid Build Coastguard Worker        dst = os.path.join(dist_dir, src_relpath)
141*7887bec8SAndroid Build Coastguard Worker        if os.path.isfile(src):
142*7887bec8SAndroid Build Coastguard Worker            dst_dirname = os.path.dirname(dst)
143*7887bec8SAndroid Build Coastguard Worker            logging.debug("Copying file: %s" % dst)
144*7887bec8SAndroid Build Coastguard Worker            if not os.path.exists(dst_dirname):
145*7887bec8SAndroid Build Coastguard Worker                os.makedirs(dst_dirname)
146*7887bec8SAndroid Build Coastguard Worker
147*7887bec8SAndroid Build Coastguard Worker            copy_with_modes(src, dst, mode_overrides)
148*7887bec8SAndroid Build Coastguard Worker        elif os.path.isdir(src):
149*7887bec8SAndroid Build Coastguard Worker            logging.debug("Copying dir: %s" % dst)
150*7887bec8SAndroid Build Coastguard Worker            if os.path.exists(dst):
151*7887bec8SAndroid Build Coastguard Worker                # make the directory temporary writable, then
152*7887bec8SAndroid Build Coastguard Worker                # shutil.copytree will restore correct permissions.
153*7887bec8SAndroid Build Coastguard Worker                os.chmod(dst, 750)
154*7887bec8SAndroid Build Coastguard Worker            shutil.copytree(
155*7887bec8SAndroid Build Coastguard Worker                os.path.abspath(src),
156*7887bec8SAndroid Build Coastguard Worker                dst,
157*7887bec8SAndroid Build Coastguard Worker                copy_function=lambda s, d: copy_with_modes(s, d, mode_overrides),
158*7887bec8SAndroid Build Coastguard Worker                dirs_exist_ok=True,
159*7887bec8SAndroid Build Coastguard Worker            )
160*7887bec8SAndroid Build Coastguard Worker
161*7887bec8SAndroid Build Coastguard Worker    for archive in archives:
162*7887bec8SAndroid Build Coastguard Worker        try:
163*7887bec8SAndroid Build Coastguard Worker            with tarfile.open(archive) as tf:
164*7887bec8SAndroid Build Coastguard Worker                dst_dirname = os.path.join(dist_dir, archive_prefix)
165*7887bec8SAndroid Build Coastguard Worker                logging.debug("Extracting archive: %s -> %s", archive, dst_dirname)
166*7887bec8SAndroid Build Coastguard Worker                tf.extractall(dst_dirname)
167*7887bec8SAndroid Build Coastguard Worker        except tarfile.TarError:
168*7887bec8SAndroid Build Coastguard Worker            # toybox does not support creating empty tar files, hence the build
169*7887bec8SAndroid Build Coastguard Worker            # system may use empty files as empty archives.
170*7887bec8SAndroid Build Coastguard Worker            if os.path.getsize(archive) == 0:
171*7887bec8SAndroid Build Coastguard Worker                logging.warning("Skipping empty tar file: %s", archive)
172*7887bec8SAndroid Build Coastguard Worker                continue
173*7887bec8SAndroid Build Coastguard Worker             # re-raise if we do not know anything about this error
174*7887bec8SAndroid Build Coastguard Worker            logging.exception("Unknown TarError.")
175*7887bec8SAndroid Build Coastguard Worker            raise
176*7887bec8SAndroid Build Coastguard Worker
177*7887bec8SAndroid Build Coastguard Worker
178*7887bec8SAndroid Build Coastguard Workerdef config_logging(log_level_str):
179*7887bec8SAndroid Build Coastguard Worker    level = getattr(logging, log_level_str.upper(), None)
180*7887bec8SAndroid Build Coastguard Worker    if not isinstance(level, int):
181*7887bec8SAndroid Build Coastguard Worker        sys.stderr.write("ERROR: Invalid --log {}\n".format(log_level_str))
182*7887bec8SAndroid Build Coastguard Worker        sys.exit(1)
183*7887bec8SAndroid Build Coastguard Worker    logging.basicConfig(level=level, format="[dist] %(levelname)s: %(message)s")
184*7887bec8SAndroid Build Coastguard Worker
185*7887bec8SAndroid Build Coastguard Worker
186*7887bec8SAndroid Build Coastguard Workerclass CheckDeprecationAction(argparse.Action):
187*7887bec8SAndroid Build Coastguard Worker    """Checks if a deprecated option is used, then do nothing."""
188*7887bec8SAndroid Build Coastguard Worker    def __call__(self, parser, namespace, values, option_string=None):
189*7887bec8SAndroid Build Coastguard Worker        if option_string in _DEPRECATED_CMDLINE_OPTIONS:
190*7887bec8SAndroid Build Coastguard Worker            logging.warning("%s is deprecated! %s", option_string,
191*7887bec8SAndroid Build Coastguard Worker                            _DEPRECATED_CMDLINE_OPTIONS[option_string])
192*7887bec8SAndroid Build Coastguard Worker
193*7887bec8SAndroid Build Coastguard Worker
194*7887bec8SAndroid Build Coastguard Workerclass StoreAndCheckDeprecationAction(CheckDeprecationAction):
195*7887bec8SAndroid Build Coastguard Worker    """Sotres the value, and checks if a deprecated option is used."""
196*7887bec8SAndroid Build Coastguard Worker    def __call__(self, parser, namespace, values, option_string=None):
197*7887bec8SAndroid Build Coastguard Worker        super().__call__(parser, namespace, values, option_string)
198*7887bec8SAndroid Build Coastguard Worker        setattr(namespace, self.dest, values)
199*7887bec8SAndroid Build Coastguard Worker
200*7887bec8SAndroid Build Coastguard Worker
201*7887bec8SAndroid Build Coastguard Workerclass StoreTrueAndCheckDeprecationAction(CheckDeprecationAction):
202*7887bec8SAndroid Build Coastguard Worker    """Sotres true, and checks if a deprecated option is used."""
203*7887bec8SAndroid Build Coastguard Worker    def __call__(self, parser, namespace, values, option_string=None):
204*7887bec8SAndroid Build Coastguard Worker        super().__call__(parser, namespace, values, option_string)
205*7887bec8SAndroid Build Coastguard Worker        setattr(namespace, self.dest, True)
206*7887bec8SAndroid Build Coastguard Worker
207*7887bec8SAndroid Build Coastguard Worker
208*7887bec8SAndroid Build Coastguard Workerclass AppendAndCheckDeprecationAction(CheckDeprecationAction):
209*7887bec8SAndroid Build Coastguard Worker    """Appends the value, and checks if a deprecated option is used."""
210*7887bec8SAndroid Build Coastguard Worker    def __call__(self, parser, namespace, values, option_string=None):
211*7887bec8SAndroid Build Coastguard Worker        super().__call__(parser, namespace, values, option_string)
212*7887bec8SAndroid Build Coastguard Worker        if not values:
213*7887bec8SAndroid Build Coastguard Worker            return
214*7887bec8SAndroid Build Coastguard Worker        metavar_len = len(self.metavar)if self.metavar else 1
215*7887bec8SAndroid Build Coastguard Worker        value_groups = [values[i:i + metavar_len]
216*7887bec8SAndroid Build Coastguard Worker                        for i in range(0, len(values), metavar_len)]
217*7887bec8SAndroid Build Coastguard Worker        setattr(namespace, self.dest,
218*7887bec8SAndroid Build Coastguard Worker                getattr(namespace, self.dest, []) + value_groups)
219*7887bec8SAndroid Build Coastguard Worker
220*7887bec8SAndroid Build Coastguard Worker
221*7887bec8SAndroid Build Coastguard Workerdef _get_parser(cmdline=False) -> argparse.ArgumentParser:
222*7887bec8SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(
223*7887bec8SAndroid Build Coastguard Worker        description="Dist Bazel output files into a custom directory.",
224*7887bec8SAndroid Build Coastguard Worker        formatter_class=argparse.RawTextHelpFormatter)
225*7887bec8SAndroid Build Coastguard Worker    deprecated = parser.add_argument_group(
226*7887bec8SAndroid Build Coastguard Worker        "Deprecated command line options",
227*7887bec8SAndroid Build Coastguard Worker        description=textwrap.dedent("""\
228*7887bec8SAndroid Build Coastguard Worker            List of command line options that are deprecated.
229*7887bec8SAndroid Build Coastguard Worker            Most of them should be specified in the BUILD file instead.
230*7887bec8SAndroid Build Coastguard Worker        """))
231*7887bec8SAndroid Build Coastguard Worker    parser.add_argument(
232*7887bec8SAndroid Build Coastguard Worker        "--destdir", "--dist_dir", required=not cmdline, dest="dist_dir",
233*7887bec8SAndroid Build Coastguard Worker        help=textwrap.dedent("""\
234*7887bec8SAndroid Build Coastguard Worker            path to the dist dir.
235*7887bec8SAndroid Build Coastguard Worker            If relative, it is interpreted as relative to Bazel workspace root
236*7887bec8SAndroid Build Coastguard Worker            set by the BUILD_WORKSPACE_DIRECTORY environment variable, or
237*7887bec8SAndroid Build Coastguard Worker            PWD if BUILD_WORKSPACE_DIRECTORY is not set.
238*7887bec8SAndroid Build Coastguard Worker
239*7887bec8SAndroid Build Coastguard Worker            Note: --dist_dir is deprecated; use --destdir instead."""),
240*7887bec8SAndroid Build Coastguard Worker        action=StoreAndCheckDeprecationAction if cmdline else "store")
241*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
242*7887bec8SAndroid Build Coastguard Worker        "--flat",
243*7887bec8SAndroid Build Coastguard Worker        action=StoreTrueAndCheckDeprecationAction if cmdline else "store_true",
244*7887bec8SAndroid Build Coastguard Worker        help="ignore subdirectories in the manifest")
245*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
246*7887bec8SAndroid Build Coastguard Worker        "--strip_components", type=int, default=0,
247*7887bec8SAndroid Build Coastguard Worker        help="number of leading components to strip from paths before applying --prefix",
248*7887bec8SAndroid Build Coastguard Worker        action=StoreAndCheckDeprecationAction if cmdline else "store")
249*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
250*7887bec8SAndroid Build Coastguard Worker        "--prefix", default="",
251*7887bec8SAndroid Build Coastguard Worker        help="path prefix to apply within dist_dir for copied files",
252*7887bec8SAndroid Build Coastguard Worker        action=StoreAndCheckDeprecationAction if cmdline else "store")
253*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
254*7887bec8SAndroid Build Coastguard Worker        "--archive_prefix", default="",
255*7887bec8SAndroid Build Coastguard Worker        help="Path prefix to apply within dist_dir for extracted archives. " +
256*7887bec8SAndroid Build Coastguard Worker             "Supported archives: tar.",
257*7887bec8SAndroid Build Coastguard Worker        action=StoreAndCheckDeprecationAction if cmdline else "store")
258*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument("--log", help="Log level (debug, info, warning, error)",
259*7887bec8SAndroid Build Coastguard Worker        default="debug",
260*7887bec8SAndroid Build Coastguard Worker        action=StoreAndCheckDeprecationAction if cmdline else "store")
261*7887bec8SAndroid Build Coastguard Worker    parser.add_argument("-q", "--quiet", action="store_const", default=False,
262*7887bec8SAndroid Build Coastguard Worker                        help="Same as --log=error", const="error", dest="log")
263*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
264*7887bec8SAndroid Build Coastguard Worker        "--wipe_dist_dir",
265*7887bec8SAndroid Build Coastguard Worker        action=StoreTrueAndCheckDeprecationAction if cmdline else "store_true",
266*7887bec8SAndroid Build Coastguard Worker        help="remove existing dist_dir prior to running",
267*7887bec8SAndroid Build Coastguard Worker    )
268*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
269*7887bec8SAndroid Build Coastguard Worker        "--allow_duplicate_filenames",
270*7887bec8SAndroid Build Coastguard Worker        action=StoreTrueAndCheckDeprecationAction if cmdline else "store_true",
271*7887bec8SAndroid Build Coastguard Worker        help="allow multiple files with the same name to be copied to dist_dir (overwriting)"
272*7887bec8SAndroid Build Coastguard Worker    )
273*7887bec8SAndroid Build Coastguard Worker    deprecated.add_argument(
274*7887bec8SAndroid Build Coastguard Worker        "--mode_override",
275*7887bec8SAndroid Build Coastguard Worker        metavar=("PATTERN", "MODE"),
276*7887bec8SAndroid Build Coastguard Worker        action=AppendAndCheckDeprecationAction if cmdline else "append",
277*7887bec8SAndroid Build Coastguard Worker        nargs=2,
278*7887bec8SAndroid Build Coastguard Worker        default=[],
279*7887bec8SAndroid Build Coastguard Worker        help='glob pattern and mode to set on files matching pattern (e.g. --mode_override "*.sh" "755")'
280*7887bec8SAndroid Build Coastguard Worker    )
281*7887bec8SAndroid Build Coastguard Worker    return parser
282*7887bec8SAndroid Build Coastguard Worker
283*7887bec8SAndroid Build Coastguard Workerdef main():
284*7887bec8SAndroid Build Coastguard Worker    args = sys.argv[1:]
285*7887bec8SAndroid Build Coastguard Worker    args.remove(_CMDLINE_FLAGS_SENTINEL)
286*7887bec8SAndroid Build Coastguard Worker    args = _get_parser().parse_args(args)
287*7887bec8SAndroid Build Coastguard Worker
288*7887bec8SAndroid Build Coastguard Worker    config_logging(args.log)
289*7887bec8SAndroid Build Coastguard Worker
290*7887bec8SAndroid Build Coastguard Worker    # Warn about arguments that should not be set in command line.
291*7887bec8SAndroid Build Coastguard Worker    _get_parser(cmdline=True).parse_args(
292*7887bec8SAndroid Build Coastguard Worker        sys.argv[sys.argv.index(_CMDLINE_FLAGS_SENTINEL) + 1:])
293*7887bec8SAndroid Build Coastguard Worker
294*7887bec8SAndroid Build Coastguard Worker    mode_overrides = []
295*7887bec8SAndroid Build Coastguard Worker    for (pattern, mode) in args.mode_override:
296*7887bec8SAndroid Build Coastguard Worker        try:
297*7887bec8SAndroid Build Coastguard Worker            mode_overrides.append((pattern, int(mode, 8)))
298*7887bec8SAndroid Build Coastguard Worker        except ValueError:
299*7887bec8SAndroid Build Coastguard Worker            logging.error("invalid octal permissions: %s", mode)
300*7887bec8SAndroid Build Coastguard Worker            sys.exit(1)
301*7887bec8SAndroid Build Coastguard Worker
302*7887bec8SAndroid Build Coastguard Worker    if not os.path.isabs(args.dist_dir):
303*7887bec8SAndroid Build Coastguard Worker        # BUILD_WORKSPACE_DIRECTORY is the root of the Bazel workspace containing
304*7887bec8SAndroid Build Coastguard Worker        # this binary target.
305*7887bec8SAndroid Build Coastguard Worker        # https://docs.bazel.build/versions/main/user-manual.html#run
306*7887bec8SAndroid Build Coastguard Worker        args.dist_dir = os.path.join(
307*7887bec8SAndroid Build Coastguard Worker            os.environ.get("BUILD_WORKSPACE_DIRECTORY"), args.dist_dir)
308*7887bec8SAndroid Build Coastguard Worker
309*7887bec8SAndroid Build Coastguard Worker    files = files_to_dist("*_dist_manifest.txt")
310*7887bec8SAndroid Build Coastguard Worker    archives = files_to_dist("*_dist_archives_manifest.txt")
311*7887bec8SAndroid Build Coastguard Worker    copy_files_to_dist_dir(files, archives, mode_overrides, **vars(args))
312*7887bec8SAndroid Build Coastguard Worker
313*7887bec8SAndroid Build Coastguard Worker
314*7887bec8SAndroid Build Coastguard Workerif __name__ == "__main__":
315*7887bec8SAndroid Build Coastguard Worker    main()
316