Introduce MainDexOverflowDiagnostic.
This CL also moves specialized messages to custom D8/R8 diagnostics handlers.
Change-Id: I2639df8aa30cdd2fb5880a30b864d812c791f66e
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 6ae611c..297d6df 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -36,7 +36,7 @@
programConsumer = null;
mode = null;
minApiLevel = 0;
- reporter = new Reporter(new DefaultDiagnosticsHandler(), this);
+ reporter = new Reporter(new DefaultDiagnosticsHandler());
enableDesugaring = true;
optimizeMultidexForLinearAlloc = false;
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 19c1bd6..4c5e0b9 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
@@ -35,6 +38,23 @@
}
}
+ private static class DefaultD8DiagnosticsHandler extends DefaultDiagnosticsHandler {
+
+ @Override
+ public void error(Diagnostic error) {
+ if (error instanceof DexFileOverflowDiagnostic) {
+ DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
+ if (!overflowDiagnostic.hasMainDexSpecification()) {
+ super.error(
+ new StringDiagnostic(
+ overflowDiagnostic.getDiagnosticMessage() + ". Try supplying a main-dex list"));
+ return;
+ }
+ }
+ super.error(error);
+ }
+ }
+
/**
* Builder for constructing a D8Command.
*
@@ -45,7 +65,9 @@
private boolean intermediate = false;
- private Builder() {}
+ private Builder() {
+ this(new DefaultD8DiagnosticsHandler());
+ }
private Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index c53f367..0970a0c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
@@ -16,6 +17,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -56,6 +58,24 @@
@Keep
public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
+ private static class DefaultR8DiagnosticsHandler extends DefaultDiagnosticsHandler {
+
+ @Override
+ public void error(Diagnostic error) {
+ if (error instanceof DexFileOverflowDiagnostic) {
+ DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
+ if (!overflowDiagnostic.hasMainDexSpecification()) {
+ super.error(
+ new StringDiagnostic(
+ overflowDiagnostic.getDiagnosticMessage()
+ + ". Try supplying a main-dex list or main-dex rules"));
+ return;
+ }
+ }
+ super.error(error);
+ }
+ }
+
private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
private Consumer<ProguardConfiguration.Builder> proguardConfigurationConsumer = null;
private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>();
@@ -74,7 +94,9 @@
private StringConsumer mainDexListConsumer = null;
// TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
- Builder() {}
+ Builder() {
+ this(new DefaultR8DiagnosticsHandler());
+ }
Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7b012ce..d085859 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.errors.MainDexOverflow;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
@@ -190,11 +190,8 @@
return;
}
throw reporter.fatalError(
- new MainDexOverflow(
- hasMainDexList,
- transaction.getNumberOfMethods(),
- transaction.getNumberOfFields(),
- MAX_ENTRIES));
+ new DexFileOverflowDiagnostic(
+ hasMainDexList, transaction.getNumberOfMethods(), transaction.getNumberOfFields()));
}
private boolean isFilledEnough(FillStrategy fillStrategy) {
diff --git a/src/main/java/com/android/tools/r8/errors/DexFileOverflowDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DexFileOverflowDiagnostic.java
new file mode 100644
index 0000000..9427603
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/DexFileOverflowDiagnostic.java
@@ -0,0 +1,98 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+/**
+ * Diagnostic information about errors when classes cannot fit in a DEX file.
+ *
+ * <p>This can happen when compiling to a single DEX file but not all classes can fit in it; or when
+ * compiling for legacy multidex but there are too many classes that need to fit in the main DEX
+ * file, e.g., classes.dex.
+ */
+@Keep
+public class DexFileOverflowDiagnostic implements Diagnostic {
+ private final boolean hasMainDexSpecification;
+ private final long numOfMethods;
+ private final long numOfFields;
+
+ public DexFileOverflowDiagnostic(
+ boolean hasMainDexSpecification, long numOfMethods, long numOfFields) {
+ this.hasMainDexSpecification = hasMainDexSpecification;
+ this.numOfMethods = numOfMethods;
+ this.numOfFields = numOfFields;
+ }
+
+ /** The number of fields that the application needs to include in the main DEX file. */
+ public long getNumberOfFields() {
+ return numOfFields;
+ }
+
+ /** The number of methods that the application needs to include in the main DEX file. */
+ public long getNumberOfMethods() {
+ return numOfMethods;
+ }
+
+ /** The maximum number of fields that can be included in a DEX file. */
+ public long getMaximumNumberOfFields() {
+ return VirtualFile.MAX_ENTRIES;
+ }
+
+ /** The maximum number of methods that can be included in a DEX file. */
+ public long getMaximumNumberOfMethods() {
+ return VirtualFile.MAX_ENTRIES;
+ }
+
+ /** True if the application has specified lists and/or rules for computing the main DEX file. */
+ public boolean hasMainDexSpecification() {
+ return hasMainDexSpecification;
+ }
+
+ /** The origin of a main DEX file overflow is not unique. (The whole app is to blame.) */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** The position of the main DEX error is not specified. */
+ @Override
+ public Position getPosition() {
+ return null;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder();
+ // General message: Cannot fit.
+ builder
+ .append("Cannot fit requested classes in ")
+ .append(hasMainDexSpecification() ? "the main-" : "a single ")
+ .append("dex file")
+ .append(" (");
+ // Show the numbers of methods and/or fields that exceed the limit.
+ if (getNumberOfMethods() > getMaximumNumberOfMethods()) {
+ builder
+ .append("# methods: ")
+ .append(getNumberOfMethods())
+ .append(" > ")
+ .append(getMaximumNumberOfMethods());
+ if (getNumberOfFields() > getMaximumNumberOfFields()) {
+ builder.append(" ; ");
+ }
+ }
+ if (getNumberOfFields() > getMaximumNumberOfFields()) {
+ builder
+ .append("# fields: ")
+ .append(getNumberOfFields())
+ .append(" > ")
+ .append(getMaximumNumberOfFields());
+ }
+ return builder.append(")").toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
deleted file mode 100644
index e33cf59..0000000
--- a/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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.errors;
-
-/**
- * Info about error when running mono dex and not all classes can fit in a dex or when running for
- * multidex legacy and there are too many classes to fit in the main dex.
- */
-public class MainDexOverflow {
- private final boolean hasMainDexList;
- private final long numOfMethods;
- private final long numOfFields;
- private final long maxNumOfEntries;
-
- public MainDexOverflow(
- boolean hasMainDexList, long numOfMethods, long numOfFields, long maxNumOfEntries) {
- super();
- this.hasMainDexList = hasMainDexList;
- this.numOfMethods = numOfMethods;
- this.numOfFields = numOfFields;
- this.maxNumOfEntries = maxNumOfEntries;
- }
-
- private StringBuilder getGeneralMessage() {
- StringBuilder messageBuilder = new StringBuilder();
- // General message: Cannot fit.
- messageBuilder.append("Cannot fit requested classes in ");
- messageBuilder.append(hasMainDexList ? "the main-" : "a single ");
- messageBuilder.append("dex file");
-
- return messageBuilder;
- }
-
- private String getNumberRelatedMessage() {
- StringBuilder messageBuilder = new StringBuilder();
- // Show the numbers of methods and/or fields that exceed the limit.
- if (numOfMethods > maxNumOfEntries) {
- messageBuilder.append("# methods: ");
- messageBuilder.append(numOfMethods);
- messageBuilder.append(" > ").append(maxNumOfEntries);
- if (numOfFields > maxNumOfEntries) {
- messageBuilder.append(" ; ");
- }
- }
- if (numOfFields > maxNumOfEntries) {
- messageBuilder.append("# fields: ");
- messageBuilder.append(numOfFields);
- messageBuilder.append(" > ").append(maxNumOfEntries);
- }
-
- return messageBuilder.toString();
- }
-
- public String getMessage() {
- // Default message
- return getGeneralMessage()
- .append(" (")
- .append(getNumberRelatedMessage())
- .append(")")
- .toString();
- }
-
- public String getMessageForD8() {
- StringBuilder messageBuilder = getGeneralMessage();
- if (!hasMainDexList) {
- messageBuilder.append(". ");
- messageBuilder.append("Try supplying a main-dex list");
- }
- messageBuilder.append(".").append(System.getProperty("line.separator"));
- messageBuilder.append(getNumberRelatedMessage());
- return messageBuilder.toString();
- }
-
- public String getMessageForR8() {
- StringBuilder messageBuilder = getGeneralMessage();
- if (!hasMainDexList) {
- messageBuilder.append(". ");
- messageBuilder.append("Try supplying a main-dex list or main dex rules");
- }
- messageBuilder.append(".").append(System.getProperty("line.separator"));
- messageBuilder.append(getNumberRelatedMessage());
- return messageBuilder.toString();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 664a065..9e9e584 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -331,16 +331,12 @@
return this;
}
- /**
- * Add classpath file resources.
- */
+ /** Add classpath file resources. */
public Builder addClasspathFiles(Path... files) {
return addClasspathFiles(Arrays.asList(files));
}
- /**
- * Add classpath file resources.
- */
+ /** Add classpath file resources. */
public Builder addClasspathFiles(Collection<Path> files) {
for (Path file : files) {
addClasspathFile(file);
@@ -348,9 +344,7 @@
return this;
}
- /**
- * Add classpath file resources.
- */
+ /** Add classpath file resources. */
public Builder addClasspathFile(Path file) {
addClasspathOrLibraryProvider(file, classpathResourceProviders);
return this;
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index b09f164..ebb7ce6 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -3,14 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
-import com.android.tools.r8.BaseCompilerCommand;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8Command;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.MainDexOverflow;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
@@ -20,18 +16,12 @@
public class Reporter implements DiagnosticsHandler {
private final DiagnosticsHandler clientHandler;
- private final BaseCompilerCommand command;
private int errorCount = 0;
private Diagnostic lastError;
private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
public Reporter(DiagnosticsHandler clientHandler) {
- this(clientHandler, null);
- }
-
- public Reporter(DiagnosticsHandler clientHandler, BaseCompilerCommand command) {
this.clientHandler = clientHandler;
- this.command = command;
}
@Override
@@ -85,19 +75,6 @@
}
/**
- * @throws AbortException always.
- */
- public RuntimeException fatalError(MainDexOverflow e) {
- if (command instanceof R8Command) {
- return fatalError(new StringDiagnostic(e.getMessageForR8()));
- } else if (command instanceof D8Command) {
- return fatalError(new StringDiagnostic(e.getMessageForD8()));
- } else {
- return fatalError(new StringDiagnostic(e.getMessage()));
- }
- }
-
- /**
* @throws AbortException if any error was reported.
*/
public void failIfPendingErrors() {