| #!/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()) |