aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2018-01-08 15:15:58 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-01-08 15:18:06 -0800
commit233807ec94189c4a322c203322163a154d90aa8f (patch)
tree3acadefc34a94f4395d52f79bdcb94a568db281c /src/tools
parent5a7d2575d3527a35b8ac541ae09591b6a040f67b (diff)
Serialize and package xml attributes from resources xml tags in values folders for aapt2.
RELNOTES: none PiperOrigin-RevId: 181226483
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java81
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java97
2 files changed, 151 insertions, 27 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
index 6ca066d06d..265d938fea 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
@@ -27,11 +27,15 @@ import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.LittleEndianDataInputStream;
import com.google.devtools.build.android.FullyQualifiedName.Factory;
+import com.google.devtools.build.android.proto.SerializeFormat;
+import com.google.devtools.build.android.proto.SerializeFormat.Header;
+import com.google.devtools.build.android.xml.ResourcesAttribute.AttributeType;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -177,6 +181,38 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
}
}
+ private void readAttributesFile(
+ InputStream resourceFileStream,
+ FileSystem fileSystem,
+ KeyValueConsumers consumers) throws IOException {
+
+ Header header = Header.parseDelimitedFrom(resourceFileStream);
+ List<DataKey> fullyQualifiedNames = new ArrayList<>();
+ for (int i = 0; i < header.getEntryCount(); i++) {
+ SerializeFormat.DataKey protoKey =
+ SerializeFormat.DataKey.parseDelimitedFrom(resourceFileStream);
+ fullyQualifiedNames.add(FullyQualifiedName.fromProto(protoKey));
+ }
+
+ DataSourceTable sourceTable = DataSourceTable.read(resourceFileStream, fileSystem, header);
+
+ for (DataKey fullyQualifiedName : fullyQualifiedNames) {
+ SerializeFormat.DataValue protoValue =
+ SerializeFormat.DataValue.parseDelimitedFrom(resourceFileStream);
+ DataSource source = sourceTable.sourceFromId(protoValue.getSourceId());
+ DataResourceXml dataResourceXml =
+ (DataResourceXml) DataResourceXml.from(protoValue, source);
+ AttributeType attributeType =
+ AttributeType.valueOf(protoValue.getXmlValue().getValueType());
+
+ if (attributeType.isCombining()) {
+ consumers.combiningConsumer.accept(fullyQualifiedName, dataResourceXml);
+ } else {
+ consumers.overwritingConsumer.accept(fullyQualifiedName, dataResourceXml);
+ }
+ }
+ }
+
@Override
public void read(Path inPath, KeyValueConsumers consumers) {
Stopwatch timer = Stopwatch.createStarted();
@@ -185,8 +221,6 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
while (resourceFiles.hasMoreElements()) {
ZipEntry resourceFile = resourceFiles.nextElement();
- InputStream resourceFileStream = zipFile.getInputStream(resourceFile);
-
String fileZipPath = resourceFile.getName();
int resourceSubdirectoryIndex = fileZipPath.indexOf('_', fileZipPath.lastIndexOf('/'));
Path filePath =
@@ -206,24 +240,31 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
continue;
}
- final String[] dirNameAndQualifiers =
- filePath.getParent().getFileName().toString().split(SdkConstants.RES_QUALIFIER_SEP);
- Factory fqnFactory = Factory.fromDirectoryName(dirNameAndQualifiers);
-
- LittleEndianDataInputStream dataInputStream =
- new LittleEndianDataInputStream(resourceFileStream);
-
- // Magic number (4 bytes), Format version (4 bytes), Number of entries (4 bytes).
- Preconditions.checkArgument(dataInputStream.skipBytes(12) == 12);
-
- int resourceType = dataInputStream.readInt();
- if (resourceType == 0) { // 0 is a resource table
- readResourceTable(dataInputStream, consumers, fqnFactory);
- } else if (resourceType == 1) { // 1 is a resource file
- readCompiledFile(dataInputStream, consumers, fqnFactory);
- } else {
- throw new RuntimeException(
- String.format("Invalid resource type enum: %s from %s", resourceType, fileZipPath));
+ try (InputStream resourceFileStream = zipFile.getInputStream(resourceFile)) {
+ final String[] dirNameAndQualifiers =
+ filePath.getParent().getFileName().toString().split(SdkConstants.RES_QUALIFIER_SEP);
+ Factory fqnFactory = Factory.fromDirectoryName(dirNameAndQualifiers);
+
+ if (fileZipPath.endsWith(".attributes")) {
+ readAttributesFile(resourceFileStream, inPath.getFileSystem(), consumers);
+ } else {
+ LittleEndianDataInputStream dataInputStream =
+ new LittleEndianDataInputStream(resourceFileStream);
+
+ // Magic number (4 bytes), Format version (4 bytes), Number of entries (4 bytes).
+ Preconditions.checkArgument(dataInputStream.skipBytes(12) == 12);
+
+ int resourceType = dataInputStream.readInt();
+ if (resourceType == 0) { // 0 is a resource table
+ readResourceTable(dataInputStream, consumers, fqnFactory);
+ } else if (resourceType == 1) { // 1 is a resource file
+ readCompiledFile(dataInputStream, consumers, fqnFactory);
+ } else {
+ throw new RuntimeException(
+ String.format(
+ "Invalid resource type enum: %s from %s", resourceType, fileZipPath));
+ }
+ }
}
}
} catch (IOException e) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
index e51192ce1e..50c5ff5b13 100644
--- a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceCompiler.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.android.aapt2;
+import com.android.SdkConstants;
import com.android.builder.core.VariantType;
import com.android.repository.Revision;
import com.google.common.base.Preconditions;
@@ -22,6 +23,15 @@ import com.google.common.collect.ImmutableList.Builder;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.devtools.build.android.AaptCommandBuilder;
+import com.google.devtools.build.android.AndroidDataSerializer;
+import com.google.devtools.build.android.DataResourceXml;
+import com.google.devtools.build.android.FullyQualifiedName;
+import com.google.devtools.build.android.FullyQualifiedName.Factory;
+import com.google.devtools.build.android.FullyQualifiedName.VirtualType;
+import com.google.devtools.build.android.XmlResourceValues;
+import com.google.devtools.build.android.xml.Namespaces;
+import com.google.devtools.build.android.xml.ResourcesAttribute;
+import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
@@ -29,11 +39,17 @@ import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
/** Invokes aapt2 to compile resources. */
public class ResourceCompiler {
@@ -58,7 +74,7 @@ public class ResourceCompiler {
private final CompilingVisitor compilingVisitor;
- private static class CompileTask implements Callable<Path> {
+ private static class CompileTask implements Callable<List<Path>> {
private final Path file;
private final Path compiledResourcesOut;
@@ -66,7 +82,10 @@ public class ResourceCompiler {
private final Revision buildToolsVersion;
private CompileTask(
- Path file, Path compiledResourcesOut, Path aapt2, Revision buildToolsVersion) {
+ Path file,
+ Path compiledResourcesOut,
+ Path aapt2,
+ Revision buildToolsVersion) {
this.file = file;
this.compiledResourcesOut = compiledResourcesOut;
this.aapt2 = aapt2;
@@ -74,7 +93,7 @@ public class ResourceCompiler {
}
@Override
- public Path call() throws Exception {
+ public List<Path> call() throws Exception {
logger.fine(
new AaptCommandBuilder(aapt2)
.forBuildToolsVersion(buildToolsVersion)
@@ -88,10 +107,34 @@ public class ResourceCompiler {
String type = file.getParent().getFileName().toString();
String filename = file.getFileName().toString();
+
+ List<Path> results = new ArrayList<>();
if (type.startsWith("values")) {
filename =
(filename.indexOf('.') != -1 ? filename.substring(0, filename.indexOf('.')) : filename)
+ ".arsc";
+
+ XMLEventReader xmlEventReader = null;
+ try {
+ // aapt2 compile strips out namespaces and attributes from the resources tag.
+ // Read them here separately and package them with the other flat files.
+ xmlEventReader =
+ XMLInputFactory.newInstance()
+ .createXMLEventReader(new FileInputStream(file.toString()));
+
+ StartElement rootElement = xmlEventReader.nextTag().asStartElement();
+ Iterator<Attribute> attributeIterator =
+ XmlResourceValues.iterateAttributesFrom(rootElement);
+
+ if (attributeIterator.hasNext()) {
+ results.add(
+ createAttributesProto(type, filename, attributeIterator));
+ }
+ } finally {
+ if (xmlEventReader != null) {
+ xmlEventReader.close();
+ }
+ }
}
final Path compiledResourcePath =
@@ -100,7 +143,47 @@ public class ResourceCompiler {
Files.exists(compiledResourcePath),
"%s does not exists after aapt2 ran.",
compiledResourcePath);
- return compiledResourcePath;
+ results.add(compiledResourcePath);
+ return results;
+ }
+
+ private Path createAttributesProto(
+ String type,
+ String filename,
+ Iterator<Attribute> attributeIterator)
+ throws IOException {
+
+ AndroidDataSerializer serializer = AndroidDataSerializer.create();
+ final Path resourcesAttributesPath =
+ compiledResourcesOut.resolve(type + "_" + filename + ".attributes");
+
+ while (attributeIterator.hasNext()) {
+ Attribute attribute = attributeIterator.next();
+ String namespaceUri = attribute.getName().getNamespaceURI();
+ String localPart = attribute.getName().getLocalPart();
+ String prefix = attribute.getName().getPrefix();
+ QName qName = new QName(namespaceUri, localPart, prefix);
+
+ Namespaces namespaces = Namespaces.from(qName);
+ String attributeName =
+ namespaceUri.isEmpty()
+ ? localPart
+ : prefix + ":" + localPart;
+
+ final String[] dirNameAndQualifiers = type.split(SdkConstants.RES_QUALIFIER_SEP);
+ Factory fqnFactory = Factory.fromDirectoryName(dirNameAndQualifiers);
+ FullyQualifiedName fqn =
+ fqnFactory.create(VirtualType.RESOURCES_ATTRIBUTE, qName.toString());
+ ResourcesAttribute resourceAttribute =
+ ResourcesAttribute.of(fqn, attributeName, attribute.getValue());
+ DataResourceXml resource =
+ DataResourceXml.createWithNamespaces(file, resourceAttribute, namespaces);
+
+ serializer.queueForSerialization(fqn, resource);
+ }
+
+ serializer.flushTo(resourcesAttributesPath);
+ return resourcesAttributesPath;
}
@Override
@@ -113,7 +196,7 @@ public class ResourceCompiler {
private final ListeningExecutorService executorService;
private final Path compiledResources;
- private final List<ListenableFuture<Path>> tasks = new ArrayList<>();
+ private final List<ListenableFuture<List<Path>>> tasks = new ArrayList<>();
private final Path aapt2;
private final Revision buildToolsVersion;
@@ -152,9 +235,9 @@ public class ResourceCompiler {
List<Path> getCompiledArtifacts() throws InterruptedException, ExecutionException {
Builder<Path> builder = ImmutableList.builder();
List<Throwable> compilationErrors = new ArrayList<>();
- for (ListenableFuture<Path> task : tasks) {
+ for (ListenableFuture<List<Path>> task : tasks) {
try {
- builder.add(task.get());
+ builder.addAll(task.get());
} catch (InterruptedException | ExecutionException e) {
compilationErrors.add(Optional.ofNullable(e.getCause()).orElse(e));
}