aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2017-08-10 20:34:02 +0200
committerGravatar Marcel Hlopko <hlopko@google.com>2017-08-11 12:56:18 +0200
commite7e7ba4d6f250899165bb8862fefa4e90a33da57 (patch)
tree675b78d5edc34b3ad6847dfe3fbf62d3c9d5a411 /src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
parenta664a5118e552504ba7962e1cccfea43b51ef28e (diff)
Adds aapt2.ResourceCompiler and CompileLibraryResourcesAction.
Refactorings: * Change data binding to have configurable archive generation * Extract a ZipBuilder class from the ZipBuilderVisitor to provide a general purpose archiving class. * Small changes to visibility AaptCommandLineBuilder for reuse in the aapt2 code. RELNOTES: None PiperOrigin-RevId: 164880571
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java183
1 files changed, 129 insertions, 54 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
index 608309f556..3cad94c83f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
@@ -16,9 +16,12 @@ package com.google.devtools.build.android;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Ordering;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.FileVisitResult;
@@ -29,6 +32,8 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.GregorianCalendar;
+import java.util.List;
import java.util.Objects;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -42,10 +47,71 @@ import java.util.zip.ZipOutputStream;
/** Collects all the functionationality for an action to create the final output artifacts. */
public class AndroidResourceOutputs {
+ static class ZipBuilder implements Closeable {
+ // ZIP timestamps have a resolution of 2 seconds.
+ // see http://www.info-zip.org/FAQ.html#limits
+ private static final long MINIMUM_TIMESTAMP_INCREMENT = 2000L;
+
+ // The earliest date representable in a zip file, 1-1-1980 (the DOS epoch).
+ private static final long ZIP_EPOCH =
+ new GregorianCalendar(1980, 01, 01, 0, 0).getTimeInMillis();
+
+ private final ZipOutputStream zip;
+
+ private ZipBuilder(ZipOutputStream zip) {
+ this.zip = zip;
+ }
+
+ public static ZipBuilder createFor(Path archivePath) throws IOException {
+ return wrap(
+ new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(archivePath))));
+ }
+
+ public static ZipBuilder wrap(ZipOutputStream zip) {
+ return new ZipBuilder(zip);
+ }
+
+ /**
+ * Normalize timestamps for deterministic builds. Stamp .class files to be a bit newer than
+ * .java files. See: {@link
+ * com.google.devtools.build.buildjar.jarhelper.JarHelper#normalizedTimestamp(String)}
+ */
+ protected long normalizeTime(String filename) {
+ if (filename.endsWith(".class")) {
+ return ZIP_EPOCH + MINIMUM_TIMESTAMP_INCREMENT;
+ } else {
+ return ZIP_EPOCH;
+ }
+ }
+
+ protected void addEntry(String rawName, byte[] content, int storageMethod) throws IOException {
+ // Fix the path for windows.
+ String relativeName = rawName.replace('\\', '/');
+ // Make sure the zip entry is not absolute.
+ Preconditions.checkArgument(!relativeName.startsWith("/"));
+ ZipEntry entry = new ZipEntry(relativeName);
+ entry.setMethod(storageMethod);
+ entry.setTime(normalizeTime(relativeName));
+ entry.setSize(content.length);
+ CRC32 crc32 = new CRC32();
+ crc32.update(content);
+ entry.setCrc(crc32.getValue());
+
+ zip.putNextEntry(entry);
+ zip.write(content);
+ zip.closeEntry();
+ }
+
+ @Override
+ public void close() throws IOException {
+ zip.close();
+ }
+ }
+
/** A FileVisitor that will add all R class files to be stored in a zip archive. */
static final class ClassJarBuildingVisitor extends ZipBuilderVisitor {
- ClassJarBuildingVisitor(ZipOutputStream zip, Path root) {
+ ClassJarBuildingVisitor(ZipBuilder zip, Path root) {
super(zip, root, null);
}
@@ -89,8 +155,8 @@ public class AndroidResourceOutputs {
private final boolean staticIds;
- private SymbolFileSrcJarBuildingVisitor(ZipOutputStream zip, Path root, boolean staticIds) {
- super(zip, root, null);
+ private SymbolFileSrcJarBuildingVisitor(ZipBuilder zipBuilder, Path root, boolean staticIds) {
+ super(zipBuilder, root, null);
this.staticIds = staticIds;
}
@@ -139,51 +205,21 @@ public class AndroidResourceOutputs {
/** A FileVisitor that will add all files to be stored in a zip archive. */
static class ZipBuilderVisitor extends SimpleFileVisitor<Path> {
- // ZIP timestamps have a resolution of 2 seconds.
- // see http://www.info-zip.org/FAQ.html#limits
- private static final long MINIMUM_TIMESTAMP_INCREMENT = 2000L;
- // The earliest date representable in a zip file, 1-1-1980 (the DOS epoch).
- private static final long ZIP_EPOCH = 315561600000L;
-
- private final String directoryPrefix;
+ protected final String directoryPrefix;
private final Collection<Path> paths = new ArrayList<>();
protected final Path root;
private int storageMethod = ZipEntry.STORED;
- private final ZipOutputStream zip;
+ private ZipBuilder zipBuilder;
- ZipBuilderVisitor(ZipOutputStream zip, Path root, String directory) {
- this.zip = zip;
+ ZipBuilderVisitor(ZipBuilder zipBuilder, Path root, String directory) {
this.root = root;
- this.directoryPrefix = directory;
+ this.directoryPrefix = directory != null ? (directory + File.separator) : "";
+ this.zipBuilder = zipBuilder;
}
protected void addEntry(Path file, byte[] content) throws IOException {
- String prefix = directoryPrefix != null ? (directoryPrefix + "/") : "";
- String relativeName = root.relativize(file).toString();
- ZipEntry entry = new ZipEntry((prefix + relativeName).replace('\\', '/'));
- entry.setMethod(storageMethod);
- entry.setTime(normalizeTime(relativeName));
- entry.setSize(content.length);
- CRC32 crc32 = new CRC32();
- crc32.update(content);
- entry.setCrc(crc32.getValue());
-
- zip.putNextEntry(entry);
- zip.write(content);
- zip.closeEntry();
- }
-
- /**
- * Normalize timestamps for deterministic builds. Stamp .class files to be a bit newer than
- * .java files. See: {@link
- * com.google.devtools.build.buildjar.jarhelper.JarHelper#normalizedTimestamp(String)}
- */
- protected long normalizeTime(String filename) {
- if (filename.endsWith(".class")) {
- return ZIP_EPOCH + MINIMUM_TIMESTAMP_INCREMENT;
- } else {
- return ZIP_EPOCH;
- }
+ Preconditions.checkArgument(file.startsWith(root), "%s does not start with %s", file, root);
+ zipBuilder.addEntry(directoryPrefix + root.relativize(file), content, storageMethod);
}
public void setCompress(boolean compress) {
@@ -270,10 +306,8 @@ public class AndroidResourceOutputs {
public static void createClassJar(Path generatedClassesRoot, Path classJar) {
try {
Files.createDirectories(classJar.getParent());
- try (final ZipOutputStream zip =
- new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(classJar)))) {
- ClassJarBuildingVisitor visitor =
- new ClassJarBuildingVisitor(zip, generatedClassesRoot);
+ try (final ZipBuilder zip = ZipBuilder.createFor(classJar)) {
+ ClassJarBuildingVisitor visitor = new ClassJarBuildingVisitor(zip, generatedClassesRoot);
Files.walkFileTree(generatedClassesRoot, visitor);
visitor.writeEntries();
visitor.writeManifestContent();
@@ -285,6 +319,23 @@ public class AndroidResourceOutputs {
}
}
+ /** Creates a zip archive from all files under the provided root. */
+ public static void archiveDirectory(Path root, Path archive) {
+ try {
+ Files.createDirectories(archive.getParent());
+ try (final ZipBuilder zip = ZipBuilder.createFor(archive)) {
+ ZipBuilderVisitor visitor = new ZipBuilderVisitor(zip, root, null);
+ visitor.setCompress(false);
+ Files.walkFileTree(root, visitor);
+ visitor.writeEntries();
+ }
+ // Set to the epoch for caching purposes.
+ Files.setLastModifiedTime(archive, FileTime.fromMillis(0L));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Creates a zip file containing the provided android resources and assets.
*
@@ -296,18 +347,15 @@ public class AndroidResourceOutputs {
*/
public static void createResourcesZip(
Path resourcesRoot, Path assetsRoot, Path output, boolean compress) throws IOException {
- try (ZipOutputStream zout =
- new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(output)))) {
+ try (final ZipBuilder zip = ZipBuilder.createFor(output)) {
if (Files.exists(resourcesRoot)) {
- ZipBuilderVisitor visitor =
- new ZipBuilderVisitor(zout, resourcesRoot, "res");
+ ZipBuilderVisitor visitor = new ZipBuilderVisitor(zip, resourcesRoot, "res");
visitor.setCompress(compress);
Files.walkFileTree(resourcesRoot, visitor);
visitor.writeEntries();
}
if (Files.exists(assetsRoot)) {
- ZipBuilderVisitor visitor =
- new ZipBuilderVisitor(zout, assetsRoot, "assets");
+ ZipBuilderVisitor visitor = new ZipBuilderVisitor(zip, assetsRoot, "assets");
visitor.setCompress(compress);
Files.walkFileTree(assetsRoot, visitor);
visitor.writeEntries();
@@ -319,11 +367,9 @@ public class AndroidResourceOutputs {
public static void createSrcJar(Path generatedSourcesRoot, Path srcJar, boolean staticIds) {
try {
Files.createDirectories(srcJar.getParent());
- try (final ZipOutputStream zip =
- new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(srcJar)))) {
+ try (final ZipBuilder zip = ZipBuilder.createFor(srcJar)) {
SymbolFileSrcJarBuildingVisitor visitor =
- new SymbolFileSrcJarBuildingVisitor(
- zip, generatedSourcesRoot, staticIds);
+ new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot, staticIds);
Files.walkFileTree(generatedSourcesRoot, visitor);
visitor.writeEntries();
}
@@ -333,4 +379,33 @@ public class AndroidResourceOutputs {
throw new RuntimeException(e);
}
}
+
+ /** Collects all the compiled resources into an archive, normalizing the paths to the root. */
+ public static void archiveCompiledResources(
+ final Path archiveOut,
+ final Path databindingResourcesRoot,
+ final Path compiledRoot,
+ final List<Path> compiledArtifacts)
+ throws IOException {
+ final Path relativeDatabindingProcessedResources =
+ databindingResourcesRoot.getRoot().relativize(databindingResourcesRoot);
+ try (ZipBuilder builder = ZipBuilder.createFor(archiveOut)) {
+ for (Path artifact : compiledArtifacts) {
+ Path relativeName = artifact;
+ // remove compiled resources prefix
+ if (artifact.startsWith(compiledRoot)) {
+ relativeName = compiledRoot.relativize(relativeName);
+ }
+ // remove databinding prefix
+ if (relativeName.startsWith(relativeDatabindingProcessedResources)) {
+ relativeName =
+ relativeName.subpath(
+ relativeDatabindingProcessedResources.getNameCount(),
+ relativeName.getNameCount());
+ }
+
+ builder.addEntry(relativeName.toString(), Files.readAllBytes(artifact), ZipEntry.STORED);
+ }
+ }
+ }
}