| // Copyright (c) 2017, 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. | |
| package com.android.tools.r8; | |
| import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING; | |
| import static com.android.tools.r8.utils.MapConsumerUtils.wrapExistingInternalMapConsumerIfNotNull; | |
| import com.android.tools.r8.ProgramResource.Kind; | |
| import com.android.tools.r8.dex.Marker.Tool; | |
| import com.android.tools.r8.dump.DumpOptions; | |
| import com.android.tools.r8.errors.DexFileOverflowDiagnostic; | |
| import com.android.tools.r8.experimental.graphinfo.GraphConsumer; | |
| import com.android.tools.r8.features.FeatureSplitConfiguration; | |
| import com.android.tools.r8.features.FeatureSplitProgramResourceProvider; | |
| import com.android.tools.r8.graph.DexEncodedMethod; | |
| import com.android.tools.r8.graph.DexItemFactory; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.inspector.Inspector; | |
| import com.android.tools.r8.inspector.internal.InspectorImpl; | |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; | |
| import com.android.tools.r8.ir.desugar.desugaredlibrary.LibraryDesugaringOptions; | |
| import com.android.tools.r8.keepanno.annotations.KeepForApi; | |
| import com.android.tools.r8.keepanno.asm.KeepEdgeReader; | |
| import com.android.tools.r8.keepanno.ast.KeepDeclaration; | |
| import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor; | |
| import com.android.tools.r8.keepradius.KeepRadiusContainerUtils; | |
| import com.android.tools.r8.metadata.R8BuildMetadata; | |
| import com.android.tools.r8.naming.ClassNameMapper; | |
| import com.android.tools.r8.naming.ClassNamingForNameMapper; | |
| import com.android.tools.r8.naming.InternalMapConsumer; | |
| import com.android.tools.r8.naming.InternalMapConsumerImpl; | |
| import com.android.tools.r8.naming.SourceFileRewriter; | |
| import com.android.tools.r8.origin.Origin; | |
| import com.android.tools.r8.origin.PathOrigin; | |
| import com.android.tools.r8.partial.R8PartialCompilationConfiguration; | |
| import com.android.tools.r8.profile.art.ArtProfileForRewriting; | |
| import com.android.tools.r8.resourceshrinker.usages.LegacyResourceShrinker; | |
| import com.android.tools.r8.shaking.FilteredClassPath; | |
| import com.android.tools.r8.shaking.KeepSpecificationSource; | |
| import com.android.tools.r8.shaking.ProguardConfiguration; | |
| import com.android.tools.r8.shaking.ProguardConfigurationParser; | |
| import com.android.tools.r8.shaking.ProguardConfigurationParserOptions; | |
| import com.android.tools.r8.shaking.ProguardConfigurationRule; | |
| import com.android.tools.r8.shaking.ProguardConfigurationSource; | |
| import com.android.tools.r8.shaking.ProguardConfigurationSourceFile; | |
| import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings; | |
| import com.android.tools.r8.startup.StartupProfileProvider; | |
| import com.android.tools.r8.tracereferences.TraceReferencesNativeReferencesConsumer; | |
| import com.android.tools.r8.utils.AndroidApiLevel; | |
| import com.android.tools.r8.utils.AndroidApp; | |
| import com.android.tools.r8.utils.ArchiveResourceProvider; | |
| import com.android.tools.r8.utils.AssertionConfigurationWithDefault; | |
| import com.android.tools.r8.utils.DumpInputFlags; | |
| import com.android.tools.r8.utils.EmbeddedRulesExtractor; | |
| import com.android.tools.r8.utils.ExceptionDiagnostic; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.InternalOptions.DesugarState; | |
| import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; | |
| import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions; | |
| import com.android.tools.r8.utils.InternalProgramClassProvider; | |
| import com.android.tools.r8.utils.MapConsumerUtils; | |
| import com.android.tools.r8.utils.ProgramClassCollection; | |
| import com.android.tools.r8.utils.ProgramResourceProviderUtils; | |
| import com.android.tools.r8.utils.ProgramResourceUtils; | |
| import com.android.tools.r8.utils.Reporter; | |
| import com.android.tools.r8.utils.SemanticVersionUtils; | |
| import com.android.tools.r8.utils.StringDiagnostic; | |
| import com.android.tools.r8.utils.SystemPropertyUtils; | |
| import com.android.tools.r8.utils.ThreadUtils; | |
| import com.android.tools.r8.utils.UncheckedApiLevel; | |
| import com.android.tools.r8.utils.internal.Box; | |
| import com.android.tools.r8.utils.internal.FileUtils; | |
| import com.android.tools.r8.utils.internal.ListUtils; | |
| import com.android.tools.r8.utils.internal.SemanticVersion; | |
| import com.android.tools.r8.utils.internal.SetUtils; | |
| import com.android.tools.r8.utils.internal.StringUtils; | |
| import com.google.common.collect.ImmutableList; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collection; | |
| import java.util.List; | |
| import java.util.Objects; | |
| import java.util.Optional; | |
| import java.util.Set; | |
| import java.util.function.BiPredicate; | |
| import java.util.function.Consumer; | |
| import java.util.function.Function; | |
| import java.util.function.Predicate; | |
| import java.util.function.Supplier; | |
| import java.util.stream.Collectors; | |
| /** | |
| * Immutable command structure for an invocation of the {@link R8} compiler. | |
| * | |
| * <p>To build a R8 command use the {@link R8Command.Builder} class. For example: | |
| * | |
| * <pre> | |
| * R8Command command = R8Command.builder() | |
| * .addProgramFiles(path1, path2) | |
| * .setMode(CompilationMode.RELEASE) | |
| * .setOutput(Paths.get("output.zip", OutputMode.DexIndexed)) | |
| * .build(); | |
| * </pre> | |
| */ | |
| @KeepForApi | |
| public final class R8Command extends BaseCompilerCommand { | |
| /** | |
| * Builder for constructing a R8Command. | |
| * | |
| * <p>A builder is obtained by calling {@link R8Command#builder}. | |
| */ | |
| @KeepForApi | |
| public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> { | |
| private static class DefaultR8DiagnosticsHandler implements DiagnosticsHandler { | |
| @Override | |
| public void error(Diagnostic error) { | |
| if (error instanceof DexFileOverflowDiagnostic) { | |
| DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error; | |
| if (!overflowDiagnostic.hasMainDexSpecification()) { | |
| DiagnosticsHandler.super.error( | |
| new StringDiagnostic( | |
| overflowDiagnostic.getDiagnosticMessage() | |
| + ". Try supplying a main-dex list or main-dex rules")); | |
| return; | |
| } | |
| } | |
| DiagnosticsHandler.super.error(error); | |
| } | |
| } | |
| private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>(); | |
| private Consumer<ProguardConfiguration.Builder> proguardConfigurationConsumerForTesting = null; | |
| private Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null; | |
| private final List<KeepSpecificationSource> keepSpecifications = new ArrayList<>(); | |
| private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>(); | |
| private boolean disableTreeShaking = false; | |
| private boolean disableMinification = false; | |
| private boolean forceProguardCompatibility = false; | |
| private boolean protectApiSurface = false; | |
| private Optional<Boolean> includeDataResources = Optional.empty(); | |
| private StringConsumer proguardUsageConsumer = null; | |
| private StringConsumer proguardSeedsConsumer = null; | |
| private StringConsumer proguardConfigurationConsumer = null; | |
| private GraphConsumer keptGraphConsumer = null; | |
| private GraphConsumer mainDexKeptGraphConsumer = null; | |
| private TraceReferencesNativeReferencesConsumer nativeReferencesConsumer = null; | |
| private InputDependencyGraphConsumer inputDependencyGraphConsumer = null; | |
| private Consumer<? super R8BuildMetadata> buildMetadataConsumer = null; | |
| private final FeatureSplitConfiguration.Builder featureSplitConfigurationBuilder = | |
| FeatureSplitConfiguration.builder(); | |
| private String synthesizedClassPrefix = | |
| System.getProperty("com.android.tools.r8.synthesizedClassPrefix", ""); | |
| private boolean enableMissingLibraryApiModeling = false; | |
| private boolean enableExperimentalDexInput = false; | |
| private boolean enableExperimentalKeepAnnotations = | |
| SystemPropertyUtils.parseSystemPropertyOrDefault( | |
| "com.android.tools.r8.enableKeepAnnotations", false); | |
| private boolean readEmbeddedRulesFromClasspathAndLibrary = | |
| System.getProperty("com.android.tools.r8.readEmbeddedRulesFromClasspathAndLibrary") != null; | |
| public boolean enableStartupLayoutOptimization = true; | |
| private SemanticVersion fakeCompilerVersion = null; | |
| private AndroidResourceProvider androidResourceProvider = null; | |
| private AndroidResourceConsumer androidResourceConsumer = null; | |
| private ByteArrayConsumer<?> configurationAnalysisDataConsumer = null; | |
| private StringConsumer configurationAnalysisHtmlReportConsumer = null; | |
| private ResourceShrinkerConfiguration resourceShrinkerConfiguration = | |
| ResourceShrinkerConfiguration.DEFAULT_CONFIGURATION; | |
| private R8PartialCompilationConfiguration partialCompilationConfiguration = | |
| R8PartialCompilationConfiguration.fromSystemProperties(); | |
| private final List<PartialOptimizationConfigurationProvider> | |
| partialOptimizationConfigurationProviders = new ArrayList<>(); | |
| private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder = | |
| ProguardConfigurationParserOptions.builder().readEnvironment(); | |
| // TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing. | |
| Builder() { | |
| this(new DefaultR8DiagnosticsHandler()); | |
| } | |
| Builder(DiagnosticsHandler diagnosticsHandler) { | |
| super(diagnosticsHandler); | |
| setIgnoreDexInArchive(true); | |
| } | |
| private Builder(AndroidApp app) { | |
| super(app); | |
| setIgnoreDexInArchive(true); | |
| } | |
| private Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) { | |
| super(app, diagnosticsHandler); | |
| setIgnoreDexInArchive(true); | |
| } | |
| // Internal | |
| @Override | |
| Builder self() { | |
| return this; | |
| } | |
| @Override | |
| CompilationMode defaultCompilationMode() { | |
| return CompilationMode.RELEASE; | |
| } | |
| Builder setSynthesizedClassesPrefix(String prefix) { | |
| synthesizedClassPrefix = prefix; | |
| return self(); | |
| } | |
| Builder setFakeCompilerVersion(SemanticVersion version) { | |
| fakeCompilerVersion = version; | |
| return self(); | |
| } | |
| /** | |
| * Disable tree shaking. | |
| * | |
| * <p>If true, tree shaking is completely disabled, otherwise tree shaking is configured by | |
| * ProGuard configuration settings. | |
| */ | |
| public Builder setDisableTreeShaking(boolean disableTreeShaking) { | |
| this.disableTreeShaking = disableTreeShaking; | |
| return self(); | |
| } | |
| /** | |
| * Disable minification of names. | |
| * | |
| * <p>If true, minification of names is completely disabled, otherwise minification of names is | |
| * configured by ProGuard configuration settings. | |
| */ | |
| public Builder setDisableMinification(boolean disableMinification) { | |
| this.disableMinification = disableMinification; | |
| return self(); | |
| } | |
| /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ | |
| public Builder addMainDexRulesFiles(Path... paths) { | |
| return addMainDexRulesFiles(Arrays.asList(paths)); | |
| } | |
| /** Add proguard configuration files with rules for automatic main-dex-list calculation. */ | |
| public Builder addMainDexRulesFiles(Collection<Path> paths) { | |
| guard(() -> paths.forEach(p -> mainDexRules.add(new ProguardConfigurationSourceFile(p)))); | |
| return self(); | |
| } | |
| /** Add proguard rules for automatic main-dex-list calculation. */ | |
| public Builder addMainDexRules(List<String> lines, Origin origin) { | |
| String config = String.join(System.lineSeparator(), lines); | |
| mainDexRules.add(new ProguardConfigurationSourceStrings(config, Paths.get("."), origin)); | |
| return self(); | |
| } | |
| public Builder addKeepSpecificationFiles(Path... paths) { | |
| return addKeepSpecificationFiles(Arrays.asList(paths)); | |
| } | |
| public Builder addKeepSpecificationFiles(Collection<Path> paths) { | |
| paths.forEach(p -> keepSpecifications.add(KeepSpecificationSource.fromFile(p))); | |
| return self(); | |
| } | |
| public Builder addKeepSpecificationData(byte[] data, Origin origin) { | |
| keepSpecifications.add(KeepSpecificationSource.fromBytes(origin, data)); | |
| return self(); | |
| } | |
| /** | |
| * Set Proguard compatibility mode. | |
| * | |
| * <p>If true, R8 will attempt to retain more compatibility with Proguard. Most notably, R8 will | |
| * introduce rules for keeping more default constructors as well as various attributes. Note | |
| * that setting R8 in compatibility mode will result in larger residual programs. | |
| */ | |
| public Builder setProguardCompatibility(boolean value) { | |
| this.forceProguardCompatibility = value; | |
| return self(); | |
| } | |
| /** Get the current value of Proguard compatibility mode. */ | |
| public boolean getProguardCompatibility() { | |
| return forceProguardCompatibility; | |
| } | |
| /** | |
| * Option to protect the API surface of the given compilation unit. | |
| * | |
| * <p>If true, R8 will not add public or protected members to classes that are kept or have kept | |
| * subclasses, to not alter the public API surface of the compilation unit. | |
| * | |
| * <p>Defaults to true when compiling to class files. | |
| */ | |
| public Builder setProtectApiSurface(boolean value) { | |
| this.protectApiSurface = value; | |
| return self(); | |
| } | |
| /** Add a proguard configuration-file resource with a given origin. */ | |
| public Builder addProguardConfigurationFile(Path path, Origin origin) { | |
| proguardConfigs.add(new ProguardConfigurationSourceFile(path, origin)); | |
| return self(); | |
| } | |
| /** Add proguard configuration-file resources. */ | |
| public Builder addProguardConfigurationFiles(Path... paths) { | |
| return addProguardConfigurationFiles(Arrays.asList(paths)); | |
| } | |
| /** Add proguard configuration-file resources. */ | |
| public Builder addProguardConfigurationFiles(List<Path> paths) { | |
| for (Path path : paths) { | |
| proguardConfigs.add(new ProguardConfigurationSourceFile(path)); | |
| } | |
| return self(); | |
| } | |
| /** Add proguard configuration. */ | |
| public Builder addProguardConfiguration(List<String> lines, Origin origin) { | |
| String config = String.join(System.lineSeparator(), lines); | |
| proguardConfigs.add(new ProguardConfigurationSourceStrings(config, Paths.get("."), origin)); | |
| return self(); | |
| } | |
| /** | |
| * Set an output destination to which proguard-map content should be written. | |
| * | |
| * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link | |
| * #setProguardMapConsumer}. Note that any subsequent call to this method or {@link | |
| * #setProguardMapConsumer} will override the previous setting. | |
| * | |
| * @param proguardMapOutput File-system path to write output at. | |
| */ | |
| @Override | |
| public Builder setProguardMapOutputPath(Path proguardMapOutput) { | |
| return super.setProguardMapOutputPath(proguardMapOutput); | |
| } | |
| /** Set input proguard map used for distribution of classes in multi-DEX. */ | |
| public Builder setProguardMapInputFile(Path proguardInputMap) { | |
| getAppBuilder().setProguardMapInputData(proguardInputMap); | |
| return self(); | |
| } | |
| /** | |
| * Set a consumer for receiving the proguard-map content. | |
| * | |
| * <p>It is possible to also retrieve the map id by passing an instance of {@link | |
| * com.android.tools.r8.MapConsumer}. | |
| * | |
| * <p>Note that any subsequent call to this method or {@link #setProguardMapOutputPath} will | |
| * override the previous setting. | |
| * | |
| * @param proguardMapConsumer Consumer to receive the content once produced. | |
| */ | |
| @Override | |
| public Builder setProguardMapConsumer(StringConsumer proguardMapConsumer) { | |
| return super.setProguardMapConsumer(proguardMapConsumer); | |
| } | |
| /** | |
| * Set a consumer for receiving the proguard usage information. | |
| * | |
| * <p>Note that any subsequent calls to this method will replace the previous setting. | |
| * | |
| * @param proguardUsageConsumer Consumer to receive usage information. | |
| */ | |
| public Builder setProguardUsageConsumer(StringConsumer proguardUsageConsumer) { | |
| this.proguardUsageConsumer = proguardUsageConsumer; | |
| return self(); | |
| } | |
| /** | |
| * Set a consumer for receiving the proguard seeds information. | |
| * | |
| * <p>Note that any subsequent calls to this method will replace the previous setting. | |
| * | |
| * @param proguardSeedsConsumer Consumer to receive seeds information. | |
| */ | |
| public Builder setProguardSeedsConsumer(StringConsumer proguardSeedsConsumer) { | |
| this.proguardSeedsConsumer = proguardSeedsConsumer; | |
| return self(); | |
| } | |
| /** | |
| * Set a consumer for receiving the proguard configuration information. | |
| * | |
| * <p>Note that any subsequent calls to this method will replace the previous setting. | |
| * | |
| * @param proguardConfigurationConsumer | |
| */ | |
| public Builder setProguardConfigurationConsumer(StringConsumer proguardConfigurationConsumer) { | |
| this.proguardConfigurationConsumer = proguardConfigurationConsumer; | |
| return self(); | |
| } | |
| /** Get the consumer for receiving the proguard configuration information if set. */ | |
| public StringConsumer getProguardConfigurationConsumer() { | |
| return proguardConfigurationConsumer; | |
| } | |
| /** Set a consumer for receiving kept-graph events. */ | |
| public Builder setKeptGraphConsumer(GraphConsumer graphConsumer) { | |
| this.keptGraphConsumer = graphConsumer; | |
| return self(); | |
| } | |
| /** Set a consumer for receiving kept-graph events for the content of the main-dex output. */ | |
| public Builder setMainDexKeptGraphConsumer(GraphConsumer graphConsumer) { | |
| this.mainDexKeptGraphConsumer = graphConsumer; | |
| return self(); | |
| } | |
| /** | |
| * Set a consumer for receiving dependency edges for files referenced from inputs. | |
| * | |
| * <p>Note that these dependency edges are for files read when reading/parsing other input | |
| * files, such as the proguard configuration files. For compilation dependencies for incremental | |
| * desugaring see {@code setDesugarGraphConsumer}. | |
| */ | |
| public Builder setInputDependencyGraphConsumer( | |
| InputDependencyGraphConsumer inputDependencyGraphConsumer) { | |
| this.inputDependencyGraphConsumer = inputDependencyGraphConsumer; | |
| return self(); | |
| } | |
| /** | |
| * Set a consumer for receiving metadata about the current build intended for being stored in | |
| * the app bundle. | |
| */ | |
| public Builder setBuildMetadataConsumer( | |
| Consumer<? super R8BuildMetadata> buildMetadataConsumer) { | |
| this.buildMetadataConsumer = buildMetadataConsumer; | |
| return self(); | |
| } | |
| /** | |
| * Set the output path-and-mode. | |
| * | |
| * <p>Setting the output path-and-mode will override any previous set consumer or any previous | |
| * output path-and-mode, and implicitly sets the appropriate program consumer to write the | |
| * output. | |
| * | |
| * <p>By default data resources from the input will be included in the output. (see {@link | |
| * #setOutput(Path, OutputMode, boolean) for details} | |
| * | |
| * @param outputPath Path to write the output to. Must be an archive or and existing directory. | |
| * @param outputMode Mode in which to write the output. | |
| */ | |
| @Override | |
| public Builder setOutput(Path outputPath, OutputMode outputMode) { | |
| setOutput(outputPath, outputMode, true); | |
| return self(); | |
| } | |
| /** | |
| * Set the output path-and-mode and control if data resources are included. | |
| * | |
| * <p>In addition to setting the output path-and-mode (see {@link #setOutput(Path, OutputMode)}) | |
| * this can control if data resources should be included or not. | |
| * | |
| * <p>Data resources are non Java classfile items in the input. | |
| * | |
| * <p>If data resources are not included they are ignored in the input and will not produce | |
| * anything in the output. If data resources are included they are processed according to the | |
| * configuration and written to the output. | |
| * | |
| * @param outputPath Path to write the output to. Must be an archive or and existing directory. | |
| * @param outputMode Mode in which to write the output. | |
| * @param includeDataResources If data resources from the input should be included in the | |
| * output. | |
| */ | |
| @Override | |
| public Builder setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) { | |
| this.includeDataResources = Optional.of(includeDataResources); | |
| return super.setOutput(outputPath, outputMode, includeDataResources); | |
| } | |
| @Override | |
| public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) { | |
| if (programProvider instanceof InternalProgramClassProvider) { | |
| InternalProgramClassProvider internalProgramProvider = | |
| (InternalProgramClassProvider) programProvider; | |
| assert verifyNonDexProgramResourceProvider(internalProgramProvider); | |
| assert internalProgramProvider.getDataResourceProvider() == null; | |
| return super.addProgramResourceProvider(internalProgramProvider); | |
| } else if (enableExperimentalDexInput) { | |
| return super.addProgramResourceProvider(programProvider); | |
| } else { | |
| return super.addProgramResourceProvider( | |
| new EnsureNonDexProgramResourceProvider(programProvider)); | |
| } | |
| } | |
| private boolean verifyNonDexProgramResourceProvider( | |
| InternalProgramClassProvider internalProgramProvider) { | |
| for (DexProgramClass clazz : internalProgramProvider.getClasses()) { | |
| for (DexEncodedMethod method : clazz.methods()) { | |
| if (!method.hasCode()) { | |
| continue; | |
| } | |
| assert !method.getCode().isDexCode() | |
| : "Unexpected method with DEX code: " + method.toSourceString(); | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Add a {@link FeatureSplit} to the app. The {@link FeatureSplit} contains input and output | |
| * providers, that enables us to generate dynamic apps with optional modules. | |
| * | |
| * @param featureSplitGenerator A function that uses the supplied {@link FeatureSplit.Builder} | |
| * to generate a {@link FeatureSplit}. | |
| */ | |
| public Builder addFeatureSplit( | |
| Function<FeatureSplit.Builder, FeatureSplit> featureSplitGenerator) { | |
| FeatureSplit featureSplit = featureSplitGenerator.apply(FeatureSplit.builder()); | |
| List<FeatureSplitProgramResourceProvider> featureSplitProgramResourceProviders = | |
| ListUtils.map( | |
| featureSplit.getProgramResourceProviders(), FeatureSplitProgramResourceProvider::new); | |
| featureSplitConfigurationBuilder.addFeatureSplit( | |
| featureSplit, featureSplitProgramResourceProviders); | |
| featureSplitProgramResourceProviders.forEach(this::addProgramResourceProvider); | |
| return self(); | |
| } | |
| /** | |
| * Used to disable that keep rules with no member rules are implicitly converted into rules that | |
| * keep the default instance constructor. | |
| * | |
| * <p>This currently defaults to true in stable versions. | |
| */ | |
| public Builder enableLegacyFullModeForKeepRules(boolean enableLegacyFullModeForKeepRules) { | |
| parserOptionsBuilder.setEnableLegacyFullModeForKeepRules(enableLegacyFullModeForKeepRules); | |
| return this; | |
| } | |
| public Builder enableLegacyFullModeForKeepRulesWarnings( | |
| boolean enableLegacyFullModeForKeepRulesWarnings) { | |
| parserOptionsBuilder.setEnableLegacyFullModeForKeepRulesWarnings( | |
| enableLegacyFullModeForKeepRulesWarnings); | |
| return this; | |
| } | |
| /** | |
| * Used to specify if the application is using isolated splits, i.e., if split APKs installed | |
| * for this application are loaded into their own Context objects. | |
| * | |
| * <p>See also <a href="https://developer.android.com/reference/android/R.attr#isolatedSplits"> | |
| * R.attr#isolatedSplits</a>. | |
| */ | |
| public Builder setEnableIsolatedSplits(boolean enableIsolatedSplits) { | |
| featureSplitConfigurationBuilder.setEnableIsolatedSplits(enableIsolatedSplits); | |
| return this; | |
| } | |
| /** Enables experimental/pre-release support for passing DEX input to R8. */ | |
| @Deprecated | |
| public Builder setEnableExperimentalDexInput(boolean enable) { | |
| if (getAppBuilder().getProgramResourceProviders().isEmpty()) { | |
| this.enableExperimentalDexInput = enable; | |
| setIgnoreDexInArchive(enableExperimentalDexInput); | |
| } else { | |
| Reporter reporter = getReporter(); | |
| reporter.error( | |
| "Invalid attempt to call setEnableExperimentalDexInput after adding program inputs"); | |
| } | |
| return self(); | |
| } | |
| /** Add DEX-bytecode program data. */ | |
| @Deprecated | |
| public Builder addExperimentalDexProgramData(byte[] data, Origin origin) { | |
| return super.addDexProgramData(data, origin); | |
| } | |
| @Deprecated | |
| @SuppressWarnings("InlineMeSuggester") | |
| public Builder setEnableExperimentalIsolatedSplits(boolean enableIsolatedSplits) { | |
| return setEnableIsolatedSplits(enableIsolatedSplits); | |
| } | |
| /** | |
| * Enable experimental/pre-release support for modeling missing library APIs. | |
| * | |
| * <p>This allows enabling the feature while it is still default disabled by the compiler. Once | |
| * the feature is default enabled, calling this method will have no affect. | |
| */ | |
| @Deprecated | |
| public Builder setEnableExperimentalMissingLibraryApiModeling(boolean enable) { | |
| this.enableMissingLibraryApiModeling = enable; | |
| return self(); | |
| } | |
| @Deprecated | |
| public Builder setEnableExperimentalKeepAnnotations(boolean enable) { | |
| this.enableExperimentalKeepAnnotations = enable; | |
| return self(); | |
| } | |
| // Package private only for testing. Use system property | |
| // com.android.tools.r8.readEmbeddedRulesFromClasspathAndLibrary to enable. | |
| @Deprecated | |
| void setReadEmbeddedRulesFromClasspathAndLibrary(boolean enable) { | |
| this.readEmbeddedRulesFromClasspathAndLibrary = enable; | |
| } | |
| /** | |
| * Configure partial shrinking in R8, where R8 is only applied to a part of the input. | |
| * | |
| * <p>The patterns in {@code includePatterns} and {@code excludePatterns} are comma separated | |
| * lists of string patterns of fully qualified names of packages/classes. The patterns support | |
| * the wildcards {@code *} and {@code **}. The wildcards are only supported at the end of the | |
| * pattern, so only prefix matching. If the character just before the wildcard is a {@code .} | |
| * (package separator) then the difference between {@code *} and {@code **} is that {@code *} | |
| * only includes classes in the same package, whereas {@code **} includes classes in subpackages | |
| * as well. If the character before the wildcard is not a {@code .} (package separator) then | |
| * {@code *} and {@code **} will both match all classes with that prefix. | |
| * | |
| * <p>If {@code includePatterns} is not specified ({@code null} or an empty string), the default | |
| * is {@code "androidx.**,kotlin.**,kotlinx.**"}. | |
| * | |
| * <p>The include patterns are processed first collecting all possible include classes. Then the | |
| * exclude patterns are applied removing all matching classes from the collected include | |
| * classes. | |
| * | |
| * @param includePatterns patterns for classes to include in R8 shrinking (see above for | |
| * semantics) | |
| * @param excludePatterns patterns for classes to exclude from R8 shrinking (see above for | |
| * semantics) | |
| * @return | |
| */ | |
| @Deprecated | |
| public Builder enableExperimentalPartialShrinking( | |
| String includePatterns, String excludePatterns) { | |
| if (partialCompilationConfiguration.isEnabled()) { | |
| getReporter().warning("Overriding partial compilation specified in system properties."); | |
| } | |
| if (includePatterns == null || includePatterns.isEmpty()) { | |
| includePatterns = "androidx.**,kotlin.**,kotlinx.**"; | |
| } | |
| partialCompilationConfiguration = | |
| R8PartialCompilationConfiguration.fromIncludeExcludePatterns( | |
| includePatterns, excludePatterns); | |
| return self(); | |
| } | |
| Builder setPartialCompilationConfiguration( | |
| R8PartialCompilationConfiguration partialCompilationConfiguration) { | |
| this.partialCompilationConfiguration = partialCompilationConfiguration; | |
| return this; | |
| } | |
| /** | |
| * Add {@link PartialOptimizationConfigurationProvider}s to enable R8 optimizations on a part of | |
| * the program only. The {@link PartialOptimizationConfigurationProvider}s specifies exactly | |
| * which classes R8 can optimize. | |
| * | |
| * @param providers instances implementing {@link PartialOptimizationConfigurationProvider} to | |
| * provide the partial compilation configuration. | |
| */ | |
| public Builder addPartialOptimizationConfigurationProviders( | |
| PartialOptimizationConfigurationProvider... providers) { | |
| partialOptimizationConfigurationProviders.addAll(Arrays.asList(providers)); | |
| return this; | |
| } | |
| /** | |
| * Add a collection of startup profile providers that should be used for distributing the | |
| * program classes in DEX. The given startup profiles are also used to disallow optimizations | |
| * across the startup and post-startup boundary. | |
| * | |
| * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does | |
| * not support native multi-DEX (API<=20). | |
| */ | |
| @Override | |
| public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) { | |
| return super.addStartupProfileProviders(startupProfileProviders); | |
| } | |
| /** | |
| * Add a collection of startup profile providers that should be used for distributing the | |
| * program classes in DEX, unless turned off using {@link #setEnableStartupLayoutOptimization}. | |
| * The given startup profiles are also used to disallow optimizations across the startup and | |
| * post-startup boundary. | |
| * | |
| * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does | |
| * not support native multi-DEX (API<=20). | |
| */ | |
| @Override | |
| public Builder addStartupProfileProviders( | |
| Collection<StartupProfileProvider> startupProfileProviders) { | |
| return super.addStartupProfileProviders(startupProfileProviders); | |
| } | |
| /** | |
| * API for specifying whether R8 should use the provided startup profiles to layout the DEX. | |
| * When this is set to {@code false}, the given startup profiles are then only used to disallow | |
| * optimizations across the startup and post-startup boundary. | |
| * | |
| * <p>Defaults to true. | |
| */ | |
| public Builder setEnableStartupLayoutOptimization(boolean enable) { | |
| enableStartupLayoutOptimization = enable; | |
| return this; | |
| } | |
| /** | |
| * Exprimental API for supporting android resource shrinking. | |
| * | |
| * <p>Add an android resource provider, providing the resource table, manifest and res table | |
| * entries. | |
| */ | |
| public Builder setAndroidResourceProvider(AndroidResourceProvider provider) { | |
| this.androidResourceProvider = provider; | |
| return this; | |
| } | |
| /** | |
| * Exprimental API for supporting android resource shrinking. | |
| * | |
| * <p>Add an android resource consumer, consuming the resource table, manifest and res table | |
| * entries. | |
| */ | |
| public Builder setAndroidResourceConsumer(AndroidResourceConsumer consumer) { | |
| this.androidResourceConsumer = consumer; | |
| return this; | |
| } | |
| /** | |
| * API for retrieving the proto message backing the R8 Configuration Analysis HTML Report. The | |
| * corresponding schema is defined in src/keepradius/proto/keepradius.proto. | |
| */ | |
| public Builder setConfigurationAnalysisDataConsumer( | |
| ByteArrayConsumer<?> configurationAnalysisDataConsumer) { | |
| this.configurationAnalysisDataConsumer = configurationAnalysisDataConsumer; | |
| return this; | |
| } | |
| /** API for retrieving the R8 Configuration Analysis HTML Report. */ | |
| public Builder setConfigurationAnalysisHtmlReportConsumer( | |
| StringConsumer configurationAnalysisHtmlReportConsumer) { | |
| this.configurationAnalysisHtmlReportConsumer = configurationAnalysisHtmlReportConsumer; | |
| return this; | |
| } | |
| /** | |
| * API for configuring resource shrinking. | |
| * | |
| * <p>Set the configuration properties on the provided builder. | |
| */ | |
| public Builder setResourceShrinkerConfiguration( | |
| Function<ResourceShrinkerConfiguration.Builder, ResourceShrinkerConfiguration> | |
| configurationBuilder) { | |
| this.resourceShrinkerConfiguration = | |
| configurationBuilder.apply(ResourceShrinkerConfiguration.builder(getReporter())); | |
| return this; | |
| } | |
| /** | |
| * By default, R8 uses the same naming scheme for synthetic classes as javac uses for anonymous | |
| * inner classes. By enabling verbose synthetic names, the synthetic classes will include a | |
| * "$$ExternalSynthetic" marker, which includes the synthetic kind (e.g., "Lambda"). | |
| */ | |
| @Override | |
| public Builder setEnableVerboseSyntheticNames(boolean enableVerboseSyntheticNames) { | |
| return super.setEnableVerboseSyntheticNames(enableVerboseSyntheticNames); | |
| } | |
| // TODO(b/481400921): Remove experimental. | |
| /** | |
| * Enable tracing of native references by adding a consumer. | |
| * | |
| * <p>THIS IS STILL AN EXPERIMENTAL API! | |
| * | |
| * @param nativeReferencesConsumer Consumer for native references | |
| */ | |
| @Deprecated | |
| public Builder setNativeReferencesConsumer( | |
| TraceReferencesNativeReferencesConsumer nativeReferencesConsumer) { | |
| this.nativeReferencesConsumer = nativeReferencesConsumer; | |
| return this; | |
| } | |
| @Override | |
| void validate() { | |
| if (isPrintHelp()) { | |
| return; | |
| } | |
| Reporter reporter = getReporter(); | |
| if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { | |
| reporter.error("R8 does not support compiling to a single DEX file per Java class file"); | |
| } | |
| if (getMainDexListConsumer() != null && !hasMainDexList() && !hasMainDexRules()) { | |
| reporter.error( | |
| "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list"); | |
| } | |
| if (!(getProgramConsumer() instanceof ClassFileConsumer) && hasNativeMultidex()) { | |
| if (getMainDexListConsumer() != null || hasMainDexRules() || hasMainDexList()) { | |
| reporter.error( | |
| "R8 does not support main-dex inputs and outputs when compiling to API level " | |
| + AndroidApiLevel.L.getMajor() | |
| + " and above"); | |
| } | |
| } | |
| for (FeatureSplit featureSplit : featureSplitConfigurationBuilder.getFeatureSplits()) { | |
| verifyResourceSplitOrProgramSplit(featureSplit); | |
| if (getProgramConsumer() != null && !(getProgramConsumer() instanceof DexIndexedConsumer)) { | |
| reporter.error("R8 does not support class file output when using feature splits"); | |
| } | |
| } | |
| if (!enableExperimentalDexInput) { | |
| for (Path file : programFiles) { | |
| if (FileUtils.isDexFile(file)) { | |
| reporter.error( | |
| new StringDiagnostic( | |
| "R8 does not support compiling DEX inputs", new PathOrigin(file))); | |
| } | |
| } | |
| } | |
| if (getProgramConsumer() instanceof ClassFileConsumer && isMinApiLevelSet()) { | |
| reporter.error("R8 does not support --min-api when compiling to class files"); | |
| } | |
| if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) { | |
| reporter.error("Using desugared library configuration requires desugaring to be enabled"); | |
| } | |
| buildAndValidateR8Partial(); | |
| super.validate(); | |
| } | |
| private void buildAndValidateR8Partial() { | |
| if (!partialCompilationConfiguration.isEnabled() | |
| && partialOptimizationConfigurationProviders.isEmpty()) { | |
| return; | |
| } | |
| Reporter reporter = getReporter(); | |
| if (partialCompilationConfiguration.isEnabled() | |
| && !partialOptimizationConfigurationProviders.isEmpty()) { | |
| reporter.error("Cannot mix experimental partial compilation with partial optimization"); | |
| } | |
| if (!partialOptimizationConfigurationProviders.isEmpty()) { | |
| assert !partialCompilationConfiguration.isEnabled(); | |
| R8PartialCompilationConfiguration.Builder configurationBuilder = | |
| R8PartialCompilationConfiguration.builder(); | |
| partialOptimizationConfigurationProviders.forEach( | |
| provider -> provider.getPartialOptimizationConfiguration(configurationBuilder)); | |
| partialCompilationConfiguration = configurationBuilder.build(); | |
| } | |
| if (!(getProgramConsumer() instanceof DexIndexedConsumer)) { | |
| reporter.error("Partial shrinking does not support generating class files"); | |
| } | |
| if (!hasNativeMultidex()) { | |
| reporter.error("Partial shrinking requires min API level >= 21"); | |
| } | |
| if (forceProguardCompatibility) { | |
| reporter.error("Partial shrinking does not support Proguard compatibility mode"); | |
| } | |
| if (androidResourceProvider != null | |
| && !resourceShrinkerConfiguration.isOptimizedShrinking()) { | |
| reporter.warning( | |
| "Partial shrinking only supports optimized resource shrinking. " | |
| + "Optimized resource shrinking will be enabled implicitly."); | |
| } | |
| } | |
| private void validateProguardConfiguration(ProguardConfiguration configuration) { | |
| Reporter reporter = getReporter(); | |
| if (partialCompilationConfiguration.isEnabled()) { | |
| if (configuration.isProtoShrinkingEnabled()) { | |
| reporter.error("Partial shrinking does not support -shrinkunusedprotofields"); | |
| } | |
| } | |
| } | |
| private boolean hasMainDexList() { | |
| return getAppBuilder().hasMainDexList(); | |
| } | |
| private boolean hasMainDexRules() { | |
| return !mainDexRules.isEmpty(); | |
| } | |
| private static void verifyResourceSplitOrProgramSplit(FeatureSplit featureSplit) { | |
| assert featureSplit.getProgramConsumer() instanceof DexIndexedConsumer | |
| || featureSplit.getAndroidResourceProvider() != null; | |
| } | |
| @Override | |
| R8Command makeCommand() { | |
| // If printing versions ignore everything else. | |
| if (isPrintHelp() || isPrintVersion()) { | |
| return new R8Command(isPrintHelp(), isPrintVersion()); | |
| } | |
| DexItemFactory factory = new DexItemFactory(); | |
| return makeR8Command(factory, makeConfiguration(factory)); | |
| } | |
| R8Command makeR8Command(DexItemFactory factory, ProguardConfiguration configuration) { | |
| Reporter reporter = getReporter(); | |
| List<ProguardConfigurationRule> mainDexKeepRules = | |
| ProguardConfigurationParser.parseMainDex(mainDexRules, factory, reporter); | |
| DesugaredLibrarySpecification desugaredLibrarySpecification = | |
| getDesugaredLibraryConfiguration(factory, false); | |
| getAppBuilder().addFilteredLibraryArchives(configuration.getLibraryjars()); | |
| assert getProgramConsumer() != null; | |
| DesugarState desugaring = | |
| (getProgramConsumer() instanceof ClassFileConsumer) | |
| ? DesugarState.OFF | |
| : getDesugaringState(); | |
| R8Command command = | |
| new R8Command( | |
| getAppBuilder().build(), | |
| getProgramConsumer(), | |
| mainDexKeepRules, | |
| getMainDexListConsumer(), | |
| configuration, | |
| getMode(), | |
| getUncheckedMinApiLevel(), | |
| reporter, | |
| desugaring, | |
| configuration.isShrinking(), | |
| configuration.isObfuscating(), | |
| enableVerboseSyntheticNames, | |
| forceProguardCompatibility, | |
| protectApiSurface, | |
| includeDataResources, | |
| proguardMapConsumer, | |
| partitionMapConsumer, | |
| proguardUsageConsumer, | |
| proguardSeedsConsumer, | |
| proguardConfigurationConsumer, | |
| keptGraphConsumer, | |
| mainDexKeptGraphConsumer, | |
| nativeReferencesConsumer, | |
| syntheticProguardRulesConsumer, | |
| isOptimizeMultidexForLinearAlloc(), | |
| getIncludeClassesChecksum(), | |
| getDexClassChecksumFilter(), | |
| desugaredLibrarySpecification, | |
| featureSplitConfigurationBuilder.build(factory), | |
| getAssertionsConfiguration(), | |
| getOutputInspections(), | |
| synthesizedClassPrefix, | |
| getThreadCount(), | |
| getDumpInputFlags(), | |
| getMapIdProvider(), | |
| getSourceFileProvider(), | |
| enableMissingLibraryApiModeling, | |
| enableStartupLayoutOptimization, | |
| getAndroidPlatformBuild(), | |
| getArtProfilesForRewriting(), | |
| getStartupProfileProviders(), | |
| getClassConflictResolver(), | |
| getCancelCompilationChecker(), | |
| androidResourceProvider, | |
| androidResourceConsumer, | |
| configurationAnalysisDataConsumer, | |
| configurationAnalysisHtmlReportConsumer, | |
| resourceShrinkerConfiguration, | |
| keepSpecifications, | |
| buildMetadataConsumer, | |
| partialCompilationConfiguration, | |
| getApiDatabasePath()); | |
| if (inputDependencyGraphConsumer != null) { | |
| inputDependencyGraphConsumer.finished(); | |
| } | |
| return command; | |
| } | |
| private ProguardConfiguration makeConfiguration(DexItemFactory factory) { | |
| ProguardConfigurationParserOptions parserOptions = | |
| parserOptionsBuilder | |
| .setCanMatchRuntimeInvisibleAnnotationsWithWildcards( | |
| getProgramConsumer() instanceof ClassFileConsumer) | |
| .setForceProguardCompatibility(forceProguardCompatibility) | |
| .build(); | |
| ProguardConfiguration.Builder configurationBuilder = | |
| ProguardConfiguration.builder(factory, getReporter()) | |
| .setForceProguardCompatibility(forceProguardCompatibility); | |
| ProguardConfigurationParser parser = | |
| new ProguardConfigurationParser( | |
| factory, | |
| getReporter(), | |
| parserOptions, | |
| inputDependencyGraphConsumer, | |
| configurationBuilder); | |
| if (!proguardConfigs.isEmpty()) { | |
| parser.parse(proguardConfigs); | |
| } | |
| if (getMode() == CompilationMode.DEBUG) { | |
| disableMinification = true; | |
| configurationBuilder.disableOptimization(); | |
| } | |
| if (disableTreeShaking) { | |
| configurationBuilder.disableShrinking(); | |
| } | |
| if (disableMinification) { | |
| configurationBuilder.disableObfuscation(); | |
| } | |
| if (proguardConfigurationConsumerForTesting != null) { | |
| proguardConfigurationConsumerForTesting.accept(configurationBuilder); | |
| } | |
| // Add embedded keep rules. | |
| amendWithRulesAndProvidersForInjarsAndMetaInf(getReporter(), parser, configurationBuilder); | |
| // Extract out rules for keep annotations and amend the configuration. | |
| // TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer. | |
| extractKeepAnnotationRules(parser); | |
| ProguardConfiguration configuration = configurationBuilder.build(parserOptions); | |
| validateProguardConfiguration(configuration); | |
| return configuration; | |
| } | |
| private void amendWithRulesAndProvidersForInjarsAndMetaInf( | |
| Reporter reporter, | |
| ProguardConfigurationParser parser, | |
| ProguardConfiguration.Builder configurationBuilder) { | |
| Supplier<SemanticVersion> semanticVersionSupplier = | |
| SemanticVersionUtils.compilerVersionSemanticVersionSupplier( | |
| fakeCompilerVersion, | |
| "Using an artificial version newer than any known version for selecting" | |
| + " Proguard configurations embedded under META-INF/. This means that" | |
| + " all rules with a '-upto-' qualifier will be excluded and all rules" | |
| + " with a -from- qualifier will be included.", | |
| reporter); | |
| Set<FilteredClassPath> seen = SetUtils.newIdentityHashSet(); | |
| // Find resources in program providers. Both from API and added through legacy -injars in | |
| // configuration files. | |
| List<DataResourceProvider> providers = | |
| new ArrayList<>( | |
| getAppBuilder().getProgramResourceProviders().stream() | |
| .map(ProgramResourceProvider::getDataResourceProvider) | |
| .filter(Objects::nonNull) | |
| .collect(Collectors.toList())); | |
| for (FilteredClassPath injar : configurationBuilder.getInjars()) { | |
| if (seen.add(injar)) { | |
| ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar); | |
| if (provider != null) { | |
| providers.add(provider); | |
| } | |
| } | |
| } | |
| if (readEmbeddedRulesFromClasspathAndLibrary) { | |
| // Find resources in classpath providers. No legacy configuration file option for classpath. | |
| getAppBuilder().getClasspathResourceProviders().stream() | |
| .map(ClassFileResourceProvider::getDataResourceProvider) | |
| .filter(Objects::nonNull) | |
| .forEach(providers::add); | |
| // Find resources in library providers. Both from API and added through legacy -libraryjars | |
| // in configuration files. | |
| getAppBuilder().getLibraryResourceProviders().stream() | |
| .map(ClassFileResourceProvider::getDataResourceProvider) | |
| .filter(Objects::nonNull) | |
| .forEach(providers::add); | |
| for (FilteredClassPath libraryjar : configurationBuilder.getLibraryJars()) { | |
| if (seen.add(libraryjar)) { | |
| ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(libraryjar); | |
| if (provider != null) { | |
| providers.add(provider); | |
| } | |
| } | |
| } | |
| } | |
| for (DataResourceProvider provider : providers) { | |
| parseEmbeddedRules(reporter, parser, semanticVersionSupplier, provider); | |
| } | |
| } | |
| private static void parseEmbeddedRules( | |
| Reporter reporter, | |
| ProguardConfigurationParser parser, | |
| Supplier<SemanticVersion> semanticVersionSupplier, | |
| DataResourceProvider dataResourceProvider) { | |
| try { | |
| EmbeddedRulesExtractor embeddedProguardConfigurationVisitor = | |
| new EmbeddedRulesExtractor(semanticVersionSupplier, dataResourceProvider, reporter); | |
| embeddedProguardConfigurationVisitor.readSources().parseRelevantRules(parser); | |
| } catch (ResourceException e) { | |
| reporter.error(new ExceptionDiagnostic(e)); | |
| } | |
| } | |
| private void extractKeepAnnotationRules(ProguardConfigurationParser parser) { | |
| if (!enableExperimentalKeepAnnotations) { | |
| return; | |
| } | |
| try { | |
| for (ProgramResourceProvider provider : getAppBuilder().getProgramResourceProviders()) { | |
| ProgramResourceProviderUtils.forEachProgramResourceCompat( | |
| provider, | |
| programResource -> { | |
| if (programResource.getKind() == Kind.CF) { | |
| List<KeepDeclaration> declarations = | |
| KeepEdgeReader.readKeepEdges( | |
| ProgramResourceUtils.getBytesUnchecked(programResource)); | |
| if (!declarations.isEmpty()) { | |
| KeepRuleExtractor extractor = | |
| new KeepRuleExtractor( | |
| rule -> { | |
| ProguardConfigurationSourceStrings source = | |
| new ProguardConfigurationSourceStrings( | |
| rule, null, programResource.getOrigin()); | |
| parser.parse(source); | |
| }); | |
| declarations.forEach(extractor::extract); | |
| } | |
| } | |
| }); | |
| } | |
| } catch (ResourceException e) { | |
| throw getAppBuilder().getReporter().fatalError(new ExceptionDiagnostic(e)); | |
| } | |
| } | |
| // Internal for-testing method to add post-processors of the proguard configuration. | |
| void addProguardConfigurationConsumerForTesting(Consumer<ProguardConfiguration.Builder> c) { | |
| Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumerForTesting; | |
| proguardConfigurationConsumerForTesting = | |
| builder -> { | |
| if (oldConsumer != null) { | |
| oldConsumer.accept(builder); | |
| } | |
| c.accept(builder); | |
| }; | |
| } | |
| void addSyntheticProguardRulesConsumerForTesting( | |
| Consumer<List<ProguardConfigurationRule>> consumer) { | |
| syntheticProguardRulesConsumer = | |
| syntheticProguardRulesConsumer == null | |
| ? consumer | |
| : syntheticProguardRulesConsumer.andThen(consumer); | |
| } | |
| // Internal for-testing method to allow proguard options only available for testing. | |
| void setEnableTestProguardOptions() { | |
| parserOptionsBuilder.setEnableTestingOptions(true); | |
| } | |
| } | |
| // Wrapper class to ensure that R8 does not allow DEX as program inputs. | |
| public static class EnsureNonDexProgramResourceProvider implements ProgramResourceProvider { | |
| final ProgramResourceProvider provider; | |
| public EnsureNonDexProgramResourceProvider(ProgramResourceProvider provider) { | |
| this.provider = provider; | |
| } | |
| @Override | |
| public void getProgramResources(Consumer<ProgramResource> consumer) throws ResourceException { | |
| Box<ProgramResource> dexResource = new Box<>(); | |
| ProgramResourceProviderUtils.forEachProgramResourceCompat( | |
| provider, | |
| resource -> { | |
| if (resource.getKind() == Kind.CF) { | |
| consumer.accept(resource); | |
| } else { | |
| assert resource.getKind() == Kind.DEX; | |
| dexResource.set(resource); | |
| } | |
| }); | |
| if (dexResource.isSet()) { | |
| throw new ResourceException( | |
| dexResource.get().getOrigin(), "R8 does not support compiling DEX inputs"); | |
| } | |
| } | |
| @Deprecated | |
| @Override | |
| public Collection<ProgramResource> getProgramResources() throws ResourceException { | |
| Collection<ProgramResource> resources = provider.getProgramResources(); | |
| for (ProgramResource resource : resources) { | |
| if (resource.getKind() == Kind.DEX) { | |
| throw new ResourceException( | |
| resource.getOrigin(), "R8 does not support compiling DEX inputs"); | |
| } | |
| } | |
| return resources; | |
| } | |
| @Override | |
| public DataResourceProvider getDataResourceProvider() { | |
| return provider.getDataResourceProvider(); | |
| } | |
| public static ProgramResourceProvider unwrap(ProgramResourceProvider provider) { | |
| if (provider instanceof EnsureNonDexProgramResourceProvider) { | |
| EnsureNonDexProgramResourceProvider wrapper = | |
| (EnsureNonDexProgramResourceProvider) provider; | |
| return wrapper.provider; | |
| } | |
| return provider; | |
| } | |
| } | |
| static String getUsageMessage() { | |
| return R8CommandParser.getUsageMessage(); | |
| } | |
| private final List<ProguardConfigurationRule> mainDexKeepRules; | |
| private final ProguardConfiguration proguardConfiguration; | |
| private final List<KeepSpecificationSource> keepSpecifications; | |
| private final boolean enableTreeShaking; | |
| private final boolean enableMinification; | |
| private final boolean forceProguardCompatibility; | |
| private final boolean protectApiSurface; | |
| private final Optional<Boolean> includeDataResources; | |
| private final MapConsumer proguardMapConsumer; | |
| private final PartitionMapConsumer partitionMapConsumer; | |
| private final StringConsumer proguardUsageConsumer; | |
| private final StringConsumer proguardSeedsConsumer; | |
| private final StringConsumer proguardConfigurationConsumer; | |
| private final GraphConsumer keptGraphConsumer; | |
| private final GraphConsumer mainDexKeptGraphConsumer; | |
| private final TraceReferencesNativeReferencesConsumer nativeReferencesConsumer; | |
| private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer; | |
| private final DesugaredLibrarySpecification desugaredLibrarySpecification; | |
| private final FeatureSplitConfiguration featureSplitConfiguration; | |
| private final String synthesizedClassPrefix; | |
| private final boolean enableMissingLibraryApiModeling; | |
| private final boolean enableStartupLayoutOptimization; | |
| private final AndroidResourceProvider androidResourceProvider; | |
| private final AndroidResourceConsumer androidResourceConsumer; | |
| private final ByteArrayConsumer<?> configurationAnalysisDataConsumer; | |
| private final StringConsumer configurationAnalysisHtmlReportConsumer; | |
| private final ResourceShrinkerConfiguration resourceShrinkerConfiguration; | |
| private final Consumer<? super R8BuildMetadata> buildMetadataConsumer; | |
| private final R8PartialCompilationConfiguration partialCompilationConfiguration; | |
| /** Get a new {@link R8Command.Builder}. */ | |
| public static Builder builder() { | |
| return new Builder(); | |
| } | |
| /** Get a new {@link R8Command.Builder} using a custom defined diagnostics handler. */ | |
| public static Builder builder(DiagnosticsHandler diagnosticsHandler) { | |
| return new Builder(diagnosticsHandler); | |
| } | |
| // Internal builder to start from an existing AndroidApp. | |
| static Builder builder(AndroidApp app) { | |
| return new Builder(app); | |
| } | |
| // Internal builder to start from an existing AndroidApp. | |
| static Builder builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) { | |
| return new Builder(app, diagnosticsHandler); | |
| } | |
| /** | |
| * Parse the R8 command-line. | |
| * | |
| * <p>Parsing will set the supplied options or their default value if they have any. | |
| * | |
| * @param args Command-line arguments array. | |
| * @param origin Origin description of the command-line arguments. | |
| * @return R8 command builder with state set up according to parsed command line. | |
| */ | |
| public static Builder parse(String[] args, Origin origin) { | |
| return R8CommandParser.parse(args, origin); | |
| } | |
| /** | |
| * Parse the R8 command-line. | |
| * | |
| * <p>Parsing will set the supplied options or their default value if they have any. | |
| * | |
| * @param args Command-line arguments array. | |
| * @param origin Origin description of the command-line arguments. | |
| * @param handler Custom defined diagnostics handler. | |
| * @return R8 command builder with state set up according to parsed command line. | |
| */ | |
| public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) { | |
| return R8CommandParser.parse(args, origin, handler); | |
| } | |
| /** Get the help description for the R8 supported flags. */ | |
| public static List<ParseFlagInfo> getParseFlagsInformation() { | |
| return ImmutableList.copyOf(R8CommandParser.getFlags()); | |
| } | |
| private R8Command( | |
| AndroidApp inputApp, | |
| ProgramConsumer programConsumer, | |
| List<ProguardConfigurationRule> mainDexKeepRules, | |
| StringConsumer mainDexListConsumer, | |
| ProguardConfiguration proguardConfiguration, | |
| CompilationMode mode, | |
| UncheckedApiLevel minApiLevel, | |
| Reporter reporter, | |
| DesugarState enableDesugaring, | |
| boolean enableTreeShaking, | |
| boolean enableMinification, | |
| boolean enableVerboseSyntheticNames, | |
| boolean forceProguardCompatibility, | |
| boolean protectApiSurface, | |
| Optional<Boolean> includeDataResources, | |
| MapConsumer proguardMapConsumer, | |
| PartitionMapConsumer partitionMapConsumer, | |
| StringConsumer proguardUsageConsumer, | |
| StringConsumer proguardSeedsConsumer, | |
| StringConsumer proguardConfigurationConsumer, | |
| GraphConsumer keptGraphConsumer, | |
| GraphConsumer mainDexKeptGraphConsumer, | |
| TraceReferencesNativeReferencesConsumer nativeReferencesConsumer, | |
| Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer, | |
| boolean optimizeMultidexForLinearAlloc, | |
| boolean encodeChecksum, | |
| BiPredicate<String, Long> dexClassChecksumFilter, | |
| DesugaredLibrarySpecification desugaredLibrarySpecification, | |
| FeatureSplitConfiguration featureSplitConfiguration, | |
| List<AssertionsConfiguration> assertionsConfiguration, | |
| List<Consumer<Inspector>> outputInspections, | |
| String synthesizedClassPrefix, | |
| int threadCount, | |
| DumpInputFlags dumpInputFlags, | |
| MapIdProvider mapIdProvider, | |
| SourceFileProvider sourceFileProvider, | |
| boolean enableMissingLibraryApiModeling, | |
| boolean enableStartupLayoutOptimization, | |
| boolean isAndroidPlatformBuild, | |
| List<ArtProfileForRewriting> artProfilesForRewriting, | |
| List<StartupProfileProvider> startupProfileProviders, | |
| ClassConflictResolver classConflictResolver, | |
| CancelCompilationChecker cancelCompilationChecker, | |
| AndroidResourceProvider androidResourceProvider, | |
| AndroidResourceConsumer androidResourceConsumer, | |
| ByteArrayConsumer<?> configurationAnalysisDataConsumer, | |
| StringConsumer configurationAnalysisHtmlReportConsumer, | |
| ResourceShrinkerConfiguration resourceShrinkerConfiguration, | |
| List<KeepSpecificationSource> keepSpecifications, | |
| Consumer<? super R8BuildMetadata> buildMetadataConsumer, | |
| R8PartialCompilationConfiguration partialCompilationConfiguration, | |
| Path apiDatabasePath) { | |
| super( | |
| inputApp, | |
| mode, | |
| programConsumer, | |
| mainDexListConsumer, | |
| minApiLevel, | |
| reporter, | |
| enableDesugaring, | |
| optimizeMultidexForLinearAlloc, | |
| encodeChecksum, | |
| dexClassChecksumFilter, | |
| assertionsConfiguration, | |
| outputInspections, | |
| threadCount, | |
| dumpInputFlags, | |
| mapIdProvider, | |
| sourceFileProvider, | |
| isAndroidPlatformBuild, | |
| artProfilesForRewriting, | |
| startupProfileProviders, | |
| classConflictResolver, | |
| cancelCompilationChecker, | |
| enableVerboseSyntheticNames, | |
| apiDatabasePath); | |
| assert proguardConfiguration != null; | |
| assert mainDexKeepRules != null; | |
| this.mainDexKeepRules = mainDexKeepRules; | |
| this.proguardConfiguration = proguardConfiguration; | |
| this.keepSpecifications = keepSpecifications; | |
| this.enableTreeShaking = enableTreeShaking; | |
| this.enableMinification = enableMinification; | |
| this.forceProguardCompatibility = forceProguardCompatibility; | |
| this.protectApiSurface = protectApiSurface; | |
| this.includeDataResources = includeDataResources; | |
| this.proguardMapConsumer = proguardMapConsumer; | |
| this.partitionMapConsumer = partitionMapConsumer; | |
| this.proguardUsageConsumer = proguardUsageConsumer; | |
| this.proguardSeedsConsumer = proguardSeedsConsumer; | |
| this.proguardConfigurationConsumer = proguardConfigurationConsumer; | |
| this.keptGraphConsumer = keptGraphConsumer; | |
| this.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer; | |
| this.nativeReferencesConsumer = nativeReferencesConsumer; | |
| this.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer; | |
| this.desugaredLibrarySpecification = desugaredLibrarySpecification; | |
| this.featureSplitConfiguration = featureSplitConfiguration; | |
| this.synthesizedClassPrefix = synthesizedClassPrefix; | |
| this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling; | |
| this.enableStartupLayoutOptimization = enableStartupLayoutOptimization; | |
| this.androidResourceProvider = androidResourceProvider; | |
| this.androidResourceConsumer = androidResourceConsumer; | |
| this.configurationAnalysisDataConsumer = configurationAnalysisDataConsumer; | |
| this.configurationAnalysisHtmlReportConsumer = configurationAnalysisHtmlReportConsumer; | |
| this.resourceShrinkerConfiguration = resourceShrinkerConfiguration; | |
| this.buildMetadataConsumer = buildMetadataConsumer; | |
| this.partialCompilationConfiguration = partialCompilationConfiguration; | |
| } | |
| private R8Command(boolean printHelp, boolean printVersion) { | |
| super(printHelp, printVersion); | |
| mainDexKeepRules = ImmutableList.of(); | |
| proguardConfiguration = null; | |
| keepSpecifications = null; | |
| enableTreeShaking = false; | |
| enableMinification = false; | |
| forceProguardCompatibility = false; | |
| protectApiSurface = false; | |
| includeDataResources = null; | |
| proguardMapConsumer = null; | |
| partitionMapConsumer = null; | |
| proguardUsageConsumer = null; | |
| proguardSeedsConsumer = null; | |
| proguardConfigurationConsumer = null; | |
| keptGraphConsumer = null; | |
| mainDexKeptGraphConsumer = null; | |
| nativeReferencesConsumer = null; | |
| syntheticProguardRulesConsumer = null; | |
| desugaredLibrarySpecification = null; | |
| featureSplitConfiguration = null; | |
| synthesizedClassPrefix = null; | |
| enableMissingLibraryApiModeling = false; | |
| enableStartupLayoutOptimization = true; | |
| androidResourceProvider = null; | |
| androidResourceConsumer = null; | |
| configurationAnalysisDataConsumer = null; | |
| configurationAnalysisHtmlReportConsumer = null; | |
| resourceShrinkerConfiguration = null; | |
| buildMetadataConsumer = null; | |
| partialCompilationConfiguration = null; | |
| } | |
| public DexItemFactory getDexItemFactory() { | |
| return proguardConfiguration.getDexItemFactory(); | |
| } | |
| /** Get the enable-tree-shaking state. */ | |
| public boolean getEnableTreeShaking() { | |
| return enableTreeShaking; | |
| } | |
| /** Get the enable-minification state. */ | |
| public boolean getEnableMinification() { | |
| return enableMinification; | |
| } | |
| /** Get the Proguard compatibility state. */ | |
| public boolean getProguardCompatibility() { | |
| return forceProguardCompatibility; | |
| } | |
| @Override | |
| InternalOptions getInternalOptions() { | |
| InternalOptions internal = new InternalOptions(getMode(), proguardConfiguration, getReporter()); | |
| assert !internal.testing.allowOutlinerInterfaceArrayArguments; // Only allow in tests. | |
| internal.programConsumer = getProgramConsumer(); | |
| internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getUncheckedMinApiLevel())); | |
| internal.apiModelingOptions().apiDatabasePath = getApiDatabasePath(); | |
| internal.desugarState = getDesugarState(); | |
| internal.desugarSpecificOptions().enableVerboseSyntheticNames = enableVerboseSyntheticNames; | |
| assert internal.isShrinking() == getEnableTreeShaking(); | |
| assert internal.isMinifying() == getEnableMinification(); | |
| assert !internal.ignoreMissingClasses; | |
| internal.ignoreMissingClasses = | |
| proguardConfiguration.isIgnoreWarnings() | |
| || (forceProguardCompatibility | |
| && !internal.isOptimizing() | |
| && !internal.isShrinking() | |
| && !internal.isMinifying()); | |
| internal.setKeepSpecificationSources(keepSpecifications); | |
| assert !internal.verbose; | |
| internal.mainDexKeepRules = mainDexKeepRules; | |
| internal.minimalMainDex = internal.debug; | |
| internal.mainDexListConsumer = getMainDexListConsumer(); | |
| internal.lineNumberOptimization = | |
| (internal.isOptimizing() || internal.isMinifying()) | |
| ? LineNumberOptimization.ON | |
| : LineNumberOptimization.OFF; | |
| MappingComposeOptions mappingComposeOptions = internal.mappingComposeOptions(); | |
| mappingComposeOptions.enableExperimentalMappingComposition = true; | |
| assert !internal.enableTreeShakingOfLibraryMethodOverrides; | |
| if (!internal.isShrinking()) { | |
| // If R8 is not shrinking, there is no point in running various optimizations since the | |
| // optimized classes will still remain in the program (the application size could increase). | |
| internal.enableEnumUnboxing = false; | |
| } | |
| // Amend the proguard-map consumer with options from the proguard configuration. | |
| MapConsumer mapConsumer = | |
| wrapMapConsumer( | |
| proguardMapConsumer, | |
| proguardConfiguration.isPrintMapping(), | |
| proguardConfiguration.getPrintMappingFile()); | |
| InternalMapConsumer internalMapConsumer = | |
| wrapExistingInternalMapConsumerIfNotNull( | |
| internal.mapConsumer, partitionMapConsumer, MapConsumerToPartitionMapConsumer::new); | |
| internalMapConsumer = | |
| wrapExistingInternalMapConsumerIfNotNull( | |
| internalMapConsumer, | |
| mapConsumer, | |
| nonNullStringConsumer -> | |
| InternalMapConsumerImpl.builder().setMapConsumer(mapConsumer).build()); | |
| internal.mapConsumer = | |
| wrapExistingInternalMapConsumerIfNotNull( | |
| internalMapConsumer, | |
| androidResourceConsumer, | |
| nonNulStringConsumer -> new ResourceShrinkerMapStringConsumer(internal)); | |
| // Amend the usage information consumer with options from the proguard configuration. | |
| internal.usageInformationConsumer = | |
| wrapStringConsumer( | |
| proguardUsageConsumer, | |
| proguardConfiguration.isPrintUsage(), | |
| proguardConfiguration.getPrintUsageFile()); | |
| // Amend the pg-seeds consumer with options from the proguard configuration. | |
| internal.proguardSeedsConsumer = | |
| wrapStringConsumer( | |
| proguardSeedsConsumer, | |
| proguardConfiguration.isPrintSeeds(), | |
| proguardConfiguration.getSeedFile()); | |
| // Amend the configuration consumer with options from the proguard configuration. | |
| internal.configurationConsumer = | |
| wrapStringConsumer( | |
| proguardConfigurationConsumer, | |
| proguardConfiguration.isPrintConfiguration(), | |
| proguardConfiguration.getPrintConfigurationFile()); | |
| // Set the kept-graph consumer if any. It will only be actively used if the enqueuer triggers. | |
| internal.keptGraphConsumer = keptGraphConsumer; | |
| internal.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer; | |
| internal.nativeReferencesConsumer = nativeReferencesConsumer; | |
| internal.r8BuildMetadataConsumer = buildMetadataConsumer; | |
| internal.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer(); | |
| internal.setFeatureSplitConfiguration(featureSplitConfiguration); | |
| internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer; | |
| internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections()); | |
| if (!enableMissingLibraryApiModeling) { | |
| internal.apiModelingOptions().disableApiModeling(); | |
| } | |
| // Default is to remove all javac generated assertion code when generating DEX. | |
| assert internal.assertionsConfiguration == null; | |
| AssertionsConfiguration.Builder builder = AssertionsConfiguration.builder(getReporter()); | |
| internal.assertionsConfiguration = | |
| new AssertionConfigurationWithDefault( | |
| getProgramConsumer() instanceof ClassFileConsumer | |
| ? AssertionsConfiguration.Builder.passthroughAllAssertions(builder) | |
| : AssertionsConfiguration.Builder.compileTimeDisableAllAssertions(builder), | |
| getAssertionsConfiguration()); | |
| // TODO(b/171552739): Enable class merging for CF. When compiling libraries, we need to be | |
| // careful when merging a public member 'm' from a class A into another class B, since B could | |
| // have a kept subclass, in which case 'm' would leak into the public API. | |
| if (internal.isGeneratingClassFiles()) { | |
| // R8 CF output does not support desugaring so disable it. | |
| internal.desugarState = DesugarState.OFF; | |
| // TODO(b/333477035): Since D8 dexing now supports outline/stubbing API calls R8/CF should | |
| // likely disable API caller identification too so as not to prevent inlining. | |
| internal.apiModelingOptions().disableOutlining().disableStubbingOfClasses(); | |
| } | |
| // EXPERIMENTAL flags. | |
| assert partialCompilationConfiguration != null; | |
| internal.partialCompilationConfiguration = partialCompilationConfiguration; | |
| assert !internal.forceProguardCompatibility; | |
| internal.forceProguardCompatibility = forceProguardCompatibility; | |
| internal.protectApiSurface = protectApiSurface; | |
| internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc(); | |
| LibraryDesugaringOptions libraryDesugaringOptions = internal.getLibraryDesugaringOptions(); | |
| libraryDesugaringOptions.configureDesugaredLibrary( | |
| desugaredLibrarySpecification, synthesizedClassPrefix); | |
| boolean l8Shrinking = libraryDesugaringOptions.isL8(); | |
| // TODO(b/214382176): Enable all the time. | |
| internal.loadAllClassDefinitions = l8Shrinking; | |
| if (l8Shrinking) { | |
| internal.apiModelingOptions().disableStubbingOfClasses(); | |
| internal.ignoreUnusedProguardRules = true; | |
| } | |
| // Set up the map and source file providers. | |
| // Note that minify/optimize settings must be set on internal options before doing this. | |
| internal.mapIdProvider = getMapIdProvider(); | |
| internal.sourceFileProvider = | |
| SourceFileRewriter.computeSourceFileProvider(getSourceFileProvider(), internal); | |
| internal.configureAndroidPlatformBuild(getAndroidPlatformBuild()); | |
| internal.getArtProfileOptions().setArtProfilesForRewriting(getArtProfilesForRewriting()); | |
| if (!getStartupProfileProviders().isEmpty()) { | |
| internal | |
| .getStartupOptions() | |
| .setStartupProfileProviders(getStartupProfileProviders()) | |
| .setEnableStartupLayoutOptimization(enableStartupLayoutOptimization); | |
| } | |
| internal.programClassConflictResolver = | |
| ProgramClassCollection.wrappedConflictResolver( | |
| getClassConflictResolver(), internal.reporter); | |
| internal.cancelCompilationChecker = getCancelCompilationChecker(); | |
| internal.androidResourceProvider = androidResourceProvider; | |
| internal.androidResourceConsumer = androidResourceConsumer; | |
| internal.resourceShrinkerConfiguration = resourceShrinkerConfiguration; | |
| if (configurationAnalysisDataConsumer != null | |
| || configurationAnalysisHtmlReportConsumer != null) { | |
| internal.getKeepRadiusOptions().keepRadiusContainerConsumer = | |
| keepRadiusContainer -> { | |
| if (configurationAnalysisDataConsumer != null) { | |
| KeepRadiusContainerUtils.writeToConsumer( | |
| keepRadiusContainer, configurationAnalysisDataConsumer, internal.reporter); | |
| } | |
| if (configurationAnalysisHtmlReportConsumer != null) { | |
| KeepRadiusContainerUtils.writeHtmlToConsumer( | |
| keepRadiusContainer, configurationAnalysisHtmlReportConsumer, internal.reporter); | |
| } | |
| }; | |
| } | |
| if (!DETERMINISTIC_DEBUGGING) { | |
| assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; | |
| internal.threadCount = getThreadCount(); | |
| } | |
| internal.tool = Tool.R8; | |
| internal.setDumpInputFlags(getDumpInputFlags()); | |
| internal.dumpOptions = dumpOptions(); | |
| return internal; | |
| } | |
| private static MapConsumer wrapMapConsumer( | |
| MapConsumer consumer, boolean optionsFlag, Path optionFile) { | |
| if (optionsFlag) { | |
| if (optionFile != null) { | |
| return new MapConsumer.FileConsumer(optionFile, consumer); | |
| } else { | |
| return MapConsumerUtils.createStandardOutConsumer(consumer); | |
| } | |
| } | |
| return consumer; | |
| } | |
| private static StringConsumer wrapStringConsumer( | |
| StringConsumer optionConsumer, boolean optionsFlag, Path optionFile) { | |
| if (optionsFlag) { | |
| if (optionFile != null) { | |
| return new StringConsumer.FileConsumer(optionFile, optionConsumer); | |
| } else { | |
| return new StandardOutConsumer(optionConsumer); | |
| } | |
| } | |
| return optionConsumer; | |
| } | |
| private static class ResourceShrinkerMapStringConsumer implements InternalMapConsumer { | |
| private final InternalOptions internal; | |
| private final Predicate<String> classNamePredicate; | |
| private StringBuilder resultBuilder = new StringBuilder(); | |
| ResourceShrinkerMapStringConsumer(InternalOptions internal) { | |
| this.internal = internal; | |
| this.classNamePredicate = | |
| LegacyResourceShrinker.classNamesNeededForResourceShrinkingPredicate(); | |
| } | |
| @Override | |
| public void finished(DiagnosticsHandler handler) { | |
| internal.androidResourceProguardMapStrings = StringUtils.splitLines(resultBuilder.toString()); | |
| resultBuilder = null; | |
| } | |
| @Override | |
| public void accept(DiagnosticsHandler diagnosticsHandler, ClassNameMapper classNameMapper) { | |
| for (ClassNamingForNameMapper entry : classNameMapper.getClassNameMappings().values()) { | |
| if (classNamePredicate.test(entry.originalName)) { | |
| resultBuilder.append(entry.toProguardMapSource()); | |
| } | |
| } | |
| } | |
| } | |
| private static class StandardOutConsumer extends StringConsumer.ForwardingConsumer { | |
| StandardOutConsumer(StringConsumer consumer) { | |
| super(consumer); | |
| } | |
| @Override | |
| public void accept(String string, DiagnosticsHandler handler) { | |
| super.accept(string, handler); | |
| System.out.print(string); | |
| } | |
| } | |
| private DumpOptions dumpOptions() { | |
| DumpOptions.Builder builder = DumpOptions.builder(Tool.R8).readCurrentSystemProperties(); | |
| dumpBaseCommandOptions(builder); | |
| return builder | |
| .setIncludeDataResources(includeDataResources) | |
| .setTreeShaking(getEnableTreeShaking()) | |
| .setMinification(getEnableMinification()) | |
| .setForceProguardCompatibility(forceProguardCompatibility) | |
| .setFeatureSplitConfiguration(featureSplitConfiguration) | |
| .setAndroidResourceProvider(androidResourceProvider) | |
| .setPartialCompilationConfiguration(partialCompilationConfiguration) | |
| .setProguardConfiguration(proguardConfiguration) | |
| .setMainDexKeepRules(mainDexKeepRules) | |
| .setDesugaredLibraryConfiguration(desugaredLibrarySpecification) | |
| .setEnableMissingLibraryApiModeling(enableMissingLibraryApiModeling) | |
| .applyIf( | |
| featureSplitConfiguration != null, | |
| b -> b.setIsolatedSplits(featureSplitConfiguration.isIsolatedSplitsEnabled())) | |
| .applyIf( | |
| resourceShrinkerConfiguration != null, | |
| b -> | |
| b.setOptimizedResourceShrinking( | |
| resourceShrinkerConfiguration.isOptimizedShrinking())) | |
| .build(); | |
| } | |
| } |