diff options
author | corysmith <corysmith@google.com> | 2017-04-25 17:33:48 +0200 |
---|---|---|
committer | Vladimir Moskva <vladmos@google.com> | 2017-04-25 20:38:40 +0200 |
commit | 641318ac48dd4f110184c42b4b75485ee61416a8 (patch) | |
tree | 8eb64d4739516a2e115544a94551201bd29ba70a /src/tools/android/java/com/google/devtools/build/android/resources | |
parent | a900f00e5f83d859bd404aa79bc9281b2886ae00 (diff) |
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
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/resources')
5 files changed, 114 insertions, 57 deletions
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..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<String> 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<String> 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<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 { + 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<ResourceType, List<FieldInitializer>> initializersToWrite) throws IOException { + Iterable<String> 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<ResourceType, List<FieldInitializer>> entry : initializers.entrySet()) { + for (Map.Entry<ResourceType, List<FieldInitializer>> entry : initializersToWrite.entrySet()) { writeInnerClass(entry.getValue(), packageDir, rClassName, entry.getKey().toString()); } } + + private Map<ResourceType, List<FieldInitializer>> filterInitializers( + Map<ResourceType, Set<String>> symbolsToWrite) { + Map<ResourceType, List<FieldInitializer>> initializersToWrite = + new EnumMap<>(ResourceType.class); + 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, 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; + } } |