diff options
8 files changed, 115 insertions, 101 deletions
diff --git a/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java b/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java index 894afb4162..074eead333 100644 --- a/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java +++ b/src/test/java/com/google/devtools/build/android/resources/RClassGeneratorTest.java @@ -87,9 +87,8 @@ public class RClassGeneratorTest { "lib.R.txt", "int attr agility 0x1", "int id someTextView 0x1", "int string ok 0x1"); Path out = temp.resolve("classes"); Files.createDirectories(out); - RClassGenerator writer = RClassGenerator.fromSymbols( - out, "com.bar", symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.bar", symbolsInLibrary.asFilterMap()); Path packageDir = out.resolve("com/bar"); checkFilesInPackage(packageDir, "R.class", "R$attr.class", "R$id.class", "R$string.class"); @@ -129,9 +128,8 @@ public class RClassGeneratorTest { ResourceSymbols symbolsInLibrary = symbolValues; Path out = temp.resolve("classes"); Files.createDirectories(out); - RClassGenerator writer = RClassGenerator.fromSymbols(out, "com.testEmptyIntArray", - symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.testEmptyIntArray", symbolsInLibrary.asFilterMap()); Path packageDir = out.resolve("com/testEmptyIntArray"); checkFilesInPackage(packageDir, "R.class", "R$styleable.class"); @@ -160,9 +158,8 @@ public class RClassGeneratorTest { Path out = temp.resolve("classes"); Files.createDirectories(out); thrown.expect(NumberFormatException.class); - RClassGenerator writer = RClassGenerator.fromSymbols(out, "com.foo", - symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.foo", symbolsInLibrary.asFilterMap()); } @Test @@ -174,9 +171,8 @@ public class RClassGeneratorTest { Path out = temp.resolve("classes"); Files.createDirectories(out); thrown.expect(NumberFormatException.class); - RClassGenerator writer = RClassGenerator.fromSymbols(out, "com.foo", - symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.foo", symbolsInLibrary.asFilterMap()); } @Test @@ -194,24 +190,15 @@ public class RClassGeneratorTest { "int layout stubbable_activity 0x1"); Path out = temp.resolve("classes"); Files.createDirectories(out); - RClassGenerator writer = RClassGenerator.fromSymbols(out, "com.foo", - symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.foo", symbolsInLibrary.asFilterMap()); Path packageDir = out.resolve("com/foo"); - checkFilesInPackage(packageDir, "R.class", "R$id.class", "R$layout.class"); + checkFilesInPackage(packageDir, "R.class", "R$layout.class"); Class<?> outerClass = checkTopLevelClass(out, "com.foo.R", - "com.foo.R$id", "com.foo.R$layout"); checkInnerClass(out, - "com.foo.R$id", - outerClass, - ImmutableMap.<String, Integer>of(), - ImmutableMap.<String, List<Integer>>of(), - finalFields - ); - checkInnerClass(out, "com.foo.R$layout", outerClass, ImmutableMap.of("stubbable_activity", 0x7f020000), @@ -255,9 +242,8 @@ public class RClassGeneratorTest { ResourceSymbols symbolsInLibrary = symbolValues; Path out = temp.resolve("classes"); Files.createDirectories(out); - RClassGenerator writer = RClassGenerator.fromSymbols( - out, "com.intArray", symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("com.intArray", symbolsInLibrary.asFilterMap()); Path packageDir = out.resolve("com/intArray"); checkFilesInPackage(packageDir, "R.class", "R$attr.class", "R$styleable.class"); @@ -309,10 +295,8 @@ public class RClassGeneratorTest { ResourceSymbols symbolsInLibrary = symbolValues; Path out = temp.resolve("classes"); Files.createDirectories(out); - RClassGenerator writer = - RClassGenerator.fromSymbols( - out, "", symbolValues, ImmutableList.of(symbolsInLibrary), finalFields); - writer.write(); + RClassGenerator writer = RClassGenerator.fromSymbols(out, symbolValues, finalFields); + writer.write("", symbolsInLibrary.asFilterMap()); Path packageDir = out.resolve(""); checkFilesInPackage(packageDir, "R.class", "R$string.class"); diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java index 37fb7a81a2..1f0360c85b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceClassWriter.java @@ -474,8 +474,8 @@ public class AndroidResourceClassWriter implements Flushable { private void writeAsClass(Map<ResourceType, List<FieldInitializer>> initializers) throws IOException { RClassGenerator rClassGenerator = - new RClassGenerator(outputBasePath, packageName, initializers, false /* finalFields */); - rClassGenerator.write(); + new RClassGenerator(outputBasePath, initializers, false /* finalFields */); + rClassGenerator.write(packageName); } private static String normalizeName(String resourceName) { 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 a9243d2584..45187f2d14 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 @@ -544,23 +544,15 @@ public class AndroidResourceProcessor { Path classesOut, boolean finalFields) throws IOException { + RClassGenerator classWriter = + RClassGenerator.fromSymbols(classesOut, fullSymbolValues, finalFields); for (String packageName : libMap.keySet()) { - Collection<ResourceSymbols> symbols = libMap.get(packageName); - RClassGenerator classWriter = RClassGenerator.fromSymbols( - classesOut, packageName, fullSymbolValues, symbols, finalFields); - classWriter.write(); + classWriter.write(packageName, ResourceSymbols.merge(libMap.get(packageName)).asFilterMap()); } if (appPackageName != null) { // Unlike the R.java generation, we also write the app's R.class file so that the class // jar file can be complete (aapt doesn't generate it for us). - RClassGenerator classWriter = - RClassGenerator.fromSymbols( - classesOut, - appPackageName, - fullSymbolValues, - ImmutableList.of(fullSymbolValues), - finalFields); - classWriter.write(); + classWriter.write(appPackageName); } } diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializer.java b/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializer.java index a1e322fef6..a0b4dd59bb 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializer.java @@ -15,6 +15,7 @@ package com.google.devtools.build.android.resources; import java.io.IOException; import java.io.Writer; +import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.InstructionAdapter; @@ -43,4 +44,7 @@ public interface FieldInitializer { * for final fields yet. */ void writeInitSource(Writer writer) throws IOException; + + /** Tests if the field's name is in the provided set. */ + boolean nameIsIn(Set<String> fieldNames); } diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/IntArrayFieldInitializer.java b/src/tools/android/java/com/google/devtools/build/android/resources/IntArrayFieldInitializer.java index 48e6496d89..eb78d53ade 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/IntArrayFieldInitializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/IntArrayFieldInitializer.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.Writer; +import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; @@ -92,7 +93,13 @@ public final class IntArrayFieldInitializer implements FieldInitializer { builder.append(String.format(", 0x%x", attrId)); } } + writer.write(String.format(" public static int[] %s = { %s };\n", fieldName, builder.toString())); } + + @Override + public boolean nameIsIn(Set<String> fieldNames) { + return fieldNames.contains(fieldName); + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/IntFieldInitializer.java b/src/tools/android/java/com/google/devtools/build/android/resources/IntFieldInitializer.java index 400c26789e..2223a1a05f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/IntFieldInitializer.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/IntFieldInitializer.java @@ -15,6 +15,7 @@ package com.google.devtools.build.android.resources; import java.io.IOException; import java.io.Writer; +import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.InstructionAdapter; @@ -56,4 +57,9 @@ public final class IntFieldInitializer implements FieldInitializer { writer.write(String.format(" public static int %s = 0x%x;\n", fieldName, value)); } + + @Override + public boolean nameIsIn(Set<String> fieldNames) { + return fieldNames.contains(fieldName); + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java index 2d92e783cc..e1149281ee 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/RClassGenerator.java @@ -18,19 +18,18 @@ import com.android.builder.internal.SymbolLoader.SymbolEntry; import com.android.resources.ResourceType; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; -import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Table; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; -import java.util.logging.Logger; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -44,112 +43,83 @@ import org.objectweb.asm.commons.InstructionAdapter; * information. Also, the order of the constant pool tends to be different. */ public class RClassGenerator { - - private static final Logger logger = Logger.getLogger(RClassGenerator.class.getName()); private static final int JAVA_VERSION = Opcodes.V1_7; private static final String SUPER_CLASS = "java/lang/Object"; private final Path outFolder; - private final String packageName; private final Map<ResourceType, List<FieldInitializer>> initializers; private final boolean finalFields; private static final Splitter PACKAGE_SPLITTER = Splitter.on('.'); /** - * Create an RClassGenerator given the final binary's symbol values, and a collection of symbols - * for the given package. + * Create an RClassGenerator initialized with the ResourceSymbols values. * * @param outFolder base folder to place the output R class files. - * @param packageName the java package to use for the R class - * @param values the final symbol values (may include more symbols than needed for this package) - * @param packageSymbols the symbols in this package + * @param values the final symbol values * @param finalFields true if the fields should be marked final */ public static RClassGenerator fromSymbols( Path outFolder, - String packageName, ResourceSymbols values, - Collection<ResourceSymbols> packageSymbols, boolean finalFields) throws IOException { - Table<String, String, SymbolEntry> symbolsTable = getAllSymbols(packageSymbols); Table<String, String, SymbolEntry> valuesTable = values.asTable(); Map<ResourceType, List<FieldInitializer>> initializers = - getInitializers(symbolsTable, valuesTable); - return new RClassGenerator(outFolder, packageName, initializers, finalFields); + getInitializers(valuesTable); + return new RClassGenerator(outFolder, initializers, finalFields); } /** * Create an RClassGenerator given a collection of initializers. * * @param outFolder base folder to place the output R class files. - * @param packageName the java package to use for the R class * @param initializers the list of initializers to use for each inner class * @param finalFields true if the fields should be marked final */ public RClassGenerator( Path outFolder, - String packageName, Map<ResourceType, List<FieldInitializer>> initializers, boolean finalFields) { this.outFolder = outFolder; - this.packageName = packageName; this.finalFields = finalFields; this.initializers = initializers; } - - private static Table<String, String, SymbolEntry> getAllSymbols( - Collection<ResourceSymbols> symbols) throws IOException { - Table<String, String, SymbolEntry> mergedSymbols = HashBasedTable.create(); - for (ResourceSymbols tableProvider : symbols) { - mergedSymbols.putAll(tableProvider.asTable()); - } - return mergedSymbols; - } - - /** Convert the {@link SymbolTableProvider} data, to a map of {@link FieldInitializer}. */ + /** Convert the {@link ResourceSymbols} data, to a map of {@link FieldInitializer}. */ private static Map<ResourceType, List<FieldInitializer>> getInitializers( - Table<String, String, SymbolEntry> symbols, Table<String, String, SymbolEntry> values) { + Table<String, String, SymbolEntry> values) { Map<ResourceType, List<FieldInitializer>> initializers = new EnumMap<>(ResourceType.class); - for (String typeName : symbols.rowKeySet()) { + for (String typeName : values.rowKeySet()) { ResourceType resourceType = ResourceType.getEnum(typeName); Preconditions.checkNotNull(resourceType); - initializers.put(resourceType, getInitializers(typeName, symbols, values)); + initializers.put(resourceType, getInitializers(typeName, values)); } return initializers; } private static List<FieldInitializer> getInitializers( String typeName, - Table<String, String, SymbolEntry> symbols, - Table<String, String, SymbolEntry> values) { + Table<String, String, SymbolEntry> symbols) { Map<String, SymbolEntry> rowMap = symbols.row(typeName); - Set<String> symbolSet = rowMap.keySet(); - List<String> symbolList = new ArrayList<>(symbolSet); + List<String> symbolList = new ArrayList<>(rowMap.keySet()); Collections.sort(symbolList); List<FieldInitializer> initializers = new ArrayList<>(); for (String symbolName : symbolList) { - // get the matching SymbolEntry from the values Table. - SymbolEntry value = values.get(typeName, symbolName); - if (value != null) { - if (value.getType().equals("int")) { - initializers.add(IntFieldInitializer.of(value.getName(), value.getValue())); - } else { - Preconditions.checkArgument(value.getType().equals("int[]")); - initializers.add(IntArrayFieldInitializer.of(value.getName(), value.getValue())); - } + SymbolEntry value = symbols.get(typeName, symbolName); + if (value.getType().equals("int")) { + initializers.add(IntFieldInitializer.of(value.getName(), value.getValue())); } else { - // Value may be missing if resource overriding eliminates resources at the binary - // level, which were originally present at the library level. - logger.fine( - String.format( - "Skipping R.%s.%s -- value not known in binary's R.txt", typeName, symbolName)); + Preconditions.checkArgument(value.getType().equals("int[]")); + initializers.add(IntArrayFieldInitializer.of(value.getName(), value.getValue())); } } return initializers; } - /** Builds the bytecode and writes out the R.class file, and R$inner.class files. */ - public void write() throws IOException { + /** + * Builds bytecode and writes out R.class file, and R$inner.class files for provided package and + * symbols + */ + public void write(String packageName, Map<ResourceType, Set<String>> symbolsToWrite) + throws IOException { Iterable<String> folders = PACKAGE_SPLITTER.split(packageName); Path packageDir = outFolder; for (String folder : folders) { @@ -158,7 +128,9 @@ public class RClassGenerator { // At least create the outFolder that was requested. However, if there are no symbols, don't // create the R.class and inner class files (no need to have an empty class). Files.createDirectories(packageDir); - if (initializers.isEmpty()) { + Map<ResourceType, List<FieldInitializer>> initializersToWrite = + filterInitializers(symbolsToWrite); + if (initializersToWrite.isEmpty()) { return; } Path rClassFile = packageDir.resolve(SdkConstants.FN_COMPILED_RESOURCE_CLASS); @@ -177,7 +149,7 @@ public class RClassGenerator { writeConstructor(classWriter); // Build the R.class w/ the inner classes, then later build the individual R$inner.class. - for (ResourceType resourceType : initializers.keySet()) { + for (ResourceType resourceType : initializersToWrite.keySet()) { String innerClassName = rClassName + "$" + resourceType; classWriter.visitInnerClass( innerClassName, @@ -189,11 +161,41 @@ public class RClassGenerator { Files.write(rClassFile, classWriter.toByteArray()); // Now generate the R$inner.class files. - for (Map.Entry<ResourceType, List<FieldInitializer>> entry : initializers.entrySet()) { + for (Map.Entry<ResourceType, List<FieldInitializer>> entry : initializersToWrite.entrySet()) { writeInnerClass(entry.getValue(), packageDir, rClassName, entry.getKey().toString()); } } + /** Builds bytecode and writes out R.class file, and R$inner.class files for provided package. */ + public void write(String packageName) throws IOException { + write(packageName, ImmutableMap.<ResourceType, Set<String>>of()); + } + + private Map<ResourceType, List<FieldInitializer>> filterInitializers( + Map<ResourceType, Set<String>> symbolsToWrite) { + Map<ResourceType, List<FieldInitializer>> initializersToWrite = + new EnumMap<>(ResourceType.class); + if (symbolsToWrite.isEmpty()) { + return initializers; + } + for (Entry<ResourceType, Set<String>> entry : symbolsToWrite.entrySet()) { + List<FieldInitializer> fieldsToWrite = new ArrayList<>(); + // Resource type may be missing if resource overriding eliminates resources at the binary + // level, which were originally present at the library level. + if (initializers.containsKey(entry.getKey())) { + for (FieldInitializer field : initializers.get(entry.getKey())) { + if (field.nameIsIn(entry.getValue())) { + fieldsToWrite.add(field); + } + } + } + if (!fieldsToWrite.isEmpty()) { + initializersToWrite.put(entry.getKey(), fieldsToWrite); + } + } + return initializersToWrite; + } + private void writeInnerClass( List<FieldInitializer> initializers, Path packageDir, diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java index 1860e228a3..051bd58de3 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/ResourceSymbols.java @@ -18,6 +18,7 @@ import com.android.builder.dependency.SymbolFileProvider; import com.android.builder.internal.SymbolLoader; import com.android.builder.internal.SymbolLoader.SymbolEntry; import com.android.builder.internal.SymbolWriter; +import com.android.resources.ResourceType; import com.android.utils.ILogger; import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashMultimap; @@ -31,10 +32,13 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Path; import java.util.Collection; +import java.util.EnumMap; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import javax.annotation.Nullable; @@ -185,4 +189,19 @@ public class ResourceSymbols { } writer.write(); } + + public Map<ResourceType, Set<String>> asFilterMap() throws IOException { + Map<ResourceType, Set<String>> filter = new EnumMap<>(ResourceType.class); + Table<String, String, SymbolEntry> symbolTable = asTable(); + for (String typeName : symbolTable.rowKeySet()) { + Set<String> fields = new HashSet<>(); + for (SymbolEntry symbolEntry : symbolTable.row(typeName).values()) { + fields.add(symbolEntry.getName()); + } + if (!fields.isEmpty()) { + filter.put(ResourceType.getEnum(typeName), fields); + } + } + return filter; + } } |