Add command line utility for compile dump files.
Change-Id: Iace6b27561a01e5e55386a346bc0284ff9471fb6
diff --git a/tools/compiledump.py b/tools/compiledump.py
new file mode 100755
index 0000000..6f78eef
--- /dev/null
+++ b/tools/compiledump.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# Copyright (c) 2020, 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.
+
+import argparse
+import os
+import subprocess
+import sys
+import zipfile
+
+import archive
+import jdk
+import retrace
+import utils
+
+
+def make_parser():
+ parser = argparse.ArgumentParser(description = 'Compile a dump artifact.')
+ parser.add_argument(
+ '-d',
+ '--dump',
+ help='Dump file to compile',
+ default=None)
+ parser.add_argument(
+ '-c',
+ '--compiler',
+ help='Compiler to use (default read from version file)',
+ default=None)
+ parser.add_argument(
+ '-v',
+ '--version',
+ help='Compiler version to use (default read from dump version file).'
+ 'Valid arguments are:'
+ ' "master" to run from your own tree,'
+ ' "X.Y.Z" to run a specific version, or'
+ ' <hash> to run that hash from master.',
+ default=None)
+ parser.add_argument(
+ '--nolib',
+ help='Use the non-lib distribution (default uses the lib distribution)',
+ default=False,
+ action='store_true')
+ parser.add_argument(
+ '--ea',
+ help='Enable Java assertions when running the compiler (default disabled)',
+ default=False,
+ action='store_true')
+ parser.add_argument(
+ '--debug-agent',
+ help='Enable Java debug agent and suspend compilation (default disabled)',
+ default=False,
+ action='store_true')
+ return parser
+
+def error(msg):
+ print msg
+ sys.exit(1)
+
+class Dump(object):
+
+ def __init__(self, directory):
+ self.directory = directory
+
+ def if_exists(self, name):
+ f = os.path.join(self.directory, name)
+ if os.path.exists(f):
+ return f
+ return None
+
+ def program_jar(self):
+ return self.if_exists('program.jar')
+
+ def library_jar(self):
+ return self.if_exists('library.jar')
+
+ def classpath_jar(self):
+ return self.if_exists('classpath.jar')
+
+ def config_file(self):
+ return self.if_exists('proguard.config')
+
+ def version_file(self):
+ return self.if_exists('r8-version')
+
+ def version(self):
+ f = self.version_file()
+ if f:
+ return open(f).read().split(' ')[0]
+ return None
+
+def read_dump(args, temp):
+ if args.dump is None:
+ error("A dump file must be specified")
+ dump_file = zipfile.ZipFile(args.dump, 'r')
+ with utils.ChangedWorkingDirectory(temp):
+ dump_file.extractall()
+ return Dump(temp)
+
+def determine_version(args, dump):
+ if args.version is None:
+ return dump.version()
+ return args.version
+
+def determine_compiler(args, dump):
+ compilers = ('d8', 'r8', 'r8full')
+ if args.compiler not in compilers:
+ error("Unable to determine a compiler to use. Valid options: %s" % compilers.join(', '))
+ return args.compiler
+
+def determine_output(args, temp):
+ return os.path.join(temp, 'out.jar')
+
+def download_distribution(args, version, temp):
+ if version == 'master':
+ return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
+ name = 'r8.jar' if args.nolib else 'r8lib.jar'
+ source = archive.GetUploadDestination(version, name, is_hash(version))
+ dest = os.path.join(temp, 'r8.jar')
+ utils.download_file_from_cloud_storage(source, dest)
+ return dest
+
+def prepare_wrapper(dist, temp):
+ wrapper_file = os.path.join(
+ utils.REPO_ROOT,
+ 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
+ subprocess.check_output([
+ jdk.GetJavacExecutable(),
+ wrapper_file,
+ '-d', temp,
+ '-cp', dist,
+ ])
+ return temp
+
+def is_hash(version):
+ return len(version) == 40
+
+def run(args, otherargs):
+ with utils.TempDir() as temp:
+ dump = read_dump(args, temp)
+ version = determine_version(args, dump)
+ compiler = determine_compiler(args, dump)
+ out = determine_output(args, temp)
+ jar = download_distribution(args, version, temp)
+ wrapper_dir = prepare_wrapper(jar, temp)
+ cmd = [jdk.GetJavaExecutable()]
+ if args.debug_agent:
+ if not args.nolib:
+ print "WARNING: Running debugging agent on r8lib is questionable..."
+ cmd.append(
+ '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
+ if args.ea:
+ cmd.append('-ea')
+ cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
+ if compiler == 'd8':
+ cmd.append('com.android.tools.r8.D8')
+ if compiler.startswith('r8'):
+ cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8')
+ if compiler == 'r8':
+ cmd.append('--compat')
+ cmd.append(dump.program_jar())
+ cmd.extend(['--output', out])
+ if dump.library_jar():
+ cmd.extend(['--lib', dump.library_jar()])
+ if dump.classpath_jar():
+ cmd.extend(['--classpath', dump.classpath_jar()])
+ if compiler != 'd8' and dump.config_file():
+ cmd.extend(['--pg-conf', dump.config_file()])
+ cmd.extend(otherargs)
+ utils.PrintCmd(cmd)
+ try:
+ print subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ return 0
+ except subprocess.CalledProcessError, e:
+ print e.output
+ if not args.nolib:
+ stacktrace = os.path.join(temp, 'stacktrace')
+ open(stacktrace, 'w+').write(e.output)
+ local_map = utils.R8LIB_MAP if version == 'master' else None
+ hash_or_version = None if version == 'master' else version
+ print "=" * 80
+ print " RETRACED OUTPUT"
+ print "=" * 80
+ retrace.run(local_map, hash_or_version, stacktrace, is_hash(version))
+ return 1
+
+if __name__ == '__main__':
+ (args, otherargs) = make_parser().parse_known_args(sys.argv[1:])
+ sys.exit(run(args, otherargs))