blob: 3828739cd55d4cf643897247ddfe19f1ab905eb4 [file] [log] [blame]
Ian Zerny5ffa58f2020-02-26 08:37:14 +01001#!/usr/bin/env python
2# Copyright (c) 2020, 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
Ian Zerny77bdf5f2020-07-08 11:46:24 +02006import archive
Ian Zerny5ffa58f2020-02-26 08:37:14 +01007import argparse
Ian Zerny77bdf5f2020-07-08 11:46:24 +02008import jdk
Ian Zerny5ffa58f2020-02-26 08:37:14 +01009import os
Ian Zerny77bdf5f2020-07-08 11:46:24 +020010import retrace
Ian Zerny5ffa58f2020-02-26 08:37:14 +010011import subprocess
12import sys
13import zipfile
14
Ian Zerny5ffa58f2020-02-26 08:37:14 +010015import utils
16
17
18def make_parser():
19 parser = argparse.ArgumentParser(description = 'Compile a dump artifact.')
20 parser.add_argument(
21 '-d',
22 '--dump',
Christoffer Quist Adamsend84a6f02020-05-16 12:29:35 +020023 help='Dump file or directory to compile',
Ian Zerny5ffa58f2020-02-26 08:37:14 +010024 default=None)
25 parser.add_argument(
Rico Wind9ed87b92020-03-06 12:37:18 +010026 '--temp',
27 help='Temp directory to extract the dump to, allows you to rerun the command'
28 ' more easily in the terminal with changes',
29 default=None)
30 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010031 '-c',
32 '--compiler',
Rico Windf73f18d2020-03-03 09:28:54 +010033 help='Compiler to use',
Ian Zerny5ffa58f2020-02-26 08:37:14 +010034 default=None)
35 parser.add_argument(
36 '-v',
37 '--version',
38 help='Compiler version to use (default read from dump version file).'
39 'Valid arguments are:'
40 ' "master" to run from your own tree,'
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +010041 ' "source" to run from build classes directly,'
Ian Zerny5ffa58f2020-02-26 08:37:14 +010042 ' "X.Y.Z" to run a specific version, or'
43 ' <hash> to run that hash from master.',
44 default=None)
45 parser.add_argument(
Morten Krogh-Jespersend8bceb52020-03-06 12:56:48 +010046 '--r8-jar',
47 help='Path to an R8 jar.',
48 default=None)
49 parser.add_argument(
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +010050 '-override',
51 help='Do not override any extracted dump in temp-dir',
52 default=False,
53 action='store_true')
54 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010055 '--nolib',
56 help='Use the non-lib distribution (default uses the lib distribution)',
57 default=False,
58 action='store_true')
59 parser.add_argument(
Rico Wind28653642020-03-31 13:59:07 +020060 '--printtimes',
61 help='Print timing information from r8',
62 default=False,
63 action='store_true')
64 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010065 '--ea',
66 help='Enable Java assertions when running the compiler (default disabled)',
67 default=False,
68 action='store_true')
69 parser.add_argument(
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010070 '--classfile',
71 help='Run with classfile output',
72 default=False,
73 action='store_true')
74 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010075 '--debug-agent',
76 help='Enable Java debug agent and suspend compilation (default disabled)',
77 default=False,
78 action='store_true')
Ian Zerny77bdf5f2020-07-08 11:46:24 +020079 parser.add_argument(
80 '--xmx',
81 help='Set JVM max heap size (-Xmx)',
82 default=None)
83 parser.add_argument(
84 '--threads',
85 help='Set the number of threads to use',
86 default=None)
87 parser.add_argument(
88 '--min-api',
89 help='Set min-api (default read from dump properties file)',
90 default=None)
Søren Gjesse7360f2b2020-08-10 09:13:35 +020091 parser.add_argument(
Clément Béradaae4ca2020-10-27 14:26:41 +000092 '--desugared-lib',
93 help='Set desugared-library (default set from dump)',
94 default=None)
95 parser.add_argument(
Morten Krogh-Jespersend86a81b2020-11-13 12:33:26 +010096 '--disable-desugared-lib',
97 help='Disable desugared-libary if it will be set from dump',
98 default=False,
99 action='store_true'
100 )
101 parser.add_argument(
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200102 '--loop',
103 help='Run the compilation in a loop',
104 default=False,
105 action='store_true')
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100106 return parser
107
108def error(msg):
Rico Wind3d369b42021-01-12 10:26:24 +0100109 print(msg)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100110 sys.exit(1)
111
112class Dump(object):
113
114 def __init__(self, directory):
115 self.directory = directory
116
117 def if_exists(self, name):
118 f = os.path.join(self.directory, name)
119 if os.path.exists(f):
120 return f
121 return None
122
123 def program_jar(self):
124 return self.if_exists('program.jar')
125
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200126 def feature_jars(self):
127 feature_jars = []
128 i = 1
129 while True:
130 feature_jar = self.if_exists('feature-%s.jar' % i)
131 if feature_jar:
132 feature_jars.append(feature_jar)
133 i = i + 1
134 else:
135 return feature_jars
136
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100137 def library_jar(self):
138 return self.if_exists('library.jar')
139
140 def classpath_jar(self):
141 return self.if_exists('classpath.jar')
142
Clément Béradaae4ca2020-10-27 14:26:41 +0000143 def desugared_library_json(self):
144 return self.if_exists('desugared-library.json')
145
Clément Béra64a3c4c2020-11-10 08:16:17 +0000146 def proguard_input_map(self):
147 if self.if_exists('proguard_input.config'):
Rico Wind3d369b42021-01-12 10:26:24 +0100148 print("Unimplemented: proguard_input configuration.")
Clément Béra64a3c4c2020-11-10 08:16:17 +0000149
Morten Krogh-Jespersen1e774252021-03-01 16:56:07 +0100150 def main_dex_list_resource(self):
Clément Béra64a3c4c2020-11-10 08:16:17 +0000151 if self.if_exists('main-dex-list.txt'):
Rico Wind3d369b42021-01-12 10:26:24 +0100152 print("Unimplemented: main-dex-list.")
Clément Béra64a3c4c2020-11-10 08:16:17 +0000153
Morten Krogh-Jespersen1e774252021-03-01 16:56:07 +0100154 def main_dex_rules_resource(self):
155 return self.if_exists('main-dex-rules.txt')
156
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200157 def build_properties_file(self):
158 return self.if_exists('build.properties')
159
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100160 def config_file(self):
161 return self.if_exists('proguard.config')
162
163 def version_file(self):
164 return self.if_exists('r8-version')
165
166 def version(self):
167 f = self.version_file()
168 if f:
169 return open(f).read().split(' ')[0]
170 return None
171
172def read_dump(args, temp):
173 if args.dump is None:
Christoffer Quist Adamsend84a6f02020-05-16 12:29:35 +0200174 error("A dump file or directory must be specified")
175 if os.path.isdir(args.dump):
176 return Dump(args.dump)
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200177 dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100178 with utils.ChangedWorkingDirectory(temp):
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +0100179 if args.override or not os.path.isfile(
Morten Krogh-Jespersenc2af7692020-12-17 16:30:41 +0100180 os.path.join(temp, 'r8-version')):
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +0100181 print("Extracting into: %s" % temp)
182 dump_file.extractall()
Morten Krogh-Jespersenc2af7692020-12-17 16:30:41 +0100183 if not os.path.isfile(os.path.join(temp, 'r8-version')):
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +0100184 error("Did not extract into %s. Either the zip file is invalid or the "
185 "dump is missing files" % temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100186 return Dump(temp)
187
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200188def determine_build_properties(args, dump):
189 build_properties = {}
190 build_properties_file = dump.build_properties_file()
191 if build_properties_file:
192 with open(build_properties_file) as f:
193 build_properties_contents = f.readlines()
194 for line in build_properties_contents:
195 stripped = line.strip()
196 if stripped:
197 pair = stripped.split('=')
198 build_properties[pair[0]] = pair[1]
199 return build_properties
200
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100201def determine_version(args, dump):
202 if args.version is None:
203 return dump.version()
204 return args.version
205
206def determine_compiler(args, dump):
Rico Windf73f18d2020-03-03 09:28:54 +0100207 compilers = ['d8', 'r8', 'r8full']
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100208 if args.compiler not in compilers:
Rico Windf73f18d2020-03-03 09:28:54 +0100209 error("Unable to determine a compiler to use. Specified %s,"
210 " Valid options: %s" % (args.compiler, ', '.join(compilers)))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100211 return args.compiler
212
213def determine_output(args, temp):
214 return os.path.join(temp, 'out.jar')
215
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200216def determine_min_api(args, build_properties):
217 if args.min_api:
218 return args.min_api
219 if 'min-api' in build_properties:
220 return build_properties.get('min-api')
221 return None
222
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200223def determine_feature_output(feature_jar, temp):
224 return os.path.join(temp, os.path.basename(feature_jar)[:-4] + ".out.jar")
225
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100226def determine_program_jar(args, dump):
227 if hasattr(args, 'program_jar') and args.program_jar:
228 return args.program_jar
229 return dump.program_jar()
230
231def determine_class_file(args, build_properties):
232 if args.classfile:
233 return args.classfile
234 if 'classfile' in build_properties:
235 return True
236 return None
237
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100238def download_distribution(args, version, temp):
239 if version == 'master':
240 return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100241 if version == 'source':
242 return '%s:%s' % (utils.BUILD_JAVA_MAIN_DIR, utils.ALL_DEPS_JAR)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100243 name = 'r8.jar' if args.nolib else 'r8lib.jar'
244 source = archive.GetUploadDestination(version, name, is_hash(version))
245 dest = os.path.join(temp, 'r8.jar')
246 utils.download_file_from_cloud_storage(source, dest)
247 return dest
248
249def prepare_wrapper(dist, temp):
250 wrapper_file = os.path.join(
251 utils.REPO_ROOT,
252 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
Ian Zerny268c9632020-11-13 11:36:35 +0100253 cmd = [
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100254 jdk.GetJavacExecutable(),
255 wrapper_file,
256 '-d', temp,
257 '-cp', dist,
Ian Zerny268c9632020-11-13 11:36:35 +0100258 ]
259 utils.PrintCmd(cmd)
260 subprocess.check_output(cmd)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100261 return temp
262
263def is_hash(version):
264 return len(version) == 40
265
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200266def run1(out, args, otherargs):
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100267 with utils.TempDir() as temp:
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200268 if out:
269 temp = out
Rico Wind9ed87b92020-03-06 12:37:18 +0100270 if not os.path.exists(temp):
271 os.makedirs(temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100272 dump = read_dump(args, temp)
Ian Zerny46bc4cf2020-08-03 15:58:27 +0200273 if not dump.program_jar():
274 error("Cannot compile dump with no program classes")
275 if not dump.library_jar():
Rico Wind3d369b42021-01-12 10:26:24 +0100276 print("WARNING: Unexpected lack of library classes in dump")
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200277 build_properties = determine_build_properties(args, dump)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100278 version = determine_version(args, dump)
279 compiler = determine_compiler(args, dump)
280 out = determine_output(args, temp)
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200281 min_api = determine_min_api(args, build_properties)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100282 classfile = determine_class_file(args, build_properties)
Morten Krogh-Jespersend8bceb52020-03-06 12:56:48 +0100283 jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
Ian Zerny268c9632020-11-13 11:36:35 +0100284 if ':' not in jar and not os.path.exists(jar):
285 error("Distribution does not exist: " + jar)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100286 wrapper_dir = prepare_wrapper(jar, temp)
287 cmd = [jdk.GetJavaExecutable()]
288 if args.debug_agent:
289 if not args.nolib:
Rico Wind3d369b42021-01-12 10:26:24 +0100290 print("WARNING: Running debugging agent on r8lib is questionable...")
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100291 cmd.append(
292 '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200293 if args.xmx:
294 cmd.append('-Xmx' + args.xmx)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100295 if args.ea:
296 cmd.append('-ea')
Rico Wind28653642020-03-31 13:59:07 +0200297 if args.printtimes:
298 cmd.append('-Dcom.android.tools.r8.printtimes=1')
Morten Krogh-Jespersen43f3cea2020-11-12 17:09:51 +0100299 if hasattr(args, 'properties'):
300 cmd.extend(args.properties);
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100301 cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
302 if compiler == 'd8':
303 cmd.append('com.android.tools.r8.D8')
304 if compiler.startswith('r8'):
305 cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8')
306 if compiler == 'r8':
307 cmd.append('--compat')
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100308 # For recompilation of dumps run_on_app_dumps pass in a program jar.
309 cmd.append(determine_program_jar(args, dump))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100310 cmd.extend(['--output', out])
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200311 for feature_jar in dump.feature_jars():
312 cmd.extend(['--feature-jar', feature_jar,
313 determine_feature_output(feature_jar, temp)])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100314 if dump.library_jar():
315 cmd.extend(['--lib', dump.library_jar()])
316 if dump.classpath_jar():
317 cmd.extend(['--classpath', dump.classpath_jar()])
Morten Krogh-Jespersend86a81b2020-11-13 12:33:26 +0100318 if dump.desugared_library_json() and not args.disable_desugared_lib:
Clément Béradaae4ca2020-10-27 14:26:41 +0000319 cmd.extend(['--desugared-lib', dump.desugared_library_json()])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100320 if compiler != 'd8' and dump.config_file():
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100321 if hasattr(args, 'config_file_consumer') and args.config_file_consumer:
322 args.config_file_consumer(dump.config_file())
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100323 cmd.extend(['--pg-conf', dump.config_file()])
Morten Krogh-Jespersen1e774252021-03-01 16:56:07 +0100324 if dump.main_dex_rules_resource():
325 cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()])
Ian Zerny588120b2020-04-03 13:08:03 +0200326 if compiler != 'd8':
327 cmd.extend(['--pg-map-output', '%s.map' % out])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200328 if min_api:
329 cmd.extend(['--min-api', min_api])
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100330 if classfile:
331 cmd.extend(['--classfile'])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200332 if args.threads:
333 cmd.extend(['--threads', args.threads])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100334 cmd.extend(otherargs)
335 utils.PrintCmd(cmd)
336 try:
Rico Wind3d369b42021-01-12 10:26:24 +0100337 print(subprocess.check_output(cmd, stderr=subprocess.STDOUT))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100338 return 0
Rico Wind3d369b42021-01-12 10:26:24 +0100339 except subprocess.CalledProcessError as e:
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100340 if not args.nolib and version != 'source':
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100341 stacktrace = os.path.join(temp, 'stacktrace')
Morten Krogh-Jespersen86222742021-03-02 11:13:33 +0100342 open(stacktrace, 'w+').write(e.output.decode('UTF-8'))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100343 local_map = utils.R8LIB_MAP if version == 'master' else None
344 hash_or_version = None if version == 'master' else version
Rico Wind3d369b42021-01-12 10:26:24 +0100345 print("=" * 80)
346 print(" RETRACED OUTPUT")
347 print("=" * 80)
Morten Krogh-Jespersen64c52722020-10-27 08:33:42 +0100348 retrace.run(
349 local_map, hash_or_version, stacktrace, is_hash(version), no_r8lib=False)
Morten Krogh-Jespersen86222742021-03-02 11:13:33 +0100350 else:
351 print(e.output.decode('UTF-8'))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100352 return 1
353
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200354def run(args, otherargs):
355 if (args.loop):
356 count = 1
357 while True:
358 print('Iteration {:03d}'.format(count))
359 out = args.temp
360 if out:
361 out = os.path.join(out, '{:03d}'.format(count))
362 run1(out, args, otherargs)
363 count += 1
364 else:
365 run1(args.temp, args, otherargs)
366
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100367if __name__ == '__main__':
368 (args, otherargs) = make_parser().parse_known_args(sys.argv[1:])
369 sys.exit(run(args, otherargs))