David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 2 | # |
| 3 | # Copyright (C) 2021 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 17 | """ |
| 18 | This scripts compiles Java files which are needed to execute run-tests. |
| 19 | It is intended to be used only from soong genrule. |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 20 | """ |
| 21 | |
| 22 | import argparse |
| 23 | import functools |
| 24 | import glob |
| 25 | import os |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 26 | import pathlib |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 27 | import shlex |
| 28 | import shutil |
| 29 | import subprocess |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 30 | import sys |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 31 | import zipfile |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 32 | |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 33 | from argparse import ArgumentParser |
| 34 | from fcntl import lockf, LOCK_EX, LOCK_NB |
| 35 | from importlib.machinery import SourceFileLoader |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 36 | from concurrent.futures import ThreadPoolExecutor |
| 37 | from os import environ, getcwd, chdir, cpu_count, chmod |
| 38 | from os.path import relpath |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 39 | from pathlib import Path |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 40 | from pprint import pprint |
David Srbecky | e0c3cd8 | 2022-08-24 14:56:24 +0100 | [diff] [blame] | 41 | from re import match |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 42 | from shutil import copytree, rmtree |
| 43 | from subprocess import run |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 44 | from tempfile import TemporaryDirectory, NamedTemporaryFile |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 45 | from typing import Dict, List, Union, Set, Optional |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 46 | |
David Srbecky | e9142f2 | 2022-08-03 11:05:19 +0100 | [diff] [blame] | 47 | USE_RBE_FOR_JAVAC = 100 # Percentage of tests that can use RBE (between 0 and 100) |
| 48 | USE_RBE_FOR_D8 = 100 # Percentage of tests that can use RBE (between 0 and 100) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 49 | |
| 50 | lock_file = None # Keep alive as long as this process is alive. |
| 51 | |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 52 | |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 53 | class BuildTestContext: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 54 | def __init__(self, args, android_build_top, test_dir): |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 55 | self.test_name = test_dir.name |
| 56 | self.test_dir = test_dir.absolute() |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 57 | self.mode = args.mode |
| 58 | self.jvm = (self.mode == "jvm") |
| 59 | self.host = (self.mode == "host") |
| 60 | self.target = (self.mode == "target") |
| 61 | assert self.jvm or self.host or self.target |
| 62 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 63 | self.android_build_top = android_build_top.absolute() |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 64 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 65 | self.java_home = Path(os.environ.get("JAVA_HOME")).absolute() |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 66 | self.java = self.java_home / "bin/java" |
| 67 | self.javac = self.java_home / "bin/javac" |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 68 | self.javac_args = "-g -Xlint:-options -source 1.8 -target 1.8" |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 69 | |
| 70 | self.bootclasspath = args.bootclasspath.absolute() |
| 71 | self.d8 = args.d8.absolute() |
| 72 | self.hiddenapi = args.hiddenapi.absolute() |
| 73 | self.jasmin = args.jasmin.absolute() |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 74 | self.smali = args.smali.absolute() |
| 75 | self.soong_zip = args.soong_zip.absolute() |
| 76 | self.zipalign = args.zipalign.absolute() |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 77 | |
| 78 | # Minimal environment needed for bash commands that we execute. |
| 79 | self.bash_env = { |
| 80 | "ANDROID_BUILD_TOP": self.android_build_top, |
| 81 | "D8": self.d8, |
| 82 | "JAVA": self.java, |
| 83 | "JAVAC": self.javac, |
| 84 | "JAVAC_ARGS": self.javac_args, |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 85 | "JAVA_HOME": self.java_home, |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 86 | "PATH": os.environ["PATH"], |
| 87 | "PYTHONDONTWRITEBYTECODE": "1", |
| 88 | "SMALI": self.smali, |
| 89 | "SOONG_ZIP": self.soong_zip, |
| 90 | "TEST_NAME": self.test_name, |
| 91 | } |
| 92 | |
| 93 | def bash(self, cmd): |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 94 | return subprocess.run(cmd, |
| 95 | shell=True, |
| 96 | cwd=self.test_dir, |
| 97 | env=self.bash_env, |
| 98 | check=True) |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 99 | |
| 100 | def default_build(self, **kwargs): |
| 101 | globals()['default_build'](self, **kwargs) |
| 102 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 103 | def rm(*patterns): |
| 104 | for pattern in patterns: |
| 105 | for path in glob.glob(pattern): |
| 106 | if os.path.isdir(path): |
| 107 | shutil.rmtree(path) |
| 108 | else: |
| 109 | os.remove(path) |
| 110 | |
David Srbecky | cf57dee | 2022-10-14 17:36:51 +0100 | [diff] [blame] | 111 | def default_build( |
| 112 | ctx, |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 113 | use_desugar=True, |
| 114 | use_hiddenapi=True, |
| 115 | need_dex=None, |
| 116 | experimental="no-experiment", |
| 117 | zip_compression_method="deflate", |
| 118 | zip_align_bytes=None, |
| 119 | api_level=None, |
| 120 | javac_args=[], |
| 121 | d8_flags=[], |
| 122 | smali_args=[], |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 123 | use_smali=True, |
| 124 | use_jasmin=True, |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 125 | ): |
| 126 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 127 | # Wrap "pathlib.Path" with our own version that ensures all paths are absolute. |
| 128 | # Plain filenames are assumed to be relative to ctx.test_dir and made absolute. |
| 129 | class Path(pathlib.Path): |
| 130 | def __new__(cls, filename: str): |
| 131 | path = pathlib.Path(filename) |
| 132 | return path if path.is_absolute() else (ctx.test_dir / path) |
| 133 | |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 134 | ANDROID_BUILD_TOP = ctx.android_build_top |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 135 | TEST_NAME = ctx.test_name |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 136 | need_dex = (ctx.host or ctx.target) if need_dex is None else need_dex |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 137 | |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 138 | RBE_exec_root = os.environ.get("RBE_exec_root") |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 139 | RBE_rewrapper = ctx.android_build_top / "prebuilts/remoteexecution-client/live/rewrapper" |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 140 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 141 | # Set default values for directories. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 142 | HAS_SRC = Path("src").exists() |
| 143 | HAS_SRC_ART = Path("src-art").exists() |
| 144 | HAS_SRC2 = Path("src2").exists() |
| 145 | HAS_SRC_MULTIDEX = Path("src-multidex").exists() |
| 146 | HAS_SMALI_MULTIDEX = Path("smali-multidex").exists() |
| 147 | HAS_JASMIN_MULTIDEX = Path("jasmin-multidex").exists() |
| 148 | HAS_SMALI_EX = Path("smali-ex").exists() |
| 149 | HAS_SRC_EX = Path("src-ex").exists() |
| 150 | HAS_SRC_EX2 = Path("src-ex2").exists() |
| 151 | HAS_SRC_AOTEX = Path("src-aotex").exists() |
| 152 | HAS_SRC_BCPEX = Path("src-bcpex").exists() |
| 153 | HAS_HIDDENAPI_SPEC = Path("hiddenapi-flags.csv").exists() |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 154 | |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 155 | JAVAC_ARGS = shlex.split(ctx.javac_args) + javac_args |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 156 | SMALI_ARGS = smali_args.copy() |
| 157 | D8_FLAGS = d8_flags.copy() |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 158 | |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 159 | BUILD_MODE = ctx.mode |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 160 | |
| 161 | # Setup experimental API level mappings in a bash associative array. |
| 162 | EXPERIMENTAL_API_LEVEL = {} |
| 163 | EXPERIMENTAL_API_LEVEL["no-experiment"] = "26" |
| 164 | EXPERIMENTAL_API_LEVEL["default-methods"] = "24" |
| 165 | EXPERIMENTAL_API_LEVEL["parameter-annotations"] = "25" |
| 166 | EXPERIMENTAL_API_LEVEL["agents"] = "26" |
| 167 | EXPERIMENTAL_API_LEVEL["method-handles"] = "26" |
| 168 | EXPERIMENTAL_API_LEVEL["var-handles"] = "28" |
| 169 | |
| 170 | if BUILD_MODE == "jvm": |
| 171 | # No desugaring on jvm because it supports the latest functionality. |
| 172 | use_desugar = False |
| 173 | # Do not attempt to build src-art directories on jvm, |
| 174 | # since it would fail without libcore. |
| 175 | HAS_SRC_ART = False |
| 176 | |
| 177 | # Set API level for smali and d8. |
| 178 | if not api_level: |
| 179 | api_level = EXPERIMENTAL_API_LEVEL[experimental] |
| 180 | |
| 181 | # Add API level arguments to smali and dx |
| 182 | SMALI_ARGS.extend(["--api", str(api_level)]) |
| 183 | D8_FLAGS.extend(["--min-api", str(api_level)]) |
| 184 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 185 | def run(executable: pathlib.Path, args: List[str]): |
| 186 | assert isinstance(executable, pathlib.Path), executable |
| 187 | cmd: List[Union[pathlib.Path, str]] = [] |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 188 | if executable.suffix == ".sh": |
| 189 | cmd += ["/bin/bash"] |
| 190 | cmd += [executable] |
| 191 | cmd += args |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 192 | env = ctx.bash_env |
| 193 | env.update({k: v for k, v in os.environ.items() if k.startswith("RBE_")}) |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 194 | # Make paths relative as otherwise we could create too long command line. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 195 | for i, arg in enumerate(cmd): |
| 196 | if isinstance(arg, pathlib.Path): |
| 197 | assert arg.absolute(), arg |
| 198 | cmd[i] = relpath(arg, ctx.test_dir) |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 199 | elif isinstance(arg, list): |
| 200 | assert all(p.absolute() for p in arg), arg |
| 201 | cmd[i] = ":".join(relpath(p, ctx.test_dir) for p in arg) |
| 202 | else: |
| 203 | assert isinstance(arg, str), arg |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 204 | p = subprocess.run(cmd, |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 205 | encoding=sys.stdout.encoding, |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 206 | cwd=ctx.test_dir, |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 207 | env=ctx.bash_env, |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 208 | stderr=subprocess.STDOUT, |
| 209 | stdout=subprocess.PIPE) |
| 210 | if p.returncode != 0: |
| 211 | raise Exception("Command failed with exit code {}\n$ {}\n{}".format( |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 212 | p.returncode, " ".join(map(str, cmd)), p.stdout)) |
David Srbecky | e0c3cd8 | 2022-08-24 14:56:24 +0100 | [diff] [blame] | 213 | return p |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 214 | |
| 215 | |
| 216 | # Helper functions to execute tools. |
David Srbecky | 51dec05 | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 217 | soong_zip = functools.partial(run, ctx.soong_zip) |
| 218 | zipalign = functools.partial(run, ctx.zipalign) |
| 219 | javac = functools.partial(run, ctx.javac) |
| 220 | jasmin = functools.partial(run, ctx.jasmin) |
| 221 | smali = functools.partial(run, ctx.smali) |
| 222 | d8 = functools.partial(run, ctx.d8) |
| 223 | hiddenapi = functools.partial(run, ctx.hiddenapi) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 224 | |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 225 | if "RBE_server_address" in os.environ: |
David Srbecky | e0c3cd8 | 2022-08-24 14:56:24 +0100 | [diff] [blame] | 226 | version = match(r"Version: (\d*)\.(\d*)\.(\d*)", run(RBE_rewrapper, ["--version"]).stdout) |
| 227 | assert version, "Could not parse RBE version" |
| 228 | assert tuple(map(int, version.groups())) >= (0, 76, 0), "Please update " + RBE_rewrapper |
| 229 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 230 | def rbe_wrap(args, inputs: Set[pathlib.Path]=None): |
| 231 | with NamedTemporaryFile(mode="w+t") as input_list: |
| 232 | inputs = inputs or set() |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 233 | for i, arg in enumerate(args): |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 234 | if isinstance(arg, pathlib.Path): |
| 235 | assert arg.absolute(), arg |
| 236 | inputs.add(arg) |
| 237 | elif isinstance(arg, list): |
| 238 | assert all(p.absolute() for p in arg), arg |
| 239 | inputs.update(arg) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 240 | input_list.writelines([relpath(i, RBE_exec_root)+"\n" for i in inputs]) |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 241 | input_list.flush() |
| 242 | return run(RBE_rewrapper, [ |
| 243 | "--platform=" + os.environ["RBE_platform"], |
| 244 | "--input_list_paths=" + input_list.name, |
| 245 | ] + args) |
| 246 | |
| 247 | if USE_RBE_FOR_JAVAC > (hash(TEST_NAME) % 100): # Use for given percentage of tests. |
| 248 | def javac(args): |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 249 | output = relpath(Path(args[args.index("-d") + 1]), RBE_exec_root) |
| 250 | return rbe_wrap(["--output_directories", output, ctx.javac] + args) |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 251 | |
| 252 | if USE_RBE_FOR_D8 > (hash(TEST_NAME) % 100): # Use for given percentage of tests. |
| 253 | def d8(args): |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 254 | inputs = set([ctx.d8.parent.parent / "framework/d8.jar"]) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 255 | output = relpath(Path(args[args.index("--output") + 1]), RBE_exec_root) |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 256 | return rbe_wrap([ |
| 257 | "--output_files" if output.endswith(".jar") else "--output_directories", output, |
David Srbecky | e0c3cd8 | 2022-08-24 14:56:24 +0100 | [diff] [blame] | 258 | "--toolchain_inputs=prebuilts/jdk/jdk11/linux-x86/bin/java", |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 259 | ctx.d8] + args, inputs) |
David Srbecky | 89010a3 | 2022-07-14 16:47:30 +0000 | [diff] [blame] | 260 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 261 | # If wrapper script exists, use it instead of the default javac. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 262 | javac_wrapper = Path("javac_wrapper.sh") |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 263 | if javac_wrapper.exists(): |
| 264 | javac = functools.partial(run, javac_wrapper) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 265 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 266 | def zip(zip_target: Path, *files: Path): |
| 267 | zip_args = ["-o", zip_target, "-C", zip_target.parent] |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 268 | if zip_compression_method == "store": |
| 269 | zip_args.extend(["-L", "0"]) |
| 270 | for f in files: |
| 271 | zip_args.extend(["-f", f]) |
| 272 | soong_zip(zip_args) |
| 273 | |
| 274 | if zip_align_bytes: |
| 275 | # zipalign does not operate in-place, so write results to a temp file. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 276 | with TemporaryDirectory() as tmp_dir: |
| 277 | tmp_file = Path(tmp_dir) / "aligned.zip" |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 278 | zipalign(["-f", str(zip_align_bytes), zip_target, tmp_file]) |
| 279 | # replace original zip target with our temp file. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 280 | tmp_file.rename(zip_target) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 281 | |
| 282 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 283 | def make_jasmin(dst_dir: Path, src_dir: Path) -> Optional[Path]: |
| 284 | if not use_jasmin or not src_dir.exists(): |
| 285 | return None # No sources to compile. |
| 286 | dst_dir.mkdir() |
| 287 | jasmin(["-d", dst_dir] + sorted(src_dir.glob("**/*.j"))) |
| 288 | return dst_dir |
| 289 | |
| 290 | def make_smali(dst_dex: Path, src_dir: Path) -> Optional[Path]: |
| 291 | if not use_smali or not src_dir.exists(): |
| 292 | return None # No sources to compile. |
| 293 | smali(["-JXmx512m", "assemble"] + SMALI_ARGS + |
| 294 | ["--output", dst_dex] + sorted(src_dir.glob("**/*.smali"))) |
| 295 | return dst_dex |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 296 | |
| 297 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 298 | java_classpath: List[Path] = [] |
| 299 | |
| 300 | def make_java(dst_dir: Path, *src_dirs: Path) -> Optional[Path]: |
| 301 | if not any(src_dir.exists() for src_dir in src_dirs): |
| 302 | return None # No sources to compile. |
| 303 | dst_dir.mkdir(exist_ok=True) |
| 304 | args = JAVAC_ARGS + ["-implicit:none", "-encoding", "utf8", "-d", dst_dir] |
| 305 | if not ctx.jvm: |
| 306 | args += ["-bootclasspath", ctx.bootclasspath] |
| 307 | if java_classpath: |
| 308 | args += ["-classpath", java_classpath] |
| 309 | for src_dir in src_dirs: |
| 310 | args += sorted(src_dir.glob("**/*.java")) |
| 311 | javac(args) |
| 312 | return dst_dir |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 313 | |
| 314 | |
| 315 | # Make a "dex" file given a directory of classes. This will be |
| 316 | # packaged in a jar file. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 317 | def make_dex(src_dir: Path): |
| 318 | dst_jar = Path(src_dir.name + ".jar") |
| 319 | args = D8_FLAGS + ["--output", dst_jar] |
| 320 | args += ["--lib", ctx.bootclasspath] if use_desugar else ["--no-desugaring"] |
| 321 | args += sorted(src_dir.glob("**/*.class")) |
| 322 | d8(args) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 323 | |
| 324 | # D8 outputs to JAR files today rather than DEX files as DX used |
| 325 | # to. To compensate, we extract the DEX from d8's output to meet the |
| 326 | # expectations of make_dex callers. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 327 | dst_dex = Path(src_dir.name + ".dex") |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 328 | with TemporaryDirectory() as tmp_dir: |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 329 | zipfile.ZipFile(dst_jar, "r").extractall(tmp_dir) |
| 330 | (Path(tmp_dir) / "classes.dex").rename(dst_dex) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 331 | |
| 332 | # Merge all the dex files. |
| 333 | # Skip non-existing files, but at least 1 file must exist. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 334 | def make_dexmerge(dst_dex: Path, *src_dexs: Path): |
| 335 | # Include destination. Skip any non-existing files. |
| 336 | srcs = [f for f in [dst_dex] + list(src_dexs) if f.exists()] |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 337 | |
| 338 | # NB: We merge even if there is just single input. |
| 339 | # It is useful to normalize non-deterministic smali output. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 340 | tmp_dir = ctx.test_dir / "dexmerge" |
| 341 | tmp_dir.mkdir() |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 342 | d8(["--min-api", api_level, "--output", tmp_dir] + srcs) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 343 | assert not (tmp_dir / "classes2.dex").exists() |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 344 | for src_file in srcs: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 345 | src_file.unlink() |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 346 | (tmp_dir / "classes.dex").rename(dst_dex) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 347 | tmp_dir.rmdir() |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 348 | |
| 349 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 350 | def make_hiddenapi(*dex_files: Path): |
| 351 | args: List[Union[str, Path]] = ["encode"] |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 352 | for dex_file in dex_files: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 353 | args.extend(["--input-dex=" + str(dex_file), "--output-dex=" + str(dex_file)]) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 354 | args.append("--api-flags=hiddenapi-flags.csv") |
| 355 | args.append("--no-force-assign-all") |
| 356 | hiddenapi(args) |
| 357 | |
| 358 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 359 | if Path("classes.dex").exists(): |
| 360 | zip(Path(TEST_NAME + ".jar"), Path("classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 361 | return |
| 362 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 363 | if Path("classes.dm").exists(): |
| 364 | zip(Path(TEST_NAME + ".jar"), Path("classes.dm")) |
Nicolas Geoffray | 3b7475b | 2022-11-09 15:33:12 +0000 | [diff] [blame] | 365 | return |
| 366 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 367 | |
| 368 | def has_multidex(): |
| 369 | return HAS_SRC_MULTIDEX or HAS_JASMIN_MULTIDEX or HAS_SMALI_MULTIDEX |
| 370 | |
| 371 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 372 | if make_jasmin(Path("jasmin_classes"), Path("jasmin")): |
| 373 | java_classpath.append(Path("jasmin_classes")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 374 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 375 | if make_jasmin(Path("jasmin_classes2"), Path("jasmin-multidex")): |
| 376 | java_classpath.append(Path("jasmin_classes2")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 377 | |
| 378 | if HAS_SRC and (HAS_SRC_MULTIDEX or HAS_SRC_AOTEX or HAS_SRC_BCPEX or |
| 379 | HAS_SRC_EX or HAS_SRC_ART or HAS_SRC2 or HAS_SRC_EX2): |
| 380 | # To allow circular references, compile src/, src-multidex/, src-aotex/, |
| 381 | # src-bcpex/, src-ex/ together and pass the output as class path argument. |
| 382 | # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols |
| 383 | # used by the other src-* sources we compile here but everything needed to |
| 384 | # compile the other src-* sources should be present in src/ (and jasmin*/). |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 385 | make_java(Path("classes-tmp-all"), |
| 386 | Path("src"), |
| 387 | Path("src-multidex"), |
| 388 | Path("src-aotex"), |
| 389 | Path("src-bcpex"), |
| 390 | Path("src-ex")) |
| 391 | java_classpath.append(Path("classes-tmp-all")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 392 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 393 | if make_java(Path("classes-aotex"), Path("src-aotex")) and need_dex: |
| 394 | make_dex(Path("classes-aotex")) |
| 395 | # rename it so it shows up as "classes.dex" in the zip file. |
| 396 | Path("classes-aotex.dex").rename(Path("classes.dex")) |
| 397 | zip(Path(TEST_NAME + "-aotex.jar"), Path("classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 398 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 399 | if make_java(Path("classes-bcpex"), Path("src-bcpex")) and need_dex: |
| 400 | make_dex(Path("classes-bcpex")) |
| 401 | # rename it so it shows up as "classes.dex" in the zip file. |
| 402 | Path("classes-bcpex.dex").rename(Path("classes.dex")) |
| 403 | zip(Path(TEST_NAME + "-bcpex.jar"), Path("classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 404 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 405 | make_java(Path("classes"), Path("src")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 406 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 407 | if not ctx.jvm: |
| 408 | # Do not attempt to build src-art directories on jvm, |
| 409 | # since it would fail without libcore. |
| 410 | make_java(Path("classes"), Path("src-art")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 411 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 412 | if make_java(Path("classes2"), Path("src-multidex")) and need_dex: |
| 413 | make_dex(Path("classes2")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 414 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 415 | make_java(Path("classes"), Path("src2")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 416 | |
| 417 | # If the classes directory is not-empty, package classes in a DEX file. |
| 418 | # NB: some tests provide classes rather than java files. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 419 | if any(Path("classes").glob("*")) and need_dex: |
| 420 | make_dex(Path("classes")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 421 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 422 | if Path("jasmin_classes").exists(): |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 423 | # Compile Jasmin classes as if they were part of the classes.dex file. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 424 | if need_dex: |
| 425 | make_dex(Path("jasmin_classes")) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 426 | make_dexmerge(Path("classes.dex"), Path("jasmin_classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 427 | else: |
| 428 | # Move jasmin classes into classes directory so that they are picked up |
| 429 | # with -cp classes. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 430 | Path("classes").mkdir(exist_ok=True) |
| 431 | copytree(Path("jasmin_classes"), Path("classes"), dirs_exist_ok=True) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 432 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 433 | if need_dex and make_smali(Path("smali_classes.dex"), Path("smali")): |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 434 | # Merge smali files into classes.dex, |
| 435 | # this takes priority over any jasmin files. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 436 | make_dexmerge(Path("classes.dex"), Path("smali_classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 437 | |
| 438 | # Compile Jasmin classes in jasmin-multidex as if they were part of |
| 439 | # the classes2.jar |
| 440 | if HAS_JASMIN_MULTIDEX: |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 441 | if need_dex: |
| 442 | make_dex(Path("jasmin_classes2")) |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 443 | make_dexmerge(Path("classes2.dex"), Path("jasmin_classes2.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 444 | else: |
| 445 | # Move jasmin classes into classes2 directory so that |
| 446 | # they are picked up with -cp classes2. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 447 | Path("classes2").mkdir() |
| 448 | copytree(Path("jasmin_classes2"), Path("classes2"), dirs_exist_ok=True) |
| 449 | rmtree(Path("jasmin_classes2")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 450 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 451 | if need_dex and make_smali(Path("smali_classes2.dex"), Path("smali-multidex")): |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 452 | # Merge smali_classes2.dex into classes2.dex |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 453 | make_dexmerge(Path("classes2.dex"), Path("smali_classes2.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 454 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 455 | make_java(Path("classes-ex"), Path("src-ex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 456 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 457 | make_java(Path("classes-ex"), Path("src-ex2")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 458 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 459 | if Path("classes-ex").exists() and need_dex: |
| 460 | make_dex(Path("classes-ex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 461 | |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 462 | if need_dex and make_smali(Path("smali_classes-ex.dex"), Path("smali-ex")): |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 463 | # Merge smali files into classes-ex.dex. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 464 | make_dexmerge(Path("classes-ex.dex"), Path("smali_classes-ex.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 465 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 466 | if Path("classes-ex.dex").exists(): |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 467 | # Apply hiddenapi on the dex files if the test has API list file(s). |
| 468 | if use_hiddenapi and HAS_HIDDENAPI_SPEC: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 469 | make_hiddenapi(Path("classes-ex.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 470 | |
| 471 | # quick shuffle so that the stored name is "classes.dex" |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 472 | Path("classes.dex").rename(Path("classes-1.dex")) |
| 473 | Path("classes-ex.dex").rename(Path("classes.dex")) |
| 474 | zip(Path(TEST_NAME + "-ex.jar"), Path("classes.dex")) |
| 475 | Path("classes.dex").rename(Path("classes-ex.dex")) |
| 476 | Path("classes-1.dex").rename(Path("classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 477 | |
| 478 | # Apply hiddenapi on the dex files if the test has API list file(s). |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 479 | if need_dex and use_hiddenapi and HAS_HIDDENAPI_SPEC: |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 480 | if has_multidex(): |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 481 | make_hiddenapi(Path("classes.dex"), Path("classes2.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 482 | else: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 483 | make_hiddenapi(Path("classes.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 484 | |
| 485 | # Create a single dex jar with two dex files for multidex. |
David Srbecky | 100fe67 | 2022-11-14 11:32:27 +0000 | [diff] [blame^] | 486 | if need_dex: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 487 | if Path("classes2.dex").exists(): |
| 488 | zip(Path(TEST_NAME + ".jar"), Path("classes.dex"), Path("classes2.dex")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 489 | else: |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 490 | zip(Path(TEST_NAME + ".jar"), Path("classes.dex")) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 491 | |
| 492 | |
| 493 | def build_test(ctx: BuildTestContext) -> None: |
| 494 | """Run the build script for single run-test""" |
| 495 | |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 496 | script = ctx.test_dir / "build.py" |
| 497 | if script.exists(): |
| 498 | module = SourceFileLoader("build_" + ctx.test_name, |
| 499 | str(script)).load_module() |
| 500 | module.build(ctx) |
| 501 | else: |
| 502 | default_build(ctx) |
| 503 | |
| 504 | |
| 505 | # If we build just individual shard, we want to split the work among all the cores, |
| 506 | # but if the build system builds all shards, we don't want to overload the machine. |
| 507 | # We don't know which situation we are in, so as simple work-around, we use a lock |
| 508 | # file to allow only one shard to use multiprocessing at the same time. |
| 509 | def use_multiprocessing(mode: str) -> bool: |
| 510 | global lock_file |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 511 | lock_path = Path(environ["TMPDIR"]) / ("art-test-run-test-build-py-" + mode) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 512 | lock_file = open(lock_path, "w") |
| 513 | try: |
| 514 | lockf(lock_file, LOCK_EX | LOCK_NB) |
| 515 | return True # We are the only instance of this script in the build system. |
| 516 | except BlockingIOError: |
| 517 | return False # Some other instance is already running. |
| 518 | |
| 519 | |
| 520 | def main() -> None: |
| 521 | parser = ArgumentParser(description=__doc__) |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 522 | parser.add_argument("--out", type=Path, help="Final zip file") |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 523 | parser.add_argument("--mode", choices=["host", "jvm", "target"]) |
David Srbecky | 6bd1cd1 | 2022-11-05 18:54:19 +0000 | [diff] [blame] | 524 | parser.add_argument("--bootclasspath", type=Path) |
| 525 | parser.add_argument("--d8", type=Path) |
| 526 | parser.add_argument("--hiddenapi", type=Path) |
| 527 | parser.add_argument("--jasmin", type=Path) |
| 528 | parser.add_argument("--smali", type=Path) |
| 529 | parser.add_argument("--soong_zip", type=Path) |
| 530 | parser.add_argument("--zipalign", type=Path) |
| 531 | parser.add_argument("srcs", nargs="+", type=Path) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 532 | args = parser.parse_args() |
| 533 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 534 | android_build_top = Path(getcwd()).absolute() |
| 535 | ziproot = args.out.absolute().parent / "zip" |
| 536 | srcdirs = set(s.parents[-4].absolute() for s in args.srcs) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 537 | |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 538 | # Initialize the test objects. |
| 539 | # We need to do this before we change the working directory below. |
| 540 | tests: List[BuildTestContext] = [] |
| 541 | for srcdir in srcdirs: |
| 542 | dstdir = ziproot / args.mode / srcdir.name |
| 543 | copytree(srcdir, dstdir) |
| 544 | tests.append(BuildTestContext(args, android_build_top, dstdir)) |
| 545 | |
| 546 | # We can not change the working directory per each thread since they all run in parallel. |
| 547 | # Create invalid read-only directory to catch accidental use of current working directory. |
| 548 | with TemporaryDirectory("-do-not-use-cwd") as invalid_tmpdir: |
| 549 | os.chdir(invalid_tmpdir) |
| 550 | os.chmod(invalid_tmpdir, 0) |
| 551 | with ThreadPoolExecutor(cpu_count() if use_multiprocessing(args.mode) else 1) as pool: |
| 552 | jobs = {} |
| 553 | for ctx in tests: |
| 554 | jobs[ctx.test_name] = pool.submit(build_test, ctx) |
| 555 | for test_name, job in jobs.items(): |
| 556 | try: |
| 557 | job.result() |
| 558 | except Exception as e: |
| 559 | raise Exception("Failed to build " + test_name) from e |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 560 | |
| 561 | # Create the final zip file which contains the content of the temporary directory. |
David Srbecky | 36b520b | 2022-10-27 14:43:12 +0100 | [diff] [blame] | 562 | proc = run([android_build_top / args.soong_zip, "-o", android_build_top / args.out, |
| 563 | "-C", ziproot, "-D", ziproot], check=True) |
David Srbecky | 37274d5 | 2022-11-03 11:04:16 +0000 | [diff] [blame] | 564 | |
| 565 | |
| 566 | if __name__ == "__main__": |
| 567 | main() |