blob: cc71d22e2cfc411d302f1019f99fb44e3db4f8a0 [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:'
Rico Wind1b52acf2021-03-21 12:36:55 +010040 ' "main" 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'
Rico Wind1b52acf2021-03-21 12:36:55 +010043 ' <hash> to run that hash from main.',
Ian Zerny5ffa58f2020-02-26 08:37:14 +010044 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):
Ian Zerny202330b2021-05-03 13:23:04 +0200179 if args.override or not os.path.isfile('r8-version'):
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +0100180 print("Extracting into: %s" % temp)
181 dump_file.extractall()
Ian Zerny202330b2021-05-03 13:23:04 +0200182 if not os.path.isfile('r8-version'):
Morten Krogh-Jespersen9206a662020-11-23 08:47:06 +0100183 error("Did not extract into %s. Either the zip file is invalid or the "
184 "dump is missing files" % temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100185 return Dump(temp)
186
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200187def determine_build_properties(args, dump):
188 build_properties = {}
189 build_properties_file = dump.build_properties_file()
190 if build_properties_file:
191 with open(build_properties_file) as f:
192 build_properties_contents = f.readlines()
193 for line in build_properties_contents:
194 stripped = line.strip()
195 if stripped:
196 pair = stripped.split('=')
197 build_properties[pair[0]] = pair[1]
198 return build_properties
199
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100200def determine_version(args, dump):
201 if args.version is None:
202 return dump.version()
203 return args.version
204
205def determine_compiler(args, dump):
Morten Krogh-Jespersenbb624e42021-04-19 14:33:48 +0200206 compilers = ['d8', 'r8', 'r8full', 'l8']
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100207 if args.compiler not in compilers:
Rico Windf73f18d2020-03-03 09:28:54 +0100208 error("Unable to determine a compiler to use. Specified %s,"
209 " Valid options: %s" % (args.compiler, ', '.join(compilers)))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100210 return args.compiler
211
212def determine_output(args, temp):
213 return os.path.join(temp, 'out.jar')
214
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200215def determine_min_api(args, build_properties):
216 if args.min_api:
217 return args.min_api
218 if 'min-api' in build_properties:
219 return build_properties.get('min-api')
220 return None
221
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200222def determine_feature_output(feature_jar, temp):
223 return os.path.join(temp, os.path.basename(feature_jar)[:-4] + ".out.jar")
224
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100225def determine_program_jar(args, dump):
226 if hasattr(args, 'program_jar') and args.program_jar:
227 return args.program_jar
228 return dump.program_jar()
229
230def determine_class_file(args, build_properties):
231 if args.classfile:
232 return args.classfile
233 if 'classfile' in build_properties:
234 return True
235 return None
236
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100237def download_distribution(args, version, temp):
Rico Wind1b52acf2021-03-21 12:36:55 +0100238 if version == 'main':
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100239 return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
Morten Krogh-Jespersen51db2b02020-11-11 12:49:26 +0100240 if version == 'source':
241 return '%s:%s' % (utils.BUILD_JAVA_MAIN_DIR, utils.ALL_DEPS_JAR)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100242 name = 'r8.jar' if args.nolib else 'r8lib.jar'
243 source = archive.GetUploadDestination(version, name, is_hash(version))
244 dest = os.path.join(temp, 'r8.jar')
245 utils.download_file_from_cloud_storage(source, dest)
246 return dest
247
Rico Wind33936d12021-04-07 08:04:14 +0200248def prepare_wrapper(dist, temp, jdkhome):
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100249 wrapper_file = os.path.join(
250 utils.REPO_ROOT,
251 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
Ian Zerny268c9632020-11-13 11:36:35 +0100252 cmd = [
Rico Wind33936d12021-04-07 08:04:14 +0200253 jdk.GetJavacExecutable(jdkhome),
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100254 wrapper_file,
255 '-d', temp,
256 '-cp', dist,
Ian Zerny268c9632020-11-13 11:36:35 +0100257 ]
258 utils.PrintCmd(cmd)
259 subprocess.check_output(cmd)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100260 return temp
261
262def is_hash(version):
263 return len(version) == 40
264
Rico Wind33936d12021-04-07 08:04:14 +0200265def run1(out, args, otherargs, jdkhome=None):
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100266 with utils.TempDir() as temp:
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200267 if out:
268 temp = out
Rico Wind9ed87b92020-03-06 12:37:18 +0100269 if not os.path.exists(temp):
270 os.makedirs(temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100271 dump = read_dump(args, temp)
Ian Zerny46bc4cf2020-08-03 15:58:27 +0200272 if not dump.program_jar():
273 error("Cannot compile dump with no program classes")
274 if not dump.library_jar():
Rico Wind3d369b42021-01-12 10:26:24 +0100275 print("WARNING: Unexpected lack of library classes in dump")
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200276 build_properties = determine_build_properties(args, dump)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100277 version = determine_version(args, dump)
278 compiler = determine_compiler(args, dump)
279 out = determine_output(args, temp)
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200280 min_api = determine_min_api(args, build_properties)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100281 classfile = determine_class_file(args, build_properties)
Morten Krogh-Jespersend8bceb52020-03-06 12:56:48 +0100282 jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
Ian Zerny268c9632020-11-13 11:36:35 +0100283 if ':' not in jar and not os.path.exists(jar):
284 error("Distribution does not exist: " + jar)
Rico Wind33936d12021-04-07 08:04:14 +0200285 wrapper_dir = prepare_wrapper(jar, temp, jdkhome)
286 cmd = [jdk.GetJavaExecutable(jdkhome)]
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100287 if args.debug_agent:
288 if not args.nolib:
Rico Wind3d369b42021-01-12 10:26:24 +0100289 print("WARNING: Running debugging agent on r8lib is questionable...")
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100290 cmd.append(
291 '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200292 if args.xmx:
293 cmd.append('-Xmx' + args.xmx)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100294 if args.ea:
295 cmd.append('-ea')
Rico Wind28653642020-03-31 13:59:07 +0200296 if args.printtimes:
297 cmd.append('-Dcom.android.tools.r8.printtimes=1')
Morten Krogh-Jespersen43f3cea2020-11-12 17:09:51 +0100298 if hasattr(args, 'properties'):
299 cmd.extend(args.properties);
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100300 cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
301 if compiler == 'd8':
302 cmd.append('com.android.tools.r8.D8')
Morten Krogh-Jespersenbb624e42021-04-19 14:33:48 +0200303 if compiler == 'l8':
304 cmd.append('com.android.tools.r8.L8')
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100305 if compiler.startswith('r8'):
306 cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8')
307 if compiler == 'r8':
308 cmd.append('--compat')
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100309 # For recompilation of dumps run_on_app_dumps pass in a program jar.
310 cmd.append(determine_program_jar(args, dump))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100311 cmd.extend(['--output', out])
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200312 for feature_jar in dump.feature_jars():
313 cmd.extend(['--feature-jar', feature_jar,
314 determine_feature_output(feature_jar, temp)])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100315 if dump.library_jar():
316 cmd.extend(['--lib', dump.library_jar()])
Morten Krogh-Jespersenbb624e42021-04-19 14:33:48 +0200317 if dump.classpath_jar() and compiler != 'l8':
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100318 cmd.extend(['--classpath', dump.classpath_jar()])
Morten Krogh-Jespersend86a81b2020-11-13 12:33:26 +0100319 if dump.desugared_library_json() and not args.disable_desugared_lib:
Clément Béradaae4ca2020-10-27 14:26:41 +0000320 cmd.extend(['--desugared-lib', dump.desugared_library_json()])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100321 if compiler != 'd8' and dump.config_file():
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100322 if hasattr(args, 'config_file_consumer') and args.config_file_consumer:
323 args.config_file_consumer(dump.config_file())
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100324 cmd.extend(['--pg-conf', dump.config_file()])
Morten Krogh-Jespersen1e774252021-03-01 16:56:07 +0100325 if dump.main_dex_rules_resource():
326 cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()])
Morten Krogh-Jespersenbb624e42021-04-19 14:33:48 +0200327 if compiler == 'l8':
328 if dump.config_file():
329 cmd.extend(['--pg-map-output', '%s.map' % out])
330 elif compiler != 'd8':
Ian Zerny588120b2020-04-03 13:08:03 +0200331 cmd.extend(['--pg-map-output', '%s.map' % out])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200332 if min_api:
333 cmd.extend(['--min-api', min_api])
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100334 if classfile:
335 cmd.extend(['--classfile'])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200336 if args.threads:
337 cmd.extend(['--threads', args.threads])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100338 cmd.extend(otherargs)
339 utils.PrintCmd(cmd)
340 try:
Rico Wind3d369b42021-01-12 10:26:24 +0100341 print(subprocess.check_output(cmd, stderr=subprocess.STDOUT))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100342 return 0
Rico Wind3d369b42021-01-12 10:26:24 +0100343 except subprocess.CalledProcessError as e:
Ian Zerny9ff31b72021-04-22 11:36:26 +0200344 if args.nolib \
345 or version == 'source' \
346 or not try_retrace_output(e, version, temp):
Morten Krogh-Jespersen86222742021-03-02 11:13:33 +0100347 print(e.output.decode('UTF-8'))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100348 return 1
349
Ian Zerny9ff31b72021-04-22 11:36:26 +0200350def try_retrace_output(e, version, temp):
351 try:
352 stacktrace = os.path.join(temp, 'stacktrace')
353 open(stacktrace, 'w+').write(e.output.decode('UTF-8'))
354 print("=" * 80)
355 print(" RETRACED OUTPUT")
356 print("=" * 80)
357 retrace.run(get_map_file(version, temp), stacktrace, no_r8lib=False)
358 return True
359 except Exception as e2:
360 print("Failed to retrace for version: %s" % version)
361 print(e2)
362 return False
363
364def get_map_file(version, temp):
365 if version == 'main':
366 return utils.R8LIB_MAP
367 download_path = archive.GetUploadDestination(
368 version,
369 'r8lib.jar.map',
370 is_hash(version))
371 if utils.file_exists_on_cloud_storage(download_path):
372 map_path = os.path.join(temp, 'mapping.map')
373 utils.download_file_from_cloud_storage(download_path, map_path)
374 return map_path
375 else:
376 print('Could not find map file from argument: %s.' % version)
377 return None
378
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200379def run(args, otherargs):
380 if (args.loop):
381 count = 1
382 while True:
383 print('Iteration {:03d}'.format(count))
384 out = args.temp
385 if out:
386 out = os.path.join(out, '{:03d}'.format(count))
387 run1(out, args, otherargs)
388 count += 1
389 else:
390 run1(args.temp, args, otherargs)
391
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100392if __name__ == '__main__':
393 (args, otherargs) = make_parser().parse_known_args(sys.argv[1:])
394 sys.exit(run(args, otherargs))