Introduce dedicated main-dex-list consumer.
Change-Id: I714cafcd816f9fa733b891edea3fec34ea3edaa8
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 3ddf91d..4a3fc2e 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -16,10 +16,6 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -50,13 +46,8 @@
.sorted()
.collect(Collectors.toList());
- if (options.printMainDexListFile != null) {
- try (OutputStream mainDexOut = Files.newOutputStream(options.printMainDexListFile,
- StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
- PrintWriter writer = new PrintWriter(mainDexOut);
- result.forEach(writer::println);
- writer.flush();
- }
+ if (options.mainDexListConsumer != null) {
+ options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
}
return result;
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index a50eb2b..5b66b6e 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -25,14 +25,15 @@
public class GenerateMainDexListCommand extends BaseCommand {
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
- private final Path mainDexListOutput;
+ private final StringConsumer mainDexListConsumer;
private final DexItemFactory factory;
- /**
- * Get the output path for the main-dex list. Null if not set.
- */
+ /** Get the output path for the main-dex list. Null if not set. */
+ @Deprecated
public Path getMainDexListOutputPath() {
- return mainDexListOutput;
+ return mainDexListConsumer instanceof StringConsumer.FileConsumer
+ ? ((StringConsumer.FileConsumer) mainDexListConsumer).getOutputPath()
+ : null;
}
public static class Builder extends BaseCommand.Builder<GenerateMainDexListCommand, Builder> {
@@ -114,11 +115,11 @@
mainDexKeepRules = parser.getConfig().getRules();
}
+ StringConsumer mainDexListConsumer =
+ mainDexListOutput != null ? new StringConsumer.FileConsumer(mainDexListOutput) : null;
+
return new GenerateMainDexListCommand(
- factory,
- getAppBuilder().build(),
- mainDexKeepRules,
- mainDexListOutput);
+ factory, getAppBuilder().build(), mainDexKeepRules, mainDexListConsumer);
}
}
@@ -173,18 +174,18 @@
DexItemFactory factory,
AndroidApp inputApp,
ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
- Path mainDexListOutput) {
+ StringConsumer mainDexListConsumer) {
super(inputApp);
this.factory = factory;
this.mainDexKeepRules = mainDexKeepRules;
- this.mainDexListOutput = mainDexListOutput;
+ this.mainDexListConsumer = mainDexListConsumer;
}
private GenerateMainDexListCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
this.factory = new DexItemFactory();
this.mainDexKeepRules = ImmutableList.of();
- this.mainDexListOutput = null;
+ this.mainDexListConsumer = null;
}
@Override
@@ -192,9 +193,7 @@
InternalOptions internal =
new InternalOptions(factory, new Reporter(new DefaultDiagnosticsHandler()));
internal.mainDexKeepRules = mainDexKeepRules;
- if (mainDexListOutput != null) {
- internal.printMainDexListFile = mainDexListOutput;
- }
+ internal.mainDexListConsumer = mainDexListConsumer;
internal.minimalMainDex = internal.debug;
internal.removeSwitchMaps = false;
internal.inlineAccessors = false;
diff --git a/src/main/java/com/android/tools/r8/OutputSink.java b/src/main/java/com/android/tools/r8/OutputSink.java
index 2ca5e73..316df01 100644
--- a/src/main/java/com/android/tools/r8/OutputSink.java
+++ b/src/main/java/com/android/tools/r8/OutputSink.java
@@ -76,15 +76,6 @@
void writeProguardSeedsFile(byte[] contents) throws IOException;
/**
- * Provides the raw bytes that would be generated by R8 or an R8-based helper tool when instructed
- * to generate a main-dex list.
- * <p>
- * This method is only invoked by R8 or R8-based tools and only if R8 is instructed to generate
- * a main-dex list.
- */
- void writeMainDexListFile(byte[] contents) throws IOException;
-
- /**
* Closes the output sink.
* <p>
* This method is invokes once all output has been generated.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b5db328..d2455f3 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -271,6 +271,9 @@
boolean useDiscardedChecker = discardedChecker.orElse(true);
boolean useMinification = minification.orElse(configuration.isObfuscating());
+ StringConsumer mainDexListConsumer =
+ mainDexListOutput != null ? new StringConsumer.FileConsumer(mainDexListOutput) : null;
+
StringConsumer proguardMapConsumer =
proguardMapOutput != null ? new StringConsumer.FileConsumer(proguardMapOutput) : null;
@@ -280,7 +283,7 @@
getOutputPath(),
getOutputMode(),
mainDexKeepRules,
- mainDexListOutput,
+ mainDexListConsumer,
configuration,
getMode(),
getMinApiLevel(),
@@ -329,7 +332,7 @@
" --help # Print this message."));
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
- private final Path mainDexListOutput;
+ private final StringConsumer mainDexListConsumer;
private final ProguardConfiguration proguardConfiguration;
private final boolean useTreeShaking;
private final boolean useDiscardedChecker;
@@ -461,7 +464,7 @@
Path outputPath,
OutputMode outputMode,
ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
- Path mainDexListOutput,
+ StringConsumer mainDexListConsumer,
ProguardConfiguration proguardConfiguration,
CompilationMode mode,
int minApiLevel,
@@ -481,7 +484,7 @@
assert mainDexKeepRules != null;
assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
this.mainDexKeepRules = mainDexKeepRules;
- this.mainDexListOutput = mainDexListOutput;
+ this.mainDexListConsumer = mainDexListConsumer;
this.proguardConfiguration = proguardConfiguration;
this.useTreeShaking = useTreeShaking;
this.useDiscardedChecker = useDiscardedChecker;
@@ -496,7 +499,7 @@
private R8Command(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
mainDexKeepRules = ImmutableList.of();
- mainDexListOutput = null;
+ mainDexListConsumer = null;
proguardConfiguration = null;
useTreeShaking = false;
useDiscardedChecker = false;
@@ -549,9 +552,8 @@
assert !internal.verbose;
internal.mainDexKeepRules = mainDexKeepRules;
internal.minimalMainDex = internal.debug;
- if (mainDexListOutput != null) {
- internal.printMainDexListFile = mainDexListOutput;
- }
+ internal.mainDexListConsumer = mainDexListConsumer;
+
internal.outputMode = getOutputMode();
if (internal.debug) {
// TODO(zerny): Should we support removeSwitchMaps in debug mode? b/62936642
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index a26b736..9e5ad47 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -80,12 +80,22 @@
this.outputPath = outputPath;
}
- /** Set the encoding. Defaults to UTF8. */
+ /** Get the output path that the consumer will write to. */
+ public Path getOutputPath() {
+ return outputPath;
+ }
+
+ /** Set the output encoding. Defaults to UTF8. */
public void setEncoding(Charset encoding) {
assert encoding != null;
this.encoding = encoding;
}
+ /** Get the output encoding. Defaults to UTF8. */
+ public Charset getEncoding() {
+ return encoding;
+ }
+
@Override
public void accept(String string, DiagnosticsHandler handler) {
super.accept(string, handler);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index a20f3ed..76c756b 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -30,9 +30,7 @@
import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ObjectArrays;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -223,9 +221,9 @@
outputSink.writeProguardSeedsFile(proguardSeedsData);
}
}
- byte[] mainDexList = writeMainDexList();
- if (mainDexList != null) {
- outputSink.writeMainDexListFile(mainDexList);
+ if (options.mainDexListConsumer != null) {
+ ExceptionUtils.withConsumeResourceHandler(
+ options.reporter, writeMainDexList(), options.mainDexListConsumer);
}
} finally {
application.timing.end();
@@ -342,16 +340,9 @@
.replace('.', '/') + ".class";
}
- private byte[] writeMainDexList() {
- if (application.mainDexList.isEmpty()) {
- return null;
- }
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- PrintWriter writer = new PrintWriter(bytes);
- application.mainDexList.forEach(
- type -> writer.println(mapMainDexListName(type))
- );
- writer.flush();
- return bytes.toByteArray();
+ private String writeMainDexList() {
+ StringBuilder builder = new StringBuilder();
+ application.mainDexList.forEach(type -> builder.append(mapMainDexListName(type)).append('\n'));
+ return builder.toString();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
index 84c7e2d..23580a5 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppOutputSink.java
@@ -22,11 +22,13 @@
private final List<DescriptorsWithContents> classFiles = new ArrayList<>();
private boolean closed = false;
+ private StringConsumer mainDexListConsumer = null;
private StringConsumer proguardMapConsumer = null;
private StringConsumer usageInformationConsumer = null;
public AndroidAppOutputSink(OutputSink forwardTo, InternalOptions options) {
super(forwardTo);
+ options.mainDexListConsumer = wrapMainDexListConsumer(options.mainDexListConsumer);
options.proguardMapConsumer = wrapProguardMapConsumer(options.proguardMapConsumer);
options.usageInformationConsumer =
wrapUsageInformationConsumer(options.usageInformationConsumer);
@@ -36,6 +38,21 @@
super(new IgnoreContentsOutputSink());
}
+ private StringConsumer wrapMainDexListConsumer(StringConsumer consumer) {
+ assert mainDexListConsumer == null;
+ if (consumer != null) {
+ mainDexListConsumer =
+ new StringConsumer.ForwardingConsumer(consumer) {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ super.accept(string, handler);
+ builder.setMainDexListOutputData(string.getBytes(StandardCharsets.UTF_8));
+ }
+ };
+ }
+ return mainDexListConsumer;
+ }
+
private StringConsumer wrapProguardMapConsumer(StringConsumer consumer) {
assert proguardMapConsumer == null;
if (consumer != null) {
@@ -100,12 +117,6 @@
}
@Override
- public void writeMainDexListFile(byte[] contents) throws IOException {
- builder.setMainDexListOutputData(contents);
- super.writeMainDexListFile(contents);
- }
-
- @Override
public void close() throws IOException {
assert !closed;
if (!dexFilesWithPrimary.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
index 6623bda..6e5b33f 100644
--- a/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/FileSystemOutputSink.java
@@ -39,11 +39,6 @@
FileUtils.writeToFile(options.proguardConfiguration.getSeedFile(), System.out, contents);
}
- @Override
- public void writeMainDexListFile(byte[] contents) throws IOException {
- FileUtils.writeToFile(options.printMainDexListFile, System.out, contents);
- }
-
protected OutputMode getOutputMode() {
return options.outputMode;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java b/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java
index 31191e3..a6fb545 100644
--- a/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/ForwardingOutputSink.java
@@ -44,11 +44,6 @@
}
@Override
- public void writeMainDexListFile(byte[] contents) throws IOException {
- forwardTo.writeMainDexListFile(contents);
- }
-
- @Override
public void close() throws IOException {
forwardTo.close();
}
diff --git a/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java b/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java
index 8359244..21f98f6 100644
--- a/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java
+++ b/src/main/java/com/android/tools/r8/utils/IgnoreContentsOutputSink.java
@@ -31,11 +31,6 @@
}
@Override
- public void writeMainDexListFile(byte[] contents) {
- // Intentionally left empty.
- }
-
- @Override
public void close() throws IOException {
// Intentionally left empty.
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 6c2b63e..dd9f970 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -120,7 +120,6 @@
public boolean printCfg = false;
public String printCfgFile;
- public Path printMainDexListFile;
public boolean ignoreMissingClasses = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
@@ -171,6 +170,10 @@
// the code contains unsupported byte codes.
public boolean skipReadingDexCode = false;
+ // If null, no main-dex list needs to be computed.
+ // If non null it must be and passed to the consumer.
+ public StringConsumer mainDexListConsumer = null;
+
// If null, no proguad map needs to be computed.
// If non null it must be and passed to the consumer.
public StringConsumer proguardMapConsumer = null;