| #!/usr/bin/env python3 | |
| # Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file | |
| # for details. All rights reserved. Use of this source code is governed by a | |
| # BSD-style license that can be found in the LICENSE file. | |
| import argparse | |
| import os | |
| import shutil | |
| import subprocess | |
| import sys | |
| import time | |
| import zipfile | |
| import archive | |
| import gradle | |
| import jdk | |
| import retrace | |
| import utils | |
| def make_parser(): | |
| parser = argparse.ArgumentParser(description='Compile a dump artifact.') | |
| parser.add_argument('--summary', | |
| help='List a summary of the contents of the dumps.', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('-d', | |
| '--dump', | |
| help='Dump file or directory to compile', | |
| default=None) | |
| parser.add_argument('-o', | |
| '--output', | |
| help='File to output (defaults to out.jar in temp)', | |
| default=None) | |
| parser.add_argument( | |
| '--temp', | |
| help= | |
| 'Temp directory to extract the dump to, allows you to rerun the command' | |
| ' more easily in the terminal with changes', | |
| default=None) | |
| parser.add_argument('-c', | |
| '--compiler', | |
| help='Compiler to use', | |
| default=None) | |
| parser.add_argument('--dump-keep-radius-to-directory', | |
| '--dump_keep_radius_to_directory', | |
| help='Dump keep radius to the given directory') | |
| parser.add_argument('--dump-trace-to-directory', | |
| '--dump_trace_to_directory', | |
| help='Dump perfetto trace to the given directory') | |
| parser.add_argument('--jdk', help='JDK to use', choices=jdk.ALL_JDKS) | |
| parser.add_argument('--minify', | |
| help='Force enable/disable minification' | |
| ' (defaults to app proguard config)', | |
| choices=['default', 'force-enable', 'force-disable'], | |
| default='default') | |
| parser.add_argument('--optimize', | |
| help='Force enable/disable optimizations' | |
| ' (defaults to app proguard config)', | |
| choices=['default', 'force-enable', 'force-disable'], | |
| default='default') | |
| parser.add_argument('--shrink', | |
| help='Force enable/disable shrinking' | |
| ' (defaults to app proguard config)', | |
| choices=['default', 'force-enable', 'force-disable'], | |
| default='default') | |
| parser.add_argument( | |
| '-v', | |
| '--version', | |
| help='Compiler version to use (default read from dump version file).' | |
| 'Valid arguments are:' | |
| ' "main" to run from your own tree,' | |
| ' "source" to run from build classes directly,' | |
| ' "X.Y.Z" to run a specific version, or' | |
| ' <hash> to run that hash from main.', | |
| default=None) | |
| parser.add_argument('--r8-jar', help='Path to an R8 jar.', default=None) | |
| parser.add_argument('--r8-flags', | |
| '--r8_flags', | |
| help='Additional option(s) for the compiler.') | |
| parser.add_argument('--pg-conf', | |
| '--pg_conf', | |
| help='Keep rule file(s).', | |
| action='append') | |
| parser.add_argument('--override', | |
| help='Do not override any extracted dump in temp-dir', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument( | |
| '--nolib', | |
| help='Use the non-lib distribution (default uses the lib distribution)', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--print-times', | |
| help='Print timing information from r8', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument( | |
| '--disable-assertions', | |
| '--disable_assertions', | |
| '-da', | |
| help= | |
| 'Disable Java assertions when running the compiler (default enabled)', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument( | |
| '--enable-test-assertions', | |
| '--enable_test_assertions', | |
| help= | |
| 'Enable additional test assertions when running the compiler (default disabled)', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--java-opts', | |
| '--java-opts', | |
| '-J', | |
| metavar='<JVM argument(s)>', | |
| default=[], | |
| action='append', | |
| help='Additional options to pass to JVM invocation') | |
| parser.add_argument('--classfile', | |
| help='Run with classfile output', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument( | |
| '--debug-agent', | |
| help= | |
| 'Enable Java debug agent and suspend compilation (default disabled)', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--xmx', | |
| help='Set JVM max heap size (-Xmx)', | |
| default=None) | |
| parser.add_argument('--threads', | |
| help='Set the number of threads to use', | |
| default=None) | |
| parser.add_argument( | |
| '--min-api', | |
| help='Set min-api (default read from dump properties file)', | |
| default=None) | |
| parser.add_argument('--desugared-lib', | |
| help='Set desugared-library (default set from dump)', | |
| default=None) | |
| parser.add_argument( | |
| '--disable-desugared-lib', | |
| help='Disable desugared-libary if it will be set from dump', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--loop', | |
| help='Run the compilation in a loop', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--enable-missing-library-api-modeling', | |
| help='Run with api modeling', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--android-platform-build', | |
| help='Run as a platform build', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--optimized-resource-shrinking', | |
| help='Use optimized resource shrinking', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument( | |
| '--android-resources-usage-log', | |
| '--android_resources_usage_log', | |
| help= | |
| 'File to write resources usage log to (or "-" / "stdout" for stdout)', | |
| default=None) | |
| parser.add_argument('--compilation-mode', | |
| '--compilation_mode', | |
| help='Run compilation in specified mode', | |
| choices=['debug', 'release'], | |
| default=None) | |
| parser.add_argument( | |
| '--ignore-features', | |
| help="Don't split into features when features are present." | |
| ' Instead include feature code in main app output.' | |
| ' This is always the case when compiler is d8.', | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--no-build', | |
| help="Don't build when using --version main", | |
| default=False, | |
| action='store_true') | |
| parser.add_argument('--print-runtimeraw', | |
| metavar='BENCHMARKNAME', | |
| help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' + | |
| ' <elapsed> ms\' at the end where <elapsed> is' + | |
| ' the elapsed time in milliseconds.') | |
| return parser | |
| def error(msg): | |
| print(msg) | |
| sys.exit(1) | |
| class Dump(object): | |
| def __init__(self, directory): | |
| self.directory = directory | |
| def if_exists(self, name): | |
| f = os.path.join(self.directory, name) | |
| if os.path.exists(f): | |
| return f | |
| return None | |
| def program_jar(self): | |
| return self.if_exists('program.jar') | |
| def feature_jars(self): | |
| feature_jars = [] | |
| i = 1 | |
| while True: | |
| feature_jar = self.if_exists('feature-%s.jar' % i) | |
| feature_res = self.if_exists('feature-%s.ap_' % i) | |
| if feature_jar or feature_res: | |
| # We can have either jar, res or both. | |
| input_string = feature_jar | |
| if feature_res: | |
| input_string = os.pathsep.join( | |
| (feature_jar if feature_jar else '', feature_res)) | |
| feature_jars.append(input_string) | |
| i = i + 1 | |
| else: | |
| return feature_jars | |
| def library_jar(self): | |
| return self.if_exists('library.jar') | |
| def classpath_jar(self): | |
| return self.if_exists('classpath.jar') | |
| def desugared_library_json(self): | |
| return self.if_exists('desugared-library.json') | |
| def proguard_input_map(self): | |
| return self.if_exists('proguard_input.map') | |
| def main_dex_list_resource(self): | |
| return self.if_exists('main-dex-list.txt') | |
| def main_dex_rules_resource(self): | |
| return self.if_exists('main-dex-rules.txt') | |
| def resource_ap_file(self): | |
| return self.if_exists('app-res.ap_') | |
| def art_profile_resources(self): | |
| art_profile_resources = [] | |
| while True: | |
| current_art_profile_index = len(art_profile_resources) + 1 | |
| art_profile_resource = self.if_exists('art-profile-%s.txt' % | |
| current_art_profile_index) | |
| if art_profile_resource is None: | |
| return art_profile_resources | |
| art_profile_resources.append(art_profile_resource) | |
| def startup_profile_resources(self): | |
| startup_profile_resources = [] | |
| while True: | |
| current_startup_profile_index = len(startup_profile_resources) + 1 | |
| startup_profile_resource = self.if_exists( | |
| 'startup-profile-%s.txt' % current_startup_profile_index) | |
| if startup_profile_resource is None: | |
| return startup_profile_resources | |
| startup_profile_resources.append(startup_profile_resource) | |
| def build_properties_file(self): | |
| return self.if_exists('build.properties') | |
| def config_file(self): | |
| return self.if_exists('proguard.config') | |
| def r8_include_file(self): | |
| return self.if_exists('r8-include.txt') | |
| def r8_exclude_file(self): | |
| return self.if_exists('r8-exclude.txt') | |
| def version_file(self): | |
| return self.if_exists('r8-version') | |
| def version(self): | |
| f = self.version_file() | |
| if f: | |
| return open(f).read().split(' ')[0] | |
| return None | |
| def read_dump_from_args(args, temp): | |
| if args.dump is None: | |
| error("A dump file or directory must be specified") | |
| return read_dump(args.dump, temp, args.override) | |
| def read_dump(dump, temp, override=False): | |
| if os.path.isdir(dump): | |
| return Dump(dump) | |
| dump_file = zipfile.ZipFile(os.path.abspath(dump), 'r') | |
| r8_version_file = os.path.join(temp, 'r8-version') | |
| if override or not os.path.isfile(r8_version_file): | |
| dump_file.extractall(temp) | |
| if not os.path.isfile(r8_version_file): | |
| error( | |
| "Did not extract into %s. Either the zip file is invalid or the " | |
| "dump is missing files" % temp) | |
| return Dump(temp) | |
| def determine_build_properties(args, dump): | |
| build_properties = {} | |
| build_properties_file = dump.build_properties_file() | |
| if build_properties_file: | |
| with open(build_properties_file) as f: | |
| build_properties_contents = f.readlines() | |
| for line in build_properties_contents: | |
| stripped = line.strip() | |
| if stripped: | |
| pair = stripped.split('=') | |
| build_properties[pair[0]] = pair[1] | |
| if 'mode' not in build_properties: | |
| build_properties['mode'] = 'release' | |
| return build_properties | |
| def determine_version(args, dump): | |
| if args.version is None: | |
| return dump.version() | |
| return args.version | |
| def determine_compiler(args, build_properties): | |
| compilers = [ | |
| 'd8', 'r8', 'r8full', 'l8', 'l8d8', 'tracereferences', 'assistant' | |
| ] | |
| compiler = args.compiler | |
| if not compiler and 'tool' in build_properties: | |
| compiler = build_properties.get('tool').lower() | |
| if compiler == 'r8': | |
| if not 'force-proguard-compatibility' in build_properties: | |
| error( | |
| "Unable to determine R8 compiler variant from build.properties." | |
| " No value for 'force-proguard-compatibility'.") | |
| if build_properties.get( | |
| 'force-proguard-compatibility').lower() == 'false': | |
| compiler = 'r8full' | |
| elif compiler == 'r8partial': | |
| compiler = 'r8full' | |
| elif compiler == 'TraceReferences': | |
| compiler = build_properties.get('tool').lower() | |
| if compiler not in compilers: | |
| error("Unable to determine a compiler to use. Specified %s," | |
| " Valid options: %s" % (args.compiler, ', '.join(compilers))) | |
| return compiler | |
| def determine_isolated_splits(build_properties, feature_jars): | |
| if feature_jars and 'isolated-splits' in build_properties: | |
| isolated_splits = build_properties.get('isolated-splits') | |
| assert isolated_splits == 'true' or isolated_splits == 'false' | |
| return isolated_splits == 'true' | |
| return None | |
| def determine_trace_references_commands(build_properties, output): | |
| trace_ref_consumer = build_properties.get('trace_references_consumer') | |
| if trace_ref_consumer == 'com.android.tools.r8.tracereferences.TraceReferencesCheckConsumer': | |
| return ["--check"] | |
| else: | |
| assert trace_ref_consumer == 'com.android.tools.r8.tracereferences.TraceReferencesKeepRules' | |
| args = ['--allowobfuscation' | |
| ] if build_properties.get('minification') == 'true' else [] | |
| args.extend(['--keep-rules', '--output', output]) | |
| return args | |
| def is_d8_compiler(compiler): | |
| return compiler.startswith('d8') | |
| def is_l8_compiler(compiler): | |
| return compiler.startswith('l8') | |
| def is_r8_compiler(compiler): | |
| return compiler.startswith('r8') | |
| def is_assistant(compiler): | |
| return compiler == 'assistant' | |
| def determine_config_files(args, dump, temp): | |
| if args.pg_conf: | |
| config_files = [] | |
| for config_file in args.pg_conf: | |
| dst = os.path.join(temp, 'proguard-%s.config' % len(config_files)) | |
| shutil.copyfile(config_file, dst) | |
| config_files.append(dst) | |
| return config_files | |
| dump_config_file = dump.config_file() | |
| if dump_config_file: | |
| return [dump_config_file] | |
| return [] | |
| def determine_output(args, temp): | |
| if (args.output): | |
| return args.output | |
| return os.path.join(temp, 'out.jar') | |
| def determine_min_api(args, build_properties): | |
| if args.min_api: | |
| return args.min_api | |
| if 'min-api' in build_properties: | |
| return build_properties.get('min-api') | |
| return None | |
| def determine_residual_art_profile_output(art_profile, temp): | |
| return os.path.join(temp, os.path.basename(art_profile)[:-4] + ".out.txt") | |
| def output_name(input_name, suffix): | |
| return os.path.basename(input_name)[:-4] + ".out" + suffix | |
| def determine_feature_output(feature_input, temp, args=None): | |
| output_dir = args.output if args is not None else None | |
| output_exists = output_dir and os.path.isdir(output_dir) | |
| base_path = output_dir if output_exists else temp | |
| if ":" in feature_input: | |
| split = feature_input.split(':') | |
| feature_jar = '' if len(split[0]) == 0 else os.path.join( | |
| base_path, output_name(split[0], '.jar')) | |
| feature_res = os.path.join(base_path, output_name(split[1], '.ap_')) | |
| return os.pathsep.join((feature_jar, feature_res)) | |
| return os.path.join(base_path, output_name(feature_input, '.jar')) | |
| def determine_program_jar(args, dump): | |
| if 'program_jar' in args and args.program_jar: | |
| return args.program_jar | |
| return dump.program_jar() | |
| def determine_class_file(args, build_properties): | |
| return args.classfile \ | |
| or build_properties.get('backend', 'dex').lower() == 'cf' | |
| def determine_android_platform_build(args, build_properties): | |
| if args.android_platform_build: | |
| return True | |
| return build_properties.get('android-platform-build') == 'true' | |
| def determine_optimized_resource_shrinking(args, build_properties): | |
| if args.optimized_resource_shrinking: | |
| return True | |
| return build_properties.get('optimized-resource-shrinking') == 'true' | |
| def determine_enable_missing_library_api_modeling(args, build_properties): | |
| if args.enable_missing_library_api_modeling: | |
| return True | |
| return build_properties.get('enable-missing-library-api-modeling') == 'true' | |
| def determine_compilation_mode(args, build_properties): | |
| if args.compilation_mode: | |
| return args.compilation_mode | |
| return build_properties.get('mode') | |
| def determine_d8_proguard_map_output(args, build_properties): | |
| if args.android_platform_build: | |
| return True | |
| return build_properties.get('proguard-map-output') == 'true' | |
| def determine_properties(build_properties): | |
| args = [] | |
| for key, value in build_properties.items(): | |
| # When writing dumps all system properties starting with com.android.tools.r8 | |
| # are written to the build.properties file in the format | |
| # system-property-com.android.tools.r8.XXX=<value> | |
| if key.startswith('system-property-'): | |
| name = key[len('system-property-'):] | |
| if name.endswith('dumpinputtofile') or name.endswith( | |
| 'dumpinputtodirectory'): | |
| continue | |
| if len(value) == 0: | |
| args.append('-D' + name) | |
| else: | |
| args.append('-D' + name + '=' + value) | |
| return args | |
| def download_distribution(version, args, temp): | |
| nolib = args.nolib | |
| if version == 'main': | |
| if not args.no_build: | |
| gradle.run_gradle( | |
| [utils.GRADLE_TASK_R8] if nolib else [utils.GRADLE_TASK_R8LIB]) | |
| return utils.R8_JAR if nolib else utils.R8LIB_JAR | |
| if version == 'source': | |
| return os.pathsep.join( | |
| (utils.BUILD_JAVA_MAIN_CLASSPATH, utils.ALL_DEPS_JAR)) | |
| name = 'r8.jar' if nolib else 'r8lib.jar' | |
| source = archive.get_upload_destination(version, name, is_hash(version)) | |
| dest = os.path.join(temp, 'r8.jar') | |
| utils.download_file_from_cloud_storage(source, dest) | |
| return dest | |
| def clean_configs(files, args): | |
| for file in files: | |
| clean_config(file, args) | |
| def clean_config(file, args): | |
| with open(file) as f: | |
| lines = f.readlines() | |
| minify = args.minify | |
| optimize = args.optimize | |
| shrink = args.shrink | |
| with open(file, 'w') as f: | |
| if minify == 'force-disable': | |
| print('Adding config line: -dontobfuscate') | |
| f.write('-dontobfuscate\n') | |
| if optimize == 'force-disable': | |
| print('Adding config line: -dontoptimize') | |
| f.write('-dontoptimize\n') | |
| if shrink == 'force-disable': | |
| print('Adding config line: -dontshrink') | |
| f.write('-dontshrink\n') | |
| for line in lines: | |
| if clean_config_line(line, minify, optimize, shrink): | |
| print('Removing from config line: \n%s' % line) | |
| else: | |
| f.write(line) | |
| def clean_config_line(line, minify, optimize, shrink): | |
| if line.lstrip().startswith('#'): | |
| return False | |
| if ('-injars' in line or '-libraryjars' in line or '-print' in line or | |
| '-applymapping' in line or '-tracing' in line): | |
| return True | |
| if minify == 'force-enable' and '-dontobfuscate' in line: | |
| return True | |
| if optimize == 'force-enable' and '-dontoptimize' in line: | |
| return True | |
| if shrink == 'force-enable' and '-dontshrink' in line: | |
| return True | |
| return False | |
| def compile_reflective_helper(temp, jdkhome, no_build=False): | |
| main_classes_dir = utils.BUILD_JAVA_MAIN_CLASSPATH.split(os.pathsep)[0] | |
| if not no_build or not os.path.exists(main_classes_dir): | |
| gradle.run_gradle([utils.GRADLE_TASK_MAIN_COMPILE]) | |
| base_path = os.path.join( | |
| utils.REPO_ROOT, 'src/main/java/com/android/tools/r8/utils/compiledump') | |
| cmd = [ | |
| jdk.GetJavacExecutable(jdkhome), | |
| '-d', | |
| temp, | |
| '-cp', | |
| utils.BUILD_JAVA_MAIN_CLASSPATH, | |
| ] | |
| cmd.extend(os.path.join(base_path, f) for f in os.listdir(base_path)) | |
| utils.PrintCmd(cmd) | |
| subprocess.check_output(cmd) | |
| def prepare_wrapper(dist, temp, jdkhome, wrapper_file, no_build=False): | |
| compile_reflective_helper(temp, jdkhome, no_build=no_build) | |
| compile_wrapper_with_javac( | |
| dist, temp, jdkhome, | |
| os.path.join( | |
| utils.REPO_ROOT, | |
| f'src/main/java/com/android/tools/r8/utils/{wrapper_file}')) | |
| def compile_wrapper_with_javac(dist, temp, jdkhome, path): | |
| base_path = os.path.join( | |
| utils.REPO_ROOT, | |
| 'src/main/java/com/android/tools/r8/utils/CompileDumpBase.java') | |
| cmd = [ | |
| jdk.GetJavacExecutable(jdkhome), | |
| path, | |
| base_path, | |
| '-d', | |
| temp, | |
| '-cp', | |
| os.pathsep.join((dist, temp)), | |
| ] | |
| utils.PrintCmd(cmd) | |
| subprocess.check_output(cmd) | |
| def is_hash(version): | |
| return len(version) == 40 | |
| def run1(out, args, otherargs, jdkhome=None, worker_id=None): | |
| if jdkhome is None: | |
| # TODO(b/510836155): Should use GetDefaultJdkHome but it breaks. | |
| jdkhome = jdk.GetJdk11Home() | |
| compilation_jdkhome = jdk.GetCompilationJdkHome() | |
| jvmargs = [] | |
| compilerargs = [] | |
| for arg in otherargs: | |
| if arg.startswith('-D'): | |
| jvmargs.append(arg) | |
| else: | |
| compilerargs.append(arg) | |
| with utils.TempDir() as temp: | |
| if out: | |
| temp = out | |
| if not os.path.exists(temp): | |
| os.makedirs(temp) | |
| dump = read_dump_from_args(args, temp) | |
| if not dump.program_jar(): | |
| error("Cannot compile dump with no program classes") | |
| if not dump.library_jar(): | |
| print("WARNING: Unexpected lack of library classes in dump") | |
| build_properties = determine_build_properties(args, dump) | |
| version = determine_version(args, dump) | |
| compiler = determine_compiler(args, build_properties) | |
| config_files = determine_config_files(args, dump, temp) | |
| out = determine_output(args, temp) | |
| min_api = determine_min_api(args, build_properties) | |
| classfile = determine_class_file(args, build_properties) | |
| android_platform_build = determine_android_platform_build( | |
| args, build_properties) | |
| optimized_resource_shrinking = determine_optimized_resource_shrinking( | |
| args, build_properties) | |
| enable_missing_library_api_modeling = determine_enable_missing_library_api_modeling( | |
| args, build_properties) | |
| mode = determine_compilation_mode(args, build_properties) | |
| jar = args.r8_jar if args.r8_jar else download_distribution( | |
| version, args, temp) | |
| if ':' not in jar and not os.path.exists(jar): | |
| error("Distribution does not exist: " + jar) | |
| cmd = [jdk.GetJavaExecutable(jdkhome)] | |
| cmd.extend(jvmargs) | |
| if args.debug_agent: | |
| if not args.nolib: | |
| print( | |
| "WARNING: Running debugging agent on r8lib is questionable..." | |
| ) | |
| cmd.append( | |
| '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005' | |
| ) | |
| if args.xmx: | |
| cmd.append('-Xmx' + args.xmx) | |
| if not args.disable_assertions: | |
| cmd.append('-ea') | |
| if args.enable_test_assertions: | |
| cmd.append('-Dcom.android.tools.r8.enableTestAssertions=1') | |
| feature_jars = dump.feature_jars() | |
| if args.dump_keep_radius_to_directory is not None: | |
| cmd.append('-Dcom.android.tools.r8.dumpkeepradiustodirectory=' + | |
| args.dump_keep_radius_to_directory) | |
| if args.dump_trace_to_directory is not None: | |
| cmd.append('-Dcom.android.tools.r8.dumptracetodirectory=' + | |
| args.dump_trace_to_directory) | |
| if args.print_times: | |
| cmd.append('-Dcom.android.tools.r8.printtimes=1') | |
| if args.r8_flags: | |
| cmd.extend(args.r8_flags.split(' ')) | |
| if 'properties' in args: | |
| cmd.extend(args.properties) | |
| cmd.extend(determine_properties(build_properties)) | |
| cmd.extend(args.java_opts) | |
| cmd.extend(['-cp', os.pathsep.join((temp, jar))]) | |
| no_build = bool(getattr(args, 'no_build', False)) | |
| if compiler == 'd8': | |
| prepare_wrapper(jar, temp, compilation_jdkhome, | |
| 'CompileDumpD8.java', no_build) | |
| cmd.append('com.android.tools.r8.utils.CompileDumpD8') | |
| if is_l8_compiler(compiler): | |
| cmd.append('com.android.tools.r8.L8') | |
| if compiler == 'tracereferences': | |
| cmd.append('com.android.tools.r8.tracereferences.TraceReferences') | |
| cmd.extend( | |
| determine_trace_references_commands(build_properties, out)) | |
| if is_assistant(compiler): | |
| prepare_wrapper(jar, temp, compilation_jdkhome, | |
| 'CompileDumpR8Assistant.java', no_build) | |
| cmd.append('com.android.tools.r8.utils.CompileDumpR8Assistant') | |
| if is_r8_compiler(compiler): | |
| prepare_wrapper(jar, temp, compilation_jdkhome, | |
| 'CompileDumpCompatR8.java', no_build) | |
| cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8') | |
| if compiler == 'r8': | |
| cmd.append('--compat') | |
| elif compiler == 'r8full': | |
| r8_partial_include_file = dump.r8_include_file() | |
| if r8_partial_include_file: | |
| cmd.append('--partial-include') | |
| cmd.append(r8_partial_include_file) | |
| r8_partial_exclude_file = dump.r8_exclude_file() | |
| if r8_partial_exclude_file: | |
| cmd.append('--partial-exclude') | |
| cmd.append(r8_partial_exclude_file) | |
| if compiler != 'tracereferences' and not is_assistant(compiler): | |
| assert mode == 'debug' or mode == 'release' | |
| cmd.append('--' + mode) | |
| # For recompilation of dumps run_on_app_dumps pass in a program jar. | |
| program_jar = determine_program_jar(args, dump) | |
| if compiler != 'tracereferences': | |
| cmd.append(program_jar) | |
| cmd.extend(['--output', out]) | |
| else: | |
| cmd.extend(['--source', program_jar]) | |
| for feature_jar in feature_jars: | |
| if not args.ignore_features and compiler != 'd8': | |
| cmd.extend([ | |
| '--feature-jar', feature_jar, | |
| determine_feature_output(feature_jar, temp, args) | |
| ]) | |
| else: | |
| cmd.append(feature_jar) | |
| if determine_isolated_splits(build_properties, feature_jars): | |
| cmd.append('--isolated-splits') | |
| if dump.library_jar(): | |
| cmd.extend(['--lib', dump.library_jar()]) | |
| if is_r8_compiler(compiler) and dump.resource_ap_file(): | |
| res_output = os.path.join(temp, 'app-res-out.ap_') | |
| cmd.extend( | |
| ['--android-resources', | |
| dump.resource_ap_file(), res_output]) | |
| if dump.classpath_jar() and not is_l8_compiler(compiler): | |
| cmd.extend([ | |
| '--target' if compiler == 'tracereferences' else '--classpath', | |
| dump.classpath_jar() | |
| ]) | |
| if dump.desugared_library_json() and not args.disable_desugared_lib: | |
| cmd.extend(['--desugared-lib', dump.desugared_library_json()]) | |
| if (is_r8_compiler(compiler) or compiler == 'l8') and config_files: | |
| if hasattr(args, | |
| 'config_files_consumer') and args.config_files_consumer: | |
| args.config_files_consumer(config_files) | |
| else: | |
| # If we get a dump from the wild we can't use -injars, -libraryjars or | |
| # -print{mapping,usage} | |
| clean_configs(config_files, args) | |
| for config_file in config_files: | |
| cmd.extend(['--pg-conf', config_file]) | |
| if is_r8_compiler( | |
| compiler | |
| ) or compiler == 'l8' or determine_d8_proguard_map_output( | |
| args, build_properties): | |
| cmd.extend(['--pg-map-output', '%s.map' % out]) | |
| if dump.main_dex_list_resource(): | |
| cmd.extend(['--main-dex-list', dump.main_dex_list_resource()]) | |
| if dump.main_dex_rules_resource(): | |
| cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()]) | |
| if not is_assistant(compiler): | |
| for art_profile_resource in dump.art_profile_resources(): | |
| residual_art_profile_output = \ | |
| determine_residual_art_profile_output(art_profile_resource, temp) | |
| cmd.extend([ | |
| '--art-profile', art_profile_resource, | |
| residual_art_profile_output | |
| ]) | |
| for startup_profile_resource in dump.startup_profile_resources(): | |
| cmd.extend(['--startup-profile', startup_profile_resource]) | |
| if min_api: | |
| cmd.extend(['--min-api', min_api]) | |
| if classfile: | |
| cmd.extend(['--classfile']) | |
| if android_platform_build: | |
| cmd.extend(['--android-platform-build']) | |
| if dump.proguard_input_map(): | |
| if is_d8_compiler(compiler): | |
| cmd.extend(['--pg-map', dump.proguard_input_map()]) | |
| else: | |
| raise Exception("Proguard map input only supported for D8 dump") | |
| if is_r8_compiler(compiler) and optimized_resource_shrinking: | |
| cmd.extend(['--optimized-resource-shrinking']) | |
| if is_r8_compiler(compiler) and args.android_resources_usage_log: | |
| cmd.extend([ | |
| '--android-resources-usage-log', | |
| args.android_resources_usage_log | |
| ]) | |
| if enable_missing_library_api_modeling: | |
| cmd.extend(['--enable-missing-library-api-modeling']) | |
| if args.threads: | |
| cmd.extend(['--threads', args.threads]) | |
| cmd.extend(compilerargs) | |
| utils.PrintCmd(cmd, worker_id=worker_id) | |
| try: | |
| start = time.time() | |
| output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) | |
| end = time.time() | |
| print(output.decode('utf-8')) | |
| if args.print_runtimeraw: | |
| benchmark_name = args.print_runtimeraw | |
| duration = int((end - start) * 1000) | |
| print('') | |
| print('%s(RunTimeRaw): %s ms' % (benchmark_name, duration)) | |
| return 0 | |
| except subprocess.CalledProcessError as e: | |
| if args.nolib \ | |
| or version == 'source' \ | |
| or not try_retrace_output(e, version, temp): | |
| print(e.output.decode('UTF-8')) | |
| return 1 | |
| def try_retrace_output(e, version, temp): | |
| try: | |
| stacktrace = os.path.join(temp, 'stacktrace') | |
| open(stacktrace, 'w+').write(e.output.decode('UTF-8')) | |
| print("=" * 80) | |
| print(" RETRACED OUTPUT") | |
| print("=" * 80) | |
| retrace.run(get_map_file(version, temp), | |
| stacktrace, | |
| None, | |
| no_r8lib=False) | |
| return True | |
| except Exception as e2: | |
| print("Failed to retrace for version: %s" % version) | |
| print(e2) | |
| return False | |
| def get_map_file(version, temp): | |
| if version == 'main': | |
| return utils.R8LIB_MAP | |
| download_path = archive.get_upload_destination(version, 'r8lib.jar.map', | |
| is_hash(version)) | |
| if utils.file_exists_on_cloud_storage(download_path): | |
| map_path = os.path.join(temp, 'mapping.map') | |
| utils.download_file_from_cloud_storage(download_path, map_path) | |
| return map_path | |
| else: | |
| print('Could not find map file from argument: %s.' % version) | |
| return None | |
| def summarize_dump_files(dumpfiles): | |
| if len(dumpfiles) == 0: | |
| error('Summary command expects a list of dumps to summarize') | |
| for f in dumpfiles: | |
| print(f + ':') | |
| try: | |
| with utils.TempDir() as temp: | |
| dump = read_dump(f, temp) | |
| summarize_dump(dump) | |
| except IOError as e: | |
| print("Error: " + str(e)) | |
| except zipfile.BadZipfile as e: | |
| print("Error: " + str(e)) | |
| def summarize_dump(dump): | |
| version = dump.version() | |
| if not version: | |
| print('No dump version info') | |
| return | |
| print('version=' + version) | |
| props = dump.build_properties_file() | |
| if props: | |
| with open(props) as props_file: | |
| print(props_file.read()) | |
| if dump.library_jar(): | |
| print('library.jar present') | |
| if dump.classpath_jar(): | |
| print('classpath.jar present') | |
| prog = dump.program_jar() | |
| if prog: | |
| print('program.jar content:') | |
| summarize_jar(prog) | |
| def summarize_jar(jar): | |
| with zipfile.ZipFile(jar) as zip: | |
| pkgs = {} | |
| for info in zip.infolist(): | |
| if info.filename.endswith('.class'): | |
| pkg, clazz = os.path.split(info.filename) | |
| count = pkgs.get(pkg, 0) | |
| pkgs[pkg] = count + 1 | |
| sorted = list(pkgs.keys()) | |
| sorted.sort() | |
| for p in sorted: | |
| print(' ' + p + ': ' + str(pkgs[p])) | |
| def run(args, otherargs): | |
| gradle.ensure_jdk() | |
| jdkhome = jdk.GetJdkRoot(args.jdk) if args.jdk else None | |
| if args.summary: | |
| summarize_dump_files(otherargs) | |
| elif args.loop: | |
| count = 1 | |
| while True: | |
| print('Iteration {:03d}'.format(count)) | |
| out = args.temp | |
| if out: | |
| out = os.path.join(out, '{:03d}'.format(count)) | |
| run1(out, args, otherargs, jdkhome=jdkhome) | |
| count += 1 | |
| else: | |
| run1(args.temp, args, otherargs, jdkhome=jdkhome) | |
| if __name__ == '__main__': | |
| (args, otherargs) = make_parser().parse_known_args(sys.argv[1:]) | |
| sys.exit(run(args, otherargs)) |