blob: 93e95997e6a10ef244325be5d22b93f2e0408045 [file] [log] [blame]
David Srbecky7cf6c582021-07-20 16:56:06 +01001#!/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"""
18This scripts compiles Java files which are needed to execute run-tests.
19It is intended to be used only from soong genrule.
20"""
21
David Srbecky8106b382022-04-20 13:37:15 +010022import argparse, os, shutil, subprocess, glob, re, json, multiprocessing, pathlib
23import art_build_rules
24from importlib.machinery import SourceFileLoader
David Srbecky7cf6c582021-07-20 16:56:06 +010025
26ZIP = "prebuilts/build-tools/linux-x86/bin/soong_zip"
David Srbecky9292b882022-03-23 13:24:15 +000027BUILDFAILURES = json.loads(open(os.path.join("art", "test", "buildfailures.json"), "rt").read())
David Srbecky7cf6c582021-07-20 16:56:06 +010028
David Srbecky271d5722021-10-06 13:40:20 +010029def copy_sources(args, tmp, mode, srcdir):
30 """Copy test files from Android tree into the build sandbox and return its path."""
31
David Srbecky7cf6c582021-07-20 16:56:06 +010032 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 Srbecky9292b882022-03-23 13:24:15 +000037 def is_buildfailure(kf):
David Srbecky7cf6c582021-07-20 16:56:06 +010038 return test in kf.get("tests", []) and mode == kf.get("variant") and not kf.get("env_vars")
David Srbecky9292b882022-03-23 13:24:15 +000039 if any(is_buildfailure(kf) for kf in BUILDFAILURES):
David Srbecky271d5722021-10-06 13:40:20 +010040 return None
David Srbecky7cf6c582021-07-20 16:56:06 +010041
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 Srbecky8106b382022-04-20 13:37:15 +010046 for name in ["build.py", "run", "check"]:
David Srbecky7cf6c582021-07-20 16:56:06 +010047 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 Srbecky7cf6c582021-07-20 16:56:06 +010053
David Srbecky271d5722021-10-06 13:40:20 +010054 return dstdir
55
David Srbecky8106b382022-04-20 13:37:15 +010056def build_test(args, mode, build_top, sbox, dstdir):
David Srbecky271d5722021-10-06 13:40:20 +010057 """Run the build script for single run-test"""
58
59 join = os.path.join
David Srbecky7cf6c582021-07-20 16:56:06 +010060 java_home = os.environ.get("JAVA_HOME")
61 tools_dir = os.path.abspath(join(os.path.dirname(__file__), "../../../out/bin"))
David Srbecky8106b382022-04-20 13:37:15 +010062 test_name = os.path.basename(dstdir)
63 env = dict(os.environ)
64 env.update({
65 "BUILD_MODE": mode,
David Srbecky7cf6c582021-07-20 16:56:06 +010066 "ANDROID_BUILD_TOP": build_top,
David Srbecky8106b382022-04-20 13:37:15 +010067 "SBOX_PATH": sbox,
David Srbecky7cf6c582021-07-20 16:56:06 +010068 "ART_TEST_RUN_TEST_BOOTCLASSPATH": join(build_top, args.bootclasspath),
David Srbecky8106b382022-04-20 13:37:15 +010069 "TEST_NAME": test_name,
David Srbecky7cf6c582021-07-20 16:56:06 +010070 "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 Srbecky9de71a42022-04-07 21:32:43 +010073 "JAVAC": join(java_home, "bin/javac"),
74 "JAVAC_ARGS": "-g -Xlint:-options -source 1.8 -target 1.8",
David Srbecky7cf6c582021-07-20 16:56:06 +010075 "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 Srbecky8106b382022-04-20 13:37:15 +010080 })
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 Srbecky7cf6c582021-07-20 16:56:06 +010097
98def 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 Srbecky8106b382022-04-20 13:37:15 +0100106 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 Srbecky7cf6c582021-07-20 16:56:06 +0100109
David Srbecky8106b382022-04-20 13:37:15 +0100110 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 Srbecky7cf6c582021-07-20 16:56:06 +0100126
127if __name__ == "__main__":
128 main()