aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2015-12-29 17:50:39 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-12-30 14:44:29 +0000
commitcbd6b43a0494c87b82000cd9e924f2e97a342ee3 (patch)
tree3ca2c6fb2d2e78b657624fdc2e6f908f37321c36
parenta4d7bc721f7b4959a7ca2285e9a320913b31a00a (diff)
Make the AndroidResourceProcessor more deterministic by setting the R.txt ids to 0x1, the java ids to a hash of pkg, resource type, and resource name (robolectric needs unique ids) and ensuring the last modified date is set to the epoch. This only affects library aapt invocations.
-- MOS_MIGRATED_REVID=111076315
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java10
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java78
2 files changed, 78 insertions, 10 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index c621ba7095..6161578504 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -45,7 +45,6 @@ import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
@@ -356,13 +355,16 @@ public class AndroidResourceProcessingAction {
options.proguardOutput);
LOGGER.fine(String.format("appt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
if (options.srcJarOutput != null) {
- resourceProcessor.createSrcJar(generatedSources, options.srcJarOutput);
+ resourceProcessor.createSrcJar(generatedSources, options.srcJarOutput,
+ VariantConfiguration.Type.LIBRARY == options.packageType);
}
if (options.rOutput != null) {
- resourceProcessor.copyRToOutput(generatedSources, options.rOutput);
+ resourceProcessor.copyRToOutput(generatedSources, options.rOutput,
+ VariantConfiguration.Type.LIBRARY == options.packageType);
}
if (options.symbolsTxtOut != null) {
- resourceProcessor.copyRToOutput(generatedSources, options.symbolsTxtOut);
+ resourceProcessor.copyRToOutput(generatedSources, options.symbolsTxtOut,
+ VariantConfiguration.Type.LIBRARY == options.packageType);
}
LOGGER.fine(String.format("Packaging finished at %sms",
timer.elapsed(TimeUnit.MILLISECONDS)));
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
index d5f1e1a3e8..4951ad9977 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java
@@ -13,6 +13,9 @@
// limitations under the License.
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.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -37,14 +40,20 @@ import com.android.utils.StdLogger;
import java.io.File;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,6 +62,7 @@ import java.util.zip.ZipOutputStream;
* Provides a wrapper around the AOSP build tools for resource processing.
*/
public class AndroidResourceProcessor {
+ private static final Pattern HEX_REGEX = Pattern.compile("0x[0-9A-Fa-f]{8}");
private final StdLogger stdLogger;
public AndroidResourceProcessor(StdLogger stdLogger) {
@@ -61,18 +71,29 @@ public class AndroidResourceProcessor {
/**
* Copies the R.txt to the expected place.
+ * @param generatedSourceRoot The path to the generated R.txt.
+ * @param rOutput The Path to write the R.txt.
+ * @param staticIds Boolean that indicates if the ids should be set to 0x1 for caching purposes.
*/
- public void copyRToOutput(Path generatedSourceRoot, Path rOutput) {
+ public void copyRToOutput(Path generatedSourceRoot, Path rOutput, boolean staticIds) {
try {
Files.createDirectories(rOutput.getParent());
final Path source = generatedSourceRoot.resolve("R.txt");
if (Files.exists(source)) {
- Files.copy(source, rOutput);
+ if (staticIds) {
+ String contents = HEX_REGEX.matcher(Joiner.on("\n").join(
+ Files.readAllLines(source, StandardCharsets.UTF_8))).replaceAll("0x1");
+ Files.write(rOutput, contents.getBytes(StandardCharsets.UTF_8));
+ } else {
+ Files.copy(source, rOutput);
+ }
} else {
// The R.txt wasn't generated, create one for future inheritance, as Bazel always requires
// outputs. This state occurs when there are no resource directories.
Files.createFile(rOutput);
}
+ // Set to the epoch for caching purposes.
+ Files.setLastModifiedTime(rOutput, FileTime.fromMillis(0L));
} catch (IOException e) {
Throwables.propagate(e);
}
@@ -80,14 +101,16 @@ public class AndroidResourceProcessor {
/**
* Creates a zip archive from all found R.java files.
+ * @param staticIds TODO(corysmith):
*/
- public void createSrcJar(Path generatedSourcesRoot, Path srcJar) {
+ public void createSrcJar(Path generatedSourcesRoot, Path srcJar, boolean staticIds) {
try {
Files.createDirectories(srcJar.getParent());
try (final ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(srcJar))) {
Files.walkFileTree(generatedSourcesRoot,
- new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot));
+ new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot, staticIds));
}
+ Files.setLastModifiedTime(srcJar, FileTime.fromMillis(0L));
} catch (IOException e) {
Throwables.propagate(e);
}
@@ -141,6 +164,12 @@ public class AndroidResourceProcessor {
resourceConfigs,
true // boolean enforceUniquePackageName
);
+ if (proguardOut != null) {
+ Files.setLastModifiedTime(proguardOut, FileTime.fromMillis(0L));
+ }
+ if (packageOut != null) {
+ Files.setLastModifiedTime(packageOut, FileTime.fromMillis(0L));
+ }
}
private File processManifest(
@@ -257,22 +286,59 @@ public class AndroidResourceProcessor {
/**
* A FileVisitor that will add all R.java files to be stored in a zip archive.
*/
- private final class SymbolFileSrcJarBuildingVisitor extends SimpleFileVisitor<Path> {
+ private static final class SymbolFileSrcJarBuildingVisitor extends SimpleFileVisitor<Path> {
+ static final Pattern PACKAGE_PATTERN = Pattern.compile(
+ "\\s*package ([a-zA-Z_$][a-zA-Z\\d_$]*(?:\\.[a-zA-Z_$][a-zA-Z\\d_$]*)*)");
+ static final Pattern ID_PATTERN = Pattern.compile(
+ "public static int ([\\w\\.]+)=0x[0-9A-fa-f]+;");
+ static final Pattern INNER_CLASS = Pattern.compile("public static class ([a-z_]*) \\{(.*?)\\}",
+ Pattern.DOTALL);
// The earliest date representable in a zip file, 1-1-1980.
private static final long ZIP_EPOCH = 315561600000L;
private final ZipOutputStream zip;
private final Path root;
+ private final boolean staticIds;
- private SymbolFileSrcJarBuildingVisitor(ZipOutputStream zip, Path root) {
+ private SymbolFileSrcJarBuildingVisitor(ZipOutputStream zip, Path root, boolean staticIds) {
this.zip = zip;
this.root = root;
+ this.staticIds = staticIds;
+ }
+
+ private String replaceIdsWithStaticIds(String contents) {
+ Matcher packageMatcher = PACKAGE_PATTERN.matcher(contents);
+ if (!packageMatcher.find()) {
+ return contents;
+ }
+ String pkg = packageMatcher.group(1);
+ StringBuilder out = new StringBuilder();
+ Matcher innerClassMatcher = INNER_CLASS.matcher(contents);
+ while (innerClassMatcher.find()) {
+ String resourceType = innerClassMatcher.group(1);
+ Matcher idMatcher = ID_PATTERN.matcher(innerClassMatcher.group(2));
+ StringBuilder resourceIds = new StringBuilder();
+ while (idMatcher.find()) {
+ String javaId = idMatcher.group(1);
+ idMatcher.appendReplacement(resourceIds, String.format("public static int %s=0x%08X;",
+ javaId, Objects.hash(pkg, resourceType, javaId)));
+ }
+ idMatcher.appendTail(resourceIds);
+ innerClassMatcher.appendReplacement(out,
+ String.format("public static class %s {%s}", resourceType, resourceIds.toString()));
+ }
+ innerClassMatcher.appendTail(out);
+ return out.toString();
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().endsWith("R.java")) {
byte[] content = Files.readAllBytes(file);
+ if (staticIds) {
+ content = replaceIdsWithStaticIds(UTF_8.decode(
+ ByteBuffer.wrap(content)).toString()).getBytes(UTF_8);
+ }
ZipEntry entry = new ZipEntry(root.relativize(file).toString());
entry.setMethod(ZipEntry.STORED);