| #!/usr/bin/env python | |
| # Copyright (c) 2019, 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. | |
| # Run R8 on a simple Hello World program | |
| # Report Golem-compatible RunTimeRaw values: | |
| # | |
| # <NAME>-Total(RunTimeRaw): <time> ms | |
| # | |
| # where <NAME> is Hello{,Dex}{,Large}{,NoOpt} | |
| import argparse | |
| import os | |
| import subprocess | |
| import sys | |
| import time | |
| import zipfile | |
| import golem | |
| import jdk | |
| import utils | |
| HELLO_JAR = os.path.join(utils.BUILD, 'test', 'examples', 'hello.jar') | |
| EXTRA_INPUTS = [ | |
| os.path.join(utils.THIRD_PARTY, 'sample_libraries', lib) for lib in [ | |
| 'animal-sniffer-annotations-1.17.jar', | |
| 'annotations-13.0.jar', | |
| 'checker-compat-qual-2.5.2.jar', | |
| 'collections-28.0.0.jar', | |
| 'common-1.1.1.jar', | |
| 'commons-collections4-4.3.jar', | |
| 'commons-compress-1.18.jar', | |
| 'commons-lang3-3.8.1.jar', | |
| 'commons-math3-3.6.1.jar', | |
| 'constraint-layout-solver-1.1.3.jar', | |
| 'converter-gson-2.5.0.jar', | |
| 'dagger-2.22.1.jar', | |
| 'error_prone_annotations-2.2.0.jar', | |
| 'failureaccess-1.0.1.jar', | |
| 'gson-2.8.2.jar', | |
| 'guava-27.1-android.jar', | |
| 'j2objc-annotations-1.1.jar', | |
| 'javax.inject-1.jar', | |
| 'jsr305-3.0.2.jar', | |
| 'kotlin-stdlib-1.3.21.jar', | |
| 'kotlin-stdlib-common-1.3.21.jar', | |
| 'kotlin-stdlib-jdk7-1.3.21.jar', | |
| 'listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar', | |
| 'okhttp-3.14.0.jar', | |
| 'okio-1.17.2.jar', | |
| 'play-services-ads-17.2.0-javadoc.jar', | |
| 'play-services-ads-base-17.2.0-javadoc.jar', | |
| 'play-services-ads-lite-17.2.0-javadoc.jar', | |
| 'play-services-analytics-16.0.8-javadoc.jar', | |
| 'play-services-analytics-impl-16.0.8-javadoc.jar', | |
| 'play-services-base-16.1.0-javadoc.jar', | |
| 'play-services-basement-16.2.0-javadoc.jar', | |
| 'play-services-cast-16.1.2-javadoc.jar', | |
| 'play-services-drive-16.1.0-javadoc.jar', | |
| 'play-services-fitness-16.0.1-javadoc.jar', | |
| 'play-services-games-17.0.0-javadoc.jar', | |
| 'play-services-gass-17.2.0-javadoc.jar', | |
| 'play-services-gcm-16.1.0-javadoc.jar', | |
| 'play-services-iid-16.0.1-javadoc.jar', | |
| 'play-services-measurement-16.4.0-javadoc.jar', | |
| 'play-services-measurement-api-16.4.0-javadoc.jar', | |
| 'play-services-measurement-base-16.4.0-javadoc.jar', | |
| 'play-services-measurement-impl-16.4.0-javadoc.jar', | |
| 'play-services-measurement-sdk-16.4.0-javadoc.jar', | |
| 'play-services-measurement-sdk-api-16.4.0-javadoc.jar', | |
| 'play-services-tagmanager-v4-impl-16.0.8-javadoc.jar', | |
| 'play-services-vision-17.0.2-javadoc.jar', | |
| 'play-services-vision-common-17.0.2-javadoc.jar', | |
| 'protobuf-lite-3.0.1.jar', | |
| 'reactive-streams-1.0.2.jar', | |
| 'retrofit-2.5.0.jar', | |
| 'rxjava-2.2.8.jar', | |
| 'support-annotations-28.0.0.jar', | |
| ] | |
| ] | |
| EXTRA_KEEP_RULES = ['-dontwarn java.lang.ClassValue'] | |
| def parse_arguments(): | |
| parser = argparse.ArgumentParser( | |
| description = 'Compile a hello world example program') | |
| parser.add_argument('--tool', | |
| choices = ['d8', 'r8', 'pg'], | |
| required = True, | |
| help = 'Compiler tool to use.') | |
| parser.add_argument('--output-mode', | |
| choices = ['dex', 'cf'], | |
| required = True, | |
| help = 'Output mode to compile to.') | |
| parser.add_argument('--golem', | |
| help = 'Running on golem, link in third_party resources.', | |
| default = False, | |
| action = 'store_true') | |
| parser.add_argument('--large', | |
| help = 'Add many additional program inputs.', | |
| default = False, | |
| action = 'store_true') | |
| parser.add_argument('--noopt', | |
| help = 'Disable most optimizations/processing.', | |
| default = False, | |
| action = 'store_true') | |
| parser.add_argument('--print-memoryuse', | |
| help = 'Prints the line \'<NAME>-Total(MemoryUse):' | |
| ' <mem>\' at the end where <mem> is the peak' | |
| ' peak resident set size (VmHWM) in bytes.', | |
| default = False, | |
| action = 'store_true') | |
| parser.add_argument('--output', | |
| help = 'Output directory to keep the generated files') | |
| return parser.parse_args() | |
| def GetConfRules(extra, noopt): | |
| rules = ['-keep class hello.Hello { void main(java.lang.String[]); }'] | |
| if len(extra) > 0: | |
| rules.extend(EXTRA_KEEP_RULES) | |
| if noopt: | |
| rules.extend([ | |
| '-dontoptimize', | |
| '-dontshrink', | |
| '-dontobfuscate', | |
| '-keepattributes *', | |
| ]) | |
| return rules | |
| def GetCompilerPrefix(tool, mode, output, input, lib, extra, noopt): | |
| return [ | |
| jdk.GetJavaExecutable(), | |
| '-jar', utils.R8_JAR if tool == 'r8' else utils.D8_JAR, | |
| '--output', output, | |
| '--lib', lib, | |
| '--debug' if noopt else '--release', | |
| input, | |
| ] + ([] if mode == 'cf' else ['--min-api', '21']) + extra | |
| def Compile(tool, output_mode, lib, extra, output_dir, noopt, temp_dir): | |
| output = os.path.join(output_dir, 'out.zip') | |
| if tool == 'd8': | |
| if output_mode != 'dex': | |
| raise ValueError('Invalid output mode for D8') | |
| classpath = [] | |
| for cp_entry in extra: | |
| classpath.extend(['--classpath', cp_entry]) | |
| return [ | |
| GetCompilerPrefix( | |
| tool, output_mode, output, HELLO_JAR, lib, classpath, noopt) | |
| ] | |
| # The compilation is either R8 or PG. | |
| # Write keep rules to a temporary file. | |
| rules = GetConfRules(extra, noopt) | |
| rules_file = os.path.join(temp_dir, 'rules.conf') | |
| open(rules_file, 'w').write('\n'.join(rules)) | |
| if tool == 'r8': | |
| cmd = GetCompilerPrefix( | |
| tool, output_mode, output, HELLO_JAR, lib, extra, noopt) | |
| cmd.extend(['--pg-conf', rules_file]) | |
| if output_mode == 'cf': | |
| cmd.append('--classfile') | |
| return [cmd] | |
| if tool == 'pg': | |
| # Build PG invokation with additional rules to silence warnings. | |
| pg_out = output if output_mode == 'cf' \ | |
| else os.path.join(output_dir, 'pgout.zip') | |
| cmds = [[ | |
| jdk.GetJavaExecutable(), | |
| '-jar', utils.PROGUARD_JAR, | |
| '-injars', ':'.join([HELLO_JAR] + extra), | |
| '-libraryjars', lib, | |
| '-outjars', pg_out, | |
| '-dontwarn **', | |
| '@' + rules_file | |
| ]] | |
| if output_mode == 'dex': | |
| cmds.append( | |
| GetCompilerPrefix('d8', 'dex', output, pg_out, lib, [], noopt)) | |
| return cmds | |
| raise ValueError('Unknown tool: ' + tool) | |
| def ProcessInput(input, tmp_dir): | |
| if not input.endswith('.aar'): | |
| return input | |
| out_dir = os.path.join(tmp_dir, input) | |
| os.makedirs(out_dir) | |
| zip = zipfile.ZipFile(input, 'r') | |
| zip.extractall(out_dir) | |
| zip.close() | |
| return os.path.join(out_dir, 'classes.jar') | |
| def Main(): | |
| args = parse_arguments() | |
| if args.golem: | |
| golem.link_third_party() | |
| utils.check_java_version() | |
| with utils.TempDir() as temp_dir: | |
| cmd_prefix = [] | |
| output_dir = args.output if args.output else temp_dir | |
| temp_dir = os.path.join(args.output, 'tmp') if args.output else temp_dir | |
| track_memory_file = None | |
| if args.print_memoryuse: | |
| track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE) | |
| cmd_prefix.extend(['tools/track_memory.sh', track_memory_file]) | |
| name = 'CompileHelloExample' | |
| tool = args.tool | |
| output_mode = args.output_mode | |
| lib = None | |
| if output_mode == 'dex': | |
| name += 'Dex' | |
| lib = utils.get_android_jar(28) | |
| else: | |
| lib = utils.RT_JAR | |
| extra = [] | |
| if args.large: | |
| name += 'Large' | |
| extra = EXTRA_INPUTS | |
| if args.noopt: | |
| name += 'NoOpt' | |
| cmds = Compile( | |
| tool, | |
| output_mode, | |
| lib, | |
| extra, | |
| output_dir, | |
| args.noopt, | |
| temp_dir, | |
| ) | |
| t0 = time.time() | |
| for cmd in cmds: | |
| fullcmd = cmd_prefix + cmd | |
| utils.PrintCmd(fullcmd) | |
| subprocess.check_output(fullcmd) | |
| dt = time.time() - t0 | |
| if args.print_memoryuse: | |
| print('{}(MemoryUse): {}' | |
| .format(name, utils.grep_memoryuse(track_memory_file))) | |
| print('{}(RunTimeRaw): {} ms' | |
| .format(name, 1000.0 * dt)) | |
| if __name__ == '__main__': | |
| sys.exit(Main()) |