Ian Zerny | 3709765 | 2019-04-11 13:13:27 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file |
| 3 | # for details. All rights reserved. Use of this source code is governed by a |
| 4 | # BSD-style license that can be found in the LICENSE file. |
| 5 | |
| 6 | # Run R8 on a simple Hello World program |
| 7 | # Report Golem-compatible RunTimeRaw values: |
| 8 | # |
| 9 | # <NAME>-Total(RunTimeRaw): <time> ms |
| 10 | # |
| 11 | # where <NAME> is Hello{,Dex}{,Large}{,NoOpt} |
| 12 | |
| 13 | import argparse |
| 14 | import os |
| 15 | import subprocess |
| 16 | import sys |
| 17 | import time |
| 18 | import zipfile |
| 19 | |
| 20 | import golem |
| 21 | import jdk |
| 22 | import utils |
| 23 | |
| 24 | HELLO_JAR = os.path.join(utils.BUILD, 'test', 'examples', 'hello.jar') |
| 25 | |
| 26 | EXTRA_INPUTS = [ |
| 27 | os.path.join(utils.THIRD_PARTY, 'sample_libraries', lib) for lib in [ |
| 28 | 'animal-sniffer-annotations-1.17.jar', |
| 29 | 'annotations-13.0.jar', |
| 30 | 'checker-compat-qual-2.5.2.jar', |
| 31 | 'collections-28.0.0.jar', |
| 32 | 'common-1.1.1.jar', |
| 33 | 'commons-collections4-4.3.jar', |
| 34 | 'commons-compress-1.18.jar', |
| 35 | 'commons-lang3-3.8.1.jar', |
| 36 | 'commons-math3-3.6.1.jar', |
| 37 | 'constraint-layout-solver-1.1.3.jar', |
| 38 | 'converter-gson-2.5.0.jar', |
| 39 | 'dagger-2.22.1.jar', |
| 40 | 'error_prone_annotations-2.2.0.jar', |
| 41 | 'failureaccess-1.0.1.jar', |
| 42 | 'gson-2.8.2.jar', |
| 43 | 'guava-27.1-android.jar', |
| 44 | 'j2objc-annotations-1.1.jar', |
| 45 | 'javax.inject-1.jar', |
| 46 | 'jsr305-3.0.2.jar', |
| 47 | 'kotlin-stdlib-1.3.21.jar', |
| 48 | 'kotlin-stdlib-common-1.3.21.jar', |
| 49 | 'kotlin-stdlib-jdk7-1.3.21.jar', |
| 50 | 'listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar', |
| 51 | 'okhttp-3.14.0.jar', |
| 52 | 'okio-1.17.2.jar', |
| 53 | 'play-services-ads-17.2.0-javadoc.jar', |
| 54 | 'play-services-ads-base-17.2.0-javadoc.jar', |
| 55 | 'play-services-ads-lite-17.2.0-javadoc.jar', |
| 56 | 'play-services-analytics-16.0.8-javadoc.jar', |
| 57 | 'play-services-analytics-impl-16.0.8-javadoc.jar', |
| 58 | 'play-services-base-16.1.0-javadoc.jar', |
| 59 | 'play-services-basement-16.2.0-javadoc.jar', |
| 60 | 'play-services-cast-16.1.2-javadoc.jar', |
| 61 | 'play-services-drive-16.1.0-javadoc.jar', |
| 62 | 'play-services-fitness-16.0.1-javadoc.jar', |
| 63 | 'play-services-games-17.0.0-javadoc.jar', |
| 64 | 'play-services-gass-17.2.0-javadoc.jar', |
| 65 | 'play-services-gcm-16.1.0-javadoc.jar', |
| 66 | 'play-services-iid-16.0.1-javadoc.jar', |
| 67 | 'play-services-measurement-16.4.0-javadoc.jar', |
| 68 | 'play-services-measurement-api-16.4.0-javadoc.jar', |
| 69 | 'play-services-measurement-base-16.4.0-javadoc.jar', |
| 70 | 'play-services-measurement-impl-16.4.0-javadoc.jar', |
| 71 | 'play-services-measurement-sdk-16.4.0-javadoc.jar', |
| 72 | 'play-services-measurement-sdk-api-16.4.0-javadoc.jar', |
| 73 | 'play-services-tagmanager-v4-impl-16.0.8-javadoc.jar', |
| 74 | 'play-services-vision-17.0.2-javadoc.jar', |
| 75 | 'play-services-vision-common-17.0.2-javadoc.jar', |
| 76 | 'protobuf-lite-3.0.1.jar', |
| 77 | 'reactive-streams-1.0.2.jar', |
| 78 | 'retrofit-2.5.0.jar', |
| 79 | 'rxjava-2.2.8.jar', |
| 80 | 'support-annotations-28.0.0.jar', |
| 81 | ] |
| 82 | ] |
| 83 | |
| 84 | EXTRA_KEEP_RULES = ['-dontwarn java.lang.ClassValue'] |
| 85 | |
| 86 | def parse_arguments(): |
| 87 | parser = argparse.ArgumentParser( |
| 88 | description = 'Compile a hello world example program') |
| 89 | parser.add_argument('--tool', |
| 90 | choices = ['d8', 'r8', 'pg'], |
| 91 | required = True, |
| 92 | help = 'Compiler tool to use.') |
| 93 | parser.add_argument('--output-mode', |
| 94 | choices = ['dex', 'cf'], |
| 95 | required = True, |
| 96 | help = 'Output mode to compile to.') |
| 97 | parser.add_argument('--golem', |
| 98 | help = 'Running on golem, link in third_party resources.', |
| 99 | default = False, |
| 100 | action = 'store_true') |
| 101 | parser.add_argument('--large', |
| 102 | help = 'Add many additional program inputs.', |
| 103 | default = False, |
| 104 | action = 'store_true') |
| 105 | parser.add_argument('--noopt', |
| 106 | help = 'Disable most optimizations/processing.', |
| 107 | default = False, |
| 108 | action = 'store_true') |
| 109 | parser.add_argument('--print-memoryuse', |
| 110 | help = 'Prints the line \'<NAME>-Total(MemoryUse):' |
| 111 | ' <mem>\' at the end where <mem> is the peak' |
| 112 | ' peak resident set size (VmHWM) in bytes.', |
| 113 | default = False, |
| 114 | action = 'store_true') |
| 115 | parser.add_argument('--output', |
| 116 | help = 'Output directory to keep the generated files') |
| 117 | return parser.parse_args() |
| 118 | |
| 119 | def GetConfRules(extra, noopt): |
| 120 | rules = ['-keep class hello.Hello { void main(java.lang.String[]); }'] |
| 121 | if len(extra) > 0: |
| 122 | rules.extend(EXTRA_KEEP_RULES) |
| 123 | if noopt: |
| 124 | rules.extend([ |
| 125 | '-dontoptimize', |
| 126 | '-dontshrink', |
| 127 | '-dontobfuscate', |
| 128 | '-keepattributes *', |
| 129 | ]) |
| 130 | return rules |
| 131 | |
| 132 | def GetCompilerPrefix(tool, mode, output, input, lib, extra, noopt): |
| 133 | return [ |
| 134 | jdk.GetJavaExecutable(), |
| 135 | '-jar', utils.R8_JAR if tool == 'r8' else utils.D8_JAR, |
| 136 | '--output', output, |
| 137 | '--lib', lib, |
| 138 | '--debug' if noopt else '--release', |
| 139 | input, |
| 140 | ] + ([] if mode == 'cf' else ['--min-api', '21']) + extra |
| 141 | |
| 142 | def Compile(tool, output_mode, lib, extra, output_dir, noopt, temp_dir): |
| 143 | output = os.path.join(output_dir, 'out.zip') |
| 144 | if tool == 'd8': |
| 145 | if output_mode != 'dex': |
| 146 | raise ValueError('Invalid output mode for D8') |
Ian Zerny | 9254536 | 2019-05-06 10:46:55 +0200 | [diff] [blame] | 147 | classpath = [] |
| 148 | for cp_entry in extra: |
| 149 | classpath.extend(['--classpath', cp_entry]) |
Ian Zerny | 3709765 | 2019-04-11 13:13:27 +0200 | [diff] [blame] | 150 | return [ |
Ian Zerny | 9254536 | 2019-05-06 10:46:55 +0200 | [diff] [blame] | 151 | GetCompilerPrefix( |
| 152 | tool, output_mode, output, HELLO_JAR, lib, classpath, noopt) |
Ian Zerny | 3709765 | 2019-04-11 13:13:27 +0200 | [diff] [blame] | 153 | ] |
| 154 | # The compilation is either R8 or PG. |
| 155 | # Write keep rules to a temporary file. |
| 156 | rules = GetConfRules(extra, noopt) |
| 157 | rules_file = os.path.join(temp_dir, 'rules.conf') |
| 158 | open(rules_file, 'w').write('\n'.join(rules)) |
| 159 | if tool == 'r8': |
| 160 | cmd = GetCompilerPrefix( |
| 161 | tool, output_mode, output, HELLO_JAR, lib, extra, noopt) |
| 162 | cmd.extend(['--pg-conf', rules_file]) |
| 163 | if output_mode == 'cf': |
| 164 | cmd.append('--classfile') |
| 165 | return [cmd] |
| 166 | if tool == 'pg': |
| 167 | # Build PG invokation with additional rules to silence warnings. |
| 168 | pg_out = output if output_mode == 'cf' \ |
| 169 | else os.path.join(output_dir, 'pgout.zip') |
| 170 | cmds = [[ |
| 171 | jdk.GetJavaExecutable(), |
| 172 | '-jar', utils.PROGUARD_JAR, |
| 173 | '-injars', ':'.join([HELLO_JAR] + extra), |
| 174 | '-libraryjars', lib, |
| 175 | '-outjars', pg_out, |
| 176 | '-dontwarn **', |
| 177 | '@' + rules_file |
| 178 | ]] |
| 179 | if output_mode == 'dex': |
| 180 | cmds.append( |
| 181 | GetCompilerPrefix('d8', 'dex', output, pg_out, lib, [], noopt)) |
| 182 | return cmds |
| 183 | raise ValueError('Unknown tool: ' + tool) |
| 184 | |
| 185 | def ProcessInput(input, tmp_dir): |
| 186 | if not input.endswith('.aar'): |
| 187 | return input |
| 188 | out_dir = os.path.join(tmp_dir, input) |
| 189 | os.makedirs(out_dir) |
| 190 | zip = zipfile.ZipFile(input, 'r') |
| 191 | zip.extractall(out_dir) |
| 192 | zip.close() |
| 193 | return os.path.join(out_dir, 'classes.jar') |
| 194 | |
| 195 | def Main(): |
| 196 | args = parse_arguments() |
| 197 | if args.golem: |
| 198 | golem.link_third_party() |
| 199 | utils.check_java_version() |
| 200 | |
| 201 | with utils.TempDir() as temp_dir: |
| 202 | cmd_prefix = [] |
| 203 | output_dir = args.output if args.output else temp_dir |
| 204 | temp_dir = os.path.join(args.output, 'tmp') if args.output else temp_dir |
| 205 | |
| 206 | track_memory_file = None |
| 207 | if args.print_memoryuse: |
| 208 | track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE) |
| 209 | cmd_prefix.extend(['tools/track_memory.sh', track_memory_file]) |
| 210 | |
Ian Zerny | 08ad2c3 | 2019-04-11 13:30:32 +0200 | [diff] [blame] | 211 | name = 'CompileHelloExample' |
Ian Zerny | 3709765 | 2019-04-11 13:13:27 +0200 | [diff] [blame] | 212 | |
| 213 | tool = args.tool |
| 214 | output_mode = args.output_mode |
| 215 | lib = None |
| 216 | if output_mode == 'dex': |
| 217 | name += 'Dex' |
| 218 | lib = utils.get_android_jar(28) |
| 219 | else: |
| 220 | lib = utils.RT_JAR |
| 221 | |
| 222 | extra = [] |
| 223 | if args.large: |
| 224 | name += 'Large' |
| 225 | extra = EXTRA_INPUTS |
| 226 | |
| 227 | if args.noopt: |
| 228 | name += 'NoOpt' |
| 229 | |
| 230 | cmds = Compile( |
| 231 | tool, |
| 232 | output_mode, |
| 233 | lib, |
| 234 | extra, |
| 235 | output_dir, |
| 236 | args.noopt, |
| 237 | temp_dir, |
| 238 | ) |
| 239 | |
| 240 | t0 = time.time() |
| 241 | for cmd in cmds: |
| 242 | fullcmd = cmd_prefix + cmd |
| 243 | utils.PrintCmd(fullcmd) |
Ian Zerny | 678683a | 2019-05-07 11:28:23 +0200 | [diff] [blame^] | 244 | subprocess.check_output(fullcmd) |
Ian Zerny | 3709765 | 2019-04-11 13:13:27 +0200 | [diff] [blame] | 245 | dt = time.time() - t0 |
| 246 | |
| 247 | if args.print_memoryuse: |
| 248 | print('{}(MemoryUse): {}' |
| 249 | .format(name, utils.grep_memoryuse(track_memory_file))) |
| 250 | |
| 251 | print('{}(RunTimeRaw): {} ms' |
| 252 | .format(name, 1000.0 * dt)) |
| 253 | |
| 254 | if __name__ == '__main__': |
| 255 | sys.exit(Main()) |