xref: /aosp_15_r20/external/angle/build/3pp_common/common.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2024 The Chromium Authors
2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
4*8975f5c5SAndroid Build Coastguard Worker
5*8975f5c5SAndroid Build Coastguard Workerimport argparse
6*8975f5c5SAndroid Build Coastguard Workerimport logging
7*8975f5c5SAndroid Build Coastguard Workerimport os
8*8975f5c5SAndroid Build Coastguard Workerimport pathlib
9*8975f5c5SAndroid Build Coastguard Workerimport shlex
10*8975f5c5SAndroid Build Coastguard Workerimport shutil
11*8975f5c5SAndroid Build Coastguard Workerimport subprocess
12*8975f5c5SAndroid Build Coastguard Workerimport sys
13*8975f5c5SAndroid Build Coastguard Workerimport tarfile
14*8975f5c5SAndroid Build Coastguard Workerimport tempfile
15*8975f5c5SAndroid Build Coastguard Workerimport time
16*8975f5c5SAndroid Build Coastguard Workerimport urllib.request
17*8975f5c5SAndroid Build Coastguard Worker
18*8975f5c5SAndroid Build Coastguard Workerimport scripthash
19*8975f5c5SAndroid Build Coastguard Worker
20*8975f5c5SAndroid Build Coastguard Worker_THIS_DIR = pathlib.Path(__file__).resolve().parent
21*8975f5c5SAndroid Build Coastguard Worker_SRC_ROOT = _THIS_DIR.parents[1]
22*8975f5c5SAndroid Build Coastguard Worker_CHECKOUT_SRC_ROOT_SUBDIR = '.3pp/chromium'
23*8975f5c5SAndroid Build Coastguard Worker
24*8975f5c5SAndroid Build Coastguard Worker
25*8975f5c5SAndroid Build Coastguard Workerdef parse_args():
26*8975f5c5SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
27*8975f5c5SAndroid Build Coastguard Worker    # TODO(agrieve): Add required=True once 3pp builds with > python3.6.
28*8975f5c5SAndroid Build Coastguard Worker    subparsers = parser.add_subparsers()
29*8975f5c5SAndroid Build Coastguard Worker
30*8975f5c5SAndroid Build Coastguard Worker    subparser = subparsers.add_parser(
31*8975f5c5SAndroid Build Coastguard Worker        'latest', help='Prints the version as $LATEST.$RUNTIME_DEPS_HASH')
32*8975f5c5SAndroid Build Coastguard Worker    subparser.set_defaults(action='latest')
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Worker    subparser = subparsers.add_parser(
35*8975f5c5SAndroid Build Coastguard Worker        'checkout', help='Copies files into the workdir used by docker')
36*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('checkout_dir')
37*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('--version', help='Output from "latest"')
38*8975f5c5SAndroid Build Coastguard Worker    subparser.set_defaults(action='checkout')
39*8975f5c5SAndroid Build Coastguard Worker
40*8975f5c5SAndroid Build Coastguard Worker    subparser = subparsers.add_parser(
41*8975f5c5SAndroid Build Coastguard Worker        'install',
42*8975f5c5SAndroid Build Coastguard Worker        help=('Run from workdir, inside docker container. '
43*8975f5c5SAndroid Build Coastguard Worker              'Builds & copies outputs into |output_prefix| directory'))
44*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('output_prefix',
45*8975f5c5SAndroid Build Coastguard Worker                           help='The path to install the compiled package to.')
46*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('deps_prefix',
47*8975f5c5SAndroid Build Coastguard Worker                           help='The path to a directory containing all deps.')
48*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('--version', help='Output from "latest"')
49*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('--checkout-dir', help='Directory to use as CWD')
50*8975f5c5SAndroid Build Coastguard Worker    subparser.set_defaults(action='install')
51*8975f5c5SAndroid Build Coastguard Worker
52*8975f5c5SAndroid Build Coastguard Worker    subparser = subparsers.add_parser(
53*8975f5c5SAndroid Build Coastguard Worker        'local-test', help='Run latest / checkout / install locally')
54*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('--checkout-dir',
55*8975f5c5SAndroid Build Coastguard Worker                           default='3pp_workdir',
56*8975f5c5SAndroid Build Coastguard Worker                           help='Workdir to use')
57*8975f5c5SAndroid Build Coastguard Worker    subparser.add_argument('--output-prefix',
58*8975f5c5SAndroid Build Coastguard Worker                           default='3pp_out',
59*8975f5c5SAndroid Build Coastguard Worker                           help='Directory for final artifacts')
60*8975f5c5SAndroid Build Coastguard Worker    subparser.set_defaults(action='local-test')
61*8975f5c5SAndroid Build Coastguard Worker
62*8975f5c5SAndroid Build Coastguard Worker    args = parser.parse_args()
63*8975f5c5SAndroid Build Coastguard Worker    if not hasattr(args, 'action'):
64*8975f5c5SAndroid Build Coastguard Worker        parser.print_help()
65*8975f5c5SAndroid Build Coastguard Worker        sys.exit(1)
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Worker    if hasattr(args, 'version'):
68*8975f5c5SAndroid Build Coastguard Worker        args.version = args.version or os.environ.get('_3PP_VERSION')
69*8975f5c5SAndroid Build Coastguard Worker        if not args.version:
70*8975f5c5SAndroid Build Coastguard Worker            parser.error('Must set --version or _3PP_VERSION')
71*8975f5c5SAndroid Build Coastguard Worker    if hasattr(args, 'output_prefix') and args.output_prefix:
72*8975f5c5SAndroid Build Coastguard Worker        args.output_prefix = os.path.abspath(args.output_prefix)
73*8975f5c5SAndroid Build Coastguard Worker    if hasattr(args, 'checkout_dir') and args.checkout_dir:
74*8975f5c5SAndroid Build Coastguard Worker        args.checkout_dir = os.path.abspath(args.checkout_dir)
75*8975f5c5SAndroid Build Coastguard Worker
76*8975f5c5SAndroid Build Coastguard Worker    if args.action == 'checkout':
77*8975f5c5SAndroid Build Coastguard Worker        # 3pp bot recipe does this, so needed only when running locally.
78*8975f5c5SAndroid Build Coastguard Worker        os.makedirs(args.checkout_dir, exist_ok=True)
79*8975f5c5SAndroid Build Coastguard Worker
80*8975f5c5SAndroid Build Coastguard Worker    if args.action == 'install':
81*8975f5c5SAndroid Build Coastguard Worker        if args.checkout_dir:
82*8975f5c5SAndroid Build Coastguard Worker            logging.info('Setting CWD=%s', args.checkout_dir)
83*8975f5c5SAndroid Build Coastguard Worker            os.chdir(args.checkout_dir)
84*8975f5c5SAndroid Build Coastguard Worker
85*8975f5c5SAndroid Build Coastguard Worker        if not os.path.exists(_CHECKOUT_SRC_ROOT_SUBDIR):
86*8975f5c5SAndroid Build Coastguard Worker            parser.error(f'Does not exist: {_CHECKOUT_SRC_ROOT_SUBDIR}.'
87*8975f5c5SAndroid Build Coastguard Worker                         f' Use --checkout-dir?')
88*8975f5c5SAndroid Build Coastguard Worker
89*8975f5c5SAndroid Build Coastguard Worker        # 3pp bot recipe does this, so needed only when running locally.
90*8975f5c5SAndroid Build Coastguard Worker        os.makedirs(args.output_prefix, exist_ok=True)
91*8975f5c5SAndroid Build Coastguard Worker
92*8975f5c5SAndroid Build Coastguard Worker    return args
93*8975f5c5SAndroid Build Coastguard Worker
94*8975f5c5SAndroid Build Coastguard Worker
95*8975f5c5SAndroid Build Coastguard Workerdef path_within_checkout(subpath):
96*8975f5c5SAndroid Build Coastguard Worker    return os.path.abspath(os.path.join(_CHECKOUT_SRC_ROOT_SUBDIR, subpath))
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Worker
99*8975f5c5SAndroid Build Coastguard Workerdef _all_files(path):
100*8975f5c5SAndroid Build Coastguard Worker    if os.path.isfile(path):
101*8975f5c5SAndroid Build Coastguard Worker        return [path]
102*8975f5c5SAndroid Build Coastguard Worker    assert os.path.isdir(path), 'Not a file or dir: ' + path
103*8975f5c5SAndroid Build Coastguard Worker    all_paths = pathlib.Path(path).glob('**/*')
104*8975f5c5SAndroid Build Coastguard Worker    return [str(f) for f in all_paths if f.is_file()]
105*8975f5c5SAndroid Build Coastguard Worker
106*8975f5c5SAndroid Build Coastguard Worker
107*8975f5c5SAndroid Build Coastguard Workerdef _resolve_runtime_deps(runtime_deps):
108*8975f5c5SAndroid Build Coastguard Worker    ret = []
109*8975f5c5SAndroid Build Coastguard Worker    for p in runtime_deps:
110*8975f5c5SAndroid Build Coastguard Worker        if p.startswith('//'):
111*8975f5c5SAndroid Build Coastguard Worker            ret.append(os.path.relpath(str(_SRC_ROOT / p[2:])))
112*8975f5c5SAndroid Build Coastguard Worker        elif os.path.isabs(p):
113*8975f5c5SAndroid Build Coastguard Worker            ret.append(os.path.relpath(p))
114*8975f5c5SAndroid Build Coastguard Worker        else:
115*8975f5c5SAndroid Build Coastguard Worker            ret.append(p)
116*8975f5c5SAndroid Build Coastguard Worker    return ret
117*8975f5c5SAndroid Build Coastguard Worker
118*8975f5c5SAndroid Build Coastguard Worker
119*8975f5c5SAndroid Build Coastguard Workerdef copy_runtime_deps(checkout_dir, runtime_deps):
120*8975f5c5SAndroid Build Coastguard Worker    # Make 3pp_common scripts available in the docker container install.py
121*8975f5c5SAndroid Build Coastguard Worker    # will run in.
122*8975f5c5SAndroid Build Coastguard Worker    dest_dir = os.path.join(checkout_dir, _CHECKOUT_SRC_ROOT_SUBDIR)
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker    for src_path in _resolve_runtime_deps(runtime_deps):
125*8975f5c5SAndroid Build Coastguard Worker        relpath = os.path.relpath(src_path, _SRC_ROOT)
126*8975f5c5SAndroid Build Coastguard Worker        dest_path = os.path.join(dest_dir, relpath)
127*8975f5c5SAndroid Build Coastguard Worker        os.makedirs(os.path.dirname(dest_path), exist_ok=True)
128*8975f5c5SAndroid Build Coastguard Worker        if os.path.isfile(src_path):
129*8975f5c5SAndroid Build Coastguard Worker            shutil.copy(src_path, dest_path)
130*8975f5c5SAndroid Build Coastguard Worker        else:
131*8975f5c5SAndroid Build Coastguard Worker            shutil.copytree(src_path,
132*8975f5c5SAndroid Build Coastguard Worker                            dest_path,
133*8975f5c5SAndroid Build Coastguard Worker                            ignore=shutil.ignore_patterns('.*', '__pycache__'))
134*8975f5c5SAndroid Build Coastguard Worker    logging.info('Runtime deps:')
135*8975f5c5SAndroid Build Coastguard Worker    sys.stderr.write('\n'.join(_all_files(checkout_dir)) + '\n')
136*8975f5c5SAndroid Build Coastguard Worker
137*8975f5c5SAndroid Build Coastguard Worker
138*8975f5c5SAndroid Build Coastguard Workerdef download_file(url, dest):
139*8975f5c5SAndroid Build Coastguard Worker    logging.info('Downloading %s', url)
140*8975f5c5SAndroid Build Coastguard Worker    with urllib.request.urlopen(url) as r:
141*8975f5c5SAndroid Build Coastguard Worker        with open(dest, 'wb') as f:
142*8975f5c5SAndroid Build Coastguard Worker            shutil.copyfileobj(r, f)
143*8975f5c5SAndroid Build Coastguard Worker
144*8975f5c5SAndroid Build Coastguard Worker
145*8975f5c5SAndroid Build Coastguard Workerdef extract_tar(path, dest):
146*8975f5c5SAndroid Build Coastguard Worker    logging.info('Extracting %s to %s', path, dest)
147*8975f5c5SAndroid Build Coastguard Worker    with tarfile.open(path) as f:
148*8975f5c5SAndroid Build Coastguard Worker        f.extractall(dest)
149*8975f5c5SAndroid Build Coastguard Worker
150*8975f5c5SAndroid Build Coastguard Worker
151*8975f5c5SAndroid Build Coastguard Workerdef run_cmd(cmd, check=True, *args, **kwargs):
152*8975f5c5SAndroid Build Coastguard Worker    logging.info('Running: %s', shlex.join(cmd))
153*8975f5c5SAndroid Build Coastguard Worker    return subprocess.run(cmd, check=check, *args, **kwargs)
154*8975f5c5SAndroid Build Coastguard Worker
155*8975f5c5SAndroid Build Coastguard Worker
156*8975f5c5SAndroid Build Coastguard Workerdef apply_patches(patches_dir, checkout_dir):
157*8975f5c5SAndroid Build Coastguard Worker    for path in sorted(pathlib.Path(patches_dir).glob('*.patch')):
158*8975f5c5SAndroid Build Coastguard Worker        cmd = ['git', 'apply', '-v', str(path)]
159*8975f5c5SAndroid Build Coastguard Worker        run_cmd(cmd, cwd=checkout_dir)
160*8975f5c5SAndroid Build Coastguard Worker
161*8975f5c5SAndroid Build Coastguard Worker
162*8975f5c5SAndroid Build Coastguard Workerdef main(*, do_latest, do_install, runtime_deps):
163*8975f5c5SAndroid Build Coastguard Worker    logging.basicConfig(
164*8975f5c5SAndroid Build Coastguard Worker        level=logging.DEBUG,
165*8975f5c5SAndroid Build Coastguard Worker        format='%(levelname).1s %(relativeCreated)6d %(message)s')
166*8975f5c5SAndroid Build Coastguard Worker    args = parse_args()
167*8975f5c5SAndroid Build Coastguard Worker    runtime_deps = [str(_THIS_DIR)] + runtime_deps
168*8975f5c5SAndroid Build Coastguard Worker
169*8975f5c5SAndroid Build Coastguard Worker    if args.action == 'local-test':
170*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Will use work dir: %s', args.checkout_dir)
171*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Will use output dir: %s', args.output_prefix)
172*8975f5c5SAndroid Build Coastguard Worker        if os.path.exists(args.checkout_dir) and os.listdir(args.checkout_dir):
173*8975f5c5SAndroid Build Coastguard Worker            logging.warning(
174*8975f5c5SAndroid Build Coastguard Worker                '*** Work dir not empty. This often causes failures. ***')
175*8975f5c5SAndroid Build Coastguard Worker            time.sleep(4)
176*8975f5c5SAndroid Build Coastguard Worker        # Approximates what 3pp recipe does for minimal configs.
177*8975f5c5SAndroid Build Coastguard Worker        # https://source.chromium.org/search?q=symbol:Chromium3ppApi.execute&ss=chromium
178*8975f5c5SAndroid Build Coastguard Worker        prog = os.path.abspath(sys.argv[0])
179*8975f5c5SAndroid Build Coastguard Worker        cmd = [prog, 'latest']
180*8975f5c5SAndroid Build Coastguard Worker        version = run_cmd(cmd, stdout=subprocess.PIPE, text=True).stdout
181*8975f5c5SAndroid Build Coastguard Worker        os.environ['_3PP_VERSION'] = version
182*8975f5c5SAndroid Build Coastguard Worker        checkout_dir = args.checkout_dir
183*8975f5c5SAndroid Build Coastguard Worker        run_cmd([prog, 'checkout', checkout_dir])
184*8975f5c5SAndroid Build Coastguard Worker        run_cmd([prog, 'install', args.output_prefix, 'UNUSED-DEPS-DIR'],
185*8975f5c5SAndroid Build Coastguard Worker                cwd=checkout_dir)
186*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Local test complete.')
187*8975f5c5SAndroid Build Coastguard Worker        return
188*8975f5c5SAndroid Build Coastguard Worker
189*8975f5c5SAndroid Build Coastguard Worker    if args.action == 'latest':
190*8975f5c5SAndroid Build Coastguard Worker        version = do_latest()
191*8975f5c5SAndroid Build Coastguard Worker        assert version, 'do_latest() returned ' + repr(version)
192*8975f5c5SAndroid Build Coastguard Worker        extra_paths = []
193*8975f5c5SAndroid Build Coastguard Worker        for p in _resolve_runtime_deps(runtime_deps):
194*8975f5c5SAndroid Build Coastguard Worker            extra_paths += _all_files(p)
195*8975f5c5SAndroid Build Coastguard Worker        deps_hash = scripthash.compute(extra_paths=extra_paths)
196*8975f5c5SAndroid Build Coastguard Worker        print(f'{version}.{deps_hash}')
197*8975f5c5SAndroid Build Coastguard Worker        return
198*8975f5c5SAndroid Build Coastguard Worker
199*8975f5c5SAndroid Build Coastguard Worker    # Remove the hash at the end: 30.4.0-alpha05.HASH => 30.4.0-alpha05
200*8975f5c5SAndroid Build Coastguard Worker    args.version = args.version.rsplit('.', 1)[0]
201*8975f5c5SAndroid Build Coastguard Worker    if args.action == 'checkout':
202*8975f5c5SAndroid Build Coastguard Worker        copy_runtime_deps(args.checkout_dir, runtime_deps)
203*8975f5c5SAndroid Build Coastguard Worker        return
204*8975f5c5SAndroid Build Coastguard Worker
205*8975f5c5SAndroid Build Coastguard Worker    assert args.action == 'install'
206*8975f5c5SAndroid Build Coastguard Worker    do_install(args)
207*8975f5c5SAndroid Build Coastguard Worker    prefix_len = len(args.output_prefix) + 1
208*8975f5c5SAndroid Build Coastguard Worker    logging.info(
209*8975f5c5SAndroid Build Coastguard Worker        'Contents of %s: \n%s\n', args.output_prefix,
210*8975f5c5SAndroid Build Coastguard Worker        '\n'.join(p[prefix_len:] for p in _all_files(args.output_prefix)))
211