API for supporting resources
Change-Id: I4cdb2bb64986b72d4bf1d49c939c95520e40b8b7
diff --git a/build.gradle b/build.gradle
index c38343b..94c002f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -866,6 +866,7 @@
// We change the options passed to javac to ignore it.
compileExamplesJava.options.compilerArgs = ["-Xlint:none"]
+
task buildExampleJars {
dependsOn downloadProguard
def examplesDir = file("src/test/examples")
@@ -884,7 +885,16 @@
into "$buildDir/runtime/examples/"
}
- task "compile_examples"(type: JavaCompile, dependsOn: "generateExamplesProto") {
+ task "copy_examples_resources"(type: org.gradle.api.tasks.Copy) {
+ from examplesDir
+ exclude "**/*.java"
+ exclude "**/keep-rules*.txt"
+ into file("build/test/examples/classes")
+ }
+
+ task "compile_examples"(type: JavaCompile) {
+ dependsOn "generateExamplesProto"
+ dependsOn "copy_examples_resources"
source examplesDir, protoSourceDir
include "**/*.java"
destinationDir = file("build/test/examples/classes")
@@ -976,9 +986,9 @@
archiveName = "${name}.jar"
destinationDir = exampleOutputDir
from "build/test/examples/classes"
- include name + "/**/*.class"
+ include name + "/**/*"
with runtimeDependencies
- includeEmptyDirs false
+ includeEmptyDirs true
}
task "jar_example_${name}_debuginfo_all"(type: Jar, dependsOn: "compile_examples_debuginfo_all") {
archiveName = "${name}_debuginfo_all.jar"
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 6c43c7b..1bf77b1 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -232,27 +232,28 @@
assert outputMode != null;
this.outputPath = outputPath;
this.outputMode = outputMode;
- programConsumer = createProgramOutputConsumer(outputPath, outputMode);
+ programConsumer = createProgramOutputConsumer(outputPath, outputMode, false);
return self();
}
- private InternalProgramOutputPathConsumer createProgramOutputConsumer(
+ protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
Path path,
- OutputMode mode) {
+ OutputMode mode,
+ boolean consumeDataResources) {
if (mode == OutputMode.DexIndexed) {
return FileUtils.isArchive(path)
- ? new DexIndexedConsumer.ArchiveConsumer(path)
- : new DexIndexedConsumer.DirectoryConsumer(path);
+ ? new DexIndexedConsumer.ArchiveConsumer(path, consumeDataResources)
+ : new DexIndexedConsumer.DirectoryConsumer(path, consumeDataResources);
}
if (mode == OutputMode.DexFilePerClassFile) {
return FileUtils.isArchive(path)
- ? new DexFilePerClassFileConsumer.ArchiveConsumer(path)
- : new DexFilePerClassFileConsumer.DirectoryConsumer(path);
+ ? new DexFilePerClassFileConsumer.ArchiveConsumer(path, consumeDataResources)
+ : new DexFilePerClassFileConsumer.DirectoryConsumer(path, consumeDataResources);
}
if (mode == OutputMode.ClassFile) {
return FileUtils.isArchive(path)
- ? new ClassFileConsumer.ArchiveConsumer(path)
- : new ClassFileConsumer.DirectoryConsumer(path);
+ ? new ClassFileConsumer.ArchiveConsumer(path, consumeDataResources)
+ : new ClassFileConsumer.DirectoryConsumer(path, consumeDataResources);
}
throw new Unreachable("Unexpected output mode: " + mode);
}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index eab3d66..0394441 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -5,11 +5,11 @@
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -61,6 +61,11 @@
}
@Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumer != null ? consumer.getDataResourceConsumer() : null;
+ }
+
+ @Override
public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
if (consumer != null) {
consumer.accept(data, descriptor, handler);
@@ -73,75 +78,70 @@
consumer.finished(handler);
}
}
-
}
- /** Archive consumer to write program resources to a zip archive. */
- class ArchiveConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
-
- private final Path archive;
- private final Origin origin;
- private ZipOutputStream stream = null;
- private boolean closed = false;
+ /** Consumer to write program resources to an output. */
+ class ArchiveConsumer extends ForwardingConsumer
+ implements DataResourceConsumer, InternalProgramOutputPathConsumer {
+ private final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResources;
public ArchiveConsumer(Path archive) {
- this(archive, null);
+ this(archive, null, false);
+ }
+
+ public ArchiveConsumer(Path archive, boolean consumeDataResouces) {
+ this(archive, null, consumeDataResouces);
}
public ArchiveConsumer(Path archive, ClassFileConsumer consumer) {
+ this(archive, consumer, false);
+ }
+
+ public ArchiveConsumer(Path archive, ClassFileConsumer consumer, boolean consumeDataResouces) {
super(consumer);
- this.archive = archive;
- origin = new PathOrigin(archive);
+ this.outputBuilder = new ArchiveBuilder(archive);
+ this.consumeDataResources = consumeDataResouces;
+ this.outputBuilder.open();
+ if (getDataResourceConsumer() != null) {
+ this.outputBuilder.open();
+ }
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumeDataResources ? this : null;
}
@Override
public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
- synchronizedWrite(getClassFileName(descriptor), data, handler);
+ outputBuilder.addFile(getClassFileName(descriptor), data, handler);
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- assert !closed;
- closed = true;
try {
- if (stream != null) {
- stream.close();
- stream = null;
- }
+ outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
@Override
public Path internalGetOutputPath() {
- return archive;
- }
-
- private ZipOutputStream getStream(DiagnosticsHandler handler) {
- assert !closed;
- if (stream == null) {
- try {
- stream =
- new ZipOutputStream(
- Files.newOutputStream(
- archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
- }
- return stream;
- }
-
- private synchronized void synchronizedWrite(
- String entry, byte[] content, DiagnosticsHandler handler) {
- try {
- ZipUtils.writeToZipStream(getStream(handler), entry, content, ZipEntry.STORED);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
+ return outputBuilder.getPath();
}
private static String getClassFileName(String classDescriptor) {
@@ -169,27 +169,47 @@
/** Directory consumer to write program resources to a directory. */
class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
-
- private final Path directory;
+ private final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResouces;
public DirectoryConsumer(Path directory) {
- this(directory, null);
+ this(directory, null, false);
+ }
+
+ public DirectoryConsumer(Path directory, boolean consumeDataResouces) {
+ this(directory, null, consumeDataResouces);
}
public DirectoryConsumer(Path directory, ClassFileConsumer consumer) {
+ this(directory, consumer, false);
+ }
+
+ public DirectoryConsumer(
+ Path directory, ClassFileConsumer consumer, boolean consumeDataResouces) {
super(consumer);
- this.directory = directory;
+ this.outputBuilder = new DirectoryBuilder(directory);
+ this.consumeDataResouces = consumeDataResouces;
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumeDataResouces ? this : null;
}
@Override
public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
- Path target = directory.resolve(ArchiveConsumer.getClassFileName(descriptor));
- try {
- writeFileFromDescriptor(data, target);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
- }
+ outputBuilder.addFile(ArchiveConsumer.getClassFileName(descriptor), data, handler);
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
}
@Override
@@ -199,12 +219,7 @@
@Override
public Path internalGetOutputPath() {
- return directory;
- }
-
- private static void writeFileFromDescriptor(byte[] contents, Path target) throws IOException {
- Files.createDirectories(target.getParent());
- FileUtils.writeToFile(target, null, contents);
+ return outputBuilder.getPath();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 39bdd96..aa99407 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -181,7 +181,7 @@
R8.unwrapExecutionException(e);
throw new AssertionError(e); // unwrapping method should have thrown
} finally {
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
// Dump timings.
if (options.printTimes) {
timing.report();
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d14e79b..c300bbf 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -7,7 +7,6 @@
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.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -140,24 +139,6 @@
intermediate,
isOptimizeMultidexForLinearAlloc());
}
-
- private static DexIndexedConsumer createIndexedConsumer(Path path) {
- if (path == null) {
- return DexIndexedConsumer.emptyConsumer();
- }
- return FileUtils.isArchive(path)
- ? new DexIndexedConsumer.ArchiveConsumer(path)
- : new DexIndexedConsumer.DirectoryConsumer(path);
- }
-
- private static DexFilePerClassFileConsumer createPerClassFileConsumer(Path path) {
- if (path == null) {
- return DexFilePerClassFileConsumer.emptyConsumer();
- }
- return FileUtils.isArchive(path)
- ? new DexFilePerClassFileConsumer.ArchiveConsumer(path)
- : new DexFilePerClassFileConsumer.DirectoryConsumer(path);
- }
}
static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
diff --git a/src/main/java/com/android/tools/r8/DataDirectoryResource.java b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
new file mode 100644
index 0000000..2f421f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
@@ -0,0 +1,69 @@
+// 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.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public interface DataDirectoryResource extends DataResource {
+
+ static DataDirectoryResource fromFile(Path dir, Path file) {
+ return new LocalDataDirectoryResource(dir.resolve(file).toFile(),
+ file.toString().replace(File.separatorChar, SEPARATOR));
+ }
+
+ static DataDirectoryResource fromZip(ZipFile zip, ZipEntry entry) {
+ return new ZipDataDirectoryResource(zip, entry);
+ }
+
+ class ZipDataDirectoryResource implements DataDirectoryResource {
+ private final ZipFile zip;
+ private final ZipEntry entry;
+
+ private ZipDataDirectoryResource(ZipFile zip, ZipEntry entry) {
+ assert zip != null;
+ assert entry != null;
+ this.zip = zip;
+ this.entry = entry;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new ArchiveEntryOrigin(entry.getName(), new PathOrigin(Paths.get(zip.getName())));
+ }
+
+ @Override
+ public String getName() {
+ return entry.getName();
+ }
+ }
+
+ class LocalDataDirectoryResource implements DataDirectoryResource {
+ private final File file;
+ private final String relativePath;
+
+ private LocalDataDirectoryResource(File file, String relativePath) {
+ assert file != null;
+ assert relativePath != null;
+ this.file = file;
+ this.relativePath = relativePath;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new PathOrigin(file.toPath());
+ }
+
+ @Override
+ public String getName() {
+ return relativePath;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
new file mode 100644
index 0000000..189d5eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -0,0 +1,93 @@
+// 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.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public interface DataEntryResource extends DataResource {
+
+ /** Get the bytes of the data entry resource. */
+ InputStream getByteStream() throws ResourceException;
+
+ static DataEntryResource fromFile(Path dir, Path file) {
+ return new LocalDataEntryResource(dir.resolve(file).toFile(),
+ file.toString().replace(File.separatorChar, SEPARATOR));
+ }
+
+ static DataEntryResource fromZip(ZipFile zip, ZipEntry entry) {
+ return new ZipDataEntryResource(zip, entry);
+ }
+
+ class ZipDataEntryResource implements DataEntryResource {
+ private final ZipFile zip;
+ private final ZipEntry entry;
+
+ private ZipDataEntryResource(ZipFile zip, ZipEntry entry) {
+ assert zip != null;
+ assert entry != null;
+ this.zip = zip;
+ this.entry = entry;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new ArchiveEntryOrigin(entry.getName(), new PathOrigin(Paths.get(zip.getName())));
+ }
+
+ @Override
+ public String getName() {
+ return entry.getName();
+ }
+
+ @Override
+ public InputStream getByteStream() throws ResourceException {
+ try {
+ return zip.getInputStream(entry);
+ } catch (IOException e) {
+ throw new ResourceException(getOrigin(), e);
+ }
+ }
+ }
+
+ class LocalDataEntryResource implements DataEntryResource {
+ private final File file;
+ private final String relativePath;
+
+ private LocalDataEntryResource(File file, String relativePath) {
+ assert file != null;
+ assert relativePath != null;
+ this.file = file;
+ this.relativePath = relativePath;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new PathOrigin(file.toPath());
+ }
+
+ @Override
+ public String getName() {
+ return relativePath;
+ }
+
+ @Override
+ public InputStream getByteStream() throws ResourceException {
+ try {
+ return new FileInputStream(file);
+ } catch (IOException e) {
+ throw new ResourceException(getOrigin(), e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/DataResource.java b/src/main/java/com/android/tools/r8/DataResource.java
new file mode 100644
index 0000000..4609c97
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DataResource.java
@@ -0,0 +1,10 @@
+// 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;
+
+public interface DataResource extends Resource {
+ char SEPARATOR = '/';
+
+ String getName();
+}
diff --git a/src/main/java/com/android/tools/r8/DataResourceConsumer.java b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
new file mode 100644
index 0000000..215ca59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
@@ -0,0 +1,12 @@
+// 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;
+
+public interface DataResourceConsumer {
+
+ void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler);
+ void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler);
+ void finished(DiagnosticsHandler handler);
+
+}
diff --git a/src/main/java/com/android/tools/r8/DataResourceProvider.java b/src/main/java/com/android/tools/r8/DataResourceProvider.java
new file mode 100644
index 0000000..e851f0f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DataResourceProvider.java
@@ -0,0 +1,15 @@
+// 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;
+
+public interface DataResourceProvider {
+
+ interface Visitor {
+ void visit(DataDirectoryResource directory);
+ void visit(DataEntryResource file);
+ }
+
+ void accept(Visitor visitor) throws ResourceException;
+
+}
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 06f6d03..9c76484 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -98,7 +98,7 @@
R8.unwrapExecutionException(e);
throw new AssertionError(e); // unwrapping method should have thrown
} finally {
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
}
} finally {
executor.shutdown();
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index 7f0a927..f93c338 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -5,11 +5,12 @@
import static com.android.tools.r8.utils.FileUtils.DEX_EXTENSION;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -68,6 +69,11 @@
}
@Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumer != null ? consumer.getDataResourceConsumer() : null;
+ }
+
+ @Override
public void accept(
String primaryClassDescriptor,
byte[] data,
@@ -84,30 +90,44 @@
consumer.finished(handler);
}
}
-
}
- /** Archive consumer to write program resources to a zip archive. */
- class ArchiveConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
+ /** Consumer to write program resources to an output. */
+ class ArchiveConsumer extends ForwardingConsumer
+ implements DataResourceConsumer, InternalProgramOutputPathConsumer {
+ private final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResources;
private static String getDexFileName(String classDescriptor) {
assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + DEX_EXTENSION;
}
- private final Path archive;
- private final Origin origin;
- private ZipOutputStream stream = null;
- private boolean closed = false;
-
public ArchiveConsumer(Path archive) {
- this(archive, null);
+ this(archive, null, false);
+ }
+
+ public ArchiveConsumer(Path archive, boolean consumeDataResouces) {
+ this(archive, null, consumeDataResouces);
}
public ArchiveConsumer(Path archive, DexFilePerClassFileConsumer consumer) {
+ this(archive, consumer, false);
+ }
+
+ public ArchiveConsumer(Path archive, DexFilePerClassFileConsumer consumer, boolean consumeDataResouces) {
super(consumer);
- this.archive = archive;
- origin = new PathOrigin(archive);
+ this.outputBuilder = new ArchiveBuilder(archive);
+ this.consumeDataResources = consumeDataResouces;
+ this.outputBuilder.open();
+ if (getDataResourceConsumer() != null) {
+ this.outputBuilder.open();
+ }
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumeDataResources ? this : null;
}
@Override
@@ -117,51 +137,32 @@
Set<String> descriptors,
DiagnosticsHandler handler) {
super.accept(primaryClassDescriptor, data, descriptors, handler);
- synchronizedWrite(getDexFileName(primaryClassDescriptor), data, handler);
+ outputBuilder.addFile(getDexFileName(primaryClassDescriptor), data, handler);
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- assert !closed;
- closed = true;
try {
- if (stream != null) {
- stream.close();
- stream = null;
- }
+ outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
@Override
public Path internalGetOutputPath() {
- return archive;
- }
-
- private ZipOutputStream getStream(DiagnosticsHandler handler) {
- assert !closed;
- if (stream == null) {
- try {
- stream =
- new ZipOutputStream(
- Files.newOutputStream(
- archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
- }
- return stream;
- }
-
- private synchronized void synchronizedWrite(
- String entry, byte[] content, DiagnosticsHandler handler) {
- try {
- ZipUtils.writeToZipStream(getStream(handler), entry, content, ZipEntry.STORED);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
+ return outputBuilder.getPath();
}
public static void writeResources(
@@ -185,17 +186,33 @@
}
/** Directory consumer to write program resources to a directory. */
- class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
+ class DirectoryConsumer extends ForwardingConsumer
+ implements DataResourceConsumer, InternalProgramOutputPathConsumer {
+ private final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResouces;
- private final Path directory;
+ private static String getDexFileName(String classDescriptor) {
+ assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
+ return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + DEX_EXTENSION;
+ }
public DirectoryConsumer(Path directory) {
- this(directory, null);
+ this(directory, null, false);
+ }
+
+ public DirectoryConsumer(Path directory, boolean consumeDataResouces) {
+ this(directory, null, consumeDataResouces);
}
public DirectoryConsumer(Path directory, DexFilePerClassFileConsumer consumer) {
+ this(directory, consumer, false);
+ }
+
+ public DirectoryConsumer(
+ Path directory, DexFilePerClassFileConsumer consumer, boolean consumeDataResouces) {
super(consumer);
- this.directory = directory;
+ this.outputBuilder = new DirectoryBuilder(directory);
+ this.consumeDataResouces = consumeDataResouces;
}
@Override
@@ -205,14 +222,19 @@
Set<String> descriptors,
DiagnosticsHandler handler) {
super.accept(primaryClassDescriptor, data, descriptors, handler);
- Path target = getTargetDexFile(directory, primaryClassDescriptor);
- try {
- writeFile(data, target);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
- }
+ outputBuilder.addFile(getDexFileName(primaryClassDescriptor), data, handler);
}
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
+ }
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
@@ -220,7 +242,7 @@
@Override
public Path internalGetOutputPath() {
- return directory;
+ return outputBuilder.getPath();
}
public static void writeResources(
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 5c7722b..0f932d5 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -3,10 +3,12 @@
// 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.origin.PathOrigin;
+import com.android.tools.r8.utils.ArchiveBuilder;
+import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.IOExceptionDiagnostic;
+import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -65,6 +67,21 @@
this.consumer = consumer;
}
+ protected static String getDefaultDexFileName(int fileIndex) {
+ return fileIndex == 0
+ ? "classes" + FileUtils.DEX_EXTENSION
+ : ("classes" + (fileIndex + 1) + FileUtils.DEX_EXTENSION);
+ }
+
+ protected String getDexFileName(int fileIndex) {
+ return getDefaultDexFileName(fileIndex);
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumer != null ? consumer.getDataResourceConsumer() : null;
+ }
+
@Override
public void accept(
int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
@@ -79,81 +96,65 @@
consumer.finished(handler);
}
}
-
}
- /** Archive consumer to write program resources to a zip archive. */
- class ArchiveConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
-
- private static String getDefaultDexFileName(int fileIndex) {
- return fileIndex == 0
- ? "classes" + FileUtils.DEX_EXTENSION
- : ("classes" + (fileIndex + 1) + FileUtils.DEX_EXTENSION);
- }
-
- private final Path archive;
- private final Origin origin;
- private ZipOutputStream stream = null;
- private boolean closed = false;
+ /** Consumer to write program resources to an output. */
+ class ArchiveConsumer extends ForwardingConsumer
+ implements DataResourceConsumer, InternalProgramOutputPathConsumer {
+ protected final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResources;
public ArchiveConsumer(Path archive) {
- this(archive, null);
+ this(archive, null, false);
+ }
+
+ public ArchiveConsumer(Path archive, boolean consumeDataResouces) {
+ this(archive, null, consumeDataResouces);
}
public ArchiveConsumer(Path archive, DexIndexedConsumer consumer) {
- super(consumer);
- this.archive = archive;
- origin = new PathOrigin(archive);
+ this(archive, consumer, false);
}
- protected String getDexFileName(int fileIndex) {
- return getDefaultDexFileName(fileIndex);
+ public ArchiveConsumer(Path archive, DexIndexedConsumer consumer, boolean consumeDataResouces) {
+ super(consumer);
+ this.outputBuilder = new ArchiveBuilder(archive);
+ this.consumeDataResources = consumeDataResouces;
+ this.outputBuilder.open();
+ if (getDataResourceConsumer() != null) {
+ this.outputBuilder.open();
+ }
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumeDataResources ? this : null;
}
@Override
public void accept(
int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
- synchronizedWrite(getDexFileName(fileIndex), data, handler);
+ outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- assert !closed;
- closed = true;
try {
- if (stream != null) {
- stream.close();
- stream = null;
- }
+ outputBuilder.close();
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
- }
-
- /** Get or open the zip output stream. */
- protected synchronized ZipOutputStream getStream(DiagnosticsHandler handler) {
- assert !closed;
- if (stream == null) {
- try {
- stream =
- new ZipOutputStream(
- Files.newOutputStream(
- archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
- }
- }
- return stream;
- }
-
- private synchronized void synchronizedWrite(
- String entry, byte[] content, DiagnosticsHandler handler) {
- try {
- ZipUtils.writeToZipStream(getStream(handler), entry, content, ZipEntry.STORED);
- } catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, origin));
+ handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
}
}
@@ -175,42 +176,72 @@
@Override
public Path internalGetOutputPath() {
- return archive;
+ return outputBuilder.getPath();
}
-
}
- /** Directory consumer to write program resources to a directory. */
- class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
-
+ class DirectoryConsumer extends ForwardingConsumer
+ implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final Path directory;
private boolean preparedDirectory = false;
+ private final OutputBuilder outputBuilder;
+ protected final boolean consumeDataResouces;
public DirectoryConsumer(Path directory) {
- this(directory, null);
+ this(directory, null, false);
+ }
+
+ public DirectoryConsumer(Path directory, boolean consumeDataResouces) {
+ this(directory, null, consumeDataResouces);
}
public DirectoryConsumer(Path directory, DexIndexedConsumer consumer) {
+ this(directory, consumer, false);
+ }
+
+ public DirectoryConsumer(
+ Path directory, DexIndexedConsumer consumer, boolean consumeDataResouces) {
super(consumer);
this.directory = directory;
+ this.outputBuilder = new DirectoryBuilder(directory);
+ this.consumeDataResouces = consumeDataResouces;
+ }
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ return consumeDataResouces ? this : null;
}
@Override
public void accept(
int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
- Path target = getTargetDexFile(directory, fileIndex);
try {
prepareDirectory();
- writeFile(data, target);
} catch (IOException e) {
- handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ handler.error(new IOExceptionDiagnostic(e, new PathOrigin(directory)));
}
+ outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
+ }
+
+ @Override
+ public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
+ outputBuilder.addDirectory(directory.getName(), handler);
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler handler) {
+ outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
+ try {
+ outputBuilder.close();
+ } catch (IOException e) {
+ handler.error(new IOExceptionDiagnostic(e, outputBuilder.getOrigin()));
+ }
}
private synchronized void prepareDirectory() throws IOException {
@@ -221,7 +252,7 @@
deleteClassesDexFiles(directory);
}
- public static void deleteClassesDexFiles(Path directory) throws IOException {
+ static void deleteClassesDexFiles(Path directory) throws IOException {
try (Stream<Path> filesInDir = Files.list(directory)) {
for (Path path : filesInDir.collect(Collectors.toList())) {
if (FileUtils.isClassesDexFile(path)) {
@@ -244,7 +275,7 @@
}
private static Path getTargetDexFile(Path directory, int fileIndex) {
- return directory.resolve(ArchiveConsumer.getDefaultDexFileName(fileIndex));
+ return directory.resolve(ForwardingConsumer.getDefaultDexFileName(fileIndex));
}
private static void writeFile(byte[] contents, Path target) throws IOException {
@@ -254,8 +285,7 @@
@Override
public Path internalGetOutputPath() {
- return directory;
+ return outputBuilder.getPath();
}
-
}
}
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index e7496f4..ab86367 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -111,7 +111,7 @@
} catch (FeatureMappingException e) {
options.reporter.error(e.getMessage());
} finally {
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
}
}
diff --git a/src/main/java/com/android/tools/r8/InternalProgramOutputPathConsumer.java b/src/main/java/com/android/tools/r8/InternalProgramOutputPathConsumer.java
index ebdfb55..a092100 100644
--- a/src/main/java/com/android/tools/r8/InternalProgramOutputPathConsumer.java
+++ b/src/main/java/com/android/tools/r8/InternalProgramOutputPathConsumer.java
@@ -6,7 +6,7 @@
import java.nio.file.Path;
// Internal interface for testing the command setup.
-interface InternalProgramOutputPathConsumer extends ProgramConsumer {
+interface InternalProgramOutputPathConsumer extends ProgramConsumer, DataResourceConsumer {
Path internalGetOutputPath();
diff --git a/src/main/java/com/android/tools/r8/ProgramConsumer.java b/src/main/java/com/android/tools/r8/ProgramConsumer.java
index 0e9b125..08d7d7c 100644
--- a/src/main/java/com/android/tools/r8/ProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/ProgramConsumer.java
@@ -9,6 +9,14 @@
public interface ProgramConsumer {
/**
+ * Returns a {@link DataResourceConsumer} that will receive data resources. If this
+ * returns <code>null</code> no data resources will be processed.
+ */
+ default DataResourceConsumer getDataResourceConsumer() {
+ return null;
+ }
+
+ /**
* Callback signifying that compilation of program resources has finished.
*
* <p>Called only once after all program outputs have been generated and consumed.
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index df3d81b..6a1d7c8 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -489,7 +489,7 @@
unwrapExecutionException(e);
throw new AssertionError(e); // unwrapping method should have thrown
} finally {
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
// Dump timings.
if (options.printTimes) {
timing.report();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 8c69b7c..83f9dc2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -238,6 +238,20 @@
new EnsureNonDexProgramResourceProvider(programProvider));
}
+ public Builder addDataResourceProvider(DataResourceProvider dataResourceProvider) {
+ assert dataResourceProvider != null;
+ getAppBuilder().addDataResourceProvider(dataResourceProvider);
+ return self();
+ }
+
+ @Override
+ protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
+ Path path,
+ OutputMode mode,
+ boolean consumeDataResources) {
+ return super.createProgramOutputConsumer(path, mode, true);
+ }
+
@Override
void validate() {
Reporter reporter = getReporter();
@@ -415,6 +429,7 @@
" --help # Print this message."));
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
+ private DataResourceConsumer dataResourceConsumer;
private final StringConsumer mainDexListConsumer;
private final ProguardConfiguration proguardConfiguration;
private final boolean enableTreeShaking;
@@ -719,6 +734,7 @@
}
internal.proguardCompatibilityRulesOutput = proguardCompatibilityRulesOutput;
+ internal.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer();
// EXPERIMENTAL flags.
assert !internal.forceProguardCompatibility;
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index f8f70d6..b64fe01 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -20,7 +20,6 @@
* client is free to provide their own.
*/
public interface Resource {
-
/**
* Get the origin of the resource.
*
@@ -28,5 +27,4 @@
* what that means for a particular resource.
*/
Origin getOrigin();
-
}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index ad04e36..1de9606 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -28,7 +28,6 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.IOExceptionDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import java.io.File;
@@ -46,7 +45,6 @@
import java.util.concurrent.ExecutorService;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
@@ -458,7 +456,8 @@
CompatDxHelper.ignoreDexInArchive(builder);
builder
.addProgramFiles(inputs)
- .setProgramConsumer(createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses))
+ .setProgramConsumer(
+ createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses))
.setMode(mode)
.setMinApiLevel(dexArgs.minApiLevel);
if (mainDexList != null) {
@@ -486,7 +485,8 @@
}
private static DexIndexedConsumer createDexConsumer(
- Path output, List<Path> inputs, boolean keepClasses) throws DxUsageMessage {
+ Path output, List<Path> inputs, boolean keepClasses)
+ throws DxUsageMessage {
if (keepClasses) {
if (!isArchive(output)) {
throw new DxCompatOptions.DxUsageMessage(
@@ -564,14 +564,14 @@
@Override
public void finished(DiagnosticsHandler handler) {
try {
- writeZipWithClasses(getStream(handler));
+ writeZipWithClasses(handler);
} catch (IOException e) {
handler.error(new IOExceptionDiagnostic(e));
}
super.finished(handler);
}
- private void writeZipWithClasses(ZipOutputStream out) throws IOException {
+ private void writeZipWithClasses(DiagnosticsHandler handler) throws IOException {
// For each input archive file, add all class files within.
for (Path input : inputs) {
if (isArchive(input)) {
@@ -581,8 +581,8 @@
ZipEntry entry = entries.nextElement();
if (isClassFile(Paths.get(entry.getName()))) {
try (InputStream entryStream = zipFile.getInputStream(entry)) {
- ZipUtils.writeToZipStream(
- out, entry.getName(), ByteStreams.toByteArray(entryStream), ZipEntry.STORED);
+ outputBuilder.addFile(
+ entry.getName(), ByteStreams.toByteArray(entryStream), handler);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 6a63a8e..7dec61a 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -114,6 +114,7 @@
classReader.readSources();
ThreadUtils.awaitFutures(futures);
classReader.initializeLazyClassCollection(builder);
+ builder.replaceDataResourceProviders(inputApp.getDataResourceProviders());
} catch (ResourceException e) {
throw options.reporter.fatalError(new StringDiagnostic(e.getMessage(), e.getOrigin()));
} finally {
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 664b8d1..b8d17ce 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -4,7 +4,14 @@
package com.android.tools.r8.dex;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceConsumer;
+import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.DataResourceProvider.Visitor;
import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -288,6 +295,28 @@
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.mainDexListConsumer, writeMainDexList(application, namingLens));
}
+ DataResourceConsumer dataResourceConsumer = options.dataResourceConsumer;
+ if (dataResourceConsumer != null) {
+ for (DataResourceProvider dataResourceProvider : application.dataResourceProviders) {
+ try {
+ dataResourceProvider.accept(new Visitor() {
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ dataResourceConsumer.accept(directory, options.reporter);
+ options.reporter.failIfPendingErrors();
+ }
+
+ @Override
+ public void visit(DataEntryResource file) {
+ dataResourceConsumer.accept(file, options.reporter);
+ options.reporter.failIfPendingErrors();
+ }
+ });
+ } catch (ResourceException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ }
+ }
}
private void insertAttributeAnnotations() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index b5a3fc2..e908b64 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -6,12 +6,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -25,6 +27,8 @@
// Maps type into class, may be used concurrently.
final ProgramClassCollection programClasses;
+ public final ImmutableList<DataResourceProvider> dataResourceProviders;
+
public final ImmutableSet<DexType> mainDexList;
public final String deadCode;
@@ -43,6 +47,7 @@
DexApplication(
ClassNameMapper proguardMap,
ProgramClassCollection programClasses,
+ ImmutableList<DataResourceProvider> dataResourceProviders,
ImmutableSet<DexType> mainDexList,
String deadCode,
DexItemFactory dexItemFactory,
@@ -51,6 +56,7 @@
assert programClasses != null;
this.proguardMap = proguardMap;
this.programClasses = programClasses;
+ this.dataResourceProviders = dataResourceProviders;
this.mainDexList = mainDexList;
this.deadCode = deadCode;
this.dexItemFactory = dexItemFactory;
@@ -112,6 +118,8 @@
final List<DexProgramClass> programClasses;
+ final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
+
public final DexItemFactory dexItemFactory;
ClassNameMapper proguardMap;
final Timing timing;
@@ -133,6 +141,7 @@
public Builder(DexApplication application) {
programClasses = application.programClasses.getAllClasses();
+ replaceDataResourceProviders(application.dataResourceProviders);
proguardMap = application.getProguardMap();
timing = application.timing;
highestSortingString = application.highestSortingString;
@@ -155,6 +164,15 @@
return self();
}
+ public synchronized T replaceDataResourceProviders(
+ List<DataResourceProvider> dataResourceProviders) {
+ this.dataResourceProviders.clear();
+ if (dataResourceProviders != null) {
+ this.dataResourceProviders.addAll(dataResourceProviders);
+ }
+ return self();
+ }
+
public T appendDeadCode(String deadCodeAtAnotherRound) {
if (deadCodeAtAnotherRound == null) {
return self();
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index be55f18..a28d8ef 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -6,9 +6,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -23,11 +25,12 @@
private DirectMappedDexApplication(ClassNameMapper proguardMap,
ProgramClassCollection programClasses,
+ ImmutableList<DataResourceProvider> dataResourceProviders,
ImmutableMap<DexType, DexLibraryClass> libraryClasses,
ImmutableSet<DexType> mainDexList, String deadCode,
DexItemFactory dexItemFactory, DexString highestSortingString,
Timing timing) {
- super(proguardMap, programClasses, mainDexList, deadCode,
+ super(proguardMap, programClasses, dataResourceProviders, mainDexList, deadCode,
dexItemFactory, highestSortingString, timing);
this.libraryClasses = libraryClasses;
}
@@ -116,6 +119,7 @@
proguardMap,
ProgramClassCollection.create(
programClasses, ProgramClassCollection::resolveClassConflictImpl),
+ ImmutableList.copyOf(dataResourceProviders),
libraryClasses.stream().collect(ImmutableMap.toImmutableMap(c -> c.type, c -> c)),
ImmutableSet.copyOf(mainDexList),
deadCode,
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 4021473..6eb9ce5 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -6,12 +6,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.ClasspathClassCollection;
import com.android.tools.r8.utils.LibraryClassCollection;
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -26,12 +28,13 @@
*/
private LazyLoadedDexApplication(ClassNameMapper proguardMap,
ProgramClassCollection programClasses,
+ ImmutableList<DataResourceProvider> dataResourceProviders,
ClasspathClassCollection classpathClasses,
LibraryClassCollection libraryClasses,
ImmutableSet<DexType> mainDexList, String deadCode,
DexItemFactory dexItemFactory, DexString highestSortingString,
Timing timing) {
- super(proguardMap, programClasses, mainDexList, deadCode,
+ super(proguardMap, programClasses, dataResourceProviders, mainDexList, deadCode,
dexItemFactory, highestSortingString, timing);
this.classpathClasses = classpathClasses;
this.libraryClasses = libraryClasses;
@@ -119,6 +122,7 @@
return new LazyLoadedDexApplication(
proguardMap,
ProgramClassCollection.create(programClasses, resolver),
+ ImmutableList.copyOf(dataResourceProviders),
classpathClasses,
libraryClasses,
ImmutableSet.copyOf(mainDexList),
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 ad1011c..d6ddf5d 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
@@ -48,6 +49,7 @@
public class AndroidApp {
private final ImmutableList<ProgramResourceProvider> programResourceProviders;
+ private final ImmutableList<DataResourceProvider> dataResourceProviders;
private final ImmutableMap<Resource, String> programResourcesMainDescriptor;
private final ImmutableList<ClassFileResourceProvider> classpathResourceProviders;
private final ImmutableList<ClassFileResourceProvider> libraryResourceProviders;
@@ -59,6 +61,7 @@
// See factory methods and AndroidApp.Builder below.
private AndroidApp(
ImmutableList<ProgramResourceProvider> programResourceProviders,
+ ImmutableList<DataResourceProvider> dataResourceProviders,
ImmutableMap<Resource, String> programResourcesMainDescriptor,
ImmutableList<ClassFileResourceProvider> classpathResourceProviders,
ImmutableList<ClassFileResourceProvider> libraryResourceProviders,
@@ -66,6 +69,7 @@
List<StringResource> mainDexListResources,
List<String> mainDexClasses) {
this.programResourceProviders = programResourceProviders;
+ this.dataResourceProviders = dataResourceProviders;
this.programResourcesMainDescriptor = programResourcesMainDescriptor;
this.classpathResourceProviders = classpathResourceProviders;
this.libraryResourceProviders = libraryResourceProviders;
@@ -128,6 +132,11 @@
return programResourceProviders;
}
+ /** Get non program resource providers. */
+ public List<DataResourceProvider> getDataResourceProviders() {
+ return dataResourceProviders;
+ }
+
/** Get classpath resource providers. */
public List<ClassFileResourceProvider> getClasspathResourceProviders() {
return classpathResourceProviders;
@@ -253,6 +262,7 @@
private final List<ProgramResourceProvider> programResourceProviders = new ArrayList<>();
private final List<ProgramResource> programResources = new ArrayList<>();
+ private final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
private final Map<ProgramResource, String> programResourcesMainDescriptor = new HashMap<>();
private final List<ClassFileResourceProvider> classpathResourceProviders = new ArrayList<>();
private final List<ClassFileResourceProvider> libraryResourceProviders = new ArrayList<>();
@@ -272,6 +282,7 @@
programResourceProviders.addAll(app.programResourceProviders);
classpathResourceProviders.addAll(app.classpathResourceProviders);
libraryResourceProviders.addAll(app.libraryResourceProviders);
+ dataResourceProviders.addAll(app.dataResourceProviders);
mainDexListResources = app.mainDexListResources;
mainDexListClasses = app.mainDexClasses;
}
@@ -294,8 +305,11 @@
throws NoSuchFileException {
for (FilteredClassPath archive : filteredArchives) {
assert isArchive(archive.getPath());
- addProgramResourceProvider(
- new FilteredArchiveProgramResourceProvider(archive, ignoreDexInArchive));
+ ArchiveResourceProvider archiveResourceProvider =
+ new ArchiveResourceProvider(archive, ignoreDexInArchive);
+ addProgramResourceProvider(archiveResourceProvider);
+ addDataResourceProvider(archiveResourceProvider);
+
}
return this;
}
@@ -306,6 +320,12 @@
return this;
}
+ public Builder addDataResourceProvider(DataResourceProvider provider) {
+ assert provider != null;
+ dataResourceProviders.add(provider);
+ return this;
+ }
+
/**
* Add classpath file resources.
*/
@@ -518,6 +538,7 @@
}
return new AndroidApp(
ImmutableList.copyOf(programResourceProviders),
+ ImmutableList.copyOf(dataResourceProviders),
ImmutableMap.copyOf(programResourcesMainDescriptor),
ImmutableList.copyOf(classpathResourceProviders),
ImmutableList.copyOf(libraryResourceProviders),
@@ -535,9 +556,10 @@
} else if (isClassFile(file)) {
addProgramResources(ProgramResource.fromFile(Kind.CF, file));
} else if (isArchive(file)) {
- addProgramResourceProvider(
- new FilteredArchiveProgramResourceProvider(
- FilteredClassPath.unfiltered(file), ignoreDexInArchive));
+ ArchiveResourceProvider archiveResourceProvider = new ArchiveResourceProvider(
+ FilteredClassPath.unfiltered(file), ignoreDexInArchive);
+ addProgramResourceProvider(archiveResourceProvider);
+ addDataResourceProvider(archiveResourceProvider);
} else {
throw new CompilationError("Unsupported source file type", new PathOrigin(file));
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index e923749..14bf6f5 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -92,9 +92,13 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- closed = true;
- files.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors));
- files = null;
+ if (!closed) {
+ closed = true;
+ files.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors));
+ files = null;
+ } else {
+ assert getDataResourceConsumer() != null;
+ }
}
synchronized void addDexFile(int fileIndex, byte[] data, Set<String> descriptors) {
@@ -132,9 +136,13 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- closed = true;
- files.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors, v));
- files = null;
+ if (!closed) {
+ closed = true;
+ files.forEach((v, d) -> builder.addDexProgramData(d.contents, d.descriptors, v));
+ files = null;
+ } else {
+ assert getDataResourceConsumer() != null;
+ }
}
};
programConsumer = wrapped;
@@ -161,10 +169,14 @@
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
- closed = true;
- files.forEach(
- d -> builder.addClassProgramData(d.contents, Origin.unknown(), d.descriptors));
- files = null;
+ if (!closed) {
+ closed = true;
+ files.forEach(
+ d -> builder.addClassProgramData(d.contents, Origin.unknown(), d.descriptors));
+ files = null;
+ } else {
+ assert getDataResourceConsumer() != null;
+ }
}
};
programConsumer = wrapped;
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
new file mode 100644
index 0000000..4a7abc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -0,0 +1,127 @@
+// 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.utils;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResource;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipOutputStream;
+
+public class ArchiveBuilder implements OutputBuilder {
+ private final Path archive;
+ private final Origin origin;
+ private ZipOutputStream stream = null;
+ private boolean closed = false;
+ private int openCount = 0;
+
+ public ArchiveBuilder(Path archive) {
+ this.archive = archive;
+ origin = new PathOrigin(archive);
+ }
+
+ @Override
+ public synchronized void open() {
+ assert !closed;
+ openCount ++;
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ assert !closed;
+ openCount--;
+ if (openCount == 0) {
+ closed = true;
+ if (stream != null) {
+ stream.close();
+ stream = null;
+ }
+ }
+ }
+
+ /** Get or open the zip output stream. */
+ private synchronized ZipOutputStream getStream(DiagnosticsHandler handler) {
+ assert !closed;
+ if (stream == null) {
+ try {
+ stream =
+ new ZipOutputStream(
+ Files.newOutputStream(
+ archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
+ } catch (IOException e) {
+ handler.error(new IOExceptionDiagnostic(e, origin));
+ }
+ }
+ return stream;
+ }
+
+ private void handleIOException(IOException e, DiagnosticsHandler handler) {
+ if (e instanceof ZipException && e.getMessage().startsWith("duplicate entry")) {
+ // For now we stick to the Proguard behaviour, see section "Warning: can't write resource ...
+ // Duplicate zip entry" on https://www.guardsquare.com/en/proguard/manual/troubleshooting.
+ handler.warning(new IOExceptionDiagnostic(e, origin));
+ } else {
+ handler.error(new IOExceptionDiagnostic(e, origin));
+ }
+ }
+
+ @Override
+ public void addDirectory(String name, DiagnosticsHandler handler) {
+ if (name.charAt(name.length() - 1) != DataResource.SEPARATOR) {
+ name += DataResource.SEPARATOR;
+ }
+ ZipEntry entry = new ZipEntry(name);
+ ZipOutputStream zip = getStream(handler);
+ synchronized (this) {
+ try {
+ zip.putNextEntry(entry);
+ zip.closeEntry();
+ } catch (IOException e) {
+ handleIOException(e, handler);
+ }
+ }
+ }
+
+ @Override
+ public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
+ try (InputStream in = content.getByteStream()) {
+ addFile(name, ByteStreams.toByteArray(in), handler);
+ } catch (IOException e) {
+ handleIOException(e, handler);
+ } catch (ResourceException e) {
+ handler.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
+ content.getOrigin()));
+ }
+ }
+
+ @Override
+ public synchronized void addFile(String name, byte[] content, DiagnosticsHandler handler) {
+ try {
+ ZipUtils.writeToZipStream(getStream(handler), name, content, ZipEntry.STORED);
+ } catch (IOException e) {
+ handleIOException(e, handler);
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Path getPath() {
+ return archive;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/FilteredArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/utils/FilteredArchiveProgramResourceProvider.java
rename to src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
index 7cde903..9c33fc5 100644
--- a/src/main/java/com/android/tools/r8/utils/FilteredArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -7,6 +7,9 @@
import static com.android.tools.r8.utils.FileUtils.isClassFile;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
@@ -30,20 +33,20 @@
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
-public class FilteredArchiveProgramResourceProvider implements ProgramResourceProvider {
+public class ArchiveResourceProvider implements ProgramResourceProvider, DataResourceProvider {
private final Origin origin;
private final FilteredClassPath archive;
private final boolean ignoreDexInArchive;
- FilteredArchiveProgramResourceProvider(FilteredClassPath archive, boolean ignoreDexInArchive) {
+ ArchiveResourceProvider(FilteredClassPath archive, boolean ignoreDexInArchive) {
+ assert isArchive(archive.getPath());
origin = new PathOrigin(archive.getPath());
this.archive = archive;
this.ignoreDexInArchive = ignoreDexInArchive;
}
private List<ProgramResource> readArchive() throws IOException {
- assert isArchive(archive.getPath());
List<ProgramResource> dexResources = new ArrayList<>();
List<ProgramResource> classResources = new ArrayList<>();
try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
@@ -94,4 +97,32 @@
throw new ResourceException(origin, e);
}
}
+
+ @Override
+ public void accept(Visitor resourceBrowser) throws ResourceException {
+ try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ Path name = Paths.get(entry.getName());
+ if (archive.matchesFile(name) && !isProgramResourceName(name)) {
+ if (entry.isDirectory()) {
+ resourceBrowser.visit(DataDirectoryResource.fromZip(zipFile, entry));
+ } else {
+ resourceBrowser.visit(DataEntryResource.fromZip(zipFile, entry));
+ }
+ }
+ }
+ } catch (ZipException e) {
+ throw new ResourceException(origin, new CompilationError(
+ "Zip error while reading '" + archive + "': " + e.getMessage(), e));
+ } catch (IOException e) {
+ throw new ResourceException(origin, new CompilationError(
+ "I/O exception while reading '" + archive + "': " + e.getMessage(), e));
+ }
+ }
+
+ private boolean isProgramResourceName(Path name) {
+ return isClassFile(name) || (isDexFile(name) && !ignoreDexInArchive);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
new file mode 100644
index 0000000..b793f19
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -0,0 +1,78 @@
+// 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.utils;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class DirectoryBuilder implements OutputBuilder {
+ private final Path root;
+ private final Origin origin;
+
+ public DirectoryBuilder(Path root) {
+ this.root = root;
+ origin = new PathOrigin(root);
+ }
+
+ @Override
+ public void open() {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void addDirectory(String name, DiagnosticsHandler handler) {
+ Path target = root.resolve(name.replace(NAME_SEPARATOR, File.separatorChar));
+ try {
+ Files.createDirectories(target.getParent());
+ } catch (IOException e) {
+ handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ }
+ }
+
+ @Override
+ public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
+ try (InputStream in = content.getByteStream()) {
+ addFile(name, ByteStreams.toByteArray(in), handler);
+ } catch (IOException e) {
+ handler.error(new IOExceptionDiagnostic(e, content.getOrigin()));
+ } catch (ResourceException e) {
+ handler.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
+ content.getOrigin()));
+ }
+ }
+
+ @Override
+ public synchronized void addFile(String name, byte[] content, DiagnosticsHandler handler) {
+ Path target = root.resolve(name.replace(NAME_SEPARATOR, File.separatorChar));
+ try {
+ Files.createDirectories(target.getParent());
+ FileUtils.writeToFile(target, null, content);
+ } catch (IOException e) {
+ handler.error(new IOExceptionDiagnostic(e, new PathOrigin(target)));
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Path getPath() {
+ return root;
+ }
+}
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 0a3b2a4..3086662 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DataResourceConsumer;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.dex.Marker;
@@ -47,6 +49,9 @@
// TODO(zerny): Make this private-final once we have full program-consumer support.
public ProgramConsumer programConsumer = null;
+ public final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
+ public DataResourceConsumer dataResourceConsumer;
+
// Constructor for testing and/or other utilities.
public InternalOptions() {
reporter = new Reporter(new DefaultDiagnosticsHandler());
@@ -154,9 +159,12 @@
return (ClassFileConsumer) programConsumer;
}
- public void signalFinishedToProgramConsumer() {
+ public void signalFinishedToConsumers() {
if (programConsumer != null) {
programConsumer.finished(reporter);
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.finished(reporter);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
new file mode 100644
index 0000000..27d07c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -0,0 +1,27 @@
+// 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.utils;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.origin.Origin;
+import java.io.Closeable;
+import java.nio.file.Path;
+
+public interface OutputBuilder extends Closeable {
+ char NAME_SEPARATOR = '/';
+
+ void open();
+
+ void addDirectory(String name, DiagnosticsHandler handler);
+
+ void addFile(String name, DataEntryResource content, DiagnosticsHandler handler);
+
+ void addFile(String name, byte[] content, DiagnosticsHandler handler);
+
+ Path getPath();
+
+ Origin getOrigin();
+}
diff --git a/src/test/examples/dataresource/ResourceTest.java b/src/test/examples/dataresource/ResourceTest.java
new file mode 100644
index 0000000..bb63826
--- /dev/null
+++ b/src/test/examples/dataresource/ResourceTest.java
@@ -0,0 +1,23 @@
+// 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.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package dataresource;
+
+import dataresource.lib.LibClass;
+import java.io.IOException;
+
+public class ResourceTest {
+ public static void main(String[] args) throws IOException {
+ System.out.println("LibClass dir: " + (LibClass.getThisDir() != null));
+ System.out.println("LibClass properties: " + (LibClass.getLibClassProperties() != null));
+ System.out.println("LibClass property: " + LibClass.getLibClassProperty());
+ System.out.println("LibClass text: " + LibClass.getText());
+ System.out.println("LibClass const string: " + LibClass.getConstString());
+ System.out.println("LibClass concat string: " + LibClass.getConcatConstString());
+ System.out.println("LibClass field: " + LibClass.getConstField());
+ }
+}
diff --git a/src/test/examples/dataresource/dataresource.lib.LibClass.txt b/src/test/examples/dataresource/dataresource.lib.LibClass.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/examples/dataresource/dataresource.lib.LibClass.txt
diff --git a/src/test/examples/dataresource/lib/LibClass.java b/src/test/examples/dataresource/lib/LibClass.java
new file mode 100644
index 0000000..870adf5
--- /dev/null
+++ b/src/test/examples/dataresource/lib/LibClass.java
@@ -0,0 +1,62 @@
+// 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.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package dataresource.lib;
+
+import dataresource.ResourceTest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+import java.util.Random;
+
+public class LibClass {
+ static final String name;
+ static {
+ name = "dataresource.lib.LibClass";
+ }
+
+ public static String getConstString() {
+ return "dataresource.lib.LibClass";
+ }
+
+ public static String getConcatConstString() throws IOException {
+ return "dataresource.lib.LibClass" + getLibClassProperty();
+ }
+
+ public static String getConstField() {
+ return name;
+ }
+
+ public static URL getThisDir() {
+ return LibClass.class.getResource("");
+ }
+
+ public static URL getLibClassProperties() {
+ return LibClass.class.getResource(LibClass.class.getSimpleName() + ".properties");
+ }
+
+ public static String getLibClassProperty() throws IOException {
+ Properties properties = new Properties();
+ properties.load(LibClass.class.getResourceAsStream(
+ LibClass.class.getSimpleName() + ".properties"));
+ return "" + properties.get(LibClass.class.getName());
+ }
+
+ public static String getText() throws IOException {
+ byte[] buffer = new byte[1000];
+ StringBuilder sb = new StringBuilder();
+ try (InputStream stream = ResourceTest.class.getResourceAsStream("resource.txt")) {
+ int size = stream.read(buffer);
+ while (size != -1) {
+ sb.append(new String(buffer, 0, size));
+ size = stream.read(buffer);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/test/examples/dataresource/lib/LibClass.properties b/src/test/examples/dataresource/lib/LibClass.properties
new file mode 100644
index 0000000..3f634e7
--- /dev/null
+++ b/src/test/examples/dataresource/lib/LibClass.properties
@@ -0,0 +1 @@
+dataresource.lib.LibClass=com.test.lib.LibClass
\ No newline at end of file
diff --git a/src/test/examples/dataresource/resource.txt b/src/test/examples/dataresource/resource.txt
new file mode 100644
index 0000000..5eb5177
--- /dev/null
+++ b/src/test/examples/dataresource/resource.txt
@@ -0,0 +1,42 @@
+this is a text with some content
+- partly matching pattern 123dataresource.lib.LibClass123
+- totally matching pattern dataresource.lib.LibClass and something after
+- matching the package dataresource.lib
+- matching class simple name LibClass
+- or only single element of the package name: lib
+- matching class descriptor dataresource/lib/LibClass
+- matching class full descriptor Ldataresource/lib/LibClass;
+- matching windows path dataresource\lib\LibClass
+- matching pattern dataresource.lib.LibClass.
+- matching pattern .dataresource.lib.LibClass
+- matching pattern dataresource.lib.LibClass,
+- matching pattern ,dataresource.lib.LibClass
+- matching pattern =dataresource.lib.LibClass
+- matching pattern dataresource.lib.LibClass=
+- matching pattern dataresource.lib.LibClass/
+- matching pattern /dataresource.lib.LibClass
+- matching pattern ?dataresource.lib.LibClass
+- matching pattern dataresource.lib.LibClass?
+- matching pattern dataresource.lib.LibClass!
+- matching pattern !dataresource.lib.LibClass
+- matching pattern :dataresource.lib.LibClass
+- matching pattern dataresource.lib.LibClass:
+- matching pattern dataresource.lib.LibClass*
+- matching pattern *dataresource.lib.LibClass
+- matching pattern $dataresource.lib.LibClass
+- matching pattern +dataresource.lib.LibClass
+- matching pattern -dataresource.lib.LibClass
+- matching pattern ^dataresource.lib.LibClass
+- matching pattern @dataresource.lib.LibClass
+- matching pattern (dataresource.lib.LibClass
+- matching pattern )dataresource.lib.LibClass
+- matching pattern àdataresource.lib.LibClass
+- matching pattern |dataresource.lib.LibClass
+- matching pattern [dataresource.lib.LibClass
+- matching pattern 'dataresource.lib.LibClass
+- matching pattern "dataresource.lib.LibClass
+- matching pattern `dataresource.lib.LibClass
+- matching pattern ~dataresource.lib.LibClass
+- matching pattern &dataresource.lib.LibClass
+- matching pattern -dataresource.lib.LibClass
+- matching pattern dataresource.lib.LibClass-
diff --git a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
index 3aa8f4a..a24e95a 100644
--- a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
@@ -6,8 +6,8 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
diff --git a/src/test/java/com/android/tools/r8/cf/NegativeZeroTestRunner.java b/src/test/java/com/android/tools/r8/cf/NegativeZeroTestRunner.java
index 7196120..7dc64fd 100644
--- a/src/test/java/com/android/tools/r8/cf/NegativeZeroTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/NegativeZeroTestRunner.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidAppConsumers;
import java.nio.file.Path;
import org.junit.Rule;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 074c704..5afb068 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -122,7 +122,7 @@
throws DexOverflowException {
try {
ToolHelper.writeApplication(application, options);
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
return consumers.build();
} catch (ExecutionException e) {
throw new RuntimeException(e);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 50a3c06..d6b26f0 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -653,7 +653,7 @@
} finally {
executor.shutdown();
}
- options.signalFinishedToProgramConsumer();
+ options.signalFinishedToConsumers();
return compatSink.build();
}
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
index 6c054e5..3b2cf8c 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.naming;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
index 6642352..3e75ac6 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
@@ -45,6 +45,7 @@
assertEquals(0, runInput.exitCode);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex));
+ // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
outDex.toString(), CLASS.getCanonicalName());
assertEquals(runInput.stdout, runDex.stdout);
diff --git a/src/test/java/com/android/tools/r8/resource/DataResourceTest.java b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
new file mode 100644
index 0000000..167262f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
@@ -0,0 +1,47 @@
+// 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.resource;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class DataResourceTest {
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void dataResourceTest()
+ throws IOException, CompilationFailedException, CompilationException {
+ String packageName = "dataresource";
+ String mainClassName = packageName + ".ResourceTest";
+ Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR,
+ packageName + FileUtils.JAR_EXTENSION);
+
+ ProcessResult referenceResult = ToolHelper.runJava(inputJar, mainClassName);
+
+ Path r8Out = temp.getRoot().toPath().resolve("r8out.jar");
+ R8Command.Builder builder = R8Command.builder()
+ .addProgramFiles(inputJar)
+ .setOutput(r8Out, OutputMode.DexIndexed);
+ ToolHelper.runR8(builder.build());
+
+ ProcessResult r8Result = ToolHelper.runArtRaw(r8Out.toString(), mainClassName);
+ Assert.assertEquals(referenceResult.stdout, r8Result.stdout);
+
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 2dfe42e..286544b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -17,7 +17,6 @@
import static org.hamcrest.core.StringContains.containsString;
import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -36,13 +35,13 @@
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.KeepingDiagnosticHandler;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
@@ -136,27 +135,6 @@
private static final String TARGET =
VALID_PROGUARD_DIR + "target.flags";
- private static class KeepingDiagnosticHandler implements DiagnosticsHandler {
- private final List<Diagnostic> infos = new ArrayList<>();
- private final List<Diagnostic> warnings = new ArrayList<>();
- private final List<Diagnostic> errors = new ArrayList<>();
-
- @Override
- public void info(Diagnostic info) {
- infos.add(info);
- }
-
- @Override
- public void warning(Diagnostic warning) {
- warnings.add(warning);
- }
-
- @Override
- public void error(Diagnostic error) {
- errors.add(error);
- }
- }
-
private Reporter reporter;
private KeepingDiagnosticHandler handler;
private ProguardConfigurationParser parser;
diff --git a/src/test/java/com/android/tools/r8/utils/KeepingDiagnosticHandler.java b/src/test/java/com/android/tools/r8/utils/KeepingDiagnosticHandler.java
new file mode 100644
index 0000000..e7ad488
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/KeepingDiagnosticHandler.java
@@ -0,0 +1,31 @@
+// 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.utils;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeepingDiagnosticHandler implements DiagnosticsHandler {
+ public final List<Diagnostic> infos = new ArrayList<>();
+ public final List<Diagnostic> warnings = new ArrayList<>();
+ public final List<Diagnostic> errors = new ArrayList<>();
+
+ @Override
+ public void info(Diagnostic info) {
+ infos.add(info);
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ warnings.add(warning);
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ errors.add(error);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ThrowingDiagnosticHandler.java b/src/test/java/com/android/tools/r8/utils/ThrowingDiagnosticHandler.java
new file mode 100644
index 0000000..7b634b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ThrowingDiagnosticHandler.java
@@ -0,0 +1,19 @@
+// 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.utils;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ThrowingDiagnosticHandler extends KeepingDiagnosticHandler {
+
+ @Override
+ public void error(Diagnostic error) {
+ super.error(error);
+ throw new AssertionError(error);
+ }
+}