1#!/usr/bin/env python
2#
3# Copyright 2013 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import argparse
8from collections import defaultdict
9import logging
10import os
11import re
12import shutil
13import sys
14import tempfile
15import zipfile
16
17import dex
18import dex_jdk_libs
19from util import build_utils
20from util import diff_utils
21
22_API_LEVEL_VERSION_CODE = [
23    (21, 'L'),
24    (22, 'LolliopoMR1'),
25    (23, 'M'),
26    (24, 'N'),
27    (25, 'NMR1'),
28    (26, 'O'),
29    (27, 'OMR1'),
30    (28, 'P'),
31    (29, 'Q'),
32    (30, 'R'),
33]
34
35
36def _ParseOptions():
37  args = build_utils.ExpandFileArgs(sys.argv[1:])
38  parser = argparse.ArgumentParser()
39  build_utils.AddDepfileOption(parser)
40  parser.add_argument('--r8-path',
41                      required=True,
42                      help='Path to the R8.jar to use.')
43  parser.add_argument(
44      '--desugar-jdk-libs-json', help='Path to desugar_jdk_libs.json.')
45  parser.add_argument('--input-paths',
46                      action='append',
47                      required=True,
48                      help='GN-list of .jar files to optimize.')
49  parser.add_argument('--desugar-jdk-libs-jar',
50                      help='Path to desugar_jdk_libs.jar.')
51  parser.add_argument('--desugar-jdk-libs-configuration-jar',
52                      help='Path to desugar_jdk_libs_configuration.jar.')
53  parser.add_argument('--output-path', help='Path to the generated .jar file.')
54  parser.add_argument(
55      '--proguard-configs',
56      action='append',
57      required=True,
58      help='GN-list of configuration files.')
59  parser.add_argument(
60      '--apply-mapping', help='Path to ProGuard mapping to apply.')
61  parser.add_argument(
62      '--mapping-output',
63      required=True,
64      help='Path for ProGuard to output mapping file to.')
65  parser.add_argument(
66      '--extra-mapping-output-paths',
67      help='GN-list of additional paths to copy output mapping file to.')
68  parser.add_argument(
69      '--classpath',
70      action='append',
71      help='GN-list of .jar files to include as libraries.')
72  parser.add_argument(
73      '--main-dex-rules-path',
74      action='append',
75      help='Path to main dex rules for multidex'
76      '- only works with R8.')
77  parser.add_argument(
78      '--min-api', help='Minimum Android API level compatibility.')
79  parser.add_argument(
80      '--verbose', '-v', action='store_true', help='Print all ProGuard output')
81  parser.add_argument(
82      '--repackage-classes', help='Package all optimized classes are put in.')
83  parser.add_argument(
84      '--disable-outlining',
85      action='store_true',
86      help='Disable the outlining optimization provided by R8.')
87  parser.add_argument(
88    '--disable-checks',
89    action='store_true',
90    help='Disable -checkdiscard directives and missing symbols check')
91  parser.add_argument('--sourcefile', help='Value for source file attribute')
92  parser.add_argument(
93      '--force-enable-assertions',
94      action='store_true',
95      help='Forcefully enable javac generated assertion code.')
96  parser.add_argument(
97      '--feature-jars',
98      action='append',
99      help='GN list of path to jars which comprise the corresponding feature.')
100  parser.add_argument(
101      '--dex-dest',
102      action='append',
103      dest='dex_dests',
104      help='Destination for dex file of the corresponding feature.')
105  parser.add_argument(
106      '--feature-name',
107      action='append',
108      dest='feature_names',
109      help='The name of the feature module.')
110  parser.add_argument(
111      '--uses-split',
112      action='append',
113      help='List of name pairs separated by : mapping a feature module to a '
114      'dependent feature module.')
115  parser.add_argument('--warnings-as-errors',
116                      action='store_true',
117                      help='Treat all warnings as errors.')
118  parser.add_argument('--show-desugar-default-interface-warnings',
119                      action='store_true',
120                      help='Enable desugaring warnings.')
121  parser.add_argument(
122      '--stamp',
123      help='File to touch upon success. Mutually exclusive with --output-path')
124  parser.add_argument('--desugared-library-keep-rule-output',
125                      help='Path to desugared library keep rule output file.')
126
127  diff_utils.AddCommandLineFlags(parser)
128  options = parser.parse_args(args)
129
130  if options.feature_names:
131    if options.output_path:
132      parser.error('Feature splits cannot specify an output in GN.')
133    if not options.actual_file and not options.stamp:
134      parser.error('Feature splits require a stamp file as output.')
135  elif not options.output_path:
136    parser.error('Output path required when feature splits aren\'t used')
137
138  options.classpath = build_utils.ParseGnList(options.classpath)
139  options.proguard_configs = build_utils.ParseGnList(options.proguard_configs)
140  options.input_paths = build_utils.ParseGnList(options.input_paths)
141  options.extra_mapping_output_paths = build_utils.ParseGnList(
142      options.extra_mapping_output_paths)
143
144  if options.feature_names:
145    if 'base' not in options.feature_names:
146      parser.error('"base" feature required when feature arguments are used.')
147    if len(options.feature_names) != len(options.feature_jars) or len(
148        options.feature_names) != len(options.dex_dests):
149      parser.error('Invalid feature argument lengths.')
150
151    options.feature_jars = [
152        build_utils.ParseGnList(x) for x in options.feature_jars
153    ]
154
155  split_map = {}
156  if options.uses_split:
157    for split_pair in options.uses_split:
158      child, parent = split_pair.split(':')
159      for name in (child, parent):
160        if name not in options.feature_names:
161          parser.error('"%s" referenced in --uses-split not present.' % name)
162      split_map[child] = parent
163  options.uses_split = split_map
164
165  return options
166
167
168class _DexPathContext(object):
169  def __init__(self, name, output_path, input_jars, work_dir):
170    self.name = name
171    self.input_paths = input_jars
172    self._final_output_path = output_path
173    self.staging_dir = os.path.join(work_dir, name)
174    os.mkdir(self.staging_dir)
175
176  def CreateOutput(self, has_imported_lib=False, keep_rule_output=None):
177    found_files = build_utils.FindInDirectory(self.staging_dir)
178    if not found_files:
179      raise Exception('Missing dex outputs in {}'.format(self.staging_dir))
180
181    if self._final_output_path.endswith('.dex'):
182      if has_imported_lib:
183        raise Exception(
184            'Trying to create a single .dex file, but a dependency requires '
185            'JDK Library Desugaring (which necessitates a second file).'
186            'Refer to %s to see what desugaring was required' %
187            keep_rule_output)
188      if len(found_files) != 1:
189        raise Exception('Expected exactly 1 dex file output, found: {}'.format(
190            '\t'.join(found_files)))
191      shutil.move(found_files[0], self._final_output_path)
192      return
193
194    # Add to .jar using Python rather than having R8 output to a .zip directly
195    # in order to disable compression of the .jar, saving ~500ms.
196    tmp_jar_output = self.staging_dir + '.jar'
197    build_utils.DoZip(found_files, tmp_jar_output, base_dir=self.staging_dir)
198    shutil.move(tmp_jar_output, self._final_output_path)
199
200
201def _OptimizeWithR8(options,
202                    config_paths,
203                    libraries,
204                    dynamic_config_data,
205                    print_stdout=False):
206  with build_utils.TempDir() as tmp_dir:
207    if dynamic_config_data:
208      tmp_config_path = os.path.join(tmp_dir, 'proguard_config.txt')
209      with open(tmp_config_path, 'w') as f:
210        f.write(dynamic_config_data)
211      config_paths = config_paths + [tmp_config_path]
212
213    tmp_mapping_path = os.path.join(tmp_dir, 'mapping.txt')
214    # If there is no output (no classes are kept), this prevents this script
215    # from failing.
216    build_utils.Touch(tmp_mapping_path)
217
218    tmp_output = os.path.join(tmp_dir, 'r8out')
219    os.mkdir(tmp_output)
220
221    feature_contexts = []
222    if options.feature_names:
223      for name, dest_dex, input_paths in zip(
224          options.feature_names, options.dex_dests, options.feature_jars):
225        feature_context = _DexPathContext(name, dest_dex, input_paths,
226                                          tmp_output)
227        if name == 'base':
228          base_dex_context = feature_context
229        else:
230          feature_contexts.append(feature_context)
231    else:
232      base_dex_context = _DexPathContext('base', options.output_path,
233                                         options.input_paths, tmp_output)
234
235    cmd = build_utils.JavaCmd(options.warnings_as_errors) + [
236        '-Dcom.android.tools.r8.allowTestProguardOptions=1',
237        '-Dcom.android.tools.r8.verticalClassMerging=1',
238    ]
239    if options.disable_outlining:
240      cmd += ['-Dcom.android.tools.r8.disableOutlining=1']
241    cmd += [
242        '-cp',
243        options.r8_path,
244        'com.android.tools.r8.R8',
245        '--no-data-resources',
246        '--output',
247        base_dex_context.staging_dir,
248        '--pg-map-output',
249        tmp_mapping_path,
250    ]
251
252    if options.disable_checks:
253      # Info level priority logs are not printed by default.
254      cmd += ['--map-diagnostics:CheckDiscardDiagnostic', 'error', 'info']
255
256    if options.desugar_jdk_libs_json:
257      cmd += [
258          '--desugared-lib',
259          options.desugar_jdk_libs_json,
260          '--desugared-lib-pg-conf-output',
261          options.desugared_library_keep_rule_output,
262      ]
263
264    if options.min_api:
265      cmd += ['--min-api', options.min_api]
266
267    if options.force_enable_assertions:
268      cmd += ['--force-enable-assertions']
269
270    for lib in libraries:
271      cmd += ['--lib', lib]
272
273    for config_file in config_paths:
274      cmd += ['--pg-conf', config_file]
275
276    if options.main_dex_rules_path:
277      for main_dex_rule in options.main_dex_rules_path:
278        cmd += ['--main-dex-rules', main_dex_rule]
279
280    base_jars = set(base_dex_context.input_paths)
281    input_path_map = defaultdict(set)
282    for feature in feature_contexts:
283      parent = options.uses_split.get(feature.name, feature.name)
284      input_path_map[parent].update(feature.input_paths)
285
286    # If a jar is present in multiple features, it should be moved to the base
287    # module.
288    all_feature_jars = set()
289    for input_paths in input_path_map.values():
290      base_jars.update(all_feature_jars.intersection(input_paths))
291      all_feature_jars.update(input_paths)
292
293    module_input_jars = base_jars.copy()
294    for feature in feature_contexts:
295      input_paths = input_path_map.get(feature.name)
296      # Input paths can be missing for a child feature present in the uses_split
297      # map. These features get their input paths added to the parent, and are
298      # split out later with DexSplitter.
299      if input_paths is None:
300        continue
301      feature_input_jars = [
302          p for p in input_paths if p not in module_input_jars
303      ]
304      module_input_jars.update(feature_input_jars)
305      for in_jar in feature_input_jars:
306        cmd += ['--feature', in_jar, feature.staging_dir]
307
308    cmd += sorted(base_jars)
309    # Add any extra input jars to the base module (e.g. desugar runtime).
310    extra_jars = set(options.input_paths) - module_input_jars
311    cmd += sorted(extra_jars)
312
313    try:
314      stderr_filter = dex.CreateStderrFilter(
315          options.show_desugar_default_interface_warnings)
316      logging.debug('Running R8')
317      build_utils.CheckOutput(cmd,
318                              print_stdout=print_stdout,
319                              stderr_filter=stderr_filter,
320                              fail_on_output=options.warnings_as_errors)
321    except build_utils.CalledProcessError as err:
322      debugging_link = ('\n\nR8 failed. Please see {}.'.format(
323          'https://chromium.googlesource.com/chromium/src/+/HEAD/build/'
324          'android/docs/java_optimization.md#Debugging-common-failures\n'))
325      raise build_utils.CalledProcessError(err.cwd, err.args,
326                                           err.output + debugging_link)
327
328    if options.uses_split:
329      _SplitChildFeatures(options, feature_contexts, tmp_dir, tmp_mapping_path,
330                          print_stdout)
331
332    base_has_imported_lib = False
333    if options.desugar_jdk_libs_json:
334      logging.debug('Running L8')
335      existing_files = build_utils.FindInDirectory(base_dex_context.staging_dir)
336      jdk_dex_output = os.path.join(base_dex_context.staging_dir,
337                                    'classes%d.dex' % (len(existing_files) + 1))
338      base_has_imported_lib = dex_jdk_libs.DexJdkLibJar(
339          options.r8_path, options.min_api, options.desugar_jdk_libs_json,
340          options.desugar_jdk_libs_jar,
341          options.desugar_jdk_libs_configuration_jar,
342          options.desugared_library_keep_rule_output, jdk_dex_output,
343          options.warnings_as_errors)
344
345    logging.debug('Collecting ouputs')
346    base_dex_context.CreateOutput(base_has_imported_lib,
347                                  options.desugared_library_keep_rule_output)
348    for feature in feature_contexts:
349      feature.CreateOutput()
350
351    with open(options.mapping_output, 'w') as out_file, \
352        open(tmp_mapping_path) as in_file:
353      # Mapping files generated by R8 include comments that may break
354      # some of our tooling so remove those (specifically: apkanalyzer).
355      out_file.writelines(l for l in in_file if not l.startswith('#'))
356
357
358def _CheckForMissingSymbols(r8_path, dex_files, classpath, warnings_as_errors):
359  cmd = build_utils.JavaCmd(warnings_as_errors) + [
360      '-cp', r8_path, 'com.android.tools.r8.tracereferences.TraceReferences',
361      '--map-diagnostics:MissingDefinitionsDiagnostic', 'error', 'warning',
362      '--check'
363  ]
364
365  for path in classpath:
366    cmd += ['--lib', path]
367  for path in dex_files:
368    cmd += ['--source', path]
369
370  def stderr_filter(stderr):
371    ignored_lines = [
372        # Summary contains warning count, which our filtering makes wrong.
373        'Warning: Tracereferences found',
374
375        # TODO(agrieve): Create interface jars for these missing classes rather
376        #     than allowlisting here.
377        'dalvik/system',
378        'libcore/io',
379        'sun/misc/Unsafe',
380
381        # Found in: com/facebook/fbui/textlayoutbuilder/StaticLayoutHelper
382        ('android/text/StaticLayout;<init>(Ljava/lang/CharSequence;IILandroid'
383         '/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/'
384         'TextDirectionHeuristic;FFZLandroid/text/TextUtils$TruncateAt;II)V'),
385
386        # Found in
387        # com/google/android/gms/cast/framework/media/internal/ResourceProvider
388        # Missing due to setting "strip_resources = true".
389        'com/google/android/gms/cast/framework/R',
390
391        # Found in com/google/android/gms/common/GoogleApiAvailability
392        # Missing due to setting "strip_drawables = true".
393        'com/google/android/gms/base/R$drawable',
394
395        # Explicictly guarded by try (NoClassDefFoundError) in Flogger's
396        # PlatformProvider.
397        'com/google/common/flogger/backend/google/GooglePlatform',
398        'com/google/common/flogger/backend/system/DefaultPlatform',
399
400        # trichrome_webview_google_bundle contains this missing reference.
401        # TODO(crbug.com/1142530): Fix this missing reference properly.
402        'org/chromium/base/library_loader/NativeLibraries',
403
404        # Currently required when enable_chrome_android_internal=true.
405        'com/google/protos/research/ink/InkEventProto',
406        'ink_sdk/com/google/protobuf/Internal$EnumVerifier',
407        'ink_sdk/com/google/protobuf/MessageLite',
408        'com/google/protobuf/GeneratedMessageLite$GeneratedExtension',
409
410        # Definition and usage in currently unused lens sdk in doubledown.
411        ('com/google/android/apps/gsa/search/shared/service/proto/'
412         'PublicStopClientEvent'),
413
414        # Referenced from GeneratedExtensionRegistryLite.
415        # Exists only for Chrome Modern (not Monochrome nor Trichrome).
416        # TODO(agrieve): Figure out why. Perhaps related to Feed V2.
417        ('com/google/wireless/android/play/playlog/proto/ClientAnalytics$'
418         'ClientInfo'),
419
420        # TODO(agrieve): Exclude these only when use_jacoco_coverage=true.
421        'Ljava/lang/instrument/ClassFileTransformer',
422        'Ljava/lang/instrument/IllegalClassFormatException',
423        'Ljava/lang/instrument/Instrumentation',
424        'Ljava/lang/management/ManagementFactory',
425        'Ljavax/management/MBeanServer',
426        'Ljavax/management/ObjectInstance',
427        'Ljavax/management/ObjectName',
428        'Ljavax/management/StandardMBean',
429    ]
430
431    had_unfiltered_items = '  ' in stderr
432    stderr = build_utils.FilterLines(
433        stderr, '|'.join(re.escape(x) for x in ignored_lines))
434    if stderr:
435      if '  ' in stderr:
436        stderr = """
437DEX contains references to non-existent symbols after R8 optimization.
438Tip: Build with:
439        is_java_debug=false
440        treat_warnings_as_errors=false
441        enable_proguard_obfuscation=false
442     and then use dexdump to see which class(s) reference them.
443
444     E.g.:
445       third_party/android_sdk/public/build-tools/*/dexdump -d \
446out/Release/apks/YourApk.apk > dex.txt
447""" + stderr
448      elif had_unfiltered_items:
449        # Left only with empty headings. All indented items filtered out.
450        stderr = ''
451    return stderr
452
453  logging.debug('cmd: %s', ' '.join(cmd))
454  build_utils.CheckOutput(cmd,
455                          print_stdout=True,
456                          stderr_filter=stderr_filter,
457                          fail_on_output=warnings_as_errors)
458
459
460def _SplitChildFeatures(options, feature_contexts, tmp_dir, mapping_path,
461                        print_stdout):
462  feature_map = {f.name: f for f in feature_contexts}
463  parent_to_child = defaultdict(list)
464  for child, parent in options.uses_split.items():
465    parent_to_child[parent].append(child)
466  for parent, children in parent_to_child.items():
467    split_output = os.path.join(tmp_dir, 'split_%s' % parent)
468    os.mkdir(split_output)
469    # DexSplitter is not perfect and can cause issues related to inlining and
470    # class merging (see crbug.com/1032609). If strange class loading errors
471    # happen in DFMs specifying uses_split, this may be the cause.
472    split_cmd = build_utils.JavaCmd(options.warnings_as_errors) + [
473        '-cp',
474        options.r8_path,
475        'com.android.tools.r8.dexsplitter.DexSplitter',
476        '--output',
477        split_output,
478        '--proguard-map',
479        mapping_path,
480    ]
481
482    parent_jars = set(feature_map[parent].input_paths)
483    for base_jar in sorted(parent_jars):
484      split_cmd += ['--base-jar', base_jar]
485
486    for child in children:
487      for feature_jar in feature_map[child].input_paths:
488        if feature_jar not in parent_jars:
489          split_cmd += ['--feature-jar', '%s:%s' % (feature_jar, child)]
490
491    # The inputs are the outputs for the parent from the original R8 call.
492    parent_dir = feature_map[parent].staging_dir
493    for file_name in os.listdir(parent_dir):
494      split_cmd += ['--input', os.path.join(parent_dir, file_name)]
495    logging.debug('Running R8 DexSplitter')
496    build_utils.CheckOutput(split_cmd,
497                            print_stdout=print_stdout,
498                            fail_on_output=options.warnings_as_errors)
499
500    # Copy the parent dex back into the parent's staging dir.
501    base_split_output = os.path.join(split_output, 'base')
502    shutil.rmtree(parent_dir)
503    os.mkdir(parent_dir)
504    for dex_file in os.listdir(base_split_output):
505      shutil.move(os.path.join(base_split_output, dex_file),
506                  os.path.join(parent_dir, dex_file))
507
508    # Copy each child dex back into the child's staging dir.
509    for child in children:
510      child_split_output = os.path.join(split_output, child)
511      child_staging_dir = feature_map[child].staging_dir
512      shutil.rmtree(child_staging_dir)
513      os.mkdir(child_staging_dir)
514      for dex_file in os.listdir(child_split_output):
515        shutil.move(os.path.join(child_split_output, dex_file),
516                    os.path.join(child_staging_dir, dex_file))
517
518
519def _CombineConfigs(configs, dynamic_config_data, exclude_generated=False):
520  ret = []
521
522  # Sort in this way so //clank versions of the same libraries will sort
523  # to the same spot in the file.
524  def sort_key(path):
525    return tuple(reversed(path.split(os.path.sep)))
526
527  for config in sorted(configs, key=sort_key):
528    if exclude_generated and config.endswith('.resources.proguard.txt'):
529      continue
530
531    ret.append('# File: ' + config)
532    with open(config) as config_file:
533      contents = config_file.read().rstrip()
534
535    # Fix up line endings (third_party configs can have windows endings).
536    contents = contents.replace('\r', '')
537    # Remove numbers from generated rule comments to make file more
538    # diff'able.
539    contents = re.sub(r' #generated:\d+', '', contents)
540    ret.append(contents)
541    ret.append('')
542
543  if dynamic_config_data:
544    ret.append('# File: //build/android/gyp/proguard.py (generated rules)')
545    ret.append(dynamic_config_data)
546    ret.append('')
547  return '\n'.join(ret)
548
549
550def _CreateDynamicConfig(options):
551  ret = []
552  if options.sourcefile:
553    ret.append("-renamesourcefileattribute '%s' # OMIT FROM EXPECTATIONS" %
554               options.sourcefile)
555
556  if options.apply_mapping:
557    ret.append("-applymapping '%s'" % os.path.abspath(options.apply_mapping))
558  if options.repackage_classes:
559    ret.append("-repackageclasses '%s'" % options.repackage_classes)
560
561  _min_api = int(options.min_api) if options.min_api else 0
562  for api_level, version_code in _API_LEVEL_VERSION_CODE:
563    annotation_name = 'org.chromium.base.annotations.VerifiesOn' + version_code
564    if api_level > _min_api:
565      ret.append('-keep @interface %s' % annotation_name)
566      ret.append("""\
567-if @%s class * {
568    *** *(...);
569}
570-keep,allowobfuscation class <1> {
571    *** <2>(...);
572}""" % annotation_name)
573      ret.append("""\
574-keepclassmembers,allowobfuscation class ** {
575  @%s <methods>;
576}""" % annotation_name)
577  return '\n'.join(ret)
578
579
580def _VerifyNoEmbeddedConfigs(jar_paths):
581  failed = False
582  for jar_path in jar_paths:
583    with zipfile.ZipFile(jar_path) as z:
584      for name in z.namelist():
585        if name.startswith('META-INF/proguard/'):
586          failed = True
587          sys.stderr.write("""\
588Found embedded proguard config within {}.
589Embedded configs are not permitted (https://crbug.com/989505)
590""".format(jar_path))
591          break
592  if failed:
593    sys.exit(1)
594
595
596def _ContainsDebuggingConfig(config_str):
597  debugging_configs = ('-whyareyoukeeping', '-whyareyounotinlining')
598  return any(config in config_str for config in debugging_configs)
599
600
601def _MaybeWriteStampAndDepFile(options, inputs):
602  output = options.output_path
603  if options.stamp:
604    build_utils.Touch(options.stamp)
605    output = options.stamp
606  if options.depfile:
607    build_utils.WriteDepfile(options.depfile, output, inputs=inputs)
608
609
610def main():
611  build_utils.InitLogging('PROGUARD_DEBUG')
612  options = _ParseOptions()
613
614  logging.debug('Preparing configs')
615  proguard_configs = options.proguard_configs
616
617  # ProGuard configs that are derived from flags.
618  dynamic_config_data = _CreateDynamicConfig(options)
619
620  # ProGuard configs that are derived from flags.
621  merged_configs = _CombineConfigs(
622      proguard_configs, dynamic_config_data, exclude_generated=True)
623  print_stdout = _ContainsDebuggingConfig(merged_configs) or options.verbose
624
625  if options.expected_file:
626    diff_utils.CheckExpectations(merged_configs, options)
627    if options.only_verify_expectations:
628      build_utils.WriteDepfile(options.depfile,
629                               options.actual_file,
630                               inputs=options.proguard_configs)
631      return
632
633  logging.debug('Looking for embedded configs')
634  libraries = []
635  for p in options.classpath:
636    # TODO(bjoyce): Remove filter once old android support libraries are gone.
637    # Fix for having Library class extend program class dependency problem.
638    if 'com_android_support' in p or 'android_support_test' in p:
639      continue
640    # If a jar is part of input no need to include it as library jar.
641    if p not in libraries and p not in options.input_paths:
642      libraries.append(p)
643  _VerifyNoEmbeddedConfigs(options.input_paths + libraries)
644
645  _OptimizeWithR8(options, proguard_configs, libraries, dynamic_config_data,
646                  print_stdout)
647
648  if not options.disable_checks:
649    logging.debug('Running tracereferences')
650    all_dex_files = []
651    if options.output_path:
652      all_dex_files.append(options.output_path)
653    if options.dex_dests:
654      all_dex_files.extend(options.dex_dests)
655    _CheckForMissingSymbols(options.r8_path, all_dex_files, options.classpath,
656                            options.warnings_as_errors)
657
658  for output in options.extra_mapping_output_paths:
659    shutil.copy(options.mapping_output, output)
660
661  inputs = options.proguard_configs + options.input_paths + libraries
662  if options.apply_mapping:
663    inputs.append(options.apply_mapping)
664
665  _MaybeWriteStampAndDepFile(options, inputs)
666
667
668if __name__ == '__main__':
669  main()
670