#!/usr/bin/env python3 # # Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ This scripts compiles Java files which are needed to execute run-tests. It is intended to be used only from soong genrule. """ import argparse, os, tempfile, shutil, subprocess, glob, textwrap, re, json, concurrent.futures ZIP = "prebuilts/build-tools/linux-x86/bin/soong_zip" BUILDFAILURES = json.loads(open(os.path.join("art", "test", "buildfailures.json"), "rt").read()) def copy_sources(args, tmp, mode, srcdir): """Copy test files from Android tree into the build sandbox and return its path.""" join = os.path.join test = os.path.basename(srcdir) dstdir = join(tmp, mode, test) # Don't build tests that are disabled since they might not compile (e.g. on jvm). def is_buildfailure(kf): return test in kf.get("tests", []) and mode == kf.get("variant") and not kf.get("env_vars") if any(is_buildfailure(kf) for kf in BUILDFAILURES): return None # Copy all source files to the temporary directory. shutil.copytree(srcdir, dstdir) # Copy the default scripts if the test does not have a custom ones. for name in ["build", "run", "check"]: src, dst = f"art/test/etc/default-{name}", join(dstdir, name) if os.path.exists(dst): shutil.copy2(src, dstdir) # Copy default script next to the custom script. else: shutil.copy2(src, dst) # Use just the default script. os.chmod(dst, 0o755) return dstdir def build_test(args, mode, dstdir): """Run the build script for single run-test""" join = os.path.join build_top = os.getcwd() java_home = os.environ.get("JAVA_HOME") tools_dir = os.path.abspath(join(os.path.dirname(__file__), "../../../out/bin")) env = { "PATH": os.environ.get("PATH"), "ANDROID_BUILD_TOP": build_top, "ART_TEST_RUN_TEST_BOOTCLASSPATH": join(build_top, args.bootclasspath), "TEST_NAME": os.path.basename(dstdir), "SOONG_ZIP": join(build_top, "prebuilts/build-tools/linux-x86/bin/soong_zip"), "ZIPALIGN": join(build_top, "prebuilts/build-tools/linux-x86/bin/zipalign"), "JAVA": join(java_home, "bin/java"), "JAVAC": join(java_home, "bin/javac"), "JAVAC_ARGS": "-g -Xlint:-options -source 1.8 -target 1.8", "D8": join(tools_dir, "d8"), "HIDDENAPI": join(tools_dir, "hiddenapi"), "JASMIN": join(tools_dir, "jasmin"), "SMALI": join(tools_dir, "smali"), "NEED_DEX": {"host": "true", "target": "true", "jvm": "false"}[mode], "USE_DESUGAR": "true", } proc = subprocess.run([join(dstdir, "build"), "--" + mode], cwd=dstdir, env=env, encoding=os.sys.stdout.encoding, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) return proc.stdout, proc.returncode def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--out", help="Path of the generated ZIP file with the build data") parser.add_argument('--mode', choices=['host', 'jvm', 'target']) parser.add_argument("--shard", help="Identifies subset of tests to build (00..99)") parser.add_argument("--bootclasspath", help="JAR files used for javac compilation") args = parser.parse_args() with tempfile.TemporaryDirectory(prefix=os.path.basename(__file__)) as tmp: srcdirs = sorted(glob.glob(os.path.join("art", "test", "*"))) srcdirs = filter(lambda srcdir: re.match(".*/\d*{}-.*".format(args.shard), srcdir), srcdirs) dstdirs = [copy_sources(args, tmp, args.mode, srcdir) for srcdir in srcdirs] dstdirs = filter(lambda dstdir: dstdir, dstdirs) # Remove None (skipped tests). with concurrent.futures.ThreadPoolExecutor() as pool: for stdout, exitcode in pool.map(lambda dstdir: build_test(args, args.mode, dstdir), dstdirs): if stdout: print(stdout.strip()) assert(exitcode == 0) # Build failed. Add test to buildfailures.json if this is expected. # Create the final zip file which contains the content of the temporary directory. proc = subprocess.run([ZIP, "-o", args.out, "-C", tmp, "-D", tmp], check=True) if __name__ == "__main__": main()