Merge changes I2342b395,Ie0918957,I0a9a2a17
* changes:
Use command-line parsing in apiUsageSample
Add @Keep and @KeepForSubclassing annotations for public R8 API
Make R8 and D8 command-line parsing use only public API
diff --git a/build.gradle b/build.gradle
index cd831cc..f379bbf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,7 +111,12 @@
}
apiUsageSample {
java {
- srcDirs = ['src/test/apiUsageSample']
+ srcDirs = ['src/test/apiUsageSample', 'src/main/java']
+ include 'com/android/tools/apiusagesample/*.java'
+ include 'com/android/tools/r8/BaseCompilerCommandParser.java'
+ include 'com/android/tools/r8/D8CommandParser.java'
+ include 'com/android/tools/r8/R8CommandParser.java'
+ include 'com/android/tools/r8/utils/FlagFile.java'
}
}
debugTestResources {
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index e17acd0..b3d59fb 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -35,6 +35,7 @@
* <p>The descriptor index is built eagerly upon creating the provider and subsequent requests for
* resources in the descriptor set will then force the read of zip entry contents.
*/
+@Keep
public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
private final Origin origin;
private final ZipFile zipFile;
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index b3a4bfb..baac9bc 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -29,8 +29,10 @@
import java.util.zip.ZipFile;
/** Provider for archives of program resources. */
+@KeepForSubclassing
public class ArchiveProgramResourceProvider implements ProgramResourceProvider {
+ @KeepForSubclassing
public interface ZipFileSupplier {
ZipFile open() throws IOException;
}
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index fbc6ca3..8828248 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -28,6 +28,7 @@
* <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
* R8Command.Builder}.
*/
+@Keep
public abstract class BaseCommand {
private final boolean printHelp;
@@ -101,6 +102,7 @@
* @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
* D8Command.Builder}.
*/
+ @Keep
public abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
private final Reporter reporter;
@@ -304,6 +306,20 @@
return self();
}
+ /** Signal an error. */
+ public void error(Diagnostic diagnostic) {
+ reporter.error(diagnostic);
+ }
+
+ /**
+ * Signal an error and throw {@link AbortException}.
+ *
+ * @throws AbortException always.
+ */
+ public RuntimeException fatalError(Diagnostic diagnostic) {
+ return reporter.fatalError(diagnostic);
+ }
+
// Internal helper for compat tools to make them ignore DEX code in input archives.
void setIgnoreDexInArchive(boolean value) {
guard(() -> app.setIgnoreDexInArchive(value));
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 799d834..5980a0c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -4,13 +4,11 @@
package com.android.tools.r8;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
@@ -23,6 +21,7 @@
* <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
* R8Command.Builder}.
*/
+@Keep
public abstract class BaseCompilerCommand extends BaseCommand {
private final CompilationMode mode;
@@ -106,6 +105,7 @@
* @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
* D8Command.Builder}.
*/
+ @Keep
public abstract static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
extends BaseCommand.Builder<C, B> {
@@ -341,31 +341,4 @@
}
}
- static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
- boolean parseMinApi(
- BaseCompilerCommand.Builder<C, B> builder,
- String minApiString,
- boolean hasDefinedApiLevel,
- Origin origin) {
- if (hasDefinedApiLevel) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot set multiple --min-api options", origin));
- return false;
- }
- int minApi;
- try {
- minApi = Integer.valueOf(minApiString);
- } catch (NumberFormatException e) {
- builder.getReporter().error(new StringDiagnostic(
- "Invalid argument to --min-api: " + minApiString, origin));
- return false;
- }
- if (minApi < 1) {
- builder.getReporter().error(new StringDiagnostic(
- "Invalid argument to --min-api: " + minApiString, origin));
- return false;
- }
- builder.setMinApiLevel(minApi);
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
new file mode 100644
index 0000000..054db58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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 com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringDiagnostic;
+
+public class BaseCompilerCommandParser {
+
+ static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+ int minApi;
+ try {
+ minApi = Integer.valueOf(minApiString);
+ } catch (NumberFormatException e) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ return;
+ }
+ if (minApi < 1) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ return;
+ }
+ builder.setMinApiLevel(minApi);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6c9a286..3bab0ab 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -26,6 +26,7 @@
*
* <p>This consumer can only be provided to R8.
*/
+@KeepForSubclassing
public interface ClassFileConsumer extends ProgramConsumer {
/**
@@ -49,6 +50,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements ClassFileConsumer {
private static final ClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -80,6 +82,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
@@ -163,6 +166,7 @@
}
/** Directory consumer to write program resources to a directory. */
+ @Keep
class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
protected final boolean consumeDataResouces;
diff --git a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
index 4c71d88..db0a9f8 100644
--- a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
@@ -12,6 +12,7 @@
* only created on-demand when they are needed by the compiler. If never needed, the resource will
* never be loaded.
*/
+@KeepForSubclassing
public interface ClassFileResourceProvider {
/**
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index a6923ba..89a33c1 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -6,6 +6,8 @@
import java.nio.file.Path;
+// This class is used by the Android Studio Gradle plugin and is thus part of the R8 API.
+@Keep
public class CompatProguardCommandBuilder extends R8Command.Builder {
public CompatProguardCommandBuilder() {
this(true);
diff --git a/src/main/java/com/android/tools/r8/CompilationFailedException.java b/src/main/java/com/android/tools/r8/CompilationFailedException.java
index 561ad0c..bcc4ad1 100644
--- a/src/main/java/com/android/tools/r8/CompilationFailedException.java
+++ b/src/main/java/com/android/tools/r8/CompilationFailedException.java
@@ -7,6 +7,7 @@
* Exception thrown when compilation failed to complete because of errors previously reported
* through {@link com.android.tools.r8.DiagnosticsHandler}.
*/
+@Keep
public class CompilationFailedException extends Exception {
public CompilationFailedException() {
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
index f7aa4f0..fb02504 100644
--- a/src/main/java/com/android/tools/r8/CompilationMode.java
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
/** Compilation mode. */
+@Keep
public enum CompilationMode {
/** Preserves debugging information during compilation, eg, line-numbers and locals. */
DEBUG,
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 06cf0bb..f4d378a 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -52,6 +52,7 @@
* them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
* and then writes the result to the directory or zip archive specified by {@code outputPath}.
*/
+@Keep
public final class D8 {
private D8() {}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6f956db..8c6c02b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,18 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import com.android.tools.r8.errors.CompilationError;
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.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
@@ -31,7 +26,8 @@
* .build();
* </pre>
*/
-public class D8Command extends BaseCompilerCommand {
+@Keep
+public final class D8Command extends BaseCompilerCommand {
private static class ClasspathInputOrigin extends InputFileOrigin {
@@ -45,6 +41,7 @@
*
* <p>A builder is obtained by calling {@link D8Command#builder}.
*/
+ @Keep
public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> {
private boolean intermediate = false;
@@ -150,24 +147,7 @@
}
}
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: d8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --debug # Compile with debugging information (default).",
- " --release # Compile without debugging information.",
- " --output <file> # Output result in <outfile>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file> # Add <file> as a library resource.",
- " --classpath <file> # Add <file> as a classpath resource.",
- " --min-api # Minimum Android API level compatibility",
- " --intermediate # Compile an intermediate result intended for later",
- " # merging.",
- " --file-per-class # Produce a separate dex file per input class",
- " --no-desugaring # Force disable desugaring.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --version # Print the version of d8.",
- " --help # Print this message."));
+ static final String USAGE_MESSAGE = D8CommandParser.USAGE_MESSAGE;
private boolean intermediate = false;
@@ -194,7 +174,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin) {
- return parse(args, origin, builder());
+ return D8CommandParser.parse(args, origin);
}
/**
@@ -208,87 +188,7 @@
* @return D8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, builder(handler));
- }
-
- private static Builder parse(String[] args, Origin origin, Builder builder) {
- CompilationMode compilationMode = null;
- Path outputPath = null;
- OutputMode outputMode = null;
- boolean hasDefinedApiLevel = false;
- String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
- try {
- for (int i = 0; i < expandedArgs.length; i++) {
- String arg = expandedArgs[i].trim();
- if (arg.length() == 0) {
- continue;
- } else if (arg.equals("--help")) {
- builder.setPrintHelp(true);
- } else if (arg.equals("--version")) {
- builder.setPrintVersion(true);
- } else if (arg.equals("--debug")) {
- if (compilationMode == CompilationMode.RELEASE) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.",
- origin));
- continue;
- }
- compilationMode = CompilationMode.DEBUG;
- } else if (arg.equals("--release")) {
- if (compilationMode == CompilationMode.DEBUG) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.",
- origin));
- continue;
- }
- compilationMode = CompilationMode.RELEASE;
- } else if (arg.equals("--file-per-class")) {
- outputMode = OutputMode.DexFilePerClassFile;
- } else if (arg.equals("--output")) {
- String output = expandedArgs[++i];
- if (outputPath != null) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
- origin));
- continue;
- }
- outputPath = Paths.get(output);
- } else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--classpath")) {
- builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
- builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--min-api")) {
- hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
- } else if (arg.equals("--intermediate")) {
- builder.setIntermediate(true);
- } else if (arg.equals("--no-desugaring")) {
- builder.setDisableDesugaring(true);
- } else {
- if (arg.startsWith("--")) {
- builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg,
- origin));
- continue;
- }
- builder.addProgramFiles(Paths.get(arg));
- }
- }
- if (compilationMode != null) {
- builder.setMode(compilationMode);
- }
- if (outputMode == null) {
- outputMode = OutputMode.DexIndexed;
- }
- if (outputPath == null) {
- outputPath = Paths.get(".");
- }
- return builder.setOutput(outputPath, outputMode);
- } catch (CompilationError e) {
- throw builder.getReporter().fatalError(e);
- }
+ return D8CommandParser.parse(args, origin, handler);
}
private D8Command(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
new file mode 100644
index 0000000..9e5d36a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2018, 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 com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class D8CommandParser extends BaseCompilerCommandParser {
+
+ public static void main(String[] args) throws CompilationFailedException {
+ D8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ D8.run(command);
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: d8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --debug # Compile with debugging information (default).",
+ " --release # Compile without debugging information.",
+ " --output <file> # Output result in <outfile>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --classpath <file> # Add <file> as a classpath resource.",
+ " --min-api # Minimum Android API level compatibility",
+ " --intermediate # Compile an intermediate result intended for later",
+ " # merging.",
+ " --file-per-class # Produce a separate dex file per input class",
+ " --no-desugaring # Force disable desugaring.",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --version # Print the version of d8.",
+ " --help # Print this message."));
+
+ /**
+ * 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 D8Command.Builder parse(String[] args, Origin origin) {
+ return parse(args, origin, D8Command.builder());
+ }
+
+ /**
+ * 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 D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return parse(args, origin, D8Command.builder(handler));
+ }
+
+ private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+ CompilationMode compilationMode = null;
+ Path outputPath = null;
+ OutputMode outputMode = null;
+ boolean hasDefinedApiLevel = false;
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ try {
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (compilationMode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (compilationMode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+ continue;
+ }
+ compilationMode = CompilationMode.RELEASE;
+ } else if (arg.equals("--file-per-class")) {
+ outputMode = OutputMode.DexFilePerClassFile;
+ } else if (arg.equals("--output")) {
+ String output = expandedArgs[++i];
+ if (outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
+ origin));
+ continue;
+ }
+ outputPath = Paths.get(output);
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--classpath")) {
+ builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list")) {
+ builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+ builder.setOptimizeMultidexForLinearAlloc(true);
+ } else if (arg.equals("--min-api")) {
+ String minApiString = expandedArgs[++i];
+ if (hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ } else {
+ parseMinApi(builder, minApiString, origin);
+ hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--intermediate")) {
+ builder.setIntermediate(true);
+ } else if (arg.equals("--no-desugaring")) {
+ builder.setDisableDesugaring(true);
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+ continue;
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ if (compilationMode != null) {
+ builder.setMode(compilationMode);
+ }
+ if (outputMode == null) {
+ outputMode = OutputMode.DexIndexed;
+ }
+ if (outputPath == null) {
+ outputPath = Paths.get(".");
+ }
+ return builder.setOutput(outputPath, outputMode);
+ } catch (CompilationError e) {
+ throw builder.fatalError(e);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/DataDirectoryResource.java b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
index 2f421f7..3e860e3 100644
--- a/src/main/java/com/android/tools/r8/DataDirectoryResource.java
+++ b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
@@ -12,6 +12,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+@Keep
public interface DataDirectoryResource extends DataResource {
static DataDirectoryResource fromFile(Path dir, Path file) {
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 189d5eb..aa9c73f 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -15,6 +15,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+@Keep
public interface DataEntryResource extends DataResource {
/** Get the bytes of the data entry resource. */
diff --git a/src/main/java/com/android/tools/r8/DataResource.java b/src/main/java/com/android/tools/r8/DataResource.java
index 4609c97..eac8b6c 100644
--- a/src/main/java/com/android/tools/r8/DataResource.java
+++ b/src/main/java/com/android/tools/r8/DataResource.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@Keep
public interface DataResource extends Resource {
char SEPARATOR = '/';
diff --git a/src/main/java/com/android/tools/r8/DataResourceConsumer.java b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
index 215ca59..e306b9b 100644
--- a/src/main/java/com/android/tools/r8/DataResourceConsumer.java
+++ b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@KeepForSubclassing
public interface DataResourceConsumer {
void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/DataResourceProvider.java b/src/main/java/com/android/tools/r8/DataResourceProvider.java
index e851f0f..5e0a24f 100644
--- a/src/main/java/com/android/tools/r8/DataResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/DataResourceProvider.java
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+@KeepForSubclassing
public interface DataResourceProvider {
+ @KeepForSubclassing
interface Visitor {
void visit(DataDirectoryResource directory);
void visit(DataEntryResource file);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f1c0fac..311a3dd 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -29,6 +29,7 @@
*
* <p>This consumer receives DEX file content for each Java class-file input.
*/
+@KeepForSubclassing
public interface DexFilePerClassFileConsumer extends ProgramConsumer {
/**
@@ -57,6 +58,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements DexFilePerClassFileConsumer {
private static final DexFilePerClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -92,6 +94,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
}
/** Directory consumer to write program resources to a directory. */
+ @Keep
class DirectoryConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final OutputBuilder outputBuilder;
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 3bbab64..e802103 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -31,6 +31,7 @@
* <p>This consumer receives DEX file content using standard indexed-multidex for programs larger
* than a single DEX file. This is the default consumer for DEX programs.
*/
+@KeepForSubclassing
public interface DexIndexedConsumer extends ProgramConsumer {
/**
@@ -58,6 +59,7 @@
}
/** Forwarding consumer to delegate to an optional existing consumer. */
+ @Keep
class ForwardingConsumer implements DexIndexedConsumer {
private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -100,6 +102,7 @@
}
/** Consumer to write program resources to an output. */
+ @Keep
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
protected final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
}
}
+ @Keep
class DirectoryConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final Path directory;
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 2d52ec9..3e299f2 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -31,7 +31,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-public class DexSplitterHelper {
+@Keep
+public final class DexSplitterHelper {
public static void run(
D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 7896251..3184d54 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -9,6 +9,7 @@
/**
* Interface for all diagnostic message produced by D8 and R8.
*/
+@Keep
public interface Diagnostic {
/**
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 8212835..74a9a96 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -10,6 +10,7 @@
*
* <p>During compilation the warning and info methods will be called.
*/
+@Keep
public interface DiagnosticsHandler {
/**
diff --git a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
index 5d5f751..50aff3f 100644
--- a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
@@ -19,6 +19,7 @@
* Lazy resource provider returning class file resources based
* on filesystem directory content.
*/
+@Keep
public final class DirectoryClassFileProvider implements ClassFileResourceProvider {
private final Path root;
diff --git a/src/main/java/com/android/tools/r8/Keep.java b/src/main/java/com/android/tools/r8/Keep.java
new file mode 100644
index 0000000..13ed8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Keep.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface Keep {
+
+}
diff --git a/src/main/java/com/android/tools/r8/KeepForSubclassing.java b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
new file mode 100644
index 0000000..6e57114
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface KeepForSubclassing {
+
+}
diff --git a/src/main/java/com/android/tools/r8/OutputMode.java b/src/main/java/com/android/tools/r8/OutputMode.java
index 0da34b7..bc8a8d8 100644
--- a/src/main/java/com/android/tools/r8/OutputMode.java
+++ b/src/main/java/com/android/tools/r8/OutputMode.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
/** Enumeration of the possible output formats of compilation. */
+@Keep
public enum OutputMode {
/** Produce DEX files using standard indexed-multidex for programs larger that a single file. */
diff --git a/src/main/java/com/android/tools/r8/ProgramConsumer.java b/src/main/java/com/android/tools/r8/ProgramConsumer.java
index 08d7d7c..b3143a0 100644
--- a/src/main/java/com/android/tools/r8/ProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/ProgramConsumer.java
@@ -6,6 +6,7 @@
/**
* Base for all program consumers to allow abstracting which concrete consumer is provided to D8/R8.
*/
+@KeepForSubclassing
public interface ProgramConsumer {
/**
diff --git a/src/main/java/com/android/tools/r8/ProgramResource.java b/src/main/java/com/android/tools/r8/ProgramResource.java
index cabeabc..ffeb558 100644
--- a/src/main/java/com/android/tools/r8/ProgramResource.java
+++ b/src/main/java/com/android/tools/r8/ProgramResource.java
@@ -20,9 +20,11 @@
* A resource may optionally include a set describing the class descriptors for each type that is
* defined by the resource.
*/
+@KeepForSubclassing
public interface ProgramResource extends Resource {
/** Type of program-format kinds. */
+ @Keep
enum Kind {
/** Format-kind for Java class-file resources. */
CF,
@@ -66,6 +68,7 @@
Set<String> getClassDescriptors();
/** File-based program resource. */
+ @Keep
class FileResource implements ProgramResource {
private final Origin origin;
private final Kind kind;
@@ -105,6 +108,7 @@
}
/** Byte-content based program resource. */
+ @Keep
class ByteResource implements ProgramResource {
private final Origin origin;
private final Kind kind;
diff --git a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
index 76d340c..d4b8a87 100644
--- a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
@@ -6,6 +6,7 @@
import java.util.Collection;
/** Program resource provider. */
+@KeepForSubclassing
public interface ProgramResourceProvider {
Collection<ProgramResource> getProgramResources() throws ResourceException;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5d29d90..077e220 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -103,6 +103,7 @@
* them to DEX bytecode, using {@code androidJar} as the reference of the system runtime library,
* and then writes the result to the directory or zip archive specified by {@code outputPath}.
*/
+@Keep
public class R8 {
private final Timing timing = new Timing("R8");
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 573f6df..86ff5a5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
@@ -46,13 +45,15 @@
* .build();
* </pre>
*/
-public class R8Command extends BaseCompilerCommand {
+@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 final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
@@ -421,38 +422,7 @@
}
}
- // Internal state to verify parsing properties not enforced by the builder.
- private static class ParseState {
- CompilationMode mode = null;
- OutputMode outputMode = null;
- Path outputPath = null;
- boolean hasDefinedApiLevel = false;
- }
-
- static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
- "Usage: r8 [options] <input-files>",
- " where <input-files> are any combination of dex, class, zip, jar, or apk files",
- " and options are:",
- " --release # Compile without debugging information (default).",
- " --debug # Compile with debugging information.",
- // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
- //" --dex # Compile program to DEX file format (default).",
- //" --classfile # Compile program to Java classfile format.",
- " --output <file> # Output result in <file>.",
- " # <file> must be an existing directory or a zip file.",
- " --lib <file> # Add <file> as a library resource.",
- " --min-api # Minimum Android API level compatibility.",
- " --pg-conf <file> # Proguard configuration <file>.",
- " --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
- " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
- " --no-minification # Force disable minification of names.",
- " --no-desugaring # Force disable desugaring.",
- " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
- " # primary dex file.",
- " --main-dex-list <file> # List of classes to place in the primary dex file.",
- " --main-dex-list-output <file> # Output the full main-dex list in <file>.",
- " --version # Print the version of r8.",
- " --help # Print this message."));
+ static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
private final StringConsumer mainDexListConsumer;
@@ -488,7 +458,7 @@
* @return R8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin) {
- return parse(args, origin, builder());
+ return R8CommandParser.parse(args, origin);
}
/**
@@ -502,102 +472,7 @@
* @return R8 command builder with state set up according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
- return parse(args, origin, builder(handler));
- }
-
- private static Builder parse(String[] args, Origin origin, Builder builder) {
- ParseState state = new ParseState();
- parse(args, origin, builder, state);
- if (state.mode != null) {
- builder.setMode(state.mode);
- }
- Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
- OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
- builder.setOutput(outputPath, outputMode);
- return builder;
- }
-
- private static ParseState parse(
- String[] args,
- Origin argsOrigin,
- Builder builder,
- ParseState state) {
- String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
- for (int i = 0; i < expandedArgs.length; i++) {
- String arg = expandedArgs[i].trim();
- if (arg.length() == 0) {
- continue;
- } else if (arg.equals("--help")) {
- builder.setPrintHelp(true);
- } else if (arg.equals("--version")) {
- builder.setPrintVersion(true);
- } else if (arg.equals("--debug")) {
- if (state.mode == CompilationMode.RELEASE) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.", argsOrigin));
- }
- state.mode = CompilationMode.DEBUG;
- } else if (arg.equals("--release")) {
- if (state.mode == CompilationMode.DEBUG) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --debug and --release mode.", argsOrigin));
- }
- state.mode = CompilationMode.RELEASE;
- } else if (arg.equals("--dex")) {
- if (state.outputMode == OutputMode.ClassFile) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
- }
- state.outputMode = OutputMode.DexIndexed;
- } else if (arg.equals("--classfile")) {
- if (state.outputMode == OutputMode.DexIndexed) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
- }
- state.outputMode = OutputMode.ClassFile;
- } else if (arg.equals("--output")) {
- String outputPath = expandedArgs[++i];
- if (state.outputPath != null) {
- builder.getReporter().error(new StringDiagnostic(
- "Cannot output both to '"
- + state.outputPath.toString()
- + "' and '"
- + outputPath
- + "'",
- argsOrigin));
- }
- state.outputPath = Paths.get(outputPath);
- } else if (arg.equals("--lib")) {
- builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--min-api")) {
- state.hasDefinedApiLevel =
- parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
- } else if (arg.equals("--no-tree-shaking")) {
- builder.setDisableTreeShaking(true);
- } else if (arg.equals("--no-minification")) {
- builder.setDisableMinification(true);
- } else if (arg.equals("--no-desugaring")) {
- builder.setDisableDesugaring(true);
- } else if (arg.equals("--main-dex-rules")) {
- builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list")) {
- builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--main-dex-list-output")) {
- builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
- builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--pg-conf")) {
- builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
- } else if (arg.equals("--pg-map-output")) {
- builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
- } else {
- if (arg.startsWith("--")) {
- builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
- }
- builder.addProgramFiles(Paths.get(arg));
- }
- }
- return state;
+ return R8CommandParser.parse(args, origin, handler);
}
private R8Command(
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
new file mode 100644
index 0000000..3d33037
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2018, 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 com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class R8CommandParser extends BaseCompilerCommandParser {
+
+ public static void main(String[] args) throws CompilationFailedException {
+ R8Command command = parse(args, Origin.root()).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ System.exit(1);
+ }
+ R8.run(command);
+ }
+
+ // Internal state to verify parsing properties not enforced by the builder.
+ private static class ParseState {
+ CompilationMode mode = null;
+ OutputMode outputMode = null;
+ Path outputPath = null;
+ boolean hasDefinedApiLevel = false;
+ }
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Arrays.asList(
+ "Usage: r8 [options] <input-files>",
+ " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+ " and options are:",
+ " --release # Compile without debugging information (default).",
+ " --debug # Compile with debugging information.",
+ // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
+ // " --dex # Compile program to DEX file format (default).",
+ // " --classfile # Compile program to Java classfile format.",
+ " --output <file> # Output result in <file>.",
+ " # <file> must be an existing directory or a zip file.",
+ " --lib <file> # Add <file> as a library resource.",
+ " --min-api # Minimum Android API level compatibility.",
+ " --pg-conf <file> # Proguard configuration <file>.",
+ " --pg-map-output <file> # Output the resulting name and line mapping to <file>.",
+ " --no-tree-shaking # Force disable tree shaking of unreachable classes.",
+ " --no-minification # Force disable minification of names.",
+ " --no-desugaring # Force disable desugaring.",
+ " --main-dex-rules <file> # Proguard keep rules for classes to place in the",
+ " # primary dex file.",
+ " --main-dex-list <file> # List of classes to place in the primary dex file.",
+ " --main-dex-list-output <file> # Output the full main-dex list in <file>.",
+ " --version # Print the version of r8.",
+ " --help # Print this message."));
+ /**
+ * Parse the R8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return R8 command builder with state set up according to parsed command line.
+ */
+ public static R8Command.Builder parse(String[] args, Origin origin) {
+ return new R8CommandParser().parse(args, origin, R8Command.builder());
+ }
+
+ /**
+ * Parse the R8 command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return R8 command builder with state set up according to parsed command line.
+ */
+ public static R8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return new R8CommandParser().parse(args, origin, R8Command.builder(handler));
+ }
+
+ private R8Command.Builder parse(String[] args, Origin origin, R8Command.Builder builder) {
+ ParseState state = new ParseState();
+ parse(args, origin, builder, state);
+ if (state.mode != null) {
+ builder.setMode(state.mode);
+ }
+ Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
+ OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
+ builder.setOutput(outputPath, outputMode);
+ return builder;
+ }
+
+ private void parse(
+ String[] args, Origin argsOrigin, R8Command.Builder builder, ParseState state) {
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ if (arg.length() == 0) {
+ continue;
+ } else if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--debug")) {
+ if (state.mode == CompilationMode.RELEASE) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --debug and --release mode.", argsOrigin));
+ }
+ state.mode = CompilationMode.DEBUG;
+ } else if (arg.equals("--release")) {
+ if (state.mode == CompilationMode.DEBUG) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --debug and --release mode.", argsOrigin));
+ }
+ state.mode = CompilationMode.RELEASE;
+ } else if (arg.equals("--dex")) {
+ if (state.outputMode == OutputMode.ClassFile) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+ }
+ state.outputMode = OutputMode.DexIndexed;
+ } else if (arg.equals("--classfile")) {
+ if (state.outputMode == OutputMode.DexIndexed) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+ }
+ state.outputMode = OutputMode.ClassFile;
+ } else if (arg.equals("--output")) {
+ String outputPath = expandedArgs[++i];
+ if (state.outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '"
+ + state.outputPath.toString()
+ + "' and '"
+ + outputPath
+ + "'",
+ argsOrigin));
+ }
+ state.outputPath = Paths.get(outputPath);
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--min-api")) {
+ String minApiString = expandedArgs[++i];
+ if (state.hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+ } else {
+ parseMinApi(builder, minApiString, argsOrigin);
+ state.hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--no-tree-shaking")) {
+ builder.setDisableTreeShaking(true);
+ } else if (arg.equals("--no-minification")) {
+ builder.setDisableMinification(true);
+ } else if (arg.equals("--no-desugaring")) {
+ builder.setDisableDesugaring(true);
+ } else if (arg.equals("--main-dex-rules")) {
+ builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list")) {
+ builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--main-dex-list-output")) {
+ builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+ builder.setOptimizeMultidexForLinearAlloc(true);
+ } else if (arg.equals("--pg-conf")) {
+ builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
+ } else if (arg.equals("--pg-map-output")) {
+ builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+ } else {
+ if (arg.startsWith("--")) {
+ builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index b64fe01..50c50cc 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -19,6 +19,7 @@
* The D8/R8 compilers uses default implementations for various file-system resources, but the
* client is free to provide their own.
*/
+@KeepForSubclassing
public interface Resource {
/**
* Get the origin of the resource.
diff --git a/src/main/java/com/android/tools/r8/ResourceException.java b/src/main/java/com/android/tools/r8/ResourceException.java
index 3af7efa..2726b92 100644
--- a/src/main/java/com/android/tools/r8/ResourceException.java
+++ b/src/main/java/com/android/tools/r8/ResourceException.java
@@ -11,6 +11,7 @@
* For example, this is the expected exception that must be thrown if a resource fails to produce
* its content. See {@link ProgramResource#getByteStream()} and {@link StringResource#getString()}.
*/
+@Keep
public class ResourceException extends Exception {
private final Origin origin;
diff --git a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
index 0a6c84a..37ae662 100644
--- a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.Keep;
+
/**
* Origin representing an entry in an archive.
*/
+@Keep
public class ArchiveEntryOrigin extends Origin {
final String entryName;
diff --git a/src/main/java/com/android/tools/r8/origin/Origin.java b/src/main/java/com/android/tools/r8/origin/Origin.java
index c0bc2ec..3a8aa97 100644
--- a/src/main/java/com/android/tools/r8/origin/Origin.java
+++ b/src/main/java/com/android/tools/r8/origin/Origin.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.KeepForSubclassing;
import java.util.ArrayList;
import java.util.List;
@@ -22,6 +23,7 @@
* Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
* origin (e.g., for generated resources of raw bytes).
*/
+@KeepForSubclassing
public abstract class Origin implements Comparable<Origin> {
private static final Origin ROOT =
diff --git a/src/main/java/com/android/tools/r8/origin/PathOrigin.java b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
index 95ee46f..0ff8080 100644
--- a/src/main/java/com/android/tools/r8/origin/PathOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.origin;
+import com.android.tools.r8.Keep;
import java.nio.file.Path;
/**
* Path component in an origin description.
*/
+@Keep
public class PathOrigin extends Origin {
private final Path path;
diff --git a/src/main/java/com/android/tools/r8/position/Position.java b/src/main/java/com/android/tools/r8/position/Position.java
index 902fad5..618fd51 100644
--- a/src/main/java/com/android/tools/r8/position/Position.java
+++ b/src/main/java/com/android/tools/r8/position/Position.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
/**
* Represent a position in a resource, it can for example be line in a text file of the byte offset
* in a binary stream.
*/
+@Keep
public interface Position {
/**
diff --git a/src/main/java/com/android/tools/r8/position/TextPosition.java b/src/main/java/com/android/tools/r8/position/TextPosition.java
index 9136fa7..0dd4090 100644
--- a/src/main/java/com/android/tools/r8/position/TextPosition.java
+++ b/src/main/java/com/android/tools/r8/position/TextPosition.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
/**
* A {@link Position} in a text file determined by line and column.
* Line and column numbers start at 1.
*/
+@Keep
public class TextPosition implements Position {
/**
diff --git a/src/main/java/com/android/tools/r8/position/TextRange.java b/src/main/java/com/android/tools/r8/position/TextRange.java
index 5f35aad..0e642d8 100644
--- a/src/main/java/com/android/tools/r8/position/TextRange.java
+++ b/src/main/java/com/android/tools/r8/position/TextRange.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.position;
+import com.android.tools.r8.Keep;
+
+@Keep
public class TextRange implements Position {
private final TextPosition start;
private final TextPosition end;
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 873013e..8e84935 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.dexsplitter.DexSplitter;
import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
import com.android.tools.r8.origin.PathOrigin;
@@ -38,6 +39,7 @@
* <p>Note that this format does not allow specifying inter-module dependencies, this is simply a
* placement tool.
*/
+@Keep
public class FeatureClassMapping {
HashMap<String, String> parsedRules = new HashMap<>(); // Already parsed rules.
@@ -198,6 +200,7 @@
"Invalid mappings specification: " + error + "\n in file " + mappingFile + ":" + line);
}
+ @Keep
public static class FeatureMappingException extends Exception {
FeatureMappingException(String message) {
super(message);
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
index 4791217..5f5003f 100644
--- a/src/main/java/com/android/tools/r8/utils/FlagFile.java
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.BaseCommand;
import com.android.tools.r8.origin.Origin;
import java.io.IOException;
import java.nio.file.Files;
@@ -28,7 +29,7 @@
}
}
- public static String[] expandFlagFiles(String[] args, Reporter reporter) {
+ public static String[] expandFlagFiles(String[] args, BaseCommand.Builder builder) {
List<String> flags = new ArrayList<>(args.length);
for (String arg : args) {
if (arg.startsWith("@")) {
@@ -37,7 +38,7 @@
flags.addAll(Files.readAllLines(flagFilePath));
} catch (IOException e) {
Origin origin = new FlagFileOrigin(flagFilePath);
- reporter.error(new ExceptionDiagnostic(e, origin));
+ builder.error(new ExceptionDiagnostic(e, origin));
}
} else {
flags.add(arg);
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index b7cd855..06ab8a9 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+@Keep
public class StringDiagnostic implements Diagnostic {
private final Origin origin;
diff --git a/src/main/keep.txt b/src/main/keep.txt
new file mode 100644
index 0000000..2d74e47
--- /dev/null
+++ b/src/main/keep.txt
@@ -0,0 +1,6 @@
+# Copyright (c) 2018, 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.
+
+-keep @com.android.tools.r8.Keep class * { public *; }
+-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
new file mode 100755
index 0000000..abaa619
--- /dev/null
+++ b/tools/build_r8lib.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, 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.
+
+'''
+Build r8lib.jar using src/main/keep.txt and test that d8_api_usage_sample.jar
+works with the minified R8.
+'''
+
+import argparse
+import os
+import subprocess
+import toolhelper
+import utils
+
+parser = argparse.ArgumentParser(description=__doc__.strip(),
+ formatter_class=argparse.RawTextHelpFormatter)
+
+SAMPLE_JAR = os.path.join(utils.REPO_ROOT, 'tests/d8_api_usage_sample.jar')
+KEEP_RULES = os.path.join(utils.REPO_ROOT, 'src/main/keep.txt')
+R8LIB_JAR = os.path.join(utils.LIBS, 'r8lib.jar')
+R8LIB_MAP_FILE = os.path.join(utils.LIBS, 'r8lib-map.txt')
+
+API_LEVEL = 26
+ANDROID_JAR = 'third_party/android_jar/lib-v%s/android.jar' % API_LEVEL
+
+
+def build_r8lib():
+ toolhelper.run(
+ 'r8',
+ ('--release',
+ '--classfile',
+ '--lib', utils.RT_JAR,
+ utils.R8_JAR,
+ '--output', R8LIB_JAR,
+ '--pg-conf', KEEP_RULES,
+ '--pg-map-output', R8LIB_MAP_FILE))
+
+
+def test_d8sample():
+ with utils.TempDir() as path:
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.apiusagesample.D8ApiUsageSample',
+ '--output', path,
+ '--min-api', str(API_LEVEL),
+ '--lib', ANDROID_JAR,
+ '--classpath', utils.R8_JAR,
+ '--main-dex-list', '/dev/null',
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def test_r8command():
+ with utils.TempDir() as path:
+ # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+ # R8CommandParser should have been minified in R8LIB_JAR.
+ # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+ # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.r8.R8CommandParser',
+ '--output', path + "/output.zip",
+ '--min-api', str(API_LEVEL),
+ '--lib', ANDROID_JAR,
+ '--main-dex-list', '/dev/null',
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def test_r8cfcommand():
+ with utils.TempDir() as path:
+ # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+ # R8CommandParser should have been minified in R8LIB_JAR.
+ # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+ # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+ args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+ 'com.android.tools.r8.R8CommandParser',
+ '--classfile',
+ '--output', path + "/output.jar",
+ '--lib', utils.RT_JAR,
+ os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+ utils.PrintCmd(args)
+ subprocess.check_call(args)
+
+
+def main():
+ # Handle --help
+ parser.parse_args()
+
+ build_r8lib()
+ test_d8sample()
+ test_r8command()
+ test_r8cfcommand()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 787c1e1..8f440dd 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -27,7 +27,6 @@
MANIFEST_PATH = 'META-INF/MANIFEST.MF'
MANIFEST = 'Manifest-Version: 1.0\nMain-Class: %s\n\n'
MANIFEST_PATTERN = r'Main-Class:\s*(\S+)'
-RT = os.path.join(utils.REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
parser = argparse.ArgumentParser(description=__doc__.strip(),
formatter_class=argparse.RawTextHelpFormatter)
@@ -38,7 +37,7 @@
'-o', '--output-jar',
help='Path to output JAR (default: build/libs/<MainClass>-min.jar)')
parser.add_argument(
- '-l', '--lib', default=RT,
+ '-l', '--lib', default=utils.RT_JAR,
help='Path to rt.jar to use instead of OpenJDK 1.8')
parser.add_argument(
'-m', '--mainclass',
@@ -85,8 +84,8 @@
'No --mainclass specified and no Main-Class in input JAR manifest.')
return mo.group(1)
-def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
- debug=True, build=True, benchmark_name=None):
+def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
+ lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None):
if output_jar is None:
output_jar = generate_output_name(input_jar, mainclass)
with utils.TempDir() as path:
diff --git a/tools/utils.py b/tools/utils.py
index 9f6959f..3aab572 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -35,6 +35,7 @@
COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
+RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
def PrintCmd(s):
if type(s) is list: