1#!/usr/bin/env python3 2# 3# Copyright 2013 The Chromium Authors 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 8import json 9import logging 10import os 11import pathlib 12import re 13import shutil 14import sys 15import zipfile 16 17import dex 18from util import build_utils 19from util import diff_utils 20import action_helpers # build_utils adds //build to sys.path. 21import zip_helpers 22 23_IGNORE_WARNINGS = ( 24 # E.g. Triggers for weblayer_instrumentation_test_apk since both it and its 25 # apk_under_test have no shared_libraries. 26 # https://crbug.com/1364192 << To fix this in a better way. 27 r'Missing class org.chromium.build.NativeLibraries', 28 # Caused by protobuf runtime using -identifiernamestring in a way that 29 # doesn't work with R8. Looks like: 30 # Rule matches the static final field `...`, which may have been inlined... 31 # com.google.protobuf.*GeneratedExtensionRegistryLite { 32 # static java.lang.String CONTAINING_TYPE_*; 33 # } 34 r'GeneratedExtensionRegistryLite\.CONTAINING_TYPE_', 35 # Relevant for R8 when optimizing an app that doesn't use protobuf. 36 r'Ignoring -shrinkunusedprotofields since the protobuf-lite runtime is', 37 # Ignore Unused Rule Warnings in third_party libraries. 38 r'/third_party/.*Proguard configuration rule does not match anything', 39 # Ignore cronet's test rules (low priority to fix). 40 r'cronet/android/test/proguard.cfg.*Proguard configuration rule does not', 41 r'Proguard configuration rule does not match anything:.*(?:' + '|'.join([ 42 # aapt2 generates keeps for these. 43 r'class android\.', 44 # Used internally. 45 r'com.no.real.class.needed.receiver', 46 # Ignore Unused Rule Warnings for annotations. 47 r'@', 48 # Ignore Unused Rule Warnings for * implements Foo (androidx has these). 49 r'class \*+ implements', 50 # Ignore rules that opt out of this check. 51 r'!cr_allowunused', 52 # https://crbug.com/1441225 53 r'EditorDialogToolbar', 54 # https://crbug.com/1441226 55 r'PaymentRequest[BH]', 56 ]) + ')', 57 # We enforce that this class is removed via -checkdiscard. 58 r'FastServiceLoader\.class:.*Could not inline ServiceLoader\.load', 59 # Happens on internal builds. It's a real failure, but happens in dead code. 60 r'(?:GeneratedExtensionRegistryLoader|ExtensionRegistryLite)\.class:.*Could not inline ServiceLoader\.load', # pylint: disable=line-too-long 61 # This class is referenced by kotlinx-coroutines-core-jvm but it does not 62 # depend on it. Not actually needed though. 63 r'Missing class org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement', 64 # TODO(b/404818708): androidx.appsearch code is referencing classes in the 65 # Android B sdk thus we must ignore these warnings until after the sdk roll. 66 r'Missing class .* androidx.appsearch.platformstorage.converter.*\$ApiHelperForB', 67 # Ignore MethodParameter attribute count isn't matching in espresso. 68 # This is a banner warning and each individual file affected will have 69 # its own warning. 70 r'Warning: Invalid parameter counts in MethodParameter attributes', 71 # Full error: "Warning: InnerClasses attribute has entries missing a 72 # corresponding EnclosingMethod attribute. Such InnerClasses attribute 73 # entries are ignored." 74 r'Warning: InnerClasses attribute has entries missing a corresponding EnclosingMethod attribute', # pylint: disable=line-too-long 75 # Warning in obj/third_party/android_deps/google_play_services_fido_java/classes.jar:com/google/android/gms/internal/fido/zzel$zza.class: # pylint: disable=line-too-long 76 # Classes with missing EnclosingMethod: com.google.android.gms.internal.fido.zzel$zza # pylint: disable=line-too-long 77 r'Classes with missing EnclosingMethod', 78 # Full error example: "Warning in <path to target prebuilt>: 79 # androidx/test/espresso/web/internal/deps/guava/collect/Maps$1.class:" 80 # Also happens in espresso core. 81 r'Warning in .*:androidx/test/espresso/.*/guava/collect/.*', 82) 83 84_BLOCKLISTED_EXPECTATION_PATHS = [ 85 # A separate expectation file is created for these files. 86 'clank/third_party/google3/cipd/pg_confs/', 87] 88 89 90def _ParseOptions(): 91 args = build_utils.ExpandFileArgs(sys.argv[1:]) 92 parser = argparse.ArgumentParser() 93 action_helpers.add_depfile_arg(parser) 94 parser.add_argument('--r8-path', 95 required=True, 96 help='Path to the R8.jar to use.') 97 parser.add_argument('--custom-r8-path', 98 required=True, 99 help='Path to our custom R8 wrapper to use.') 100 parser.add_argument('--input-paths', 101 action='append', 102 help='GN-list of .jar files to optimize, excluding' 103 ' those --feature-jars.') 104 parser.add_argument('--output-path', help='Path to the generated .jar file.') 105 parser.add_argument('--tracerefs-json-out') 106 parser.add_argument( 107 '--proguard-configs', 108 action='append', 109 required=True, 110 help='GN-list of configuration files.') 111 parser.add_argument( 112 '--apply-mapping', help='Path to ProGuard mapping to apply.') 113 parser.add_argument( 114 '--mapping-output', 115 required=True, 116 help='Path for ProGuard to output mapping file to.') 117 parser.add_argument( 118 '--extra-mapping-output-paths', 119 help='GN-list of additional paths to copy output mapping file to.') 120 parser.add_argument('--sdk-jars', 121 action='append', 122 help='GN-list of .jar files to include as libraries.') 123 parser.add_argument( 124 '--sdk-extension-jars', 125 action='append', 126 help='GN-list of .jar files to include as libraries, and that are not a ' 127 'part of R8\'s API database.') 128 parser.add_argument('--main-dex-rules-path', 129 action='append', 130 help='Path to main dex rules for multidex.') 131 parser.add_argument( 132 '--min-api', help='Minimum Android API level compatibility.') 133 parser.add_argument('--enable-obfuscation', 134 action='store_true', 135 help='Minify symbol names') 136 parser.add_argument( 137 '--verbose', '-v', action='store_true', help='Print all ProGuard output') 138 parser.add_argument('--repackage-classes', 139 default='', 140 help='Value for -repackageclasses.') 141 parser.add_argument('--disable-checks', 142 action='store_true', 143 help='Disable -checkdiscard directives') 144 parser.add_argument('--source-file', help='Value for source file attribute.') 145 parser.add_argument('--package-name', 146 help='Goes into a comment in the mapping file.') 147 parser.add_argument( 148 '--force-enable-assertions', 149 action='store_true', 150 help='Forcefully enable javac generated assertion code.') 151 parser.add_argument('--assertion-handler', 152 help='The class name of the assertion handler class.') 153 parser.add_argument( 154 '--feature-jars', 155 action='append', 156 help='GN list of path to jars which comprise the corresponding feature.') 157 parser.add_argument( 158 '--dex-dest', 159 action='append', 160 dest='dex_dests', 161 help='Destination for dex file of the corresponding feature.') 162 parser.add_argument( 163 '--feature-name', 164 action='append', 165 dest='feature_names', 166 help='The name of the feature module.') 167 parser.add_argument( 168 '--uses-split', 169 action='append', 170 help='List of name pairs separated by : mapping a feature module to a ' 171 'dependent feature module.') 172 parser.add_argument('--input-art-profile', 173 help='Path to the input unobfuscated ART profile.') 174 parser.add_argument('--output-art-profile', 175 help='Path to the output obfuscated ART profile.') 176 parser.add_argument( 177 '--apply-startup-profile', 178 action='store_true', 179 help='Whether to pass --input-art-profile as a startup profile to R8.') 180 parser.add_argument( 181 '--keep-rules-targets-regex', 182 metavar='KEEP_RULES_REGEX', 183 help='If passed outputs keep rules for references from all other inputs ' 184 'to the subset of inputs that satisfy the KEEP_RULES_REGEX.') 185 parser.add_argument( 186 '--keep-rules-output-path', 187 help='Output path to the keep rules for references to the ' 188 '--keep-rules-targets-regex inputs from the rest of the inputs.') 189 parser.add_argument('--warnings-as-errors', 190 action='store_true', 191 help='Treat all warnings as errors.') 192 parser.add_argument('--show-desugar-default-interface-warnings', 193 action='store_true', 194 help='Enable desugaring warnings.') 195 parser.add_argument('--dump-inputs', 196 action='store_true', 197 help='Use when filing R8 bugs to capture inputs.' 198 ' Stores inputs to r8inputs.zip') 199 parser.add_argument( 200 '--dump-unknown-refs', 201 action='store_true', 202 help='Log all reasons why API modelling cannot determine API level') 203 parser.add_argument( 204 '--stamp', 205 help='File to touch upon success. Mutually exclusive with --output-path') 206 parser.add_argument('--desugared-library-keep-rule-output', 207 help='Path to desugared library keep rule output file.') 208 209 diff_utils.AddCommandLineFlags(parser) 210 options = parser.parse_args(args) 211 212 if options.feature_names: 213 if options.output_path: 214 parser.error('Feature splits cannot specify an output in GN.') 215 if not options.actual_file and not options.stamp: 216 parser.error('Feature splits require a stamp file as output.') 217 elif not options.output_path: 218 parser.error('Output path required when feature splits aren\'t used') 219 220 if bool(options.keep_rules_targets_regex) != bool( 221 options.keep_rules_output_path): 222 parser.error('You must path both --keep-rules-targets-regex and ' 223 '--keep-rules-output-path') 224 225 if options.output_art_profile and not options.input_art_profile: 226 parser.error('--output-art-profile requires --input-art-profile') 227 if options.apply_startup_profile and not options.input_art_profile: 228 parser.error('--apply-startup-profile requires --input-art-profile') 229 230 if options.force_enable_assertions and options.assertion_handler: 231 parser.error('Cannot use both --force-enable-assertions and ' 232 '--assertion-handler') 233 234 options.sdk_jars = action_helpers.parse_gn_list(options.sdk_jars) 235 options.sdk_extension_jars = action_helpers.parse_gn_list( 236 options.sdk_extension_jars) 237 options.proguard_configs = action_helpers.parse_gn_list( 238 options.proguard_configs) 239 options.extra_mapping_output_paths = action_helpers.parse_gn_list( 240 options.extra_mapping_output_paths) 241 if os.environ.get('R8_VERBOSE') == '1': 242 options.verbose = True 243 244 if options.feature_names: 245 if 'base' not in options.feature_names: 246 parser.error('"base" feature required when feature arguments are used.') 247 if len(options.feature_names) != len(options.feature_jars) or len( 248 options.feature_names) != len(options.dex_dests): 249 parser.error('Invalid feature argument lengths.') 250 251 options.feature_jars = [ 252 action_helpers.parse_gn_list(x) for x in options.feature_jars 253 ] 254 assert not options.input_paths 255 input_paths = set() 256 for jar_paths in options.feature_jars: 257 input_paths.update(jar_paths) 258 options.input_paths = sorted(input_paths) 259 else: 260 options.input_paths = action_helpers.parse_gn_list(options.input_paths) 261 262 split_map = {} 263 if options.uses_split: 264 for split_pair in options.uses_split: 265 child, parent = split_pair.split(':') 266 for name in (child, parent): 267 if name not in options.feature_names: 268 parser.error('"%s" referenced in --uses-split not present.' % name) 269 split_map[child] = parent 270 options.uses_split = split_map 271 272 return options 273 274 275class _SplitContext: 276 def __init__(self, name, output_path, input_jars, work_dir, parent_name=None): 277 self.name = name 278 self.parent_name = parent_name 279 self.input_jars = set(input_jars) 280 self.final_output_path = output_path 281 self.staging_dir = os.path.join(work_dir, name) 282 os.mkdir(self.staging_dir) 283 284 def CreateOutput(self): 285 found_files = build_utils.FindInDirectory(self.staging_dir) 286 if not found_files: 287 raise Exception('Missing dex outputs in {}'.format(self.staging_dir)) 288 289 if self.final_output_path.endswith('.dex'): 290 if len(found_files) != 1: 291 raise Exception('Expected exactly 1 dex file output, found: {}'.format( 292 '\t'.join(found_files))) 293 shutil.move(found_files[0], self.final_output_path) 294 return 295 296 # Add to .jar using Python rather than having R8 output to a .zip directly 297 # in order to disable compression of the .jar, saving ~500ms. 298 tmp_jar_output = self.staging_dir + '.jar' 299 zip_helpers.add_files_to_zip(found_files, 300 tmp_jar_output, 301 base_dir=self.staging_dir) 302 shutil.move(tmp_jar_output, self.final_output_path) 303 304 305def _OptimizeWithR8(options, config_paths, libraries, dynamic_config_data): 306 with build_utils.TempDir() as tmp_dir: 307 if dynamic_config_data: 308 dynamic_config_path = os.path.join(tmp_dir, 'dynamic_config.flags') 309 with open(dynamic_config_path, 'w') as f: 310 f.write(dynamic_config_data) 311 config_paths = config_paths + [dynamic_config_path] 312 313 tmp_mapping_path = os.path.join(tmp_dir, 'mapping.txt') 314 # If there is no output (no classes are kept), this prevents this script 315 # from failing. 316 build_utils.Touch(tmp_mapping_path) 317 318 tmp_output = os.path.join(tmp_dir, 'r8out') 319 os.mkdir(tmp_output) 320 321 split_contexts_by_name = {} 322 if options.feature_names: 323 for name, dest_dex, input_jars in zip(options.feature_names, 324 options.dex_dests, 325 options.feature_jars): 326 parent_name = options.uses_split.get(name) 327 if parent_name is None and name != 'base': 328 parent_name = 'base' 329 split_context = _SplitContext(name, 330 dest_dex, 331 input_jars, 332 tmp_output, 333 parent_name=parent_name) 334 split_contexts_by_name[name] = split_context 335 else: 336 split_contexts_by_name['base'] = _SplitContext('base', 337 options.output_path, 338 options.input_paths, 339 tmp_output) 340 base_context = split_contexts_by_name['base'] 341 342 # R8 OOMs with xmx=3G. 343 cmd = build_utils.JavaCmd(xmx='4G') + [ 344 # Allows -whyareyounotinlining, which we don't have by default, but 345 # which is useful for one-off queries. 346 '-Dcom.android.tools.r8.experimental.enablewhyareyounotinlining=1', 347 # Restricts horizontal class merging to apply only to classes that 348 # share a .java file (nested classes). https://crbug.com/1363709 349 '-Dcom.android.tools.r8.enableSameFilePolicy=1', 350 # Allow ServiceLoaderUtil.maybeCreate() to work with types that are 351 # -kept (e.g. due to containing JNI). 352 '-Dcom.android.tools.r8.allowServiceLoaderRewritingPinnedTypes=1', 353 # Allow R8 to inline kept methods by default. 354 # See: b/364267880#2 355 '-Dcom.android.tools.r8.allowCodeReplacement=false', 356 # Required to use "-keep,allowcodereplacement" 357 '-Dcom.android.tools.r8.allowTestProguardOptions=true', 358 # Needed because we don't add an unconditional -keep for Enum.values() 359 # methods. http://b/204939965 360 '-Dcom.android.tools.r8.experimentalTraceEnumReflection=1', 361 ] 362 if options.sdk_extension_jars: 363 # Enable API modelling for OS extensions. https://b/326252366 364 cmd += [ 365 '-Dcom.android.tools.r8.androidApiExtensionLibraries=' + 366 ','.join(options.sdk_extension_jars) 367 ] 368 if options.dump_inputs: 369 cmd += ['-Dcom.android.tools.r8.dumpinputtofile=r8inputs.zip'] 370 if options.dump_unknown_refs: 371 cmd += ['-Dcom.android.tools.r8.reportUnknownApiReferences=1'] 372 cmd += [ 373 '-cp', 374 '{}:{}'.format(options.r8_path, options.custom_r8_path), 375 'org.chromium.build.CustomR8', 376 '--no-data-resources', 377 '--map-id-template', 378 f'{options.source_file} ({options.package_name})', 379 '--source-file-template', 380 options.source_file, 381 '--output', 382 base_context.staging_dir, 383 '--pg-map-output', 384 tmp_mapping_path, 385 ] 386 387 if options.uses_split: 388 cmd += ['--isolated-splits'] 389 390 if options.disable_checks: 391 cmd += ['--map-diagnostics:CheckDiscardDiagnostic', 'error', 'none'] 392 # Triggered by rules from deps we cannot control. 393 cmd += [('--map-diagnostics:EmptyMemberRulesToDefaultInitRuleConversion' 394 'Diagnostic'), 'warning', 'none'] 395 cmd += ['--map-diagnostics', 'info', 'warning'] 396 # An "error" level diagnostic causes r8 to return an error exit code. Doing 397 # this allows our filter to decide what should/shouldn't break our build. 398 cmd += ['--map-diagnostics', 'error', 'warning'] 399 400 if options.min_api: 401 cmd += ['--min-api', options.min_api] 402 403 if options.assertion_handler: 404 cmd += ['--force-assertions-handler:' + options.assertion_handler] 405 elif options.force_enable_assertions: 406 cmd += ['--force-enable-assertions'] 407 408 for lib in libraries: 409 cmd += ['--lib', lib] 410 411 for config_file in config_paths: 412 cmd += ['--pg-conf', config_file] 413 414 if options.main_dex_rules_path: 415 for main_dex_rule in options.main_dex_rules_path: 416 cmd += ['--main-dex-rules', main_dex_rule] 417 418 if options.output_art_profile: 419 cmd += [ 420 '--art-profile', 421 options.input_art_profile, 422 options.output_art_profile, 423 ] 424 if options.apply_startup_profile: 425 cmd += [ 426 '--startup-profile', 427 options.input_art_profile, 428 ] 429 430 for split_context in split_contexts_by_name.values(): 431 if split_context is base_context: 432 continue 433 for in_jar in sorted(split_context.input_jars): 434 cmd += ['--feature', in_jar, split_context.staging_dir] 435 436 cmd += sorted(base_context.input_jars) 437 438 if options.verbose: 439 stderr_filter = None 440 else: 441 filters = list(dex.DEFAULT_IGNORE_WARNINGS) 442 filters += _IGNORE_WARNINGS 443 if options.show_desugar_default_interface_warnings: 444 filters += dex.INTERFACE_DESUGARING_WARNINGS 445 stderr_filter = dex.CreateStderrFilter(filters) 446 447 try: 448 logging.debug('Running R8') 449 build_utils.CheckOutput(cmd, 450 print_stdout=True, 451 stderr_filter=stderr_filter, 452 fail_on_output=options.warnings_as_errors) 453 except build_utils.CalledProcessError as e: 454 # Do not output command line because it is massive and makes the actual 455 # error message hard to find. 456 sys.stderr.write(e.output) 457 sys.exit(1) 458 459 logging.debug('Collecting ouputs') 460 base_context.CreateOutput() 461 for split_context in split_contexts_by_name.values(): 462 if split_context is not base_context: 463 split_context.CreateOutput() 464 465 shutil.move(tmp_mapping_path, options.mapping_output) 466 return split_contexts_by_name 467 468 469def _OutputKeepRules(r8_path, input_paths, libraries, targets_re_string, 470 keep_rules_output): 471 472 cmd = build_utils.JavaCmd(xmx='2G') + [ 473 '-cp', r8_path, 'com.android.tools.r8.tracereferences.TraceReferences', 474 '--map-diagnostics:MissingDefinitionsDiagnostic', 'error', 'warning', 475 '--keep-rules', '--output', keep_rules_output 476 ] 477 targets_re = re.compile(targets_re_string) 478 for path in input_paths: 479 if targets_re.search(path): 480 cmd += ['--target', path] 481 else: 482 cmd += ['--source', path] 483 for path in libraries: 484 cmd += ['--lib', path] 485 486 build_utils.CheckOutput(cmd, print_stderr=False, fail_on_output=False) 487 488 489def _CombineConfigs(configs, 490 dynamic_config_data, 491 embedded_configs, 492 exclude_generated=False): 493 # Sort in this way so //clank versions of the same libraries will sort 494 # to the same spot in the file. 495 def sort_key(path): 496 return tuple(reversed(path.split(os.path.sep))) 497 498 def format_config_contents(path, contents): 499 formatted_contents = [] 500 # Ignore files that contain only comments (androidx has a lot of these). 501 if all(l.isspace() or l.rstrip().startswith('#') 502 for l in contents.splitlines()): 503 return [] 504 505 # Fix up line endings (third_party configs can have windows endings). 506 contents = contents.replace('\r', '') 507 # Remove numbers from generated rule comments to make file more 508 # diff'able. 509 contents = re.sub(r' #generated:\d+', '', contents) 510 formatted_contents.append('# File: ' + path) 511 formatted_contents.append(contents) 512 formatted_contents.append('') 513 return formatted_contents 514 515 ret = [] 516 for config in sorted(configs, key=sort_key): 517 if exclude_generated and config.endswith('.resources.proguard.txt'): 518 continue 519 520 # Exclude some confs from expectations. 521 if any(entry in config for entry in _BLOCKLISTED_EXPECTATION_PATHS): 522 continue 523 524 with open(config) as config_file: 525 contents = config_file.read().rstrip() 526 527 ret.extend(format_config_contents(config, contents)) 528 529 for path, contents in sorted(embedded_configs.items()): 530 ret.extend(format_config_contents(path, contents)) 531 532 533 if dynamic_config_data: 534 ret.append('# File: //build/android/gyp/proguard.py (generated rules)') 535 ret.append(dynamic_config_data) 536 ret.append('') 537 return '\n'.join(ret) 538 539 540def _CreateDynamicConfig(options): 541 ret = [] 542 if options.enable_obfuscation: 543 ret.append(f"-repackageclasses '{options.repackage_classes}'") 544 else: 545 ret.append("-dontobfuscate") 546 547 if options.apply_mapping: 548 ret.append("-applymapping '%s'" % options.apply_mapping) 549 550 return '\n'.join(ret) 551 552 553def _ExtractEmbeddedConfigs(jar_path, embedded_configs): 554 with zipfile.ZipFile(jar_path) as z: 555 proguard_names = [] 556 r8_names = [] 557 for info in z.infolist(): 558 if info.is_dir(): 559 continue 560 if info.filename.startswith('META-INF/proguard/'): 561 proguard_names.append(info.filename) 562 elif info.filename.startswith('META-INF/com.android.tools/r8/'): 563 r8_names.append(info.filename) 564 elif info.filename.startswith('META-INF/com.android.tools/r8-from'): 565 # Assume our version of R8 is always latest. 566 if '-upto-' not in info.filename: 567 r8_names.append(info.filename) 568 569 # Give preference to r8-from-*, then r8/, then proguard/. 570 active = r8_names or proguard_names 571 for filename in active: 572 config_path = '{}:{}'.format(jar_path, filename) 573 embedded_configs[config_path] = z.read(filename).decode('utf-8').rstrip() 574 575 576def _MaybeWriteStampAndDepFile(options, inputs): 577 output = options.output_path 578 if options.stamp: 579 build_utils.Touch(options.stamp) 580 output = options.stamp 581 if options.depfile: 582 action_helpers.write_depfile(options.depfile, output, inputs=inputs) 583 584 585def _IterParentContexts(context_name, split_contexts_by_name): 586 while context_name: 587 context = split_contexts_by_name[context_name] 588 yield context 589 context_name = context.parent_name 590 591 592def _WriteTraceReferencesJson(options, split_contexts_by_name): 593 # Set of all contexts that are a parent to another. 594 parent_splits_context_names = { 595 c.parent_name 596 for c in split_contexts_by_name.values() if c.parent_name 597 } 598 context_sets = [ 599 list(_IterParentContexts(n, split_contexts_by_name)) 600 for n in parent_splits_context_names 601 ] 602 # Visit them in order of: base, base+chrome, base+chrome+thing. 603 context_sets.sort(key=lambda x: (len(x), x[0].name)) 604 605 # Ensure there are no missing references when considering all dex files. 606 dex_files = sorted(c.final_output_path 607 for c in split_contexts_by_name.values()) 608 payload = { 609 'r8jar': options.r8_path, 610 'libs': options.sdk_jars + options.sdk_extension_jars, 611 'jobs': [], 612 } 613 614 # Ensure there are no missing references when considering all dex files. 615 payload['jobs'].append({'name': '', 'jars': dex_files}) 616 617 # Ensure there are no references from base -> chrome module, or from 618 # base+chrome -> feature modules. 619 for context_set in context_sets: 620 dex_files = [c.final_output_path for c in context_set] 621 payload['jobs'].append({'name': context_set[0].name, 'jars': dex_files}) 622 623 with action_helpers.atomic_output(options.tracerefs_json_out, 'wt') as f: 624 json.dump(payload, f, indent=2) 625 626 627def _Run(options): 628 # ProGuard configs that are derived from flags. 629 logging.debug('Preparing configs') 630 dynamic_config_data = _CreateDynamicConfig(options) 631 632 logging.debug('Looking for embedded configs') 633 libraries = options.sdk_jars + options.sdk_extension_jars 634 635 embedded_configs = {} 636 for jar_path in options.input_paths: 637 _ExtractEmbeddedConfigs(jar_path, embedded_configs) 638 639 # ProGuard configs that are derived from flags. 640 merged_configs = _CombineConfigs(options.proguard_configs, 641 dynamic_config_data, 642 embedded_configs, 643 exclude_generated=True) 644 645 depfile_inputs = options.proguard_configs + options.input_paths + libraries 646 if options.expected_file: 647 diff_utils.CheckExpectations(merged_configs, options) 648 if options.only_verify_expectations: 649 action_helpers.write_depfile(options.depfile, 650 options.actual_file, 651 inputs=depfile_inputs) 652 return 653 654 if options.keep_rules_output_path: 655 _OutputKeepRules(options.r8_path, options.input_paths, libraries, 656 options.keep_rules_targets_regex, 657 options.keep_rules_output_path) 658 return 659 660 split_contexts_by_name = _OptimizeWithR8(options, options.proguard_configs, 661 libraries, dynamic_config_data) 662 663 if options.tracerefs_json_out: 664 logging.debug('Writing TraceReferences .json') 665 _WriteTraceReferencesJson(options, split_contexts_by_name) 666 667 for output in options.extra_mapping_output_paths: 668 shutil.copy(options.mapping_output, output) 669 670 if options.apply_mapping: 671 depfile_inputs.append(options.apply_mapping) 672 673 _MaybeWriteStampAndDepFile(options, depfile_inputs) 674 675 676def main(): 677 build_utils.InitLogging('PROGUARD_DEBUG') 678 options = _ParseOptions() 679 680 if options.dump_inputs: 681 # Dumping inputs causes output to be emitted, avoid failing due to stdout. 682 options.warnings_as_errors = False 683 684 _Run(options) 685 686 687if __name__ == '__main__': 688 main() 689