diff options
Diffstat (limited to 'src/tools/android/java/com')
10 files changed, 161 insertions, 214 deletions
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 444278ac86..e5ae303d1e 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 @@ -42,7 +42,6 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.devtools.build.android.Converters.ExistingPathConverter; import com.google.devtools.build.android.Converters.RevisionConverter; import com.google.devtools.build.android.SplitConfigurationFilter.UnrecognizedSplitsException; -import com.google.devtools.build.android.resources.RClassGenerator; import com.google.devtools.build.android.resources.ResourceSymbols; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; import com.google.devtools.common.options.Option; @@ -530,31 +529,11 @@ public class AndroidResourceProcessor { // Loop on all the package name, merge all the symbols to write, and write. for (String packageName : libSymbolMap.keySet()) { Collection<ResourceSymbols> symbols = libSymbolMap.get(packageName); - fullSymbolValues.writeTo(sourceOut, packageName, symbols, true /* finalFields */); + fullSymbolValues.writeSourcesTo(sourceOut, packageName, symbols, true /* finalFields */); } } } - void writePackageRClasses( - Multimap<String, ResourceSymbols> libMap, - ResourceSymbols fullSymbolValues, - @Nullable String appPackageName, - Path classesOut, - boolean finalFields) - throws IOException { - RClassGenerator classWriter = - RClassGenerator.fromSymbols(classesOut, fullSymbolValues, finalFields); - for (String packageName : libMap.keySet()) { - classWriter.write( - packageName, ResourceSymbols.merge(libMap.get(packageName)).asInitializers()); - } - 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). - classWriter.write(appPackageName); - } - } - /** Finds aapt's split outputs and renames them according to the input flags. */ private Iterable<Path> findAndRenameSplitPackages(Path packageOut, Iterable<String> splits) throws UnrecognizedSplitsException, IOException { diff --git a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java index fc6d7636ff..189ea76d8e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java +++ b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java @@ -31,7 +31,6 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; @@ -327,7 +326,7 @@ class PlaceholderIdFieldInitializerBuilder { } public FieldInitializers build() throws AttrLookupException { - Map<ResourceType, Collection<FieldInitializer>> initializers = + Map<ResourceType, Map<String, FieldInitializer>> initializers = new EnumMap<>(ResourceType.class); Map<ResourceType, Integer> typeIdMap = chooseTypeIds(); Map<String, Integer> attrAssignments = assignAttrIds(typeIdMap.get(ResourceType.ATTR)); @@ -335,7 +334,7 @@ class PlaceholderIdFieldInitializerBuilder { ResourceType type = fieldEntries.getKey(); ImmutableList<String> sortedFields = Ordering.natural().immutableSortedCopy(fieldEntries.getValue()); - List<FieldInitializer> fields; + Map<String, FieldInitializer> fields; if (type == ResourceType.STYLEABLE) { fields = getStyleableInitializers(attrAssignments, sortedFields); } else if (type == ResourceType.ATTR) { @@ -379,19 +378,19 @@ class PlaceholderIdFieldInitializerBuilder { return allocatedTypeIds; } - private List<FieldInitializer> getAttrInitializers( + private Map<String, FieldInitializer> getAttrInitializers( Map<String, Integer> attrAssignments, Collection<String> sortedFields) { - ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder(); + ImmutableMap.Builder<String, FieldInitializer> initList = ImmutableMap.builder(); for (String field : sortedFields) { int attrId = attrAssignments.get(field); - initList.add(new IntFieldInitializer(field, attrId)); + initList.put(field, IntFieldInitializer.of(attrId)); } return initList.build(); } - private List<FieldInitializer> getResourceInitializers( + private Map<String, FieldInitializer> getResourceInitializers( ResourceType type, int typeId, Collection<String> sortedFields) { - ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder(); + ImmutableMap.Builder<String, FieldInitializer> initList = ImmutableMap.builder(); Map<String, Integer> publicNameToId = new HashMap<>(); Set<Integer> assignedIds = ImmutableSet.of(); if (publicIds.containsKey(type)) { @@ -404,15 +403,15 @@ class PlaceholderIdFieldInitializerBuilder { fieldValue = resourceIds; resourceIds = nextFreeId(resourceIds + 1, assignedIds); } - initList.add(new IntFieldInitializer(field, fieldValue)); + initList.put(field, IntFieldInitializer.of(fieldValue)); } return initList.build(); } - private List<FieldInitializer> getStyleableInitializers( + private Map<String, FieldInitializer> getStyleableInitializers( Map<String, Integer> attrAssignments, Collection<String> styleableFields) throws AttrLookupException { - ImmutableList.Builder<FieldInitializer> initList = ImmutableList.builder(); + ImmutableMap.Builder<String, FieldInitializer> initList = ImmutableMap.builder(); for (String field : styleableFields) { Set<String> attrs = styleableAttrs.get(field).keySet(); ImmutableMap.Builder<String, Integer> arrayInitValues = ImmutableMap.builder(); @@ -432,10 +431,10 @@ class PlaceholderIdFieldInitializerBuilder { // Make sure that if we have android: framework attributes, their IDs are listed first. ImmutableMap<String, Integer> arrayInitMap = arrayInitValues.orderEntriesByValue(Ordering.<Integer>natural()).build(); - initList.add(new IntArrayFieldInitializer(field, arrayInitMap.values())); + initList.put(field, IntArrayFieldInitializer.of(arrayInitMap.values())); int index = 0; for (String attr : arrayInitMap.keySet()) { - initList.add(new IntFieldInitializer(field + "_" + attr, index)); + initList.put(field + "_" + attr, IntFieldInitializer.of(index)); ++index; } } diff --git a/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java index 6ed0d8ff05..ebc89df41f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/RClassGeneratorAction.java @@ -133,8 +133,8 @@ public class RClassGeneratorAction { logger.fine( String.format("Load symbols finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); // For now, assuming not used for libraries and setting final access for fields. - resourceProcessor.writePackageRClasses(libSymbolMap, fullSymbolValues, appPackageName, - classOutPath, true /* finalFields */); + fullSymbolValues.writeClassesTo( + libSymbolMap, appPackageName, classOutPath, true /* finalFields */); logger.fine( String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } else if (!libraries.isEmpty()) { @@ -144,8 +144,7 @@ public class RClassGeneratorAction { logger.fine( String.format("Load symbols finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); // For now, assuming not used for libraries and setting final access for fields. - resourceProcessor.writePackageRClasses( - libSymbolMap, fullSymbolValues, null, classOutPath, true /* finalFields */); + fullSymbolValues.writeClassesTo(libSymbolMap, null, classOutPath, true /* finalFields */); logger.fine( String.format("Finished R.class at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); } else { 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 edcc464c18..ff7e9b875e 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,7 +15,6 @@ package com.google.devtools.build.android.resources; import java.io.IOException; import java.io.Writer; -import java.util.Collection; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.InstructionAdapter; @@ -23,27 +22,20 @@ import org.objectweb.asm.commons.InstructionAdapter; * Represents a field and its initializer (where initialization is either part of the field * definition, or done via code in the static clinit function). */ -public interface FieldInitializer extends Comparable<FieldInitializer> { +public interface FieldInitializer { /** * Write the bytecode for the field definition. * * @return true if the initializer is deferred to clinit code. */ - boolean writeFieldDefinition(ClassWriter cw, int accessLevel, boolean isFinal); + boolean writeFieldDefinition(String fieldName, ClassWriter cw, int accessLevel, boolean isFinal); /** * Write the bytecode for the clinit portion of initializer. - * * @return the number of stack slots needed for the code. */ - int writeCLInit(InstructionAdapter insts, String className); + int writeCLInit(String fieldName, InstructionAdapter insts, String className); /** Write the source code for the initializer to the given writer. */ - void writeInitSource(Writer writer, boolean finalFields) throws IOException; - - /** Tests if the field's name is in the provided collection. */ - boolean nameIsIn(Collection<String> fieldNames); - - /** Adds fieldName to the provided set. */ - void addTo(Collection<String> fieldNames); + void writeInitSource(String fieldName, Writer writer, boolean finalFields) throws IOException; } diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializers.java b/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializers.java index 45ffadf66d..ae8307ed90 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializers.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/FieldInitializers.java @@ -16,65 +16,78 @@ package com.google.devtools.build.android.resources; import com.android.resources.ResourceType; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.SortedSetMultimap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Sets; import java.util.Collection; +import java.util.EnumMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import java.util.SortedSet; +import java.util.TreeMap; /** * Represents a collection of resource symbols and values suitable for writing java sources and * classes. */ public class FieldInitializers - implements Iterable<Entry<ResourceType, Collection<FieldInitializer>>> { + implements Iterable<Entry<ResourceType, Map<String, FieldInitializer>>> { - private final Map<ResourceType, Collection<FieldInitializer>> initializers; + private final Map<ResourceType, Map<String, FieldInitializer>> initializers; - private FieldInitializers(Map<ResourceType, Collection<FieldInitializer>> initializers) { + private FieldInitializers(Map<ResourceType, Map<String, FieldInitializer>> initializers) { this.initializers = initializers; } /** Creates a {@link FieldInitializers} copying the contents from a {@link Map}. */ public static FieldInitializers copyOf( - Map<ResourceType, Collection<FieldInitializer>> initializers) { + Map<ResourceType, Map<String, FieldInitializer>> initializers) { return new FieldInitializers(ImmutableMap.copyOf(initializers)); } - public Iterable<Entry<ResourceType, Collection<FieldInitializer>>> filter( - FieldInitializers fieldsToWrite) { - final SortedSetMultimap<ResourceType, FieldInitializer> initializersToWrite = - MultimapBuilder.enumKeys(ResourceType.class).treeSetValues().build(); - - // Create a map to filter with. - final SortedSetMultimap<ResourceType, String> symbolsToWrite = - MultimapBuilder.enumKeys(ResourceType.class).treeSetValues().build(); - for (Entry<ResourceType, Collection<FieldInitializer>> entry : - fieldsToWrite.initializers.entrySet()) { - for (FieldInitializer initializer : entry.getValue()) { - final SortedSet<String> fieldNames = symbolsToWrite.get(entry.getKey()); - initializer.addTo(fieldNames); + public static FieldInitializers mergedFrom(Collection<FieldInitializers> toMerge) { + final Map<ResourceType, Map<String, FieldInitializer>> merged = + new EnumMap<>(ResourceType.class); + for (FieldInitializers mergee : toMerge) { + for (Entry<ResourceType, Map<String, FieldInitializer>> entry : mergee) { + final Map<String, FieldInitializer> fieldMap = + merged.containsKey(entry.getKey()) + ? merged.get(entry.getKey()) + : new TreeMap<String, FieldInitializer>(); + merged.put(entry.getKey(), fieldMap); + for (Entry<String, FieldInitializer> field : entry.getValue().entrySet()) { + fieldMap.put(field.getKey(), field.getValue()); + } } } + return copyOf(merged); + } - for (Entry<ResourceType, Collection<String>> entry : symbolsToWrite.asMap().entrySet()) { - // Resource type may be missing if resource overriding eliminates resources at the binary - // level, which were originally present at the library level. + public Iterable<Entry<ResourceType, Map<String, FieldInitializer>>> filter( + FieldInitializers fieldsToWrite) { + Map<ResourceType, Map<String, FieldInitializer>> initializersToWrite = + new EnumMap<>(ResourceType.class); + + for (Entry<ResourceType, Map<String, FieldInitializer>> entry : + fieldsToWrite.initializers.entrySet()) { if (initializers.containsKey(entry.getKey())) { - for (FieldInitializer field : initializers.get(entry.getKey())) { - if (field.nameIsIn(entry.getValue())) { - initializersToWrite.put(entry.getKey(), field); - } + final Map<String, FieldInitializer> valueFields = initializers.get(entry.getKey()); + final ImmutableMap.Builder<String, FieldInitializer> fields = + ImmutableSortedMap.naturalOrder(); + // Resource type may be missing if resource overriding eliminates resources at the binary + // level, which were originally present at the library level. + for (String fieldName : + Sets.intersection(entry.getValue().keySet(), valueFields.keySet())) { + fields.put(fieldName, valueFields.get(fieldName)); } + initializersToWrite.put(entry.getKey(), fields.build()); } } - return initializersToWrite.asMap().entrySet(); + + return initializersToWrite.entrySet(); } @Override - public Iterator<Entry<ResourceType, Collection<FieldInitializer>>> iterator() { + public Iterator<Entry<ResourceType, Map<String, FieldInitializer>>> iterator() { return initializers.entrySet().iterator(); } 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 2f595c90f5..140439cd5b 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 @@ -20,53 +20,50 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.Writer; -import java.util.Collection; import java.util.Objects; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; -/** - * Models an int[] field initializer. - */ +/** Models an int[] field initializer. */ public final class IntArrayFieldInitializer implements FieldInitializer { public static final String DESC = "[I"; - private final String fieldName; private final ImmutableCollection<Integer> values; - public IntArrayFieldInitializer(String fieldName, ImmutableCollection<Integer> values) { - this.fieldName = fieldName; + private IntArrayFieldInitializer(ImmutableCollection<Integer> values) { this.values = values; } - public static FieldInitializer of(String name, String value) { + public static FieldInitializer of(String value) { Preconditions.checkArgument(value.startsWith("{ "), "Expected list starting with { "); Preconditions.checkArgument(value.endsWith(" }"), "Expected list ending with } "); // Check for an empty list, which is "{ }". if (value.length() < 4) { - return new IntArrayFieldInitializer(name, ImmutableList.<Integer>of()); + return of(ImmutableList.<Integer>of()); } ImmutableList.Builder<Integer> intValues = ImmutableList.builder(); String trimmedValue = value.substring(2, value.length() - 2); - Iterable<String> valueStrings = Splitter.on(',') - .trimResults() - .split(trimmedValue); + Iterable<String> valueStrings = Splitter.on(',').trimResults().split(trimmedValue); for (String valueString : valueStrings) { intValues.add(Integer.decode(valueString)); } - return new IntArrayFieldInitializer(name, intValues.build()); + return of(intValues.build()); + } + + public static IntArrayFieldInitializer of(ImmutableCollection<Integer> values) { + return new IntArrayFieldInitializer(values); } @Override - public boolean writeFieldDefinition(ClassWriter cw, int accessLevel, boolean isFinal) { - cw.visitField(accessLevel, fieldName, DESC, null, null) - .visitEnd(); + public boolean writeFieldDefinition( + String fieldName, ClassWriter cw, int accessLevel, boolean isFinal) { + cw.visitField(accessLevel, fieldName, DESC, null, null).visitEnd(); return true; } @Override - public int writeCLInit(InstructionAdapter insts, String className) { + public int writeCLInit(String fieldName, InstructionAdapter insts, String className) { insts.iconst(values.size()); insts.newarray(Type.INT_TYPE); int curIndex = 0; @@ -84,7 +81,8 @@ public final class IntArrayFieldInitializer implements FieldInitializer { } @Override - public void writeInitSource(Writer writer, boolean finalFields) throws IOException { + public void writeInitSource(String fieldName, Writer writer, boolean finalFields) + throws IOException { StringBuilder builder = new StringBuilder(); boolean first = true; for (Integer attrId : values) { @@ -103,43 +101,21 @@ public final class IntArrayFieldInitializer implements FieldInitializer { } @Override - public boolean nameIsIn(Collection<String> fieldNames) { - return fieldNames.contains(fieldName); - } - - @Override public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("fieldName", fieldName) - .add("values", values) - .toString(); - } - - @Override - public int compareTo(FieldInitializer other) { - if (other instanceof IntArrayFieldInitializer) { - return fieldName.compareTo(((IntArrayFieldInitializer) other).fieldName); - } - // IntArrays will go after IntFields - return 1; + return MoreObjects.toStringHelper(getClass()).add("values", values).toString(); } @Override public int hashCode() { - return Objects.hash(fieldName, values); + return values.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof IntArrayFieldInitializer) { IntArrayFieldInitializer other = (IntArrayFieldInitializer) obj; - return Objects.equals(fieldName, other.fieldName) && Objects.equals(values, other.values); + return Objects.equals(values, other.values); } return false; } - - @Override - public void addTo(Collection<String> fieldNames) { - fieldNames.add(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 ffbd64fcda..888e888f0b 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 @@ -16,38 +16,36 @@ package com.google.devtools.build.android.resources; import com.google.common.base.MoreObjects; import java.io.IOException; import java.io.Writer; -import java.util.Collection; -import java.util.Objects; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.InstructionAdapter; -/** - * Models an int field initializer. - */ +/** Models an int field initializer. */ public final class IntFieldInitializer implements FieldInitializer { - private final String fieldName; private final int value; private static final String DESC = "I"; - public IntFieldInitializer(String fieldName, int value) { - this.fieldName = fieldName; + private IntFieldInitializer(int value) { this.value = value; } - public static FieldInitializer of(String name, String value) { - return new IntFieldInitializer(name, Integer.decode(value)); + public static FieldInitializer of(String value) { + return of(Integer.decode(value)); + } + + public static IntFieldInitializer of(int value) { + return new IntFieldInitializer(value); } @Override - public boolean writeFieldDefinition(ClassWriter cw, int accessLevel, boolean isFinal) { - cw.visitField(accessLevel, fieldName, DESC, null, isFinal ? value : null) - .visitEnd(); + public boolean writeFieldDefinition( + String fieldName, ClassWriter cw, int accessLevel, boolean isFinal) { + cw.visitField(accessLevel, fieldName, DESC, null, isFinal ? value : null).visitEnd(); return !isFinal; } @Override - public int writeCLInit(InstructionAdapter insts, String className) { + public int writeCLInit(String fieldName, InstructionAdapter insts, String className) { insts.iconst(value); insts.putstatic(className, fieldName, DESC); // Just needs one stack slot for the iconst. @@ -55,7 +53,8 @@ public final class IntFieldInitializer implements FieldInitializer { } @Override - public void writeInitSource(Writer writer, boolean finalFields) throws IOException { + public void writeInitSource(String fieldName, Writer writer, boolean finalFields) + throws IOException { writer.write( String.format( " public static %sint %s = 0x%x;\n", @@ -63,43 +62,21 @@ public final class IntFieldInitializer implements FieldInitializer { } @Override - public boolean nameIsIn(Collection<String> fieldNames) { - return fieldNames.contains(fieldName); - } - - @Override public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("fieldName", fieldName) - .add("value", value) - .toString(); - } - - @Override - public int compareTo(FieldInitializer other) { - if (other instanceof IntFieldInitializer) { - return fieldName.compareTo(((IntFieldInitializer) other).fieldName); - } - // IntFields will go before Intarrays - return -1; + return MoreObjects.toStringHelper(getClass()).add("value", value).toString(); } @Override public int hashCode() { - return Objects.hash(fieldName, value); + return value; } @Override public boolean equals(Object obj) { if (obj instanceof IntFieldInitializer) { IntFieldInitializer other = (IntFieldInitializer) obj; - return Objects.equals(fieldName, other.fieldName) && value == other.value; + return value == other.value; } return false; } - - @Override - public void addTo(Collection<String> fieldNames) { - fieldNames.add(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 54375fab14..ea09b5bbd3 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 @@ -20,9 +20,7 @@ import com.google.common.collect.Iterables; 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.List; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.objectweb.asm.ClassWriter; @@ -46,18 +44,6 @@ public class RClassGenerator { private static final Splitter PACKAGE_SPLITTER = Splitter.on('.'); /** - * Create an RClassGenerator initialized with the ResourceSymbols values. - * - * @param outFolder base folder to place the output R class files. - * @param values the final symbol values - * @param finalFields true if the fields should be marked final - */ - public static RClassGenerator fromSymbols( - Path outFolder, ResourceSymbols values, boolean finalFields) { - return with(outFolder, values.asInitializers(), finalFields); - } - - /** * Create an RClassGenerator given a collection of initializers. * * @param outFolder base folder to place the output R class files. @@ -82,17 +68,17 @@ public class RClassGenerator { public void write(String packageName, FieldInitializers symbolsToWrite) throws IOException { writeClasses(packageName, initializers.filter(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, - Iterable<Entry<ResourceType, Collection<FieldInitializer>>> initializersToWrite) + Iterable<Entry<ResourceType, Map<String, FieldInitializer>>> initializersToWrite) throws IOException { - + Iterable<String> folders = PACKAGE_SPLITTER.split(packageName); Path packageDir = outFolder; for (String folder : folders) { @@ -101,11 +87,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 (Iterables.isEmpty(initializersToWrite)) { return; } - + Path rClassFile = packageDir.resolve(SdkConstants.FN_COMPILED_RESOURCE_CLASS); String packageWithSlashes = packageName.replaceAll("\\.", "/"); @@ -121,7 +107,7 @@ public class RClassGenerator { 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 (Entry<ResourceType, Collection<FieldInitializer>> entry : initializersToWrite) { + for (Entry<ResourceType, Map<String, FieldInitializer>> entry : initializersToWrite) { String innerClassName = rClassName + "$" + entry.getKey().toString(); classWriter.visitInnerClass( innerClassName, @@ -132,13 +118,13 @@ public class RClassGenerator { classWriter.visitEnd(); Files.write(rClassFile, classWriter.toByteArray()); // Now generate the R$inner.class files. - for (Map.Entry<ResourceType, Collection<FieldInitializer>> entry : initializersToWrite) { + for (Map.Entry<ResourceType, Map<String, FieldInitializer>> entry : initializersToWrite) { writeInnerClass(entry.getValue(), packageDir, rClassName, entry.getKey().toString()); } } private void writeInnerClass( - Collection<FieldInitializer> initializers, + Map<String, FieldInitializer> initializers, Path packageDir, String fullyQualifiedOuterClass, String innerClass) @@ -147,14 +133,16 @@ public class RClassGenerator { String fullyQualifiedInnerClass = writeInnerClassHeader(fullyQualifiedOuterClass, innerClass, innerClassWriter); - List<FieldInitializer> deferredInitializers = new ArrayList<>(); + Map<String, FieldInitializer> deferredInitializers = new LinkedHashMap<>(); int fieldAccessLevel = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC; if (finalFields) { fieldAccessLevel |= Opcodes.ACC_FINAL; } - for (FieldInitializer init : initializers) { - if (init.writeFieldDefinition(innerClassWriter, fieldAccessLevel, finalFields)) { - deferredInitializers.add(init); + for (Entry<String, FieldInitializer> entry : initializers.entrySet()) { + FieldInitializer init = entry.getValue(); + if (init.writeFieldDefinition( + entry.getKey(), innerClassWriter, fieldAccessLevel, finalFields)) { + deferredInitializers.put(entry.getKey(), init); } } if (!deferredInitializers.isEmpty()) { @@ -199,15 +187,19 @@ public class RClassGenerator { } private static void writeStaticClassInit( - ClassWriter classWriter, String className, List<FieldInitializer> initializers) { + ClassWriter classWriter, + String className, + Map<String, FieldInitializer> deferredInitializers) { MethodVisitor visitor = classWriter.visitMethod( Opcodes.ACC_STATIC, "<clinit>", "()V", null, /* signature */ null /* exceptions */); visitor.visitCode(); int stackSlotsNeeded = 0; InstructionAdapter insts = new InstructionAdapter(visitor); - for (FieldInitializer fieldInit : initializers) { - stackSlotsNeeded = Math.max(stackSlotsNeeded, fieldInit.writeCLInit(insts, className)); + for (Entry<String, FieldInitializer> fieldEntry : deferredInitializers.entrySet()) { + final FieldInitializer fieldInit = fieldEntry.getValue(); + stackSlotsNeeded = + Math.max(stackSlotsNeeded, fieldInit.writeCLInit(fieldEntry.getKey(), insts, className)); } insts.areturn(Type.VOID_TYPE); visitor.visitMaxs(stackSlotsNeeded, 0); diff --git a/src/tools/android/java/com/google/devtools/build/android/resources/RSourceGenerator.java b/src/tools/android/java/com/google/devtools/build/android/resources/RSourceGenerator.java index 4c3ef8581f..ffaab026d0 100644 --- a/src/tools/android/java/com/google/devtools/build/android/resources/RSourceGenerator.java +++ b/src/tools/android/java/com/google/devtools/build/android/resources/RSourceGenerator.java @@ -22,7 +22,7 @@ import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collection; +import java.util.Map; import java.util.Map.Entry; /** Writes out an R.java source. */ @@ -62,7 +62,7 @@ public class RSourceGenerator { private void writeSource( String packageName, - Iterable<Entry<ResourceType, Collection<FieldInitializer>>> initializersToWrite) + Iterable<Entry<ResourceType, Map<String, FieldInitializer>>> initializersToWrite) throws IOException { String packageDir = packageName.replace('.', '/'); Path packagePath = outputBasePath.resolve(packageDir); @@ -82,11 +82,11 @@ public class RSourceGenerator { writer.write(" */\n"); writer.write(String.format("package %s;\n", packageName)); writer.write("public final class R {\n"); - for (Entry<ResourceType, Collection<FieldInitializer>> entry : initializersToWrite) { + for (Entry<ResourceType, Map<String, FieldInitializer>> entry : initializersToWrite) { writer.write( String.format(" public static final class %s {\n", entry.getKey().getName())); - for (FieldInitializer field : entry.getValue()) { - field.writeInitSource(writer, finalFields); + for (Entry<String, FieldInitializer> fieldEntry : entry.getValue().entrySet()) { + fieldEntry.getValue().writeInitSource(fieldEntry.getKey(), writer, finalFields); } writer.write(" }\n"); } 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 6b2dbcff7b..b98dfefa64 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,8 +18,6 @@ import com.android.builder.dependency.SymbolFileProvider; import com.android.resources.ResourceType; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.SortedSetMultimap; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import java.io.File; @@ -27,12 +25,15 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.logging.Logger; @@ -55,8 +56,8 @@ public class ResourceSymbols { public ResourceSymbols call() throws Exception { List<String> lines = Files.readAllLines(rTxtSymbols, StandardCharsets.UTF_8); - final SortedSetMultimap<ResourceType, FieldInitializer> initializers = - MultimapBuilder.enumKeys(ResourceType.class).treeSetValues().build(); + Map<ResourceType, Map<String, FieldInitializer>> initializers = + new EnumMap<>(ResourceType.class); for (int lineIndex = 1; lineIndex <= lines.size(); lineIndex++) { String line = null; @@ -74,10 +75,17 @@ public class ResourceSymbols { String value = line.substring(pos3 + 1); final ResourceType resourceType = ResourceType.getEnum(className); + final Map<String, FieldInitializer> fields; + if (initializers.containsKey(resourceType)) { + fields = initializers.get(resourceType); + } else { + fields = new TreeMap<>(); + initializers.put(resourceType, fields); + } if ("int".equals(type)) { - initializers.put(resourceType, IntFieldInitializer.of(name, value)); + fields.put(name, IntFieldInitializer.of(value)); } else { - initializers.put(resourceType, IntArrayFieldInitializer.of(name, value)); + fields.put(name, IntArrayFieldInitializer.of(value)); } } catch (IndexOutOfBoundsException e) { String s = @@ -88,7 +96,7 @@ public class ResourceSymbols { throw new IOException(s, e); } } - return ResourceSymbols.from(FieldInitializers.copyOf(initializers.asMap())); + return ResourceSymbols.from(FieldInitializers.copyOf(initializers)); } } @@ -141,15 +149,11 @@ public class ResourceSymbols { } public static ResourceSymbols merge(Collection<ResourceSymbols> symbolTables) { - final SortedSetMultimap<ResourceType, FieldInitializer> initializers = - MultimapBuilder.enumKeys(ResourceType.class).treeSetValues().build(); + List<FieldInitializers> fieldInitializers = new ArrayList<>(symbolTables.size()); for (ResourceSymbols symbolTableProvider : symbolTables) { - for (Entry<ResourceType, Collection<FieldInitializer>> entry : - symbolTableProvider.asInitializers()) { - initializers.putAll(entry.getKey(), entry.getValue()); - } + fieldInitializers.add(symbolTableProvider.asInitializers()); } - return from(FieldInitializers.copyOf(initializers.asMap())); + return from(FieldInitializers.mergedFrom(fieldInitializers)); } /** Read the symbols from the provided symbol file. */ @@ -173,7 +177,7 @@ public class ResourceSymbols { * @param finalFields * @throws IOException when encountering an error during writing. */ - public void writeTo( + public void writeSourcesTo( Path sourceOut, String packageName, Collection<ResourceSymbols> packageSymbols, @@ -187,4 +191,20 @@ public class ResourceSymbols { return values; } + public void writeClassesTo( + Multimap<String, ResourceSymbols> libMap, + String appPackageName, + Path classesOut, + boolean finalFields) + throws IOException { + RClassGenerator classWriter = RClassGenerator.with(classesOut, values, finalFields); + for (String packageName : libMap.keySet()) { + classWriter.write(packageName, ResourceSymbols.merge(libMap.get(packageName)).values); + } + 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). + classWriter.write(appPackageName); + } + } } |