| #!/usr/bin/env python3 | 
 | # Copyright (c) 2018, 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. | 
 |  | 
 | ''' | 
 | Run R8 (with the class-file backend) to optimize a command-line program. | 
 |  | 
 | Given an input JAR (default: r8.jar) and a main-class, generates a new input JAR | 
 | with the given main-class in the manifest along with a -keep rule for keeping | 
 | just the main entrypoint, and runs R8 in release+classfile mode on the JAR. | 
 |  | 
 | If --benchmark-name NAME is given, prints "<NAME>(RunTimeRaw): <elapsed> ms" | 
 | to standard output at the end of the run. | 
 | ''' | 
 |  | 
 | import argparse | 
 | import os | 
 | import re | 
 | import sys | 
 | import time | 
 | import zipfile | 
 |  | 
 | import toolhelper | 
 | import utils | 
 |  | 
 | KEEP = '-keep public class %s { public static void main(...); }\n' | 
 | MANIFEST_PATH = 'META-INF/MANIFEST.MF' | 
 | MANIFEST = 'Manifest-Version: 1.0\nMain-Class: %s\n\n' | 
 | MANIFEST_PATTERN = r'Main-Class:\s*(\S+)' | 
 |  | 
 | parser = argparse.ArgumentParser(description=__doc__.strip(), | 
 |                                  formatter_class=argparse.RawTextHelpFormatter) | 
 | parser.add_argument( | 
 |     '-i', '--input-jar', default=utils.R8_JAR, | 
 |     help='Input JAR to use (default: build/libs/r8.jar)') | 
 | parser.add_argument( | 
 |     '-o', '--output-jar', | 
 |     help='Path to output JAR (default: build/libs/<MainClass>-min.jar)') | 
 | parser.add_argument( | 
 |     '-l', '--lib', default=utils.RT_JAR, | 
 |     help='Path to rt.jar to use instead of OpenJDK 1.8') | 
 | parser.add_argument( | 
 |     '-m', '--mainclass', | 
 |     help='Create/overwrite MANIFEST.MF with the given Main-Class') | 
 | parser.add_argument( | 
 |     '-O', '--no-debug', dest='debug', action='store_false', | 
 |     help='Disable assertions when running R8') | 
 | parser.add_argument( | 
 |     '--benchmark-name', | 
 |     help='Print benchmarks with the given name') | 
 |  | 
 | def generate_output_name(input_jar, mainclass): | 
 |   if not mainclass: | 
 |     input_base, input_ext = os.path.splitext(input_jar) | 
 |     return '%s-min%s' % (input_base, input_ext) | 
 |   base = mainclass[mainclass.rindex('.')+1:] if '.' in mainclass else mainclass | 
 |   return os.path.join(utils.LIBS, '%s-min.jar' % base) | 
 |  | 
 | def repackage(input_jar, output_jar, mainclass): | 
 |   print("Repackaging %s to %s with Main-Class: %s..." % | 
 |         (input_jar, output_jar, mainclass)) | 
 |   manifest = MANIFEST % mainclass | 
 |   with zipfile.ZipFile(input_jar, 'r') as input_zf: | 
 |     with zipfile.ZipFile(output_jar, 'w') as output_zf: | 
 |       for zipinfo in input_zf.infolist(): | 
 |         if zipinfo.filename.upper() == MANIFEST_PATH: | 
 |           assert manifest is not None | 
 |           output_zf.writestr(MANIFEST_PATH, manifest) | 
 |           manifest = None | 
 |         else: | 
 |           output_zf.writestr(zipinfo, input_zf.read(zipinfo)) | 
 |       if manifest is not None: | 
 |         output_zf.writestr(MANIFEST_PATH, manifest) | 
 |  | 
 | def extract_mainclass(input_jar): | 
 |   with zipfile.ZipFile(input_jar, 'r') as input_zf: | 
 |     try: | 
 |       manifest = input_zf.getinfo(MANIFEST_PATH) | 
 |     except KeyError: | 
 |       raise SystemExit('No --mainclass specified and no manifest in input JAR.') | 
 |     mo = re.search(MANIFEST_PATTERN, input_zf.read(manifest)) | 
 |     if not mo: | 
 |       raise SystemExit( | 
 |           'No --mainclass specified and no Main-Class in input JAR manifest.') | 
 |     return mo.group(1) | 
 |  | 
 | def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, | 
 |                 lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None, | 
 |                 track_memory_file=None, additional_args=[], java_args=[]): | 
 |   if output_jar is None: | 
 |     output_jar = generate_output_name(input_jar, mainclass) | 
 |   with utils.TempDir() as path: | 
 |     if mainclass: | 
 |       tmp_input_path = os.path.join(path, 'input.jar') | 
 |       repackage(input_jar, tmp_input_path, mainclass) | 
 |     else: | 
 |       tmp_input_path = input_jar | 
 |       mainclass = extract_mainclass(input_jar) | 
 |     keep_path = os.path.join(path, 'keep.txt') | 
 |     with open(keep_path, 'w') as fp: | 
 |       fp.write(KEEP % mainclass) | 
 |     args = ['--lib', lib, | 
 |             '--classfile', | 
 |             '--output', output_jar, | 
 |             '--pg-conf', keep_path, | 
 |             '--release', | 
 |             tmp_input_path] + additional_args | 
 |     start_time = time.time() | 
 |     return_code = toolhelper.run('r8', args, debug=debug, build=build, | 
 |                                  track_memory_file=track_memory_file, | 
 |                                  extra_args=java_args) | 
 |     if benchmark_name: | 
 |       elapsed_ms = 1000 * (time.time() - start_time) | 
 |       print('%s(RunTimeRaw): %s ms' % (benchmark_name, elapsed_ms)) | 
 |       if track_memory_file: | 
 |         print('%s(MemoryUse): %s' % | 
 |               (benchmark_name, utils.grep_memoryuse(track_memory_file))) | 
 |  | 
 |     return return_code | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(minify_tool(**vars(parser.parse_args()))) |