15f9996aaSopenharmony_ci#!/usr/bin/env python 25f9996aaSopenharmony_ci# -*- coding: utf-8 -*- 35f9996aaSopenharmony_ci# Copyright 2013 The Chromium Authors. All rights reserved. 45f9996aaSopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 55f9996aaSopenharmony_ci# found in the LICENSE file. 65f9996aaSopenharmony_ci"""Contains common helpers for GN action()s.""" 75f9996aaSopenharmony_ci 85f9996aaSopenharmony_ciimport collections 95f9996aaSopenharmony_ciimport contextlib 105f9996aaSopenharmony_cifrom distutils import extension 115f9996aaSopenharmony_ciimport filecmp 125f9996aaSopenharmony_ciimport fnmatch 135f9996aaSopenharmony_ciimport json 145f9996aaSopenharmony_ciimport os 155f9996aaSopenharmony_ciimport pipes 165f9996aaSopenharmony_ciimport re 175f9996aaSopenharmony_ciimport shutil 185f9996aaSopenharmony_ciimport stat 195f9996aaSopenharmony_ciimport subprocess 205f9996aaSopenharmony_ciimport sys 215f9996aaSopenharmony_ciimport tempfile 225f9996aaSopenharmony_ciimport zipfile 235f9996aaSopenharmony_ciimport optparse 245f9996aaSopenharmony_ci 255f9996aaSopenharmony_ci# Any new non-system import must be added to: 265f9996aaSopenharmony_ci 275f9996aaSopenharmony_cisys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) 285f9996aaSopenharmony_ciimport gn_helpers 295f9996aaSopenharmony_ci 305f9996aaSopenharmony_ci# Some clients do not add //build/scripts/util to PYTHONPATH. 315f9996aaSopenharmony_cifrom . import md5_check # pylint: disable=relative-import 325f9996aaSopenharmony_ci 335f9996aaSopenharmony_ci# Definition copied from pylib/constants/__init__.py to avoid adding 345f9996aaSopenharmony_ci# a dependency on pylib. 355f9996aaSopenharmony_ciDIR_SOURCE_ROOT = os.environ.get( 365f9996aaSopenharmony_ci 'CHECKOUT_SOURCE_ROOT', 375f9996aaSopenharmony_ci os.path.abspath( 385f9996aaSopenharmony_ci os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 395f9996aaSopenharmony_ci os.pardir, os.pardir))) 405f9996aaSopenharmony_ci 415f9996aaSopenharmony_ciHERMETIC_TIMESTAMP = (2001, 1, 1, 0, 0, 0) 425f9996aaSopenharmony_ci_HERMETIC_FILE_ATTR = (0o644 << 16) 435f9996aaSopenharmony_ci 445f9996aaSopenharmony_ci 455f9996aaSopenharmony_ci@contextlib.contextmanager 465f9996aaSopenharmony_cidef temp_dir(): 475f9996aaSopenharmony_ci dirname = tempfile.mkdtemp() 485f9996aaSopenharmony_ci try: 495f9996aaSopenharmony_ci yield dirname 505f9996aaSopenharmony_ci finally: 515f9996aaSopenharmony_ci shutil.rmtree(dirname) 525f9996aaSopenharmony_ci 535f9996aaSopenharmony_ci 545f9996aaSopenharmony_cidef make_directory(dir_path): 555f9996aaSopenharmony_ci try: 565f9996aaSopenharmony_ci os.makedirs(dir_path, exist_ok=True) 575f9996aaSopenharmony_ci except OSError: 585f9996aaSopenharmony_ci pass 595f9996aaSopenharmony_ci 605f9996aaSopenharmony_ci 615f9996aaSopenharmony_cidef delete_directory(dir_path): 625f9996aaSopenharmony_ci if os.path.exists(dir_path): 635f9996aaSopenharmony_ci shutil.rmtree(dir_path) 645f9996aaSopenharmony_ci 655f9996aaSopenharmony_ci 665f9996aaSopenharmony_cidef touch(path, fail_if_missing=False): 675f9996aaSopenharmony_ci if fail_if_missing and not os.path.exists(path): 685f9996aaSopenharmony_ci raise Exception(path + ' doesn\'t exist.') 695f9996aaSopenharmony_ci 705f9996aaSopenharmony_ci make_directory(os.path.dirname(path)) 715f9996aaSopenharmony_ci with open(path, 'a'): 725f9996aaSopenharmony_ci os.utime(path, None) 735f9996aaSopenharmony_ci 745f9996aaSopenharmony_ci 755f9996aaSopenharmony_cidef find_in_directory(directory, filename_filter): 765f9996aaSopenharmony_ci files = [] 775f9996aaSopenharmony_ci for root, _dirnames, filenames in os.walk(directory): 785f9996aaSopenharmony_ci matched_files = fnmatch.filter(filenames, filename_filter) 795f9996aaSopenharmony_ci files.extend((os.path.join(root, f) for f in matched_files)) 805f9996aaSopenharmony_ci return files 815f9996aaSopenharmony_ci 825f9996aaSopenharmony_ci 835f9996aaSopenharmony_cidef read_build_vars(path): 845f9996aaSopenharmony_ci """Parses a build_vars.txt into a dict.""" 855f9996aaSopenharmony_ci with open(path) as f: 865f9996aaSopenharmony_ci return dict(l.rstrip().split('=', 1) for l in f) 875f9996aaSopenharmony_ci 885f9996aaSopenharmony_ci 895f9996aaSopenharmony_cidef parse_gn_list(gn_string): 905f9996aaSopenharmony_ci """Converts a command-line parameter into a list. 915f9996aaSopenharmony_ci 925f9996aaSopenharmony_ci If the input starts with a '[' it is assumed to be a GN-formatted list and 935f9996aaSopenharmony_ci it will be parsed accordingly. When empty an empty list will be returned. 945f9996aaSopenharmony_ci Otherwise, the parameter will be treated as a single raw string (not 955f9996aaSopenharmony_ci GN-formatted in that it's not assumed to have literal quotes that must be 965f9996aaSopenharmony_ci removed) and a list will be returned containing that string. 975f9996aaSopenharmony_ci 985f9996aaSopenharmony_ci The common use for this behavior is in the ohos build where things can 995f9996aaSopenharmony_ci take lists of @FileArg references that are expanded via expand_file_args. 1005f9996aaSopenharmony_ci """ 1015f9996aaSopenharmony_ci if gn_string.startswith('['): 1025f9996aaSopenharmony_ci parser = gn_helpers.GNValueParser(gn_string) 1035f9996aaSopenharmony_ci return parser.parse_list() 1045f9996aaSopenharmony_ci if len(gn_string): 1055f9996aaSopenharmony_ci return [gn_string] 1065f9996aaSopenharmony_ci return [] 1075f9996aaSopenharmony_ci 1085f9996aaSopenharmony_ci 1095f9996aaSopenharmony_cidef parse_and_flatten_gn_lists(gn_lists): 1105f9996aaSopenharmony_ci ret = [] 1115f9996aaSopenharmony_ci for arg in gn_lists: 1125f9996aaSopenharmony_ci ret.extend(parse_gn_list(arg)) 1135f9996aaSopenharmony_ci return ret 1145f9996aaSopenharmony_ci 1155f9996aaSopenharmony_ci 1165f9996aaSopenharmony_cidef check_options(options, parser, required=None): 1175f9996aaSopenharmony_ci if not required: 1185f9996aaSopenharmony_ci return 1195f9996aaSopenharmony_ci for option_name in required: 1205f9996aaSopenharmony_ci if getattr(options, option_name) is None: 1215f9996aaSopenharmony_ci parser.error('--%s is required' % option_name.replace('_', '-')) 1225f9996aaSopenharmony_ci 1235f9996aaSopenharmony_ci 1245f9996aaSopenharmony_cidef write_json(obj, path, only_if_changed=False): 1255f9996aaSopenharmony_ci old_dump = None 1265f9996aaSopenharmony_ci if os.path.exists(path): 1275f9996aaSopenharmony_ci with open(path, 'r') as oldfile: 1285f9996aaSopenharmony_ci old_dump = oldfile.read() 1295f9996aaSopenharmony_ci 1305f9996aaSopenharmony_ci new_dump = json.dumps(obj, 1315f9996aaSopenharmony_ci sort_keys=True, 1325f9996aaSopenharmony_ci indent=2, 1335f9996aaSopenharmony_ci separators=(',', ': ')) 1345f9996aaSopenharmony_ci 1355f9996aaSopenharmony_ci if not only_if_changed or old_dump != new_dump: 1365f9996aaSopenharmony_ci with open(path, 'w') as outfile: 1375f9996aaSopenharmony_ci outfile.write(new_dump) 1385f9996aaSopenharmony_ci 1395f9996aaSopenharmony_ci 1405f9996aaSopenharmony_ci@contextlib.contextmanager 1415f9996aaSopenharmony_cidef atomic_output(path, only_if_changed=True): 1425f9996aaSopenharmony_ci """Helper to prevent half-written outputs. 1435f9996aaSopenharmony_ci 1445f9996aaSopenharmony_ci Args: 1455f9996aaSopenharmony_ci path: Path to the final output file, which will be written atomically. 1465f9996aaSopenharmony_ci only_if_changed: If True (the default), do not touch the filesystem 1475f9996aaSopenharmony_ci if the content has not changed. 1485f9996aaSopenharmony_ci Returns: 1495f9996aaSopenharmony_ci A python context manager that yields a NamedTemporaryFile instance 1505f9996aaSopenharmony_ci that must be used by clients to write the data to. On exit, the 1515f9996aaSopenharmony_ci manager will try to replace the final output file with the 1525f9996aaSopenharmony_ci temporary one if necessary. The temporary file is always destroyed 1535f9996aaSopenharmony_ci on exit. 1545f9996aaSopenharmony_ci Example: 1555f9996aaSopenharmony_ci with build_utils.atomic_output(output_path) as tmp_file: 1565f9996aaSopenharmony_ci subprocess.check_call(['prog', '--output', tmp_file.name]) 1575f9996aaSopenharmony_ci """ 1585f9996aaSopenharmony_ci # Create in same directory to ensure same filesystem when moving. 1595f9996aaSopenharmony_ci with tempfile.NamedTemporaryFile(suffix=os.path.basename(path), 1605f9996aaSopenharmony_ci dir=os.path.dirname(path), 1615f9996aaSopenharmony_ci delete=False) as f: 1625f9996aaSopenharmony_ci try: 1635f9996aaSopenharmony_ci # Change tempfile permission to 664 1645f9996aaSopenharmony_ci os.fchmod(f.fileno(), 0o664) 1655f9996aaSopenharmony_ci yield f 1665f9996aaSopenharmony_ci 1675f9996aaSopenharmony_ci # file should be closed before comparison/move. 1685f9996aaSopenharmony_ci f.close() 1695f9996aaSopenharmony_ci if not (only_if_changed and os.path.exists(path) 1705f9996aaSopenharmony_ci and filecmp.cmp(f.name, path)): 1715f9996aaSopenharmony_ci shutil.move(f.name, path) 1725f9996aaSopenharmony_ci finally: 1735f9996aaSopenharmony_ci if os.path.exists(f.name): 1745f9996aaSopenharmony_ci os.unlink(f.name) 1755f9996aaSopenharmony_ci 1765f9996aaSopenharmony_ci 1775f9996aaSopenharmony_ciclass CalledProcessError(Exception): 1785f9996aaSopenharmony_ci """This exception is raised when the process run by check_output 1795f9996aaSopenharmony_ci exits with a non-zero exit code. 1805f9996aaSopenharmony_ci """ 1815f9996aaSopenharmony_ci def __init__(self, cwd, args, output): 1825f9996aaSopenharmony_ci super(CalledProcessError, self).__init__() 1835f9996aaSopenharmony_ci self.cwd = cwd 1845f9996aaSopenharmony_ci self.args = args 1855f9996aaSopenharmony_ci if isinstance(output, bytes): 1865f9996aaSopenharmony_ci self.output = output.decode() 1875f9996aaSopenharmony_ci else: 1885f9996aaSopenharmony_ci self.output = output 1895f9996aaSopenharmony_ci 1905f9996aaSopenharmony_ci def __str__(self): 1915f9996aaSopenharmony_ci # A user should be able to simply copy and paste the command that failed 1925f9996aaSopenharmony_ci # into their shell. 1935f9996aaSopenharmony_ci copyable_command = '( cd {}; {} )'.format( 1945f9996aaSopenharmony_ci os.path.abspath(self.cwd), ' '.join(map(pipes.quote, self.args))) 1955f9996aaSopenharmony_ci return 'Command failed: {}\n{}'.format(copyable_command, self.output) 1965f9996aaSopenharmony_ci 1975f9996aaSopenharmony_ci 1985f9996aaSopenharmony_cidef filter_lines(output, filter_string): 1995f9996aaSopenharmony_ci """Output filter from build_utils.check_output. 2005f9996aaSopenharmony_ci 2015f9996aaSopenharmony_ci Args: 2025f9996aaSopenharmony_ci output: Executable output as from build_utils.check_output. 2035f9996aaSopenharmony_ci filter_string: An RE string that will filter (remove) matching 2045f9996aaSopenharmony_ci lines from |output|. 2055f9996aaSopenharmony_ci 2065f9996aaSopenharmony_ci Returns: 2075f9996aaSopenharmony_ci The filtered output, as a single string. 2085f9996aaSopenharmony_ci """ 2095f9996aaSopenharmony_ci re_filter = re.compile(filter_string) 2105f9996aaSopenharmony_ci return '\n'.join(line for line in output.splitlines() 2115f9996aaSopenharmony_ci if not re_filter.search(line)) 2125f9996aaSopenharmony_ci 2135f9996aaSopenharmony_ci 2145f9996aaSopenharmony_ci# This can be used in most cases like subprocess.check_output(). The output, 2155f9996aaSopenharmony_ci# particularly when the command fails, better highlights the command's failure. 2165f9996aaSopenharmony_ci# If the command fails, raises a build_utils.CalledProcessError. 2175f9996aaSopenharmony_cidef check_output(args, 2185f9996aaSopenharmony_ci cwd=None, 2195f9996aaSopenharmony_ci env=None, 2205f9996aaSopenharmony_ci print_stdout=False, 2215f9996aaSopenharmony_ci print_stderr=True, 2225f9996aaSopenharmony_ci stdout_filter=None, 2235f9996aaSopenharmony_ci stderr_filter=None, 2245f9996aaSopenharmony_ci fail_func=lambda returncode, stderr: returncode != 0): 2255f9996aaSopenharmony_ci if not cwd: 2265f9996aaSopenharmony_ci cwd = os.getcwd() 2275f9996aaSopenharmony_ci 2285f9996aaSopenharmony_ci cache_exec = None 2295f9996aaSopenharmony_ci if env and env.pop("useCompileCache", False): 2305f9996aaSopenharmony_ci cache_exec = os.environ.get("COMPILE_CACHE_EXEC") 2315f9996aaSopenharmony_ci if cache_exec: 2325f9996aaSopenharmony_ci execute_args = [cache_exec, "--cwd", cwd] 2335f9996aaSopenharmony_ci execute_args.extend(args) 2345f9996aaSopenharmony_ci execute_args.extend(["--build-env"] + [f"{k}={v}" for k, v in env.items() if k != "addTestRunner"]) 2355f9996aaSopenharmony_ci if env.pop("addTestRunner", False): 2365f9996aaSopenharmony_ci execute_args.append("--add-test-runner") 2375f9996aaSopenharmony_ci child = subprocess.Popen(execute_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 2385f9996aaSopenharmony_ci else: 2395f9996aaSopenharmony_ci child = subprocess.Popen(args, 2405f9996aaSopenharmony_ci stdout=subprocess.PIPE, 2415f9996aaSopenharmony_ci stderr=subprocess.PIPE, 2425f9996aaSopenharmony_ci cwd=cwd, 2435f9996aaSopenharmony_ci env=env) 2445f9996aaSopenharmony_ci stdout, stderr = child.communicate() 2455f9996aaSopenharmony_ci 2465f9996aaSopenharmony_ci if stdout_filter is not None: 2475f9996aaSopenharmony_ci stdout = stdout_filter(stdout) 2485f9996aaSopenharmony_ci 2495f9996aaSopenharmony_ci if stderr_filter is not None: 2505f9996aaSopenharmony_ci stderr = stderr_filter(stderr) 2515f9996aaSopenharmony_ci if isinstance(stdout, bytes): 2525f9996aaSopenharmony_ci stdout = stdout.decode() 2535f9996aaSopenharmony_ci if isinstance(stderr, bytes): 2545f9996aaSopenharmony_ci stderr = stderr.decode() 2555f9996aaSopenharmony_ci 2565f9996aaSopenharmony_ci if fail_func(child.returncode, stderr): 2575f9996aaSopenharmony_ci raise CalledProcessError(cwd, args, stdout + stderr) 2585f9996aaSopenharmony_ci 2595f9996aaSopenharmony_ci if print_stdout: 2605f9996aaSopenharmony_ci if isinstance(stdout, bytes): 2615f9996aaSopenharmony_ci stdout = stdout.decode() 2625f9996aaSopenharmony_ci if stdout: 2635f9996aaSopenharmony_ci sys.stdout.write(stdout) 2645f9996aaSopenharmony_ci if print_stderr: 2655f9996aaSopenharmony_ci if isinstance(stderr, bytes): 2665f9996aaSopenharmony_ci stderr = stderr.decode() 2675f9996aaSopenharmony_ci if stderr: 2685f9996aaSopenharmony_ci sys.stderr.write(stderr) 2695f9996aaSopenharmony_ci return stdout 2705f9996aaSopenharmony_ci 2715f9996aaSopenharmony_ci 2725f9996aaSopenharmony_cidef get_modified_time(path): 2735f9996aaSopenharmony_ci # For a symlink, the modified time should be the greater of the link's 2745f9996aaSopenharmony_ci # modified time and the modified time of the target. 2755f9996aaSopenharmony_ci return max(os.lstat(path).st_mtime, os.stat(path).st_mtime) 2765f9996aaSopenharmony_ci 2775f9996aaSopenharmony_ci 2785f9996aaSopenharmony_cidef is_time_stale(output, inputs): 2795f9996aaSopenharmony_ci if not os.path.exists(output): 2805f9996aaSopenharmony_ci return True 2815f9996aaSopenharmony_ci 2825f9996aaSopenharmony_ci output_time = get_modified_time(output) 2835f9996aaSopenharmony_ci for i in inputs: 2845f9996aaSopenharmony_ci if get_modified_time(i) > output_time: 2855f9996aaSopenharmony_ci return True 2865f9996aaSopenharmony_ci return False 2875f9996aaSopenharmony_ci 2885f9996aaSopenharmony_ci 2895f9996aaSopenharmony_cidef _check_zip_path(name): 2905f9996aaSopenharmony_ci if os.path.normpath(name) != name: 2915f9996aaSopenharmony_ci raise Exception('Non-canonical zip path: %s' % name) 2925f9996aaSopenharmony_ci if os.path.isabs(name): 2935f9996aaSopenharmony_ci raise Exception('Absolute zip path: %s' % name) 2945f9996aaSopenharmony_ci 2955f9996aaSopenharmony_ci 2965f9996aaSopenharmony_cidef _is_symlink(zip_file, name): 2975f9996aaSopenharmony_ci zi = zip_file.getinfo(name) 2985f9996aaSopenharmony_ci 2995f9996aaSopenharmony_ci # The two high-order bytes of ZipInfo.external_attr represent 3005f9996aaSopenharmony_ci # UNIX permissions and file type bits. 3015f9996aaSopenharmony_ci return stat.S_ISLNK(zi.external_attr >> 16) 3025f9996aaSopenharmony_ci 3035f9996aaSopenharmony_ci 3045f9996aaSopenharmony_cidef extract_all(zip_path, 3055f9996aaSopenharmony_ci path=None, 3065f9996aaSopenharmony_ci no_clobber=True, 3075f9996aaSopenharmony_ci pattern=None, 3085f9996aaSopenharmony_ci predicate=None): 3095f9996aaSopenharmony_ci if path is None: 3105f9996aaSopenharmony_ci path = os.getcwd() 3115f9996aaSopenharmony_ci elif not os.path.exists(path): 3125f9996aaSopenharmony_ci make_directory(path) 3135f9996aaSopenharmony_ci 3145f9996aaSopenharmony_ci if not zipfile.is_zipfile(zip_path): 3155f9996aaSopenharmony_ci raise Exception('Invalid zip file: %s' % zip_path) 3165f9996aaSopenharmony_ci 3175f9996aaSopenharmony_ci extracted = [] 3185f9996aaSopenharmony_ci with zipfile.ZipFile(zip_path) as z: 3195f9996aaSopenharmony_ci for name in z.namelist(): 3205f9996aaSopenharmony_ci if name.endswith('/'): 3215f9996aaSopenharmony_ci make_directory(os.path.join(path, name)) 3225f9996aaSopenharmony_ci continue 3235f9996aaSopenharmony_ci if pattern is not None: 3245f9996aaSopenharmony_ci if not fnmatch.fnmatch(name, pattern): 3255f9996aaSopenharmony_ci continue 3265f9996aaSopenharmony_ci if predicate and not predicate(name): 3275f9996aaSopenharmony_ci continue 3285f9996aaSopenharmony_ci _check_zip_path(name) 3295f9996aaSopenharmony_ci if no_clobber: 3305f9996aaSopenharmony_ci output_path = os.path.join(path, name) 3315f9996aaSopenharmony_ci if os.path.exists(output_path): 3325f9996aaSopenharmony_ci raise Exception('Path already exists from zip: %s %s %s' % 3335f9996aaSopenharmony_ci (zip_path, name, output_path)) 3345f9996aaSopenharmony_ci if _is_symlink(z, name): 3355f9996aaSopenharmony_ci dest = os.path.join(path, name) 3365f9996aaSopenharmony_ci make_directory(os.path.dirname(dest)) 3375f9996aaSopenharmony_ci os.symlink(z.read(name), dest) 3385f9996aaSopenharmony_ci extracted.append(dest) 3395f9996aaSopenharmony_ci else: 3405f9996aaSopenharmony_ci z.extract(name, path) 3415f9996aaSopenharmony_ci extracted.append(os.path.join(path, name)) 3425f9996aaSopenharmony_ci 3435f9996aaSopenharmony_ci return extracted 3445f9996aaSopenharmony_ci 3455f9996aaSopenharmony_ci 3465f9996aaSopenharmony_cidef add_to_zip_hermetic(zip_file, 3475f9996aaSopenharmony_ci zip_path, 3485f9996aaSopenharmony_ci src_path=None, 3495f9996aaSopenharmony_ci data=None, 3505f9996aaSopenharmony_ci compress=None, 3515f9996aaSopenharmony_ci compress_level=6): 3525f9996aaSopenharmony_ci """Adds a file to the given ZipFile with a hard-coded modified time. 3535f9996aaSopenharmony_ci 3545f9996aaSopenharmony_ci Args: 3555f9996aaSopenharmony_ci zip_file: ZipFile instance to add the file to. 3565f9996aaSopenharmony_ci zip_path: Destination path within the zip file. 3575f9996aaSopenharmony_ci src_path: Path of the source file. Mutually exclusive with |data|. 3585f9996aaSopenharmony_ci data: File data as a string. 3595f9996aaSopenharmony_ci compress: Whether to enable compression. Default is taken from ZipFile 3605f9996aaSopenharmony_ci constructor. 3615f9996aaSopenharmony_ci """ 3625f9996aaSopenharmony_ci assert (src_path is None) != (data is None), ( 3635f9996aaSopenharmony_ci '|src_path| and |data| are mutually exclusive.') 3645f9996aaSopenharmony_ci _check_zip_path(zip_path) 3655f9996aaSopenharmony_ci zipinfo = zipfile.ZipInfo(filename=zip_path, date_time=HERMETIC_TIMESTAMP) 3665f9996aaSopenharmony_ci zipinfo.external_attr = _HERMETIC_FILE_ATTR 3675f9996aaSopenharmony_ci 3685f9996aaSopenharmony_ci if src_path and os.path.islink(src_path): 3695f9996aaSopenharmony_ci zipinfo.filename = zip_path 3705f9996aaSopenharmony_ci zipinfo.external_attr |= stat.S_IFLNK << 16 # mark as a symlink 3715f9996aaSopenharmony_ci zip_file.writestr(zipinfo, os.readlink(src_path)) 3725f9996aaSopenharmony_ci return 3735f9996aaSopenharmony_ci 3745f9996aaSopenharmony_ci # we want to use _HERMETIC_FILE_ATTR, so manually set 3755f9996aaSopenharmony_ci # the few attr bits we care about. 3765f9996aaSopenharmony_ci if src_path: 3775f9996aaSopenharmony_ci st = os.stat(src_path) 3785f9996aaSopenharmony_ci for mode in (stat.S_IXUSR, stat.S_IXGRP, stat.S_IXOTH): 3795f9996aaSopenharmony_ci if st.st_mode & mode: 3805f9996aaSopenharmony_ci zipinfo.external_attr |= mode << 16 3815f9996aaSopenharmony_ci 3825f9996aaSopenharmony_ci if src_path: 3835f9996aaSopenharmony_ci with open(src_path, 'rb') as f: 3845f9996aaSopenharmony_ci data = f.read() 3855f9996aaSopenharmony_ci 3865f9996aaSopenharmony_ci # zipfile will deflate even when it makes the file bigger. To avoid 3875f9996aaSopenharmony_ci # growing files, disable compression at an arbitrary cut off point. 3885f9996aaSopenharmony_ci if len(data) < 16: 3895f9996aaSopenharmony_ci compress = False 3905f9996aaSopenharmony_ci 3915f9996aaSopenharmony_ci # None converts to ZIP_STORED, when passed explicitly rather than the 3925f9996aaSopenharmony_ci # default passed to the ZipFile constructor. 3935f9996aaSopenharmony_ci compress_type = zip_file.compression 3945f9996aaSopenharmony_ci if compress is not None: 3955f9996aaSopenharmony_ci compress_type = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED 3965f9996aaSopenharmony_ci if os.getenv("ZIP_COMPRESS_LEVEL"): 3975f9996aaSopenharmony_ci compress_level = int(os.getenv("ZIP_COMPRESS_LEVEL")) 3985f9996aaSopenharmony_ci zip_file.writestr(zipinfo, data, compress_type, compress_level) 3995f9996aaSopenharmony_ci 4005f9996aaSopenharmony_ci 4015f9996aaSopenharmony_cidef do_zip(inputs, 4025f9996aaSopenharmony_ci output, 4035f9996aaSopenharmony_ci base_dir=None, 4045f9996aaSopenharmony_ci compress_fn=None, 4055f9996aaSopenharmony_ci zip_prefix_path=None): 4065f9996aaSopenharmony_ci """Creates a zip file from a list of files. 4075f9996aaSopenharmony_ci 4085f9996aaSopenharmony_ci Args: 4095f9996aaSopenharmony_ci inputs: A list of paths to zip, or a list of (zip_path, fs_path) tuples. 4105f9996aaSopenharmony_ci output: Destination .zip file. 4115f9996aaSopenharmony_ci base_dir: Prefix to strip from inputs. 4125f9996aaSopenharmony_ci compress_fn: Applied to each input to determine whether or not to compress. 4135f9996aaSopenharmony_ci By default, items will be |zipfile.ZIP_STORED|. 4145f9996aaSopenharmony_ci zip_prefix_path: Path prepended to file path in zip file. 4155f9996aaSopenharmony_ci """ 4165f9996aaSopenharmony_ci input_tuples = [] 4175f9996aaSopenharmony_ci for tup in inputs: 4185f9996aaSopenharmony_ci if isinstance(tup, str): 4195f9996aaSopenharmony_ci tup = (os.path.relpath(tup, base_dir), tup) 4205f9996aaSopenharmony_ci input_tuples.append(tup) 4215f9996aaSopenharmony_ci 4225f9996aaSopenharmony_ci # Sort by zip path to ensure stable zip ordering. 4235f9996aaSopenharmony_ci input_tuples.sort(key=lambda tup: tup[0]) 4245f9996aaSopenharmony_ci with zipfile.ZipFile(output, 'w') as outfile: 4255f9996aaSopenharmony_ci for zip_path, fs_path in input_tuples: 4265f9996aaSopenharmony_ci if zip_prefix_path: 4275f9996aaSopenharmony_ci zip_path = os.path.join(zip_prefix_path, zip_path) 4285f9996aaSopenharmony_ci compress = compress_fn(zip_path) if compress_fn else None 4295f9996aaSopenharmony_ci add_to_zip_hermetic(outfile, 4305f9996aaSopenharmony_ci zip_path, 4315f9996aaSopenharmony_ci src_path=fs_path, 4325f9996aaSopenharmony_ci compress=compress) 4335f9996aaSopenharmony_ci 4345f9996aaSopenharmony_ci 4355f9996aaSopenharmony_cidef zip_dir(output, base_dir, compress_fn=None, zip_prefix_path=None): 4365f9996aaSopenharmony_ci """Creates a zip file from a directory.""" 4375f9996aaSopenharmony_ci inputs = [] 4385f9996aaSopenharmony_ci for root, _, files in os.walk(base_dir): 4395f9996aaSopenharmony_ci for f in files: 4405f9996aaSopenharmony_ci inputs.append(os.path.join(root, f)) 4415f9996aaSopenharmony_ci 4425f9996aaSopenharmony_ci with atomic_output(output) as f: 4435f9996aaSopenharmony_ci do_zip(inputs, 4445f9996aaSopenharmony_ci f, 4455f9996aaSopenharmony_ci base_dir, 4465f9996aaSopenharmony_ci compress_fn=compress_fn, 4475f9996aaSopenharmony_ci zip_prefix_path=zip_prefix_path) 4485f9996aaSopenharmony_ci 4495f9996aaSopenharmony_ci 4505f9996aaSopenharmony_cidef matches_glob(path, filters): 4515f9996aaSopenharmony_ci """Returns whether the given path matches any of the given glob patterns.""" 4525f9996aaSopenharmony_ci return filters and any(fnmatch.fnmatch(path, f) for f in filters) 4535f9996aaSopenharmony_ci 4545f9996aaSopenharmony_ci 4555f9996aaSopenharmony_cidef _strip_dst_name(dst_name, options): 4565f9996aaSopenharmony_ci # Strip specific directories and file if options is not None 4575f9996aaSopenharmony_ci if options and options.stripFile: 4585f9996aaSopenharmony_ci for f in options.stripFile: 4595f9996aaSopenharmony_ci if fnmatch.fnmatch(dst_name, '*/' + f): 4605f9996aaSopenharmony_ci return True 4615f9996aaSopenharmony_ci if options and options.stripDir: 4625f9996aaSopenharmony_ci for d in options.stripDir: 4635f9996aaSopenharmony_ci if fnmatch.fnmatch(dst_name, d + '/*'): 4645f9996aaSopenharmony_ci return True 4655f9996aaSopenharmony_ci return False 4665f9996aaSopenharmony_ci 4675f9996aaSopenharmony_ci 4685f9996aaSopenharmony_cidef merge_zips(output, input_zips, path_transform=None, merge_args=None): 4695f9996aaSopenharmony_ci """Combines all files from |input_zips| into |output|. 4705f9996aaSopenharmony_ci 4715f9996aaSopenharmony_ci Args: 4725f9996aaSopenharmony_ci output: Path or ZipFile instance to add files to. 4735f9996aaSopenharmony_ci input_zips: Iterable of paths to zip files to merge. 4745f9996aaSopenharmony_ci path_transform: Called for each entry path. Returns a new path, or None to 4755f9996aaSopenharmony_ci skip the file. 4765f9996aaSopenharmony_ci """ 4775f9996aaSopenharmony_ci options = None 4785f9996aaSopenharmony_ci if merge_args: 4795f9996aaSopenharmony_ci parser = optparse.OptionParser() 4805f9996aaSopenharmony_ci parser.add_option('--stripDir', 4815f9996aaSopenharmony_ci action='append', 4825f9996aaSopenharmony_ci help='strip specific directory') 4835f9996aaSopenharmony_ci parser.add_option('--stripFile', 4845f9996aaSopenharmony_ci action='append', 4855f9996aaSopenharmony_ci help='strip specific file.') 4865f9996aaSopenharmony_ci 4875f9996aaSopenharmony_ci args = expand_file_args(merge_args) 4885f9996aaSopenharmony_ci options, _ = parser.parse_args(args) 4895f9996aaSopenharmony_ci 4905f9996aaSopenharmony_ci path_transform = path_transform or (lambda p: p) 4915f9996aaSopenharmony_ci added_names = set() 4925f9996aaSopenharmony_ci 4935f9996aaSopenharmony_ci output_is_already_open = not isinstance(output, str) 4945f9996aaSopenharmony_ci if output_is_already_open: 4955f9996aaSopenharmony_ci assert isinstance(output, zipfile.ZipFile) 4965f9996aaSopenharmony_ci out_zip = output 4975f9996aaSopenharmony_ci else: 4985f9996aaSopenharmony_ci out_zip = zipfile.ZipFile(output, 'w') 4995f9996aaSopenharmony_ci 5005f9996aaSopenharmony_ci try: 5015f9996aaSopenharmony_ci for in_file in input_zips: 5025f9996aaSopenharmony_ci with zipfile.ZipFile(in_file, 'r') as in_zip: 5035f9996aaSopenharmony_ci # ijar creates zips with null CRCs. 5045f9996aaSopenharmony_ci in_zip._expected_crc = None 5055f9996aaSopenharmony_ci for info in in_zip.infolist(): 5065f9996aaSopenharmony_ci # Ignore directories. 5075f9996aaSopenharmony_ci if info.filename[-1] == '/': 5085f9996aaSopenharmony_ci continue 5095f9996aaSopenharmony_ci dst_name = path_transform(info.filename) 5105f9996aaSopenharmony_ci if not dst_name: 5115f9996aaSopenharmony_ci continue 5125f9996aaSopenharmony_ci if _strip_dst_name(dst_name, options): 5135f9996aaSopenharmony_ci continue 5145f9996aaSopenharmony_ci already_added = dst_name in added_names 5155f9996aaSopenharmony_ci if not already_added: 5165f9996aaSopenharmony_ci add_to_zip_hermetic( 5175f9996aaSopenharmony_ci out_zip, 5185f9996aaSopenharmony_ci dst_name, 5195f9996aaSopenharmony_ci data=in_zip.read(info), 5205f9996aaSopenharmony_ci compress=info.compress_type != zipfile.ZIP_STORED) 5215f9996aaSopenharmony_ci added_names.add(dst_name) 5225f9996aaSopenharmony_ci finally: 5235f9996aaSopenharmony_ci if not output_is_already_open: 5245f9996aaSopenharmony_ci out_zip.close() 5255f9996aaSopenharmony_ci 5265f9996aaSopenharmony_ci 5275f9996aaSopenharmony_cidef get_sorted_transitive_dependencies(top, deps_func): 5285f9996aaSopenharmony_ci """Gets the list of all transitive dependencies in sorted order. 5295f9996aaSopenharmony_ci 5305f9996aaSopenharmony_ci There should be no cycles in the dependency graph (crashes if cycles exist). 5315f9996aaSopenharmony_ci 5325f9996aaSopenharmony_ci Args: 5335f9996aaSopenharmony_ci top: A list of the top level nodes 5345f9996aaSopenharmony_ci deps_func: A function that takes a node and returns a list of its direct 5355f9996aaSopenharmony_ci dependencies. 5365f9996aaSopenharmony_ci Returns: 5375f9996aaSopenharmony_ci A list of all transitive dependencies of nodes in top, in order (a node 5385f9996aaSopenharmony_ci will appear in the list at a higher index than all of its dependencies). 5395f9996aaSopenharmony_ci """ 5405f9996aaSopenharmony_ci # Find all deps depth-first, maintaining original order in the case of ties. 5415f9996aaSopenharmony_ci deps_map = collections.OrderedDict() 5425f9996aaSopenharmony_ci 5435f9996aaSopenharmony_ci def discover(nodes): 5445f9996aaSopenharmony_ci for node in nodes: 5455f9996aaSopenharmony_ci if node in deps_map: 5465f9996aaSopenharmony_ci continue 5475f9996aaSopenharmony_ci deps = deps_func(node) 5485f9996aaSopenharmony_ci discover(deps) 5495f9996aaSopenharmony_ci deps_map[node] = deps 5505f9996aaSopenharmony_ci 5515f9996aaSopenharmony_ci discover(top) 5525f9996aaSopenharmony_ci return list(deps_map.keys()) 5535f9996aaSopenharmony_ci 5545f9996aaSopenharmony_ci 5555f9996aaSopenharmony_cidef _compute_python_dependencies(): 5565f9996aaSopenharmony_ci """Gets the paths of imported non-system python modules. 5575f9996aaSopenharmony_ci 5585f9996aaSopenharmony_ci A path is assumed to be a "system" import if it is outside of chromium's 5595f9996aaSopenharmony_ci src/. The paths will be relative to the current directory. 5605f9996aaSopenharmony_ci """ 5615f9996aaSopenharmony_ci _force_lazy_modules_to_load() 5625f9996aaSopenharmony_ci module_paths = (m.__file__ for m in sys.modules.values() 5635f9996aaSopenharmony_ci if m is not None and hasattr(m, '__file__') and m.__file__) 5645f9996aaSopenharmony_ci abs_module_paths = list(map(os.path.abspath, module_paths)) 5655f9996aaSopenharmony_ci 5665f9996aaSopenharmony_ci assert os.path.isabs(DIR_SOURCE_ROOT) 5675f9996aaSopenharmony_ci non_system_module_paths = [ 5685f9996aaSopenharmony_ci p for p in abs_module_paths if p.startswith(DIR_SOURCE_ROOT) 5695f9996aaSopenharmony_ci ] 5705f9996aaSopenharmony_ci 5715f9996aaSopenharmony_ci def convert_pyc_to_py(s): 5725f9996aaSopenharmony_ci if s.endswith('.pyc'): 5735f9996aaSopenharmony_ci return s[:-1] 5745f9996aaSopenharmony_ci return s 5755f9996aaSopenharmony_ci 5765f9996aaSopenharmony_ci non_system_module_paths = list( 5775f9996aaSopenharmony_ci map(convert_pyc_to_py, non_system_module_paths)) 5785f9996aaSopenharmony_ci non_system_module_paths = list( 5795f9996aaSopenharmony_ci map(os.path.relpath, non_system_module_paths)) 5805f9996aaSopenharmony_ci return sorted(set(non_system_module_paths)) 5815f9996aaSopenharmony_ci 5825f9996aaSopenharmony_ci 5835f9996aaSopenharmony_cidef _force_lazy_modules_to_load(): 5845f9996aaSopenharmony_ci """Forces any lazily imported modules to fully load themselves. 5855f9996aaSopenharmony_ci 5865f9996aaSopenharmony_ci Inspecting the modules' __file__ attribute causes lazily imported modules 5875f9996aaSopenharmony_ci (e.g. from email) to get fully imported and update sys.modules. Iterate 5885f9996aaSopenharmony_ci over the values until sys.modules stabilizes so that no modules are missed. 5895f9996aaSopenharmony_ci """ 5905f9996aaSopenharmony_ci while True: 5915f9996aaSopenharmony_ci num_modules_before = len(list(sys.modules.keys())) 5925f9996aaSopenharmony_ci for m in list(sys.modules.values()): 5935f9996aaSopenharmony_ci if m is not None and hasattr(m, '__file__'): 5945f9996aaSopenharmony_ci _ = m.__file__ 5955f9996aaSopenharmony_ci num_modules_after = len(list(sys.modules.keys())) 5965f9996aaSopenharmony_ci if num_modules_before == num_modules_after: 5975f9996aaSopenharmony_ci break 5985f9996aaSopenharmony_ci 5995f9996aaSopenharmony_ci 6005f9996aaSopenharmony_cidef add_depfile_option(parser): 6015f9996aaSopenharmony_ci if hasattr(parser, 'add_option'): 6025f9996aaSopenharmony_ci func = parser.add_option 6035f9996aaSopenharmony_ci else: 6045f9996aaSopenharmony_ci func = parser.add_argument 6055f9996aaSopenharmony_ci func('--depfile', help='Path to depfile (refer to `gn help depfile`)') 6065f9996aaSopenharmony_ci 6075f9996aaSopenharmony_ci 6085f9996aaSopenharmony_cidef write_depfile(depfile_path, first_gn_output, inputs=None, add_pydeps=True): 6095f9996aaSopenharmony_ci assert depfile_path != first_gn_output # http://crbug.com/646165 6105f9996aaSopenharmony_ci inputs = inputs or [] 6115f9996aaSopenharmony_ci if add_pydeps: 6125f9996aaSopenharmony_ci inputs = _compute_python_dependencies() + inputs 6135f9996aaSopenharmony_ci inputs = sorted(inputs) 6145f9996aaSopenharmony_ci make_directory(os.path.dirname(depfile_path)) 6155f9996aaSopenharmony_ci # Ninja does not support multiple outputs in depfiles. 6165f9996aaSopenharmony_ci with open(depfile_path, 'w') as depfile: 6175f9996aaSopenharmony_ci depfile.write(first_gn_output.replace(' ', '\\ ')) 6185f9996aaSopenharmony_ci depfile.write(': ') 6195f9996aaSopenharmony_ci depfile.write(' '.join(i.replace(' ', '\\ ') for i in inputs)) 6205f9996aaSopenharmony_ci depfile.write('\n') 6215f9996aaSopenharmony_ci 6225f9996aaSopenharmony_ci 6235f9996aaSopenharmony_cidef expand_file_args(args): 6245f9996aaSopenharmony_ci """Replaces file-arg placeholders in args. 6255f9996aaSopenharmony_ci 6265f9996aaSopenharmony_ci These placeholders have the form: 6275f9996aaSopenharmony_ci @FileArg(filename:key1:key2:...:keyn) 6285f9996aaSopenharmony_ci 6295f9996aaSopenharmony_ci The value of such a placeholder is calculated by reading 'filename' as json. 6305f9996aaSopenharmony_ci And then extracting the value at [key1][key2]...[keyn]. 6315f9996aaSopenharmony_ci 6325f9996aaSopenharmony_ci Note: This intentionally does not return the list of files that appear in 6335f9996aaSopenharmony_ci such placeholders. An action that uses file-args *must* know the paths of 6345f9996aaSopenharmony_ci those files prior to the parsing of the arguments (typically by explicitly 6355f9996aaSopenharmony_ci listing them in the action's inputs in build files). 6365f9996aaSopenharmony_ci """ 6375f9996aaSopenharmony_ci new_args = list(args) 6385f9996aaSopenharmony_ci file_jsons = dict() 6395f9996aaSopenharmony_ci r = re.compile(r'@FileArg\((.*?)\)') 6405f9996aaSopenharmony_ci for i, arg in enumerate(args): 6415f9996aaSopenharmony_ci match = r.search(arg) 6425f9996aaSopenharmony_ci if not match: 6435f9996aaSopenharmony_ci continue 6445f9996aaSopenharmony_ci 6455f9996aaSopenharmony_ci if match.end() != len(arg): 6465f9996aaSopenharmony_ci raise Exception( 6475f9996aaSopenharmony_ci 'Unexpected characters after FileArg: {}'.format(arg)) 6485f9996aaSopenharmony_ci 6495f9996aaSopenharmony_ci lookup_path = match.group(1).split(':') 6505f9996aaSopenharmony_ci file_path = lookup_path[0] 6515f9996aaSopenharmony_ci if file_path not in file_jsons: 6525f9996aaSopenharmony_ci with open(file_path) as f: 6535f9996aaSopenharmony_ci file_jsons[file_path] = json.load(f) 6545f9996aaSopenharmony_ci 6555f9996aaSopenharmony_ci expansion = file_jsons[file_path] 6565f9996aaSopenharmony_ci 6575f9996aaSopenharmony_ci for k in lookup_path[1:]: 6585f9996aaSopenharmony_ci if k in expansion: 6595f9996aaSopenharmony_ci expansion = expansion[k] 6605f9996aaSopenharmony_ci else: 6615f9996aaSopenharmony_ci expansion = "" 6625f9996aaSopenharmony_ci print("WARNNING", lookup_path[1:], "is not in metadata file, set default ''") 6635f9996aaSopenharmony_ci # This should match parse_gn_list. The output is either a GN-formatted list 6645f9996aaSopenharmony_ci # or a literal (with no quotes). 6655f9996aaSopenharmony_ci if isinstance(expansion, list): 6665f9996aaSopenharmony_ci new_args[i] = arg[:match.start()] + gn_helpers.to_gn_string( 6675f9996aaSopenharmony_ci expansion) 6685f9996aaSopenharmony_ci else: 6695f9996aaSopenharmony_ci new_args[i] = arg[:match.start()] + str(expansion) 6705f9996aaSopenharmony_ci 6715f9996aaSopenharmony_ci return new_args 6725f9996aaSopenharmony_ci 6735f9996aaSopenharmony_ci 6745f9996aaSopenharmony_cidef read_sources_list(sources_list_file_name): 6755f9996aaSopenharmony_ci """Reads a GN-written file containing list of file names and returns a list. 6765f9996aaSopenharmony_ci 6775f9996aaSopenharmony_ci Note that this function should not be used to parse response files. 6785f9996aaSopenharmony_ci """ 6795f9996aaSopenharmony_ci with open(sources_list_file_name) as f: 6805f9996aaSopenharmony_ci return [file_name.strip() for file_name in f] 6815f9996aaSopenharmony_ci 6825f9996aaSopenharmony_ci 6835f9996aaSopenharmony_cidef call_and_write_depfile_if_stale(function, 6845f9996aaSopenharmony_ci options, 6855f9996aaSopenharmony_ci record_path=None, 6865f9996aaSopenharmony_ci input_paths=None, 6875f9996aaSopenharmony_ci input_strings=None, 6885f9996aaSopenharmony_ci output_paths=None, 6895f9996aaSopenharmony_ci force=False, 6905f9996aaSopenharmony_ci pass_changes=False, 6915f9996aaSopenharmony_ci depfile_deps=None, 6925f9996aaSopenharmony_ci add_pydeps=True): 6935f9996aaSopenharmony_ci """Wraps md5_check.call_and_record_if_stale() and writes a depfile if applicable. 6945f9996aaSopenharmony_ci 6955f9996aaSopenharmony_ci Depfiles are automatically added to output_paths when present in the 6965f9996aaSopenharmony_ci |options| argument. They are then created after |function| is called. 6975f9996aaSopenharmony_ci 6985f9996aaSopenharmony_ci By default, only python dependencies are added to the depfile. If there are 6995f9996aaSopenharmony_ci other input paths that are not captured by GN deps, then they should be 7005f9996aaSopenharmony_ci listed in depfile_deps. It's important to write paths to the depfile that 7015f9996aaSopenharmony_ci are already captured by GN deps since GN args can cause GN deps to change, 7025f9996aaSopenharmony_ci and such changes are not immediately reflected in depfiles 7035f9996aaSopenharmony_ci (http://crbug.com/589311). 7045f9996aaSopenharmony_ci """ 7055f9996aaSopenharmony_ci if not output_paths: 7065f9996aaSopenharmony_ci raise Exception('At least one output_path must be specified.') 7075f9996aaSopenharmony_ci input_paths = list(input_paths or []) 7085f9996aaSopenharmony_ci input_strings = list(input_strings or []) 7095f9996aaSopenharmony_ci output_paths = list(output_paths or []) 7105f9996aaSopenharmony_ci 7115f9996aaSopenharmony_ci python_deps = None 7125f9996aaSopenharmony_ci if hasattr(options, 'depfile') and options.depfile: 7135f9996aaSopenharmony_ci python_deps = _compute_python_dependencies() 7145f9996aaSopenharmony_ci input_paths += python_deps 7155f9996aaSopenharmony_ci output_paths += [options.depfile] 7165f9996aaSopenharmony_ci 7175f9996aaSopenharmony_ci def on_stale_md5(changes): 7185f9996aaSopenharmony_ci args = (changes, ) if pass_changes else () 7195f9996aaSopenharmony_ci function(*args) 7205f9996aaSopenharmony_ci if python_deps is not None: 7215f9996aaSopenharmony_ci all_depfile_deps = list(python_deps) if add_pydeps else [] 7225f9996aaSopenharmony_ci if depfile_deps: 7235f9996aaSopenharmony_ci all_depfile_deps.extend(depfile_deps) 7245f9996aaSopenharmony_ci write_depfile(options.depfile, 7255f9996aaSopenharmony_ci output_paths[0], 7265f9996aaSopenharmony_ci all_depfile_deps, 7275f9996aaSopenharmony_ci add_pydeps=False) 7285f9996aaSopenharmony_ci 7295f9996aaSopenharmony_ci md5_check.call_and_record_if_stale(on_stale_md5, 7305f9996aaSopenharmony_ci record_path=record_path, 7315f9996aaSopenharmony_ci input_paths=input_paths, 7325f9996aaSopenharmony_ci input_strings=input_strings, 7335f9996aaSopenharmony_ci output_paths=output_paths, 7345f9996aaSopenharmony_ci force=force, 7355f9996aaSopenharmony_ci pass_changes=True) 7365f9996aaSopenharmony_ci 7375f9996aaSopenharmony_ci 7385f9996aaSopenharmony_cidef get_all_files(base, follow_symlinks=False): 7395f9996aaSopenharmony_ci """Returns a list of all the files in |base|. Each entry is relative to the 7405f9996aaSopenharmony_ci last path entry of |base|. 7415f9996aaSopenharmony_ci """ 7425f9996aaSopenharmony_ci result = [] 7435f9996aaSopenharmony_ci for root, _, files in os.walk(base, followlinks=follow_symlinks): 7445f9996aaSopenharmony_ci result.extend([os.path.join(root, f) for f in files]) 7455f9996aaSopenharmony_ci 7465f9996aaSopenharmony_ci return result 7475f9996aaSopenharmony_ci 7485f9996aaSopenharmony_ci 7495f9996aaSopenharmony_cidef rebase_path(path_to_rebase, new_base=None, current_base="."): 7505f9996aaSopenharmony_ci if new_base: 7515f9996aaSopenharmony_ci return os.path.relpath(os.path.join(current_base, path_to_rebase), new_base) 7525f9996aaSopenharmony_ci else: 7535f9996aaSopenharmony_ci return os.path.realpath(os.path.join(current_base, path_to_rebase)) 754