| // 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.wrapExistingMapConsumerIfNotNull; |
| |
| import com.android.tools.r8.R8Command.Builder; |
| 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.graph.DexItemFactory; |
| 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.keepanno.annotations.KeepForApi; |
| import com.android.tools.r8.metadata.D8BuildMetadata; |
| import com.android.tools.r8.naming.MapConsumer; |
| import com.android.tools.r8.naming.ProguardMapStringConsumer; |
| import com.android.tools.r8.origin.ArchiveEntryOrigin; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.origin.PathOrigin; |
| import com.android.tools.r8.profile.art.ArtProfileForRewriting; |
| import com.android.tools.r8.shaking.ProguardConfigurationParser; |
| 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.synthesis.GlobalSyntheticsResourceBytes; |
| import com.android.tools.r8.synthesis.GlobalSyntheticsResourceFile; |
| import com.android.tools.r8.synthesis.GlobalSyntheticsUtils; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.AssertionConfigurationWithDefault; |
| import com.android.tools.r8.utils.DumpInputFlags; |
| import com.android.tools.r8.utils.FileUtils; |
| import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramProvider; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.InternalOptions.DesugarState; |
| import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions; |
| import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; |
| import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions; |
| import com.android.tools.r8.utils.ProgramClassCollection; |
| import com.android.tools.r8.utils.Reporter; |
| import com.android.tools.r8.utils.StringDiagnostic; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.ZipUtils; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.io.ByteStreams; |
| import java.io.IOException; |
| 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.function.BiPredicate; |
| import java.util.function.Consumer; |
| |
| /** |
| * Immutable command structure for an invocation of the {@link D8} compiler. |
| * |
| * <p>To build a D8 command use the {@link D8Command.Builder} class. For example: |
| * |
| * <pre> |
| * D8Command command = D8Command.builder() |
| * .addProgramFiles(path1, path2) |
| * .setMode(CompilationMode.RELEASE) |
| * .setOutput(Paths.get("output.zip", OutputMode.DexIndexed)) |
| * .build(); |
| * </pre> |
| */ |
| @KeepForApi |
| public final class D8Command extends BaseCompilerCommand { |
| |
| private static class DefaultD8DiagnosticsHandler 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")); |
| return; |
| } |
| } |
| DiagnosticsHandler.super.error(error); |
| } |
| } |
| |
| /** |
| * Builder for constructing a D8Command. |
| * |
| * <p>A builder is obtained by calling {@link D8Command#builder}. |
| */ |
| @KeepForApi |
| public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> { |
| |
| private boolean intermediate = false; |
| private Path globalSyntheticsOutput = null; |
| private GlobalSyntheticsConsumer globalSyntheticsConsumer = null; |
| private final List<GlobalSyntheticsResourceProvider> globalSyntheticsResourceProviders = |
| new ArrayList<>(); |
| private DesugarGraphConsumer desugarGraphConsumer = null; |
| private SyntheticInfoConsumer syntheticInfoConsumer = null; |
| private StringConsumer desugaredLibraryKeepRuleConsumer = null; |
| private String synthesizedClassPrefix = ""; |
| private boolean enableMainDexListCheck = true; |
| private boolean minimalMainDex = false; |
| private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>(); |
| private boolean enableMissingLibraryApiModeling = false; |
| private boolean enableRewritingOfArtProfilesIsNopCheck = false; |
| private Consumer<? super D8BuildMetadata> buildMetadataConsumer = null; |
| |
| private Builder() { |
| this(new DefaultD8DiagnosticsHandler()); |
| } |
| |
| private Builder(DiagnosticsHandler diagnosticsHandler) { |
| super(diagnosticsHandler); |
| } |
| |
| private Builder(AndroidApp app) { |
| super(app); |
| } |
| |
| /** |
| * Add dex program-data. |
| */ |
| @Override |
| public Builder addDexProgramData(byte[] data, Origin origin) { |
| guard(() -> getAppBuilder().addDexProgramData(data, origin)); |
| return self(); |
| } |
| |
| /** |
| * Add classpath file resources. These have @Override to ensure binary compatibility. |
| */ |
| @Override |
| public Builder addClasspathFiles(Path... files) { |
| return super.addClasspathFiles(files); |
| } |
| |
| /** |
| * Add classpath file resources. |
| */ |
| @Override |
| public Builder addClasspathFiles(Collection<Path> files) { |
| return super.addClasspathFiles(files); |
| } |
| |
| /** |
| * Add classfile resources provider for class-path resources. |
| */ |
| @Override |
| public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) { |
| return super.addClasspathResourceProvider(provider); |
| } |
| |
| /** |
| * Set input proguard map used for distribution of classes in multi-dex. Use {@link |
| * #setProguardMapInputFile} |
| */ |
| @Deprecated() |
| public Builder setProguardInputMapFile(Path proguardInputMap) { |
| getAppBuilder().setProguardMapInputData(proguardInputMap); |
| return self(); |
| } |
| |
| /** 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>Note that when a proguard-map consumer is specified for a release build, the compiler will |
| * optimize the line-number information and obtaining a source-level stacktrace will require the |
| * use of a retrace tool exactly as is needed for programs built by R8. |
| * |
| * <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 an output destination to which proguard-map content should be written. |
| * |
| * <p>Note that when a proguard-map output is specified for a release build, the compiler will |
| * optimize the line-number information and obtaining a source-level stacktrace will require the |
| * use of a retrace tool exactly as is needed for programs built by R8. |
| * |
| * <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 an output destination to which partition-map content should be written. |
| * |
| * <p>Note that when a proguard-map output is specified for a release build, the compiler will |
| * optimize the line-number information and obtaining a source-level stacktrace will require the |
| * use of a retrace tool exactly as is needed for programs built by R8. |
| * |
| * <p>This is a short-hand for setting a {@link PartitionMapConsumer} using {@link |
| * #setPartitionMapConsumer}. Note that any subsequent call to this method or {@link |
| * #setPartitionMapConsumer} will override the previous setting. |
| * |
| * @param partitionMapOutput File-system path to write output at. |
| */ |
| @Override |
| public Builder setPartitionMapOutputPath(Path partitionMapOutput) { |
| assert partitionMapOutput != null; |
| return super.setPartitionMapOutputPath(partitionMapOutput); |
| } |
| |
| /** |
| * Set a consumer for receiving the partition map content. |
| * |
| * <p>Note that when a proguard-map output is specified for a release build, the compiler will |
| * optimize the line-number information and obtaining a source-level stacktrace will require the |
| * use of a retrace tool exactly as is needed for programs built by R8. |
| * |
| * <p>Note that any subsequent call to this method or {@link #setPartitionMapOutputPath} will |
| * override the previous setting. |
| * |
| * @param partitionMapConsumer Consumer to receive the content once produced. |
| */ |
| @Override |
| public Builder setPartitionMapConsumer(PartitionMapConsumer partitionMapConsumer) { |
| assert partitionMapConsumer != null; |
| return super.setPartitionMapConsumer(partitionMapConsumer); |
| } |
| |
| /** |
| * Indicate if compilation is to intermediate results, i.e., intended for later merging. |
| * |
| * <p>When compiling to intermediate mode, the compiler will avoid sharing of synthetic items, |
| * and instead annotate them as synthetics for possible later merging. For global synthetics, |
| * the compiler will emit these to a separate consumer (see {@code GlobalSyntheticsConsumer} |
| * with the expectation that a later build step will consume them again as part of a |
| * non-intermediate build (see {@code GlobalSyntheticsResourceProvider}. Synthetic items |
| * typically come from the desugaring of various language features, such as lambdas and default |
| * interface methods. Global synthetics are non-local in that many compilation units may |
| * reference the same synthetic. For example, desugaring records requires a global tag to |
| * distinguish the class of all records. |
| * |
| * <p>Intermediate mode is implied if compiling results to a "file-per-class-file". |
| */ |
| public Builder setIntermediate(boolean value) { |
| this.intermediate = value; |
| return self(); |
| } |
| |
| /** |
| * Set an output path for receiving the global synthetic content for the given compilation. |
| * |
| * <p>Note: this output is ignored if the compilation is not an "intermediate mode" compilation. |
| * |
| * <p>Note: setting this will clear out any consumer set by setGlobalSyntheticsConsumer. |
| */ |
| public Builder setGlobalSyntheticsOutput(Path globalSyntheticsOutput) { |
| this.globalSyntheticsConsumer = null; |
| this.globalSyntheticsOutput = globalSyntheticsOutput; |
| return self(); |
| } |
| |
| /** |
| * Set a consumer for receiving the global synthetic content for the given compilation. |
| * |
| * <p>Note: this consumer is ignored if the compilation is not an "intermediate mode" |
| * compilation. |
| * |
| * <p>Note: setting this will clear out any output path set by setGlobalSyntheticsOutput. |
| */ |
| public Builder setGlobalSyntheticsConsumer(GlobalSyntheticsConsumer globalSyntheticsConsumer) { |
| this.globalSyntheticsOutput = null; |
| this.globalSyntheticsConsumer = globalSyntheticsConsumer; |
| return self(); |
| } |
| |
| /** Add global synthetics resource providers. */ |
| public Builder addGlobalSyntheticsResourceProviders( |
| GlobalSyntheticsResourceProvider... providers) { |
| return addGlobalSyntheticsResourceProviders(Arrays.asList(providers)); |
| } |
| |
| /** Add global synthetics resource providers. */ |
| public Builder addGlobalSyntheticsResourceProviders( |
| Collection<GlobalSyntheticsResourceProvider> providers) { |
| providers.forEach(globalSyntheticsResourceProviders::add); |
| return self(); |
| } |
| |
| /** Add global synthetics resource files. */ |
| public Builder addGlobalSyntheticsFiles(Path... files) { |
| return addGlobalSyntheticsFiles(Arrays.asList(files)); |
| } |
| |
| /** Add global synthetics resource files. */ |
| public Builder addGlobalSyntheticsFiles(Collection<Path> files) { |
| for (Path file : files) { |
| addGlobalSyntheticsFileOrArchiveOfGlobalSynthetics(file); |
| } |
| return self(); |
| } |
| |
| private void addGlobalSyntheticsFileOrArchiveOfGlobalSynthetics(Path file) { |
| if (!FileUtils.isZipFile(file)) { |
| addGlobalSyntheticsResourceProviders(new GlobalSyntheticsResourceFile(file)); |
| return; |
| } |
| PathOrigin origin = new PathOrigin(file); |
| try { |
| ZipUtils.iter( |
| file, |
| (entry, input) -> |
| addGlobalSyntheticsResourceProviders( |
| new GlobalSyntheticsResourceBytes( |
| new ArchiveEntryOrigin(entry.getName(), origin), |
| ByteStreams.toByteArray(input)))); |
| } catch (IOException e) { |
| error(origin, e); |
| } |
| } |
| |
| /** |
| * Set a consumer for receiving the keep rules to use when compiling the desugared library for |
| * the program being compiled in this compilation. |
| * |
| * @param keepRuleConsumer Consumer to receive the content once produced. |
| */ |
| public Builder setDesugaredLibraryKeepRuleConsumer(StringConsumer keepRuleConsumer) { |
| this.desugaredLibraryKeepRuleConsumer = keepRuleConsumer; |
| return self(); |
| } |
| |
| /** |
| * Get the consumer that will receive dependency information for desugaring. |
| */ |
| public DesugarGraphConsumer getDesugarGraphConsumer() { |
| return desugarGraphConsumer; |
| } |
| |
| /** |
| * Set the consumer that will receive dependency information for desugaring. |
| * |
| * <p>Setting the consumer will clear any previously set consumer. |
| */ |
| public Builder setDesugarGraphConsumer(DesugarGraphConsumer desugarGraphConsumer) { |
| this.desugarGraphConsumer = desugarGraphConsumer; |
| return self(); |
| } |
| |
| /** Get the consumer that will receive information about compiler synthesized classes. */ |
| public SyntheticInfoConsumer getSyntheticInfoConsumer() { |
| return syntheticInfoConsumer; |
| } |
| |
| /** |
| * Set the consumer that will receive information about compiler synthesized classes. |
| * |
| * <p>Setting the consumer will clear any previously set consumer. |
| */ |
| public Builder setSyntheticInfoConsumer(SyntheticInfoConsumer syntheticInfoConsumer) { |
| this.syntheticInfoConsumer = syntheticInfoConsumer; |
| return self(); |
| } |
| |
| /** |
| * Add a collection of startup profile providers that should be used for distributing the |
| * program classes in dex. |
| * |
| * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does |
| * not support native multidex (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. |
| * |
| * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does |
| * not support native multidex (API<=20). |
| */ |
| @Override |
| public Builder addStartupProfileProviders( |
| Collection<StartupProfileProvider> startupProfileProviders) { |
| return super.addStartupProfileProviders(startupProfileProviders); |
| } |
| |
| @Override |
| Builder self() { |
| return this; |
| } |
| |
| @Override |
| CompilationMode defaultCompilationMode() { |
| return CompilationMode.DEBUG; |
| } |
| |
| Builder setSynthesizedClassesPrefix(String prefix) { |
| synthesizedClassPrefix = prefix; |
| return self(); |
| } |
| |
| @Deprecated |
| // Internal helper for supporting bazel integration. |
| Builder setEnableMainDexListCheck(boolean value) { |
| enableMainDexListCheck = value; |
| return self(); |
| } |
| |
| @Deprecated |
| // Internal helper for supporting bazel integration. |
| Builder setMinimalMainDex(boolean value) { |
| minimalMainDex = value; |
| 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) { |
| guard( |
| () -> |
| mainDexRules.add( |
| new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin))); |
| return self(); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| Builder setEnableRewritingOfArtProfilesIsNopCheck() { |
| enableRewritingOfArtProfilesIsNopCheck = true; |
| 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 D8BuildMetadata> buildMetadataConsumer) { |
| this.buildMetadataConsumer = buildMetadataConsumer; |
| return self(); |
| } |
| |
| @Override |
| void validate() { |
| if (isPrintHelp()) { |
| return; |
| } |
| Reporter reporter = getReporter(); |
| if (getAppBuilder().hasMainDexList()) { |
| if (intermediate) { |
| reporter.error("Option --main-dex-list cannot be used with --intermediate"); |
| } |
| if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { |
| reporter.error("Option --main-dex-list cannot be used with --file-per-class"); |
| } |
| } |
| if (!mainDexRules.isEmpty()) { |
| if (intermediate) { |
| reporter.error("Option --main-dex-rules cannot be used with --intermediate"); |
| } |
| if (getProgramConsumer() instanceof DexFilePerClassFileConsumer) { |
| reporter.error("Option --main-dex-rules cannot be used with --file-per-class"); |
| } |
| } |
| if (getMainDexListConsumer() != null |
| && mainDexRules.isEmpty() |
| && !getAppBuilder().hasMainDexList()) { |
| reporter.error( |
| "Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list"); |
| } |
| if (getMinApiLevel() >= AndroidApiLevel.L_MR1.getLevel()) { |
| if (getMainDexListConsumer() != null || getAppBuilder().hasMainDexList()) { |
| reporter.error( |
| "D8 does not support main-dex inputs and outputs when compiling to API level " |
| + AndroidApiLevel.L_MR1.getLevel() |
| + " and above (min API level " |
| + getMinApiLevel() |
| + " was provided)"); |
| } |
| } |
| if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) { |
| reporter.error("Using desugared library configuration requires desugaring to be enabled"); |
| } |
| if (getProgramConsumer() instanceof ClassFileConsumer |
| && getDisableDesugaring() |
| && isMinApiLevelSet()) { |
| reporter.error("Compiling to CF with --min-api and --no-desugaring is not supported"); |
| } |
| if (!getStartupProfileProviders().isEmpty()) { |
| if (intermediate) { |
| reporter.error("D8 startup layout is not supported in intermediate mode"); |
| } |
| if (getMinApiLevel() < AndroidApiLevel.L.getLevel()) { |
| reporter.error( |
| "D8 startup layout requires native multi dex support (API level " |
| + AndroidApiLevel.L.getLevel() |
| + " and above, min API level " |
| + getMinApiLevel() |
| + " was provided)"); |
| } |
| } |
| super.validate(); |
| } |
| |
| @Override |
| D8Command makeCommand() { |
| if (isPrintHelp() || isPrintVersion()) { |
| return new D8Command(isPrintHelp(), isPrintVersion()); |
| } |
| |
| final ProgramConsumer programConsumer = getProgramConsumer(); |
| intermediate |= programConsumer instanceof DexFilePerClassFileConsumer; |
| |
| DexItemFactory factory = new DexItemFactory(); |
| DesugaredLibrarySpecification desugaredLibrarySpecification = |
| getDesugaredLibraryConfiguration(factory, false); |
| |
| ImmutableList<ProguardConfigurationRule> mainDexKeepRules = |
| ProguardConfigurationParser.parse(mainDexRules, factory, getReporter()); |
| |
| if (!globalSyntheticsResourceProviders.isEmpty()) { |
| addProgramResourceProvider( |
| new InternalGlobalSyntheticsProgramProvider(globalSyntheticsResourceProviders)); |
| } |
| |
| // If compiling to CF with --no-desugaring then the target API is B for consistency with R8. |
| int minApiLevel = |
| programConsumer instanceof ClassFileConsumer && getDisableDesugaring() |
| ? AndroidApiLevel.B.getLevel() |
| : getMinApiLevel(); |
| |
| GlobalSyntheticsConsumer globalConsumer = |
| GlobalSyntheticsUtils.determineGlobalSyntheticsConsumer( |
| intermediate, globalSyntheticsOutput, globalSyntheticsConsumer, programConsumer); |
| |
| return new D8Command( |
| getAppBuilder().build(), |
| getMode(), |
| programConsumer, |
| getMainDexListConsumer(), |
| minApiLevel, |
| getReporter(), |
| getDesugaringState(), |
| intermediate, |
| globalConsumer, |
| isOptimizeMultidexForLinearAlloc(), |
| getIncludeClassesChecksum(), |
| getDexClassChecksumFilter(), |
| getDesugarGraphConsumer(), |
| getSyntheticInfoConsumer(), |
| desugaredLibraryKeepRuleConsumer, |
| desugaredLibrarySpecification, |
| getAssertionsConfiguration(), |
| getOutputInspections(), |
| synthesizedClassPrefix, |
| enableMainDexListCheck, |
| minimalMainDex, |
| mainDexKeepRules, |
| getThreadCount(), |
| getDumpInputFlags(), |
| getMapIdProvider(), |
| proguardMapConsumer, |
| partitionMapConsumer, |
| enableMissingLibraryApiModeling, |
| enableRewritingOfArtProfilesIsNopCheck, |
| buildMetadataConsumer, |
| getAndroidPlatformBuild(), |
| getArtProfilesForRewriting(), |
| getStartupProfileProviders(), |
| getClassConflictResolver(), |
| getCancelCompilationChecker(), |
| factory); |
| } |
| } |
| |
| private final boolean intermediate; |
| private final GlobalSyntheticsConsumer globalSyntheticsConsumer; |
| private final SyntheticInfoConsumer syntheticInfoConsumer; |
| private final DesugarGraphConsumer desugarGraphConsumer; |
| private final StringConsumer desugaredLibraryKeepRuleConsumer; |
| private final DesugaredLibrarySpecification desugaredLibrarySpecification; |
| private final String synthesizedClassPrefix; |
| private final boolean enableMainDexListCheck; |
| private final boolean minimalMainDex; |
| private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules; |
| private final StringConsumer proguardMapConsumer; |
| private final PartitionMapConsumer partitionMapConsumer; |
| private final boolean enableMissingLibraryApiModeling; |
| private final boolean enableRewritingOfArtProfilesIsNopCheck; |
| private final DexItemFactory factory; |
| private final Consumer<? super D8BuildMetadata> buildMetadataConsumer; |
| |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| 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); |
| } |
| |
| /** |
| * Parse the D8 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 D8 command builder with state set up according to parsed command line. |
| */ |
| public static Builder parse(String[] args, Origin origin) { |
| return D8CommandParser.parse(args, origin); |
| } |
| |
| /** |
| * Parse the D8 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 D8 command builder with state set up according to parsed command line. |
| */ |
| public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) { |
| return D8CommandParser.parse(args, origin, handler); |
| } |
| |
| /** Get the help description for the D8 supported flags. */ |
| public static List<ParseFlagInfo> getParseFlagsInformation() { |
| return D8CommandParser.getFlags(); |
| } |
| |
| private D8Command( |
| AndroidApp inputApp, |
| CompilationMode mode, |
| ProgramConsumer programConsumer, |
| StringConsumer mainDexListConsumer, |
| int minApiLevel, |
| Reporter diagnosticsHandler, |
| DesugarState enableDesugaring, |
| boolean intermediate, |
| GlobalSyntheticsConsumer globalSyntheticsConsumer, |
| boolean optimizeMultidexForLinearAlloc, |
| boolean encodeChecksum, |
| BiPredicate<String, Long> dexClassChecksumFilter, |
| DesugarGraphConsumer desugarGraphConsumer, |
| SyntheticInfoConsumer syntheticInfoConsumer, |
| StringConsumer desugaredLibraryKeepRuleConsumer, |
| DesugaredLibrarySpecification desugaredLibrarySpecification, |
| List<AssertionsConfiguration> assertionsConfiguration, |
| List<Consumer<Inspector>> outputInspections, |
| String synthesizedClassPrefix, |
| boolean enableMainDexListCheck, |
| boolean minimalMainDex, |
| ImmutableList<ProguardConfigurationRule> mainDexKeepRules, |
| int threadCount, |
| DumpInputFlags dumpInputFlags, |
| MapIdProvider mapIdProvider, |
| StringConsumer proguardMapConsumer, |
| PartitionMapConsumer partitionMapConsumer, |
| boolean enableMissingLibraryApiModeling, |
| boolean enableRewritingOfArtProfilesIsNopCheck, |
| Consumer<? super D8BuildMetadata> buildMetadataConsumer, |
| boolean isAndroidPlatformBuild, |
| List<ArtProfileForRewriting> artProfilesForRewriting, |
| List<StartupProfileProvider> startupProfileProviders, |
| ClassConflictResolver classConflictResolver, |
| CancelCompilationChecker cancelCompilationChecker, |
| DexItemFactory factory) { |
| super( |
| inputApp, |
| mode, |
| programConsumer, |
| mainDexListConsumer, |
| minApiLevel, |
| diagnosticsHandler, |
| enableDesugaring, |
| optimizeMultidexForLinearAlloc, |
| encodeChecksum, |
| dexClassChecksumFilter, |
| assertionsConfiguration, |
| outputInspections, |
| threadCount, |
| dumpInputFlags, |
| mapIdProvider, |
| null, |
| isAndroidPlatformBuild, |
| artProfilesForRewriting, |
| startupProfileProviders, |
| classConflictResolver, |
| cancelCompilationChecker); |
| this.intermediate = intermediate; |
| this.globalSyntheticsConsumer = globalSyntheticsConsumer; |
| this.syntheticInfoConsumer = syntheticInfoConsumer; |
| this.desugarGraphConsumer = desugarGraphConsumer; |
| this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; |
| this.desugaredLibrarySpecification = desugaredLibrarySpecification; |
| this.synthesizedClassPrefix = synthesizedClassPrefix; |
| this.enableMainDexListCheck = enableMainDexListCheck; |
| this.minimalMainDex = minimalMainDex; |
| this.mainDexKeepRules = mainDexKeepRules; |
| this.proguardMapConsumer = proguardMapConsumer; |
| this.partitionMapConsumer = partitionMapConsumer; |
| this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling; |
| this.enableRewritingOfArtProfilesIsNopCheck = enableRewritingOfArtProfilesIsNopCheck; |
| this.factory = factory; |
| this.buildMetadataConsumer = buildMetadataConsumer; |
| } |
| |
| private D8Command(boolean printHelp, boolean printVersion) { |
| super(printHelp, printVersion); |
| intermediate = false; |
| globalSyntheticsConsumer = null; |
| syntheticInfoConsumer = null; |
| desugarGraphConsumer = null; |
| desugaredLibraryKeepRuleConsumer = null; |
| desugaredLibrarySpecification = null; |
| synthesizedClassPrefix = null; |
| enableMainDexListCheck = true; |
| minimalMainDex = false; |
| mainDexKeepRules = null; |
| proguardMapConsumer = null; |
| partitionMapConsumer = null; |
| enableMissingLibraryApiModeling = false; |
| enableRewritingOfArtProfilesIsNopCheck = false; |
| factory = null; |
| buildMetadataConsumer = null; |
| } |
| |
| @Override |
| InternalOptions getInternalOptions() { |
| InternalOptions internal = new InternalOptions(factory, getReporter()); |
| assert !internal.debug; |
| internal.d8BuildMetadataConsumer = buildMetadataConsumer; |
| internal.debug = getMode() == CompilationMode.DEBUG; |
| internal.programConsumer = getProgramConsumer(); |
| if (internal.isGeneratingClassFiles()) { |
| // Turn off switch optimizations when generating class files. |
| assert internal.enableSwitchRewriting; |
| internal.enableSwitchRewriting = false; |
| assert internal.enableStringSwitchConversion; |
| internal.enableStringSwitchConversion = false; |
| } else { |
| assert !internal.desugarSpecificOptions().allowAllDesugaredInput |
| || getDesugarState() == DesugarState.OFF; |
| } |
| internal.mainDexListConsumer = getMainDexListConsumer(); |
| internal.minimalMainDex = internal.debug || minimalMainDex; |
| internal.enableMainDexListCheck = enableMainDexListCheck; |
| internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(getMinApiLevel())); |
| internal.intermediate = intermediate; |
| internal.retainCompileTimeAnnotations = intermediate; |
| internal.setGlobalSyntheticsConsumer(globalSyntheticsConsumer); |
| internal.setSyntheticInfoConsumer(syntheticInfoConsumer); |
| internal.desugarGraphConsumer = desugarGraphConsumer; |
| internal.mainDexKeepRules = mainDexKeepRules; |
| MapConsumer mapConsumer = |
| wrapExistingMapConsumerIfNotNull( |
| internal.mapConsumer, partitionMapConsumer, MapConsumerToPartitionMapConsumer::new); |
| internal.mapConsumer = |
| wrapExistingMapConsumerIfNotNull( |
| mapConsumer, |
| proguardMapConsumer, |
| nonNullStringConsumer -> |
| ProguardMapStringConsumer.builder().setStringConsumer(proguardMapConsumer).build()); |
| internal.lineNumberOptimization = |
| !internal.debug && proguardMapConsumer != null |
| ? LineNumberOptimization.ON |
| : LineNumberOptimization.OFF; |
| MappingComposeOptions mappingComposeOptions = internal.mappingComposeOptions(); |
| mappingComposeOptions.enableExperimentalMappingComposition = true; |
| // Assert and fixup defaults. |
| assert !internal.isShrinking(); |
| assert !internal.isMinifying(); |
| assert !internal.passthroughDexCode; |
| internal.passthroughDexCode = true; |
| |
| // Assert some of R8 optimizations are disabled. |
| assert !internal.inlinerOptions().enableInlining; |
| assert !internal.enableClassInlining; |
| assert !internal.enableEnumValueOptimization; |
| assert !internal.outline.enabled; |
| assert !internal.enableTreeShakingOfLibraryMethodOverrides; |
| |
| internal.desugarState = getDesugarState(); |
| internal.encodeChecksums = getIncludeClassesChecksum(); |
| internal.dexClassChecksumFilter = getDexClassChecksumFilter(); |
| internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc(); |
| |
| internal.configureDesugaredLibrary(desugaredLibrarySpecification, synthesizedClassPrefix); |
| internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; |
| |
| if (internal.isGeneratingClassFiles() |
| || (System.getProperty("com.android.tools.r8.enableApiOutliningAndStubbing") == null |
| && !enableMissingLibraryApiModeling)) { |
| internal.apiModelingOptions().disableApiCallerIdentification(); |
| internal.apiModelingOptions().disableOutliningAndStubbing(); |
| } |
| |
| if (enableRewritingOfArtProfilesIsNopCheck) { |
| internal.getArtProfileOptions().setEnableNopCheckForTesting(); |
| } |
| |
| // Default is to remove all javac generated assertion code when generating dex. |
| assert internal.assertionsConfiguration == null; |
| internal.assertionsConfiguration = |
| new AssertionConfigurationWithDefault( |
| AssertionsConfiguration.builder(getReporter()) |
| .setCompileTimeDisable() |
| .setScopeAll() |
| .build(), |
| getAssertionsConfiguration()); |
| |
| internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections()); |
| |
| if (!DETERMINISTIC_DEBUGGING) { |
| assert internal.threadCount == ThreadUtils.NOT_SPECIFIED; |
| internal.threadCount = getThreadCount(); |
| } |
| |
| // Disable global optimizations. |
| internal.disableGlobalOptimizations(); |
| |
| HorizontalClassMergerOptions horizontalClassMergerOptions = |
| internal.horizontalClassMergerOptions(); |
| if (internal.isGeneratingDex()) { |
| horizontalClassMergerOptions.setRestrictToSynthetics(); |
| } else { |
| assert internal.isGeneratingClassFiles(); |
| horizontalClassMergerOptions.disable(); |
| } |
| |
| internal.configureAndroidPlatformBuild(getAndroidPlatformBuild()); |
| |
| internal.getArtProfileOptions().setArtProfilesForRewriting(getArtProfilesForRewriting()); |
| if (!getStartupProfileProviders().isEmpty()) { |
| internal.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders()); |
| } |
| |
| internal.programClassConflictResolver = |
| ProgramClassCollection.wrappedConflictResolver( |
| getClassConflictResolver(), internal.reporter); |
| |
| internal.cancelCompilationChecker = getCancelCompilationChecker(); |
| |
| internal.tool = Tool.D8; |
| internal.setDumpInputFlags(getDumpInputFlags()); |
| internal.dumpOptions = dumpOptions(); |
| |
| return internal; |
| } |
| |
| private DumpOptions dumpOptions() { |
| DumpOptions.Builder builder = DumpOptions.builder(Tool.D8).readCurrentSystemProperties(); |
| dumpBaseCommandOptions(builder); |
| return builder |
| .setIntermediate(intermediate) |
| .setDesugaredLibraryConfiguration(desugaredLibrarySpecification) |
| .setMainDexKeepRules(mainDexKeepRules) |
| .setEnableMissingLibraryApiModeling(enableMissingLibraryApiModeling) |
| .build(); |
| } |
| |
| } |