blob: 844d01a0a1b46e2012ee2c362745b39368c83353 [file] [log] [blame]
// 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 com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Marker.Tool;
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.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.ProguardConfiguration;
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.ProguardConfigurationSourceBytes;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
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.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
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.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import java.io.InputStream;
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.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* 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>
*/
@Keep
public final class R8Command extends BaseCompilerCommand {
/**
* Builder for constructing a R8Command.
*
* <p>A builder is obtained by calling {@link R8Command#builder}.
*/
@Keep
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 StringConsumer desugaredLibraryKeepRuleConsumer = null;
private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>();
private boolean disableTreeShaking = false;
private boolean disableMinification = false;
private boolean disableVerticalClassMerging = false;
private boolean forceProguardCompatibility = 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 BiFunction<String, Long, Boolean> dexClassChecksumFilter = (name, checksum) -> true;
private final List<FeatureSplit> featureSplits = new ArrayList<>();
private String synthesizedClassPrefix = "";
private boolean skipDump = false;
private boolean allowPartiallyImplementedProguardOptions = false;
private boolean allowTestProguardOptions =
System.getProperty("com.android.tools.r8.allowTestProguardOptions") != null;
// TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
Builder() {
this(new DefaultR8DiagnosticsHandler());
}
Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
}
private Builder(AndroidApp app) {
super(app);
}
private Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
super(app, diagnosticsHandler);
}
// Internal
void internalForceProguardCompatibility() {
this.forceProguardCompatibility = true;
}
void setDisableVerticalClassMerging(boolean disableVerticalClassMerging) {
this.disableVerticalClassMerging = disableVerticalClassMerging;
}
@Override
Builder self() {
return this;
}
@Override
CompilationMode defaultCompilationMode() {
return CompilationMode.RELEASE;
}
Builder setSynthesizedClassesPrefix(String prefix) {
synthesizedClassPrefix = prefix;
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) {
guard(
() ->
mainDexRules.add(
new ProguardConfigurationSourceStrings(lines, Paths.get("."), origin)));
return self();
}
/** Add proguard configuration-file resources. */
public Builder addProguardConfigurationFiles(Path... paths) {
guard(() -> {
for (Path path : paths) {
proguardConfigs.add(new ProguardConfigurationSourceFile(path));
}
});
return self();
}
/** Add proguard configuration-file resources. */
public Builder addProguardConfigurationFiles(List<Path> paths) {
guard(() -> {
for (Path path : paths) {
proguardConfigs.add(new ProguardConfigurationSourceFile(path));
}
});
return self();
}
/** Add proguard configuration. */
public Builder addProguardConfiguration(List<String> lines, Origin origin) {
guard(() -> proguardConfigs.add(
new ProguardConfigurationSourceStrings(lines, 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 a consumer for receiving the proguard-map content.
*
* <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 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();
}
/**
* Allow to skip to dump into file and dump into directory instruction, this is primarily used
* for chained compilation in L8 so there are no duplicated dumps.
*/
Builder skipDump() {
skipDump = true;
return self();
}
/**
* 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();
}
/**
* 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 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) {
return super.addProgramResourceProvider(
new EnsureNonDexProgramResourceProvider(programProvider));
}
/**
* 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(getReporter()));
featureSplits.add(featureSplit);
for (ProgramResourceProvider programResourceProvider : featureSplit
.getProgramResourceProviders()) {
// Data resources are handled separately and passed directly to the feature split consumer.
ProgramResourceProvider providerWithoutDataResources = new ProgramResourceProvider() {
@Override
public Collection<ProgramResource> getProgramResources() throws ResourceException {
return programResourceProvider.getProgramResources();
}
@Override
public DataResourceProvider getDataResourceProvider() {
return null;
}
};
addProgramResourceProvider(providerWithoutDataResources);
}
return self();
}
@Override
protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
Path path,
OutputMode mode,
boolean consumeDataResources) {
return super.createProgramOutputConsumer(path, mode, consumeDataResources);
}
@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
&& mainDexRules.isEmpty()
&& !getAppBuilder().hasMainDexList()) {
reporter.error(
"Option --main-dex-list-output requires --main-dex-rules and/or --main-dex-list");
}
if (!(getProgramConsumer() instanceof ClassFileConsumer)
&& getMinApiLevel() >= AndroidApiLevel.L.getLevel()) {
if (getMainDexListConsumer() != null
|| !mainDexRules.isEmpty()
|| getAppBuilder().hasMainDexList()) {
reporter.error(
"R8 does not support main-dex inputs and outputs when compiling to API level "
+ AndroidApiLevel.L.getLevel()
+ " and above");
}
}
for (FeatureSplit featureSplit : featureSplits) {
assert featureSplit.getProgramConsumer() instanceof DexIndexedConsumer;
if (!(getProgramConsumer() instanceof DexIndexedConsumer)) {
reporter.error("R8 does not support class file output when using feature splits");
}
}
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");
}
super.validate();
}
@Override
R8Command makeCommand() {
// If printing versions ignore everything else.
if (isPrintHelp() || isPrintVersion()) {
return new R8Command(isPrintHelp(), isPrintVersion());
}
return makeR8Command();
}
private R8Command makeR8Command() {
Reporter reporter = getReporter();
DexItemFactory factory = new DexItemFactory();
List<ProguardConfigurationRule> mainDexKeepRules =
ProguardConfigurationParser.parse(mainDexRules, factory, reporter);
DesugaredLibraryConfiguration libraryConfiguration =
getDesugaredLibraryConfiguration(factory, false);
ProguardConfigurationParser parser =
new ProguardConfigurationParser(factory, reporter, allowTestProguardOptions);
if (!proguardConfigs.isEmpty()) {
parser.parse(proguardConfigs);
}
ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
if (InternalOptions.shouldEnableKeepRuleSynthesisForRecompilation()) {
configurationBuilder.enableKeepRuleSynthesisForRecompilation();
}
if (proguardConfigurationConsumerForTesting != null) {
proguardConfigurationConsumerForTesting.accept(configurationBuilder);
}
// Process Proguard configurations supplied through data resources in the input.
DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
new DataResourceProvider.Visitor() {
@Override
public void visit(DataDirectoryResource directory) {
// Don't do anything.
}
@Override
public void visit(DataEntryResource resource) {
if (resource.getName().startsWith("META-INF/proguard/")) {
try (InputStream in = resource.getByteStream()) {
ProguardConfigurationSource source =
new ProguardConfigurationSourceBytes(in, resource.getOrigin());
parser.parse(source);
} catch (ResourceException e) {
reporter.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
resource.getOrigin()));
} catch (Exception e) {
reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
}
}
}
};
getAppBuilder().getProgramResourceProviders().stream()
.map(ProgramResourceProvider::getDataResourceProvider)
.filter(Objects::nonNull)
.forEach(
dataResourceProvider -> {
try {
dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
} catch (ResourceException e) {
reporter.error(new ExceptionDiagnostic(e));
}
});
if (disableTreeShaking) {
configurationBuilder.disableShrinking();
}
if (disableMinification) {
configurationBuilder.disableObfuscation();
}
ProguardConfiguration configuration = configurationBuilder.build();
getAppBuilder()
.addFilteredProgramArchives(configuration.getInjars())
.addFilteredLibraryArchives(configuration.getLibraryjars());
assert getProgramConsumer() != null;
DesugarState desugaring =
(getProgramConsumer() instanceof ClassFileConsumer)
? DesugarState.OFF
: getDesugaringState();
FeatureSplitConfiguration featureSplitConfiguration =
!featureSplits.isEmpty() ? new FeatureSplitConfiguration(featureSplits) : null;
R8Command command =
new R8Command(
getAppBuilder().build(),
getProgramConsumer(),
mainDexKeepRules,
getMainDexListConsumer(),
configuration,
getMode(),
getMinApiLevel(),
reporter,
desugaring,
configuration.isShrinking(),
configuration.isObfuscating(),
disableVerticalClassMerging,
forceProguardCompatibility,
includeDataResources,
proguardMapConsumer,
proguardUsageConsumer,
proguardSeedsConsumer,
proguardConfigurationConsumer,
keptGraphConsumer,
mainDexKeptGraphConsumer,
syntheticProguardRulesConsumer,
isOptimizeMultidexForLinearAlloc(),
getIncludeClassesChecksum(),
getDexClassChecksumFilter(),
desugaredLibraryKeepRuleConsumer,
libraryConfiguration,
featureSplitConfiguration,
getAssertionsConfiguration(),
getOutputInspections(),
synthesizedClassPrefix,
skipDump,
getThreadCount(),
getDumpInputFlags());
return command;
}
// 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 add post-processors of the proguard configuration.
void allowPartiallyImplementedProguardOptions() {
allowPartiallyImplementedProguardOptions = true;
}
// Internal for-testing method to allow proguard options only available for testing.
void allowTestProguardOptions() {
allowTestProguardOptions = true;
}
}
// Wrapper class to ensure that R8 does not allow DEX as program inputs.
private static class EnsureNonDexProgramResourceProvider implements ProgramResourceProvider {
final ProgramResourceProvider provider;
public EnsureNonDexProgramResourceProvider(ProgramResourceProvider provider) {
this.provider = provider;
}
@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();
}
}
static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
private final List<ProguardConfigurationRule> mainDexKeepRules;
private final ProguardConfiguration proguardConfiguration;
private final boolean enableTreeShaking;
private final boolean enableMinification;
private final boolean disableVerticalClassMerging;
private final boolean forceProguardCompatibility;
private final Optional<Boolean> includeDataResources;
private final StringConsumer proguardMapConsumer;
private final StringConsumer proguardUsageConsumer;
private final StringConsumer proguardSeedsConsumer;
private final StringConsumer proguardConfigurationConsumer;
private final GraphConsumer keptGraphConsumer;
private final GraphConsumer mainDexKeptGraphConsumer;
private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer;
private final StringConsumer desugaredLibraryKeepRuleConsumer;
private final DesugaredLibraryConfiguration libraryConfiguration;
private final FeatureSplitConfiguration featureSplitConfiguration;
private final String synthesizedClassPrefix;
private final boolean skipDump;
/** 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.
*
* 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.
*
* 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);
}
private R8Command(
AndroidApp inputApp,
ProgramConsumer programConsumer,
List<ProguardConfigurationRule> mainDexKeepRules,
StringConsumer mainDexListConsumer,
ProguardConfiguration proguardConfiguration,
CompilationMode mode,
int minApiLevel,
Reporter reporter,
DesugarState enableDesugaring,
boolean enableTreeShaking,
boolean enableMinification,
boolean disableVerticalClassMerging,
boolean forceProguardCompatibility,
Optional<Boolean> includeDataResources,
StringConsumer proguardMapConsumer,
StringConsumer proguardUsageConsumer,
StringConsumer proguardSeedsConsumer,
StringConsumer proguardConfigurationConsumer,
GraphConsumer keptGraphConsumer,
GraphConsumer mainDexKeptGraphConsumer,
Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
boolean optimizeMultidexForLinearAlloc,
boolean encodeChecksum,
BiPredicate<String, Long> dexClassChecksumFilter,
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
FeatureSplitConfiguration featureSplitConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
List<Consumer<Inspector>> outputInspections,
String synthesizedClassPrefix,
boolean skipDump,
int threadCount,
DumpInputFlags dumpInputFlags) {
super(
inputApp,
mode,
programConsumer,
mainDexListConsumer,
minApiLevel,
reporter,
enableDesugaring,
optimizeMultidexForLinearAlloc,
encodeChecksum,
dexClassChecksumFilter,
assertionsConfiguration,
outputInspections,
threadCount,
dumpInputFlags);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
this.proguardConfiguration = proguardConfiguration;
this.enableTreeShaking = enableTreeShaking;
this.enableMinification = enableMinification;
this.disableVerticalClassMerging = disableVerticalClassMerging;
this.forceProguardCompatibility = forceProguardCompatibility;
this.includeDataResources = includeDataResources;
this.proguardMapConsumer = proguardMapConsumer;
this.proguardUsageConsumer = proguardUsageConsumer;
this.proguardSeedsConsumer = proguardSeedsConsumer;
this.proguardConfigurationConsumer = proguardConfigurationConsumer;
this.keptGraphConsumer = keptGraphConsumer;
this.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer;
this.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
this.libraryConfiguration = libraryConfiguration;
this.featureSplitConfiguration = featureSplitConfiguration;
this.synthesizedClassPrefix = synthesizedClassPrefix;
this.skipDump = skipDump;
}
private R8Command(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
mainDexKeepRules = ImmutableList.of();
proguardConfiguration = null;
enableTreeShaking = false;
enableMinification = false;
disableVerticalClassMerging = false;
forceProguardCompatibility = false;
includeDataResources = null;
proguardMapConsumer = null;
proguardUsageConsumer = null;
proguardSeedsConsumer = null;
proguardConfigurationConsumer = null;
keptGraphConsumer = null;
mainDexKeptGraphConsumer = null;
syntheticProguardRulesConsumer = null;
desugaredLibraryKeepRuleConsumer = null;
libraryConfiguration = null;
featureSplitConfiguration = null;
synthesizedClassPrefix = null;
skipDump = false;
}
/** Get the enable-tree-shaking state. */
public boolean getEnableTreeShaking() {
return enableTreeShaking;
}
/** Get the enable-minification state. */
public boolean getEnableMinification() {
return enableMinification;
}
@Override
InternalOptions getInternalOptions() {
InternalOptions internal = new InternalOptions(proguardConfiguration, getReporter());
assert !internal.testing.allowOutlinerInterfaceArrayArguments; // Only allow in tests.
assert !internal.debug;
internal.debug = getMode() == CompilationMode.DEBUG;
internal.programConsumer = getProgramConsumer();
internal.minApiLevel = AndroidApiLevel.getAndroidApiLevel(getMinApiLevel());
internal.desugarState = getDesugarState();
assert internal.isShrinking() == getEnableTreeShaking();
assert internal.isMinifying() == getEnableMinification();
assert !internal.ignoreMissingClasses;
internal.ignoreMissingClasses =
proguardConfiguration.isIgnoreWarnings()
// TODO(70706667): We probably only want this in Proguard compatibility mode.
|| (forceProguardCompatibility
&& !proguardConfiguration.isOptimizing()
&& !internal.isShrinking()
&& !internal.isMinifying());
assert !internal.verbose;
internal.mainDexKeepRules = mainDexKeepRules;
internal.minimalMainDex = getMode() == CompilationMode.DEBUG;
internal.mainDexListConsumer = getMainDexListConsumer();
internal.lineNumberOptimization =
!internal.debug && (proguardConfiguration.isOptimizing() || internal.isMinifying())
? LineNumberOptimization.ON
: LineNumberOptimization.OFF;
HorizontalClassMergerOptions horizontalClassMergerOptions =
internal.horizontalClassMergerOptions();
assert proguardConfiguration.isOptimizing()
|| horizontalClassMergerOptions.isRestrictedToSynthetics();
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing();
if (internal.debug) {
internal.getProguardConfiguration().getKeepAttributes().lineNumberTable = true;
internal.getProguardConfiguration().getKeepAttributes().localVariableTable = true;
internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
internal.enableInlining = false;
internal.enableClassInlining = false;
internal.enableVerticalClassMerging = false;
internal.enableClassStaticizer = false;
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
}
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;
internal.enableVerticalClassMerging = false;
}
// Amend the proguard-map consumer with options from the proguard configuration.
internal.proguardMapConsumer =
wrapStringConsumer(
proguardMapConsumer,
proguardConfiguration.isPrintMapping(),
proguardConfiguration.getPrintMappingFile());
// 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.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer();
internal.featureSplitConfiguration = featureSplitConfiguration;
internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
// Default is to remove all javac generated assertion code when generating dex.
assert internal.assertionsConfiguration == null;
internal.assertionsConfiguration =
new AssertionConfigurationWithDefault(
getProgramConsumer() instanceof ClassFileConsumer
? AssertionTransformation.PASSTHROUGH
: AssertionTransformation.DISABLE,
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()) {
horizontalClassMergerOptions.disable();
}
// EXPERIMENTAL flags.
assert !internal.forceProguardCompatibility;
internal.forceProguardCompatibility = forceProguardCompatibility;
if (disableVerticalClassMerging) {
internal.enableVerticalClassMerging = false;
}
internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.synthesizedClassPrefix = synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
internal.threadCount = getThreadCount();
}
internal.setDumpInputFlags(getDumpInputFlags(), skipDump);
internal.dumpOptions = dumpOptions();
return internal;
}
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 StandardOutConsumer extends StringConsumer.ForwardingConsumer {
public 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);
dumpBaseCommandOptions(builder);
return builder
.setIncludeDataResources(includeDataResources)
.setTreeShaking(getEnableTreeShaking())
.setMinification(getEnableMinification())
.setForceProguardCompatibility(forceProguardCompatibility)
.setFeatureSplitConfiguration(featureSplitConfiguration)
.setProguardConfiguration(proguardConfiguration)
.setMainDexKeepRules(mainDexKeepRules)
.setDesugaredLibraryConfiguration(libraryConfiguration)
.build();
}
}