diff options
author | Googler <noreply@google.com> | 2015-12-29 17:50:39 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2015-12-30 14:44:29 +0000 |
commit | cbd6b43a0494c87b82000cd9e924f2e97a342ee3 (patch) | |
tree | 3ca2c6fb2d2e78b657624fdc2e6f908f37321c36 /src/tools/android/java/com/google/devtools/build | |
parent | a4d7bc721f7b4959a7ca2285e9a320913b31a00a (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
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build')
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); |