aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2017-09-22 02:02:35 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2017-09-22 12:16:02 +0200
commit54c86b4c6f29f4b0d52e1db702d30c10f3ac8b56 (patch)
tree08141e8298a2eadae818e7d8cce1198280e3c1be /src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java
parent87c70eef9cae2c2bb652ecc0baa16adb90a21eab (diff)
Action for resource shrinking with aapt2
Introduces the ResourcesZip class to more easily handle processing merged resources. RELNOTES: None PiperOrigin-RevId: 169622715
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java b/src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java
new file mode 100644
index 0000000000..e5436ad388
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/ResourcesZip.java
@@ -0,0 +1,184 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.android;
+
+import static com.google.common.base.Predicates.not;
+
+import com.android.build.gradle.tasks.ResourceUsageAnalyzer;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.devtools.build.android.AndroidResourceOutputs.ZipBuilder;
+import com.google.devtools.build.android.AndroidResourceOutputs.ZipBuilderVisitor;
+import com.google.devtools.build.android.aapt2.CompiledResources;
+import com.google.devtools.build.android.aapt2.ResourceCompiler;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import javax.annotation.Nullable;
+import javax.xml.parsers.ParserConfigurationException;
+import org.xml.sax.SAXException;
+
+/** Represents a collection of raw, merged resources with an optional id list. */
+public class ResourcesZip {
+
+ private final Path resourcesRoot;
+ private final Path assetsRoot;
+ private final Optional<Path> ids;
+
+ private ResourcesZip(Path resourcesRoot, Path assetsRoot, Optional<Path> ids) {
+ this.resourcesRoot = resourcesRoot;
+ this.assetsRoot = assetsRoot;
+ this.ids = ids;
+ }
+
+ /**
+ * @param resourcesRoot The root of the raw resources.
+ * @param assetsRoot The root of the raw assets.
+ */
+ public static ResourcesZip from(Path resourcesRoot, Path assetsRoot) {
+ return new ResourcesZip(resourcesRoot, assetsRoot, Optional.empty());
+ }
+
+ /**
+ * @param resourcesRoot The root of the raw resources.
+ * @param assetsRoot The root of the raw assets.
+ * @param resourceIds Optional path to a file containing the resource ids.
+ */
+ public static ResourcesZip from(Path resourcesRoot, Path assetsRoot, Path resourceIds) {
+ return new ResourcesZip(
+ resourcesRoot, assetsRoot, Optional.of(resourceIds).filter(Files::exists));
+ }
+
+ /** Creates a ResourcesZip from an archive by expanding into the workingDirectory. */
+ public static ResourcesZip createFrom(Path resourcesZip, Path workingDirectory)
+ throws IOException {
+ // Expand resource files zip into working directory.
+ final ZipFile zipFile = new ZipFile(resourcesZip.toFile());
+
+ zipFile
+ .stream()
+ .filter(not(ZipEntry::isDirectory))
+ .forEach(
+ entry -> {
+ Path output = workingDirectory.resolve(entry.getName());
+ try {
+ Files.createDirectories(output.getParent());
+ try (FileOutputStream fos = new FileOutputStream(output.toFile())) {
+ ByteStreams.copy(zipFile.getInputStream(entry), fos);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return from(
+ Files.createDirectories(workingDirectory.resolve("res")),
+ Files.createDirectories(workingDirectory.resolve("assets")),
+ workingDirectory.resolve("ids.txt"));
+ }
+
+ /**
+ * Creates a zip file containing the provided android resources and assets.
+ *
+ * @param output The path to write the zip file
+ * @param compress Whether or not to compress the content
+ * @throws IOException
+ */
+ public void writeTo(Path output, boolean compress) throws IOException {
+ try (final ZipBuilder zip = ZipBuilder.createFor(output)) {
+ if (Files.exists(resourcesRoot)) {
+ ZipBuilderVisitor visitor = new ZipBuilderVisitor(zip, resourcesRoot, "res");
+ visitor.setCompress(compress);
+ Files.walkFileTree(resourcesRoot, visitor);
+ if (!Files.exists(resourcesRoot.resolve("values/public.xml"))) {
+ // add an empty public xml, if one doesn't exist. The ResourceUsageAnalyzer expects one.
+ visitor.addEntry(
+ resourcesRoot.resolve("values").resolve("public.xml"),
+ "<resources></resources>".getBytes(StandardCharsets.UTF_8));
+ }
+ visitor.writeEntries();
+ }
+ if (Files.exists(assetsRoot)) {
+ ZipBuilderVisitor visitor = new ZipBuilderVisitor(zip, assetsRoot, "assets");
+ visitor.setCompress(compress);
+ Files.walkFileTree(assetsRoot, visitor);
+ visitor.writeEntries();
+ }
+
+ ids.ifPresent(
+ p -> {
+ try {
+ zip.addEntry("ids.txt", Files.readAllBytes(p), ZipEntry.STORED);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ }
+
+ /** Removes unused resources from the archived resources. */
+ public ShrunkResources shrink(
+ Set<String> packages,
+ Path rTxt,
+ Path classJar,
+ Path manifest,
+ @Nullable Path proguardMapping,
+ Path logFile,
+ Path workingDirectory)
+ throws ParserConfigurationException, IOException, SAXException {
+
+ new ResourceUsageAnalyzer(
+ packages, rTxt, classJar, manifest, proguardMapping, resourcesRoot, logFile)
+ .shrink(workingDirectory);
+ return ShrunkResources.of(
+ new ResourcesZip(workingDirectory, assetsRoot, ids),
+ new UnvalidatedAndroidData(
+ ImmutableList.of(workingDirectory), ImmutableList.of(assetsRoot), manifest));
+ }
+
+ static class ShrunkResources {
+
+ private ResourcesZip resourcesZip;
+ private UnvalidatedAndroidData unvalidatedAndroidData;
+
+ private ShrunkResources(
+ ResourcesZip resourcesZip, UnvalidatedAndroidData unvalidatedAndroidData) {
+ this.resourcesZip = resourcesZip;
+ this.unvalidatedAndroidData = unvalidatedAndroidData;
+ }
+
+ public static ShrunkResources of(
+ ResourcesZip resourcesZip, UnvalidatedAndroidData unvalidatedAndroidData) {
+ return new ShrunkResources(resourcesZip, unvalidatedAndroidData);
+ }
+
+ public ShrunkResources writeArchiveTo(Path archivePath, boolean compress) throws IOException {
+ resourcesZip.writeTo(archivePath, compress);
+ return this;
+ }
+
+ public CompiledResources compile(ResourceCompiler compiler, Path workingDirectory)
+ throws InterruptedException, ExecutionException, IOException {
+ return unvalidatedAndroidData
+ .compile(compiler, workingDirectory)
+ .addStableIds(resourcesZip.ids);
+ }
+ }
+}