|  | #!/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 proguard | 
|  | 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'] + proguard.getVersions(), | 
|  | 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 proguard.isValidVersion(tool): | 
|  | # 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 = [proguard.getCmd([ | 
|  | '-injars', ':'.join([HELLO_JAR] + extra), | 
|  | '-libraryjars', lib, | 
|  | '-outjars', pg_out, | 
|  | '-dontwarn **', | 
|  | '@' + rules_file | 
|  | ], version=tool)] | 
|  | 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()) |