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