David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 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 | |
| 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. |
| 20 | """ |
| 21 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 22 | import argparse, os, shutil, subprocess, glob, re, json, multiprocessing, pathlib |
| 23 | import art_build_rules |
| 24 | from importlib.machinery import SourceFileLoader |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 25 | |
| 26 | ZIP = "prebuilts/build-tools/linux-x86/bin/soong_zip" |
David Srbecky | 9292b88 | 2022-03-23 13:24:15 +0000 | [diff] [blame] | 27 | BUILDFAILURES = json.loads(open(os.path.join("art", "test", "buildfailures.json"), "rt").read()) |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 28 | |
David Srbecky | 271d572 | 2021-10-06 13:40:20 +0100 | [diff] [blame] | 29 | def copy_sources(args, tmp, mode, srcdir): |
| 30 | """Copy test files from Android tree into the build sandbox and return its path.""" |
| 31 | |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 32 | join = os.path.join |
| 33 | test = os.path.basename(srcdir) |
| 34 | dstdir = join(tmp, mode, test) |
| 35 | |
| 36 | # Don't build tests that are disabled since they might not compile (e.g. on jvm). |
David Srbecky | 9292b88 | 2022-03-23 13:24:15 +0000 | [diff] [blame] | 37 | def is_buildfailure(kf): |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 38 | return test in kf.get("tests", []) and mode == kf.get("variant") and not kf.get("env_vars") |
David Srbecky | 9292b88 | 2022-03-23 13:24:15 +0000 | [diff] [blame] | 39 | if any(is_buildfailure(kf) for kf in BUILDFAILURES): |
David Srbecky | 271d572 | 2021-10-06 13:40:20 +0100 | [diff] [blame] | 40 | return None |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 41 | |
| 42 | # Copy all source files to the temporary directory. |
| 43 | shutil.copytree(srcdir, dstdir) |
| 44 | |
| 45 | # Copy the default scripts if the test does not have a custom ones. |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 46 | for name in ["build.py", "run", "check"]: |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 47 | src, dst = f"art/test/etc/default-{name}", join(dstdir, name) |
| 48 | if os.path.exists(dst): |
| 49 | shutil.copy2(src, dstdir) # Copy default script next to the custom script. |
| 50 | else: |
| 51 | shutil.copy2(src, dst) # Use just the default script. |
| 52 | os.chmod(dst, 0o755) |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 53 | |
David Srbecky | 271d572 | 2021-10-06 13:40:20 +0100 | [diff] [blame] | 54 | return dstdir |
| 55 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 56 | def build_test(args, mode, build_top, sbox, dstdir): |
David Srbecky | 271d572 | 2021-10-06 13:40:20 +0100 | [diff] [blame] | 57 | """Run the build script for single run-test""" |
| 58 | |
| 59 | join = os.path.join |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 60 | java_home = os.environ.get("JAVA_HOME") |
| 61 | tools_dir = os.path.abspath(join(os.path.dirname(__file__), "../../../out/bin")) |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 62 | test_name = os.path.basename(dstdir) |
| 63 | env = dict(os.environ) |
| 64 | env.update({ |
| 65 | "BUILD_MODE": mode, |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 66 | "ANDROID_BUILD_TOP": build_top, |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 67 | "SBOX_PATH": sbox, |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 68 | "ART_TEST_RUN_TEST_BOOTCLASSPATH": join(build_top, args.bootclasspath), |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 69 | "TEST_NAME": test_name, |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 70 | "SOONG_ZIP": join(build_top, "prebuilts/build-tools/linux-x86/bin/soong_zip"), |
| 71 | "ZIPALIGN": join(build_top, "prebuilts/build-tools/linux-x86/bin/zipalign"), |
| 72 | "JAVA": join(java_home, "bin/java"), |
David Srbecky | 9de71a4 | 2022-04-07 21:32:43 +0100 | [diff] [blame] | 73 | "JAVAC": join(java_home, "bin/javac"), |
| 74 | "JAVAC_ARGS": "-g -Xlint:-options -source 1.8 -target 1.8", |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 75 | "D8": join(tools_dir, "d8"), |
| 76 | "HIDDENAPI": join(tools_dir, "hiddenapi"), |
| 77 | "JASMIN": join(tools_dir, "jasmin"), |
| 78 | "SMALI": join(tools_dir, "smali"), |
| 79 | "NEED_DEX": {"host": "true", "target": "true", "jvm": "false"}[mode], |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 80 | }) |
| 81 | |
| 82 | generate_sources = join(dstdir, "generate-sources") |
| 83 | if os.path.exists(generate_sources): |
| 84 | proc = subprocess.run([generate_sources, "--" + mode], |
| 85 | cwd=dstdir, |
| 86 | env=env, |
| 87 | encoding=os.sys.stdout.encoding, |
| 88 | stderr=subprocess.STDOUT, |
| 89 | stdout=subprocess.PIPE) |
| 90 | if proc.returncode: |
| 91 | raise Exception("Failed to generate sources for " + test_name + ":\n" + proc.stdout) |
| 92 | |
| 93 | os.chdir(dstdir) |
| 94 | for name, value in env.items(): |
| 95 | os.environ[name] = str(value) |
| 96 | SourceFileLoader("build_" + test_name, join(dstdir, "build.py")).load_module() |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 97 | |
| 98 | def main(): |
| 99 | parser = argparse.ArgumentParser(description=__doc__) |
| 100 | parser.add_argument("--out", help="Path of the generated ZIP file with the build data") |
| 101 | parser.add_argument('--mode', choices=['host', 'jvm', 'target']) |
| 102 | parser.add_argument("--shard", help="Identifies subset of tests to build (00..99)") |
| 103 | parser.add_argument("--bootclasspath", help="JAR files used for javac compilation") |
| 104 | args = parser.parse_args() |
| 105 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 106 | build_top = os.getcwd() |
| 107 | sbox = pathlib.Path(__file__).absolute().parent.parent.parent.parent.parent |
| 108 | assert sbox.parent.name == "sbox" and len(sbox.name) == 40 |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 109 | |
David Srbecky | 8106b38 | 2022-04-20 13:37:15 +0100 | [diff] [blame] | 110 | ziproot = os.path.join(sbox, "zip") |
| 111 | srcdirs = sorted(glob.glob(os.path.join("art", "test", "*"))) |
| 112 | srcdirs = filter(lambda srcdir: re.match(".*/\d*{}-.*".format(args.shard), srcdir), srcdirs) |
| 113 | dstdirs = [copy_sources(args, ziproot, args.mode, srcdir) for srcdir in srcdirs] |
| 114 | dstdirs = filter(lambda dstdir: dstdir, dstdirs) # Remove None (skipped tests). |
| 115 | # Use multiprocess (i.e. forking) since tests modify their current working directory. |
| 116 | with multiprocessing.Pool() as pool: |
| 117 | jobs = [(d, pool.apply_async(build_test, (args, args.mode, build_top, sbox, d))) for d in dstdirs] |
| 118 | for dstdir, job in jobs: |
| 119 | try: |
| 120 | job.get() |
| 121 | except Exception as e: |
| 122 | raise Exception("Failed to build " + os.path.basename(dstdir)) from e.__cause__ |
| 123 | |
| 124 | # Create the final zip file which contains the content of the temporary directory. |
| 125 | proc = subprocess.run([ZIP, "-o", args.out, "-C", ziproot, "-D", ziproot], check=True) |
David Srbecky | 7cf6c58 | 2021-07-20 16:56:06 +0100 | [diff] [blame] | 126 | |
| 127 | if __name__ == "__main__": |
| 128 | main() |