blob: 48c596b916cfc976175f31c90cb1742fd03b9dc1 [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,'
41 ' "X.Y.Z" to run a specific version, or'
42 ' <hash> to run that hash from master.',
43 default=None)
44 parser.add_argument(
Morten Krogh-Jespersend8bceb52020-03-06 12:56:48 +010045 '--r8-jar',
46 help='Path to an R8 jar.',
47 default=None)
48 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010049 '--nolib',
50 help='Use the non-lib distribution (default uses the lib distribution)',
51 default=False,
52 action='store_true')
53 parser.add_argument(
Rico Wind28653642020-03-31 13:59:07 +020054 '--printtimes',
55 help='Print timing information from r8',
56 default=False,
57 action='store_true')
58 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010059 '--ea',
60 help='Enable Java assertions when running the compiler (default disabled)',
61 default=False,
62 action='store_true')
63 parser.add_argument(
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +010064 '--classfile',
65 help='Run with classfile output',
66 default=False,
67 action='store_true')
68 parser.add_argument(
Ian Zerny5ffa58f2020-02-26 08:37:14 +010069 '--debug-agent',
70 help='Enable Java debug agent and suspend compilation (default disabled)',
71 default=False,
72 action='store_true')
Ian Zerny77bdf5f2020-07-08 11:46:24 +020073 parser.add_argument(
74 '--xmx',
75 help='Set JVM max heap size (-Xmx)',
76 default=None)
77 parser.add_argument(
78 '--threads',
79 help='Set the number of threads to use',
80 default=None)
81 parser.add_argument(
82 '--min-api',
83 help='Set min-api (default read from dump properties file)',
84 default=None)
Søren Gjesse7360f2b2020-08-10 09:13:35 +020085 parser.add_argument(
Clément Béradaae4ca2020-10-27 14:26:41 +000086 '--desugared-lib',
87 help='Set desugared-library (default set from dump)',
88 default=None)
89 parser.add_argument(
Søren Gjesse7360f2b2020-08-10 09:13:35 +020090 '--loop',
91 help='Run the compilation in a loop',
92 default=False,
93 action='store_true')
Ian Zerny5ffa58f2020-02-26 08:37:14 +010094 return parser
95
96def error(msg):
97 print msg
98 sys.exit(1)
99
100class Dump(object):
101
102 def __init__(self, directory):
103 self.directory = directory
104
105 def if_exists(self, name):
106 f = os.path.join(self.directory, name)
107 if os.path.exists(f):
108 return f
109 return None
110
111 def program_jar(self):
112 return self.if_exists('program.jar')
113
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200114 def feature_jars(self):
115 feature_jars = []
116 i = 1
117 while True:
118 feature_jar = self.if_exists('feature-%s.jar' % i)
119 if feature_jar:
120 feature_jars.append(feature_jar)
121 i = i + 1
122 else:
123 return feature_jars
124
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100125 def library_jar(self):
126 return self.if_exists('library.jar')
127
128 def classpath_jar(self):
129 return self.if_exists('classpath.jar')
130
Clément Béradaae4ca2020-10-27 14:26:41 +0000131 def desugared_library_json(self):
132 return self.if_exists('desugared-library.json')
133
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200134 def build_properties_file(self):
135 return self.if_exists('build.properties')
136
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100137 def config_file(self):
138 return self.if_exists('proguard.config')
139
140 def version_file(self):
141 return self.if_exists('r8-version')
142
143 def version(self):
144 f = self.version_file()
145 if f:
146 return open(f).read().split(' ')[0]
147 return None
148
149def read_dump(args, temp):
150 if args.dump is None:
Christoffer Quist Adamsend84a6f02020-05-16 12:29:35 +0200151 error("A dump file or directory must be specified")
152 if os.path.isdir(args.dump):
153 return Dump(args.dump)
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200154 dump_file = zipfile.ZipFile(os.path.abspath(args.dump), 'r')
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100155 with utils.ChangedWorkingDirectory(temp):
156 dump_file.extractall()
157 return Dump(temp)
158
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200159def determine_build_properties(args, dump):
160 build_properties = {}
161 build_properties_file = dump.build_properties_file()
162 if build_properties_file:
163 with open(build_properties_file) as f:
164 build_properties_contents = f.readlines()
165 for line in build_properties_contents:
166 stripped = line.strip()
167 if stripped:
168 pair = stripped.split('=')
169 build_properties[pair[0]] = pair[1]
170 return build_properties
171
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100172def determine_version(args, dump):
173 if args.version is None:
174 return dump.version()
175 return args.version
176
177def determine_compiler(args, dump):
Rico Windf73f18d2020-03-03 09:28:54 +0100178 compilers = ['d8', 'r8', 'r8full']
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100179 if args.compiler not in compilers:
Rico Windf73f18d2020-03-03 09:28:54 +0100180 error("Unable to determine a compiler to use. Specified %s,"
181 " Valid options: %s" % (args.compiler, ', '.join(compilers)))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100182 return args.compiler
183
184def determine_output(args, temp):
185 return os.path.join(temp, 'out.jar')
186
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200187def determine_min_api(args, build_properties):
188 if args.min_api:
189 return args.min_api
190 if 'min-api' in build_properties:
191 return build_properties.get('min-api')
192 return None
193
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200194def determine_feature_output(feature_jar, temp):
195 return os.path.join(temp, os.path.basename(feature_jar)[:-4] + ".out.jar")
196
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100197def determine_program_jar(args, dump):
198 if hasattr(args, 'program_jar') and args.program_jar:
199 return args.program_jar
200 return dump.program_jar()
201
202def determine_class_file(args, build_properties):
203 if args.classfile:
204 return args.classfile
205 if 'classfile' in build_properties:
206 return True
207 return None
208
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100209def download_distribution(args, version, temp):
210 if version == 'master':
211 return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
212 name = 'r8.jar' if args.nolib else 'r8lib.jar'
213 source = archive.GetUploadDestination(version, name, is_hash(version))
214 dest = os.path.join(temp, 'r8.jar')
215 utils.download_file_from_cloud_storage(source, dest)
216 return dest
217
218def prepare_wrapper(dist, temp):
219 wrapper_file = os.path.join(
220 utils.REPO_ROOT,
221 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
222 subprocess.check_output([
223 jdk.GetJavacExecutable(),
224 wrapper_file,
225 '-d', temp,
226 '-cp', dist,
227 ])
228 return temp
229
230def is_hash(version):
231 return len(version) == 40
232
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200233def run1(out, args, otherargs):
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100234 with utils.TempDir() as temp:
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200235 if out:
236 temp = out
Rico Wind9ed87b92020-03-06 12:37:18 +0100237 if not os.path.exists(temp):
238 os.makedirs(temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100239 dump = read_dump(args, temp)
Ian Zerny46bc4cf2020-08-03 15:58:27 +0200240 if not dump.program_jar():
241 error("Cannot compile dump with no program classes")
242 if not dump.library_jar():
243 print "WARNING: Unexpected lack of library classes in dump"
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200244 build_properties = determine_build_properties(args, dump)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100245 version = determine_version(args, dump)
246 compiler = determine_compiler(args, dump)
247 out = determine_output(args, temp)
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200248 min_api = determine_min_api(args, build_properties)
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100249 classfile = determine_class_file(args, build_properties)
Morten Krogh-Jespersend8bceb52020-03-06 12:56:48 +0100250 jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100251 wrapper_dir = prepare_wrapper(jar, temp)
252 cmd = [jdk.GetJavaExecutable()]
253 if args.debug_agent:
254 if not args.nolib:
255 print "WARNING: Running debugging agent on r8lib is questionable..."
256 cmd.append(
257 '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200258 if args.xmx:
259 cmd.append('-Xmx' + args.xmx)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100260 if args.ea:
261 cmd.append('-ea')
Rico Wind28653642020-03-31 13:59:07 +0200262 if args.printtimes:
263 cmd.append('-Dcom.android.tools.r8.printtimes=1')
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100264 cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
265 if compiler == 'd8':
266 cmd.append('com.android.tools.r8.D8')
267 if compiler.startswith('r8'):
268 cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8')
269 if compiler == 'r8':
270 cmd.append('--compat')
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100271 # For recompilation of dumps run_on_app_dumps pass in a program jar.
272 cmd.append(determine_program_jar(args, dump))
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100273 cmd.extend(['--output', out])
Christoffer Quist Adamsenfcfc63a2020-05-15 19:04:24 +0200274 for feature_jar in dump.feature_jars():
275 cmd.extend(['--feature-jar', feature_jar,
276 determine_feature_output(feature_jar, temp)])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100277 if dump.library_jar():
278 cmd.extend(['--lib', dump.library_jar()])
279 if dump.classpath_jar():
280 cmd.extend(['--classpath', dump.classpath_jar()])
Clément Béradaae4ca2020-10-27 14:26:41 +0000281 if dump.desugared_library_json():
282 cmd.extend(['--desugared-lib', dump.desugared_library_json()])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100283 if compiler != 'd8' and dump.config_file():
Morten Krogh-Jespersen51a16352020-11-04 09:31:15 +0100284 if hasattr(args, 'config_file_consumer') and args.config_file_consumer:
285 args.config_file_consumer(dump.config_file())
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100286 cmd.extend(['--pg-conf', dump.config_file()])
Ian Zerny588120b2020-04-03 13:08:03 +0200287 if compiler != 'd8':
288 cmd.extend(['--pg-map-output', '%s.map' % out])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200289 if min_api:
290 cmd.extend(['--min-api', min_api])
Morten Krogh-Jespersen45d7a7b2020-11-02 08:31:09 +0100291 if classfile:
292 cmd.extend(['--classfile'])
Ian Zerny77bdf5f2020-07-08 11:46:24 +0200293 if args.threads:
294 cmd.extend(['--threads', args.threads])
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100295 cmd.extend(otherargs)
296 utils.PrintCmd(cmd)
297 try:
298 print subprocess.check_output(cmd, stderr=subprocess.STDOUT)
299 return 0
300 except subprocess.CalledProcessError, e:
301 print e.output
302 if not args.nolib:
303 stacktrace = os.path.join(temp, 'stacktrace')
304 open(stacktrace, 'w+').write(e.output)
305 local_map = utils.R8LIB_MAP if version == 'master' else None
306 hash_or_version = None if version == 'master' else version
307 print "=" * 80
308 print " RETRACED OUTPUT"
309 print "=" * 80
Morten Krogh-Jespersen64c52722020-10-27 08:33:42 +0100310 retrace.run(
311 local_map, hash_or_version, stacktrace, is_hash(version), no_r8lib=False)
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100312 return 1
313
Søren Gjesse7360f2b2020-08-10 09:13:35 +0200314def run(args, otherargs):
315 if (args.loop):
316 count = 1
317 while True:
318 print('Iteration {:03d}'.format(count))
319 out = args.temp
320 if out:
321 out = os.path.join(out, '{:03d}'.format(count))
322 run1(out, args, otherargs)
323 count += 1
324 else:
325 run1(args.temp, args, otherargs)
326
Ian Zerny5ffa58f2020-02-26 08:37:14 +0100327if __name__ == '__main__':
328 (args, otherargs) = make_parser().parse_known_args(sys.argv[1:])
329 sys.exit(run(args, otherargs))