aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar Andrew Pellegrini <apell@google.com>2016-02-17 19:48:28 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2016-02-17 21:42:24 +0000
commitd8ffb712a8d4247c312c6f7df346d5c21e3246aa (patch)
tree3cdbed63775fc89bc755d7019907522bf451e993 /src/tools/android/java/com/google/devtools/build
parentcf521ddece8e8878f6d6dec538c09da3f0c6bca7 (diff)
Add merged resources output generation to AndroidResourceProcessingAction in preparation for the ResourceShrinkerAction and move common option flags to AndroidResourceProcessor to prevent duplication in ResourceShrinkerAction.
-- MOS_MIGRATED_REVID=114883341
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java193
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java121
2 files changed, 193 insertions, 121 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index 8a853146e7..a3291ca849 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -13,15 +13,13 @@
// limitations under the License.
package com.google.devtools.build.android;
-
-import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing;
+import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
+import com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions;
import com.google.devtools.build.android.Converters.DependencyAndroidDataListConverter;
-import com.google.devtools.build.android.Converters.ExistingPathConverter;
-import com.google.devtools.build.android.Converters.FullRevisionConverter;
import com.google.devtools.build.android.Converters.PathConverter;
import com.google.devtools.build.android.Converters.UnvalidatedAndroidDataConverter;
import com.google.devtools.build.android.Converters.VariantConfigurationTypeConverter;
@@ -32,23 +30,27 @@ import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.TriState;
import com.android.builder.core.VariantConfiguration;
-import com.android.builder.model.AaptOptions;
import com.android.ide.common.internal.AaptCruncher;
import com.android.ide.common.internal.CommandLineRunner;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.ide.common.res2.MergingException;
-import com.android.sdklib.repository.FullRevision;
import com.android.utils.StdLogger;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collection;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
/**
@@ -81,50 +83,6 @@ public class AndroidResourceProcessingAction {
/** Flag specifications for this action. */
public static final class Options extends OptionsBase {
-
- @Option(name = "buildToolsVersion",
- defaultValue = "null",
- converter = FullRevisionConverter.class,
- category = "config",
- help = "Version of the build tools (e.g. aapt) being used, e.g. 23.0.2")
- public FullRevision buildToolsVersion;
-
- @Option(name = "aapt",
- defaultValue = "null",
- converter = ExistingPathConverter.class,
- category = "tool",
- help = "Aapt tool location for resource packaging.")
- public Path aapt;
-
- @Option(name = "annotationJar",
- defaultValue = "null",
- converter = ExistingPathConverter.class,
- category = "tool",
- help = "Annotation Jar for builder invocations.")
- public Path annotationJar;
-
- @Option(name = "adb",
- defaultValue = "null",
- converter = ExistingPathConverter.class,
- category = "tool",
- help = "Path to adb for builder functions.")
- //TODO(bazel-team): Determine if this is completely necessary for running AndroidBuilder.
- public Path adb;
-
- @Option(name = "zipAlign",
- defaultValue = "null",
- converter = ExistingPathConverter.class,
- category = "tool",
- help = "Path to zipAlign for building apks.")
- public Path zipAlign;
-
- @Option(name = "androidJar",
- defaultValue = "null",
- converter = ExistingPathConverter.class,
- category = "tool",
- help = "Path to the android jar for resource packaging and building apks.")
- public Path androidJar;
-
@Option(name = "primaryData",
defaultValue = "null",
converter = UnvalidatedAndroidDataConverter.class,
@@ -175,6 +133,13 @@ public class AndroidResourceProcessingAction {
help = "Path to the write the archive.")
public Path packagePath;
+ @Option(name = "resourcesOutput",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path to the write merged resources archive.")
+ public Path resourcesOutput;
+
@Option(name = "proguardOutput",
defaultValue = "null",
converter = PathConverter.class,
@@ -211,36 +176,6 @@ public class AndroidResourceProcessingAction {
help = "A list densities to filter the resource drawables by.")
public List<String> densities;
- @Option(name = "debug",
- defaultValue = "false",
- category = "config",
- help = "Indicates if it is a debug build.")
- public boolean debug;
-
- @Option(name = "resourceConfigs",
- defaultValue = "",
- converter = CommaSeparatedOptionListConverter.class,
- category = "config",
- help = "A list of resource config filters to pass to aapt.")
- public List<String> resourceConfigs;
-
- @Option(name = "useAaptCruncher",
- defaultValue = "auto",
- category = "config",
- help = "Use the legacy aapt cruncher, defaults to true for non-LIBRARY packageTypes. "
- + " LIBRARY packages do not benefit from the additional processing as the resources"
- + " will need to be reprocessed during the generation of the final apk. See"
- + " https://code.google.com/p/android/issues/detail?id=67525 for a discussion of the"
- + " different png crunching methods.")
- public TriState useAaptCruncher;
-
- @Option(name = "uncompressedExtensions",
- defaultValue = "",
- converter = CommaSeparatedOptionListConverter.class,
- category = "config",
- help = "A list of file extensions not to compress.")
- public List<String> uncompressedExtensions;
-
@Option(name = "packageForR",
defaultValue = "null",
category = "config",
@@ -264,22 +199,19 @@ public class AndroidResourceProcessingAction {
category = "config",
help = "Version code to stamp into the packaged manifest.")
public int versionCode;
-
- @Option(name = "assetsToIgnore",
- defaultValue = "",
- converter = CommaSeparatedOptionListConverter.class,
- category = "config",
- help = "A list of assets extensions to ignore.")
- public List<String> assetsToIgnore;
}
+ private static AaptConfigOptions aaptConfigOptions;
private static Options options;
public static void main(String[] args) {
final Stopwatch timer = Stopwatch.createStarted();
- OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
+ OptionsParser optionsParser = OptionsParser.newOptionsParser(
+ Options.class, AaptConfigOptions.class);
optionsParser.parseAndExitUponError(args);
+ aaptConfigOptions = optionsParser.getOptions(AaptConfigOptions.class);
options = optionsParser.getOptions(Options.class);
+
FileSystem fileSystem = FileSystems.getDefault();
Path working = fileSystem.getPath("").toAbsolutePath();
final AndroidResourceProcessor resourceProcessor = new AndroidResourceProcessor(STD_LOGGER);
@@ -324,7 +256,7 @@ public class AndroidResourceProcessingAction {
mergedResources,
mergedAssets,
modifiers,
- useAaptCruncher() ? new AaptCruncher(options.aapt.toString(),
+ useAaptCruncher() ? new AaptCruncher(aaptConfigOptions.aapt.toString(),
new CommandLineRunner(STD_LOGGER)) : null,
true);
@@ -335,7 +267,7 @@ public class AndroidResourceProcessingAction {
new DensitySpecificManifestProcessor(options.densities, densityManifest));
LOGGER.fine(String.format("Density filtering finished at %sms",
- timer.elapsed(TimeUnit.MILLISECONDS)));
+ timer.elapsed(TimeUnit.MILLISECONDS)));
final MergedAndroidData processedManifestData = resourceProcessor.processManifest(
options.packageType,
@@ -347,19 +279,22 @@ public class AndroidResourceProcessingAction {
processedManifest);
resourceProcessor.processResources(
- options.aapt,
- options.androidJar,
- options.buildToolsVersion,
+ aaptConfigOptions.aapt,
+ aaptConfigOptions.androidJar,
+ aaptConfigOptions.buildToolsVersion,
options.packageType,
- options.debug,
+ aaptConfigOptions.debug,
options.packageForR,
- new FlagAaptOptions(),
- options.resourceConfigs,
+ new FlagAaptOptions(aaptConfigOptions),
+ aaptConfigOptions.resourceConfigs,
processedManifestData,
data,
generatedSources,
options.packagePath,
- options.proguardOutput);
+ options.proguardOutput,
+ options.resourcesOutput != null
+ ? filteredData.getResourceDir().resolve("values").resolve("public.xml")
+ : null);
LOGGER.fine(String.format("appt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
if (options.manifestOutput != null) {
@@ -377,6 +312,9 @@ public class AndroidResourceProcessingAction {
resourceProcessor.copyRToOutput(generatedSources, options.symbolsTxtOut,
VariantConfiguration.Type.LIBRARY == options.packageType);
}
+ if (options.resourcesOutput != null) {
+ createResourcesZip(processedManifestData, options.resourcesOutput);
+ }
LOGGER.fine(String.format("Packaging finished at %sms",
timer.elapsed(TimeUnit.MILLISECONDS)));
} catch (MergingException e) {
@@ -395,39 +333,54 @@ public class AndroidResourceProcessingAction {
private static boolean useAaptCruncher() {
// If the value was set, use that.
- if (options.useAaptCruncher != TriState.AUTO) {
- return options.useAaptCruncher == TriState.YES;
+ if (aaptConfigOptions.useAaptCruncher != TriState.AUTO) {
+ return aaptConfigOptions.useAaptCruncher == TriState.YES;
}
// By default png cruncher shouldn't be invoked on a library -- the work is just thrown away.
return options.packageType != VariantConfiguration.Type.LIBRARY;
}
- private static final class FlagAaptOptions implements AaptOptions {
- @Override
- public boolean getUseAaptPngCruncher() {
- return options.useAaptCruncher != TriState.NO;
- }
-
- @Override
- public Collection<String> getNoCompress() {
- if (!options.uncompressedExtensions.isEmpty()) {
- return options.uncompressedExtensions;
+ private static void createResourcesZip(MergedAndroidData data, Path output) throws IOException {
+ try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output.toFile()))) {
+ Path resourcesRoot = data.getResourceDir();
+ Path assetsRoot = data.getAssetDir();
+ Files.walkFileTree(resourcesRoot, new ZipBuilderVisitor(zout, resourcesRoot, "res"));
+ if (Files.exists(assetsRoot)) {
+ Files.walkFileTree(assetsRoot, new ZipBuilderVisitor(zout, assetsRoot, "assets"));
}
- return ImmutableList.of();
}
+ }
- @Override
- public String getIgnoreAssets() {
- if (!options.assetsToIgnore.isEmpty()) {
- return Joiner.on(":").join(options.assetsToIgnore);
- }
- return null;
+ private static final class ZipBuilderVisitor extends SimpleFileVisitor<Path> {
+ // The earliest date representable in a zip file, 1-1-1980.
+ private static final long ZIP_EPOCH = 315561600000L;
+ private final ZipOutputStream zip;
+ private final Path root;
+ private final String directory;
+
+ private ZipBuilderVisitor(ZipOutputStream zip, Path root, String directory) {
+ this.zip = zip;
+ this.root = root;
+ this.directory = directory;
}
@Override
- public boolean getFailOnMissingConfigEntry() {
- return false;
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ byte[] content = Files.readAllBytes(file);
+
+ CRC32 crc32 = new CRC32();
+ crc32.update(content);
+
+ ZipEntry entry = new ZipEntry(directory + "/" + root.relativize(file));
+ entry.setMethod(ZipEntry.STORED);
+ entry.setTime(ZIP_EPOCH);
+ entry.setSize(content.length);
+ entry.setCrc(crc32.getValue());
+
+ zip.putNextEntry(entry);
+ zip.write(content);
+ zip.closeEntry();
+ return FileVisitResult.CONTINUE;
}
}
}
-
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 7a8c91f32d..e7ee6dfe7c 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
@@ -20,6 +20,12 @@ import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
+import com.google.devtools.build.android.Converters.ExistingPathConverter;
+import com.google.devtools.build.android.Converters.FullRevisionConverter;
+import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionsBase;
+import com.google.devtools.common.options.TriState;
import com.android.annotations.Nullable;
import com.android.builder.core.VariantConfiguration;
@@ -75,6 +81,113 @@ import javax.xml.parsers.ParserConfigurationException;
* Provides a wrapper around the AOSP build tools for resource processing.
*/
public class AndroidResourceProcessor {
+ /**
+ * Options class containing flags for Aapt setup.
+ */
+ public static final class AaptConfigOptions extends OptionsBase {
+ @Option(name = "buildToolsVersion",
+ defaultValue = "null",
+ converter = FullRevisionConverter.class,
+ category = "config",
+ help = "Version of the build tools (e.g. aapt) being used, e.g. 23.0.2")
+ public FullRevision buildToolsVersion;
+
+ @Option(name = "aapt",
+ defaultValue = "null",
+ converter = ExistingPathConverter.class,
+ category = "tool",
+ help = "Aapt tool location for resource packaging.")
+ public Path aapt;
+
+ @Option(name = "annotationJar",
+ defaultValue = "null",
+ converter = ExistingPathConverter.class,
+ category = "tool",
+ help = "Annotation Jar for builder invocations.")
+ public Path annotationJar;
+
+ @Option(name = "androidJar",
+ defaultValue = "null",
+ converter = ExistingPathConverter.class,
+ category = "tool",
+ help = "Path to the android jar for resource packaging and building apks.")
+ public Path androidJar;
+
+ @Option(name = "useAaptCruncher",
+ defaultValue = "auto",
+ category = "config",
+ help = "Use the legacy aapt cruncher, defaults to true for non-LIBRARY packageTypes. "
+ + " LIBRARY packages do not benefit from the additional processing as the resources"
+ + " will need to be reprocessed during the generation of the final apk. See"
+ + " https://code.google.com/p/android/issues/detail?id=67525 for a discussion of the"
+ + " different png crunching methods.")
+ public TriState useAaptCruncher;
+
+ @Option(name = "uncompressedExtensions",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ help = "A list of file extensions not to compress.")
+ public List<String> uncompressedExtensions;
+
+ @Option(name = "assetsToIgnore",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ help = "A list of assets extensions to ignore.")
+ public List<String> assetsToIgnore;
+
+ @Option(name = "debug",
+ defaultValue = "false",
+ category = "config",
+ help = "Indicates if it is a debug build.")
+ public boolean debug;
+
+ @Option(name = "resourceConfigs",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ help = "A list of resource config filters to pass to aapt.")
+ public List<String> resourceConfigs;
+ }
+
+ /**
+ * {@link AaptOptions} backed by an {@link AaptConfigOptions}.
+ */
+ public static final class FlagAaptOptions implements AaptOptions {
+ private final AaptConfigOptions options;
+
+ public FlagAaptOptions(AaptConfigOptions options) {
+ this.options = options;
+ }
+
+ @Override
+ public boolean getUseAaptPngCruncher() {
+ return options.useAaptCruncher != TriState.NO;
+ }
+
+ @Override
+ public Collection<String> getNoCompress() {
+ if (!options.uncompressedExtensions.isEmpty()) {
+ return options.uncompressedExtensions;
+ }
+ return ImmutableList.of();
+ }
+
+ @Override
+ public String getIgnoreAssets() {
+ if (!options.assetsToIgnore.isEmpty()) {
+ return Joiner.on(":").join(options.assetsToIgnore);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean getFailOnMissingConfigEntry() {
+ return false;
+ }
+ }
+
private static final Pattern HEX_REGEX = Pattern.compile("0x[0-9A-Fa-f]{8}");
private final StdLogger stdLogger;
@@ -122,6 +235,7 @@ public class AndroidResourceProcessor {
Files.walkFileTree(generatedSourcesRoot,
new SymbolFileSrcJarBuildingVisitor(zip, generatedSourcesRoot, staticIds));
}
+ // Set to the epoch for caching purposes.
Files.setLastModifiedTime(srcJar, FileTime.fromMillis(0L));
} catch (IOException e) {
Throwables.propagate(e);
@@ -163,7 +277,8 @@ public class AndroidResourceProcessor {
List<DependencyAndroidData> dependencyData,
Path sourceOut,
Path packageOut,
- Path proguardOut)
+ Path proguardOut,
+ Path publicResourcesOut)
throws IOException, InterruptedException, LoggedErrorException {
List<SymbolFileProvider> libraries = new ArrayList<>();
List<String> packages = new ArrayList<>();
@@ -201,6 +316,7 @@ public class AndroidResourceProcessor {
.maybeAdd("--output-text-symbols", prepareOutputPath(sourceOut), sourceOut != null)
.add("-F", packageOut)
.add("-G", proguardOut)
+ .add("-P", publicResourcesOut)
.maybeAdd("--debug-mode", debug)
.add("--custom-package", customPackageForR)
// If it is a library, do not generate final java ids.
@@ -231,6 +347,9 @@ public class AndroidResourceProcessor {
if (packageOut != null) {
Files.setLastModifiedTime(packageOut, FileTime.fromMillis(0L));
}
+ if (publicResourcesOut != null) {
+ Files.setLastModifiedTime(publicResourcesOut, FileTime.fromMillis(0L));
+ }
}
private void writeDependencyPackageRs(VariantConfiguration.Type variantType,