From 641318ac48dd4f110184c42b4b75485ee61416a8 Mon Sep 17 00:00:00 2001 From: corysmith Date: Tue, 25 Apr 2017 17:33:48 +0200 Subject: Automated g4 rollback of commit 3c0bb56a74478cff675b636d5bf605a652451739. *** Reason for rollback *** Rolling forward with a fix to avoid writing all resources to empty libraries. *** Original change description *** Automated g4 rollback of commit bdf0230534a59dab954ee76c5bf640394c88984e. *** Reason for rollback *** Causes issues with library R generation. *** Original change description *** Refactor the RClassGenerator to be reusable for different packages. RELNOTES: None PiperOrigin-RevId: 154175593 --- .../build/android/AndroidResourceClassWriter.java | 4 +- .../build/android/AndroidResourceProcessor.java | 18 +--- .../build/android/resources/FieldInitializer.java | 4 + .../resources/IntArrayFieldInitializer.java | 16 +++ .../android/resources/IntFieldInitializer.java | 15 +++ .../build/android/resources/RClassGenerator.java | 117 +++++++++++---------- .../build/android/resources/ResourceSymbols.java | 19 ++++ 7 files changed, 121 insertions(+), 72 deletions(-) (limited to 'src/tools/android/java/com/google/devtools') 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> 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..d86861649b 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 { - for (String packageName : libMap.keySet()) { - Collection symbols = libMap.get(packageName); - RClassGenerator classWriter = RClassGenerator.fromSymbols( - classesOut, packageName, fullSymbolValues, symbols, finalFields); - classWriter.write(); + RClassGenerator classWriter = + RClassGenerator.fromSymbols(classesOut, fullSymbolValues, finalFields); + for (String packageName : libMap.keySet()) { + 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 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..be5fec2a46 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 @@ -13,12 +13,14 @@ // limitations under the License. package com.google.devtools.build.android.resources; +import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; 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 +94,21 @@ 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 fieldNames) { + return fieldNames.contains(fieldName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("fieldName", fieldName) + .add("values", values) + .toString(); + } } 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..8442711b88 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 @@ -13,8 +13,10 @@ // limitations under the License. package com.google.devtools.build.android.resources; +import com.google.common.base.MoreObjects; 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 +58,17 @@ public final class IntFieldInitializer implements FieldInitializer { writer.write(String.format(" public static int %s = 0x%x;\n", fieldName, value)); } + + @Override + public boolean nameIsIn(Set fieldNames) { + return fieldNames.contains(fieldName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("fieldName", fieldName) + .add("value", value) + .toString(); + } } 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..5d414bf11f 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,17 @@ 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.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 +42,94 @@ 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> 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 packageSymbols, boolean finalFields) throws IOException { - Table symbolsTable = getAllSymbols(packageSymbols); Table valuesTable = values.asTable(); Map> 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> initializers, boolean finalFields) { this.outFolder = outFolder; - this.packageName = packageName; this.finalFields = finalFields; this.initializers = initializers; } - - private static Table getAllSymbols( - Collection symbols) throws IOException { - Table 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> getInitializers( - Table symbols, Table values) { + Table values) { Map> 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 getInitializers( String typeName, - Table symbols, - Table values) { + Table symbols) { Map rowMap = symbols.row(typeName); - Set symbolSet = rowMap.keySet(); - List symbolList = new ArrayList<>(symbolSet); + List symbolList = new ArrayList<>(rowMap.keySet()); Collections.sort(symbolList); List 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> symbolsToWrite) + throws IOException { + writeClasses(packageName, filterInitializers(symbolsToWrite)); + } + + /** Builds bytecode and writes out R.class file, and R$inner.class files for provided package. */ + public void write(String packageName) throws IOException { + writeClasses(packageName, initializers); + } + + private void writeClasses(String packageName, + Map> initializersToWrite) throws IOException { + Iterable folders = PACKAGE_SPLITTER.split(packageName); Path packageDir = outFolder; for (String folder : folders) { @@ -158,9 +138,11 @@ 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()) { + + if (initializersToWrite.isEmpty()) { return; } + Path rClassFile = packageDir.resolve(SdkConstants.FN_COMPILED_RESOURCE_CLASS); String packageWithSlashes = packageName.replaceAll("\\.", "/"); @@ -175,9 +157,8 @@ public class RClassGenerator { null /* interfaces */); classWriter.visitSource(SdkConstants.FN_RESOURCE_CLASS, null); 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,10 +170,32 @@ public class RClassGenerator { Files.write(rClassFile, classWriter.toByteArray()); // Now generate the R$inner.class files. - for (Map.Entry> entry : initializers.entrySet()) { + for (Map.Entry> entry : initializersToWrite.entrySet()) { writeInnerClass(entry.getValue(), packageDir, rClassName, entry.getKey().toString()); } } + + private Map> filterInitializers( + Map> symbolsToWrite) { + Map> initializersToWrite = + new EnumMap<>(ResourceType.class); + for (Entry> entry : symbolsToWrite.entrySet()) { + List 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 initializers, 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> asFilterMap() throws IOException { + Map> filter = new EnumMap<>(ResourceType.class); + Table symbolTable = asTable(); + for (String typeName : symbolTable.rowKeySet()) { + Set 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; + } } -- cgit v1.2.3