aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
diff options
context:
space:
mode:
authorGravatar Andrew Pellegrini <apell@google.com>2015-06-25 17:12:49 +0000
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-06-26 15:29:53 +0000
commitd13716a201b2dcfe952e843ffcc566056519aaa5 (patch)
tree8f6fd28465854ed94d5915e73a746a2fa651f70d /src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
parent643063d582dcf346f276680288b11f958f5c551d (diff)
Open source AarGeneratorAction and AndroidResourceProcessingAction.
-- MOS_MIGRATED_REVID=96883818
Diffstat (limited to 'src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java393
1 files changed, 393 insertions, 0 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
new file mode 100644
index 0000000000..7d52289ed4
--- /dev/null
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -0,0 +1,393 @@
+// Copyright 2015 Google Inc. 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 com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.hash.Hashing;
+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;
+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.OptionsParser;
+import com.google.devtools.common.options.TriState;
+
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.model.AaptOptions;
+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.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+
+/**
+ * Provides an entry point for the resource processing using the AOSP build tools.
+ *
+ * <pre>
+ * Example Usage:
+ * java/com/google/build/android/AndroidResourceProcessingAction\
+ * --sdkRoot path/to/sdk\
+ * --aapt path/to/sdk/aapt\
+ * --annotationJar path/to/sdk/annotationJar\
+ * --adb path/to/sdk/adb\
+ * --zipAlign path/to/sdk/zipAlign\
+ * --androidJar path/to/sdk/androidJar\
+ * --manifest path/to/manifest\
+ * --primaryData path/to/resources:path/to/assets:path/to/manifest:path/to/R.txt
+ * --data p/t/res1:p/t/assets1:p/t/1/AndroidManifest.xml:p/t/1/R.txt,\
+ * p/t/res2:p/t/assets2:p/t/2/AndroidManifest.xml:p/t/2/R.txt
+ * --generatedSourcePath path/to/write/generated/sources
+ * --packagePath path/to/write/archive.ap_
+ * --srcJarOutput path/to/write/archive.srcjar
+ * </pre>
+ */
+public class AndroidResourceProcessingAction {
+
+ private static final StdLogger STD_LOGGER =
+ new StdLogger(com.android.utils.StdLogger.Level.WARNING);
+
+ private static final Logger LOGGER =
+ Logger.getLogger(AndroidResourceProcessingAction.class.getName());
+
+ /** Flag specifications for this action. */
+ public static final class Options extends OptionsBase {
+ @Option(name = "apiVersion",
+ defaultValue = "21.0.0",
+ converter = FullRevisionConverter.class,
+ category = "config",
+ help = "ApiVersion indicates the version passed to the AndroidBuilder. ApiVersion must be"
+ + " > 19.10 when defined.")
+ // TODO(bazel-team): Determine what the API version changes in AndroidBuilder.
+ public FullRevision apiVersion;
+
+ @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,
+ category = "input",
+ help = "The directory containing the primary resource directory. The contents will override"
+ + " the contents of any other resource directories during merging. The expected format"
+ + " is resources[|resources]:assets[|assets]:manifest")
+ public UnvalidatedAndroidData primaryData;
+
+ @Option(name = "data",
+ defaultValue = "",
+ converter = DependencyAndroidDataListConverter.class,
+ category = "input",
+ help = "Additional Data dependencies. These values will be used if not defined in the "
+ + "primary resources. The expected format is "
+ + "resources[#resources]:assets[#assets]:manifest:r.txt:symbols.txt"
+ + "[,resources[#resources]:assets[#assets]:manifest:r.txt:symbols.txt]")
+ public List<DependencyAndroidData> data;
+
+ @Option(name = "generatedSourcePath",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path for generated sources.")
+ public Path generatedSourcePath;
+
+ @Option(name = "rOutput",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path to where the R.txt should be written.")
+ public Path rOutput;
+
+ @Option(name = "symbolsTxtOut",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path to where the symbolsTxt should be written.")
+ public Path symbolsTxtOut;
+
+ @Option(name = "packagePath",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path to the write the archive.")
+ public Path packagePath;
+
+ @Option(name = "proguardOutput",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path for the proguard file.")
+ public Path proguardOutput;
+
+ @Option(name = "srcJarOutput",
+ defaultValue = "null",
+ converter = PathConverter.class,
+ category = "output",
+ help = "Path for the generated java source jar.")
+ public Path srcJarOutput;
+
+ @Option(name = "packageType",
+ defaultValue = "DEFAULT",
+ converter = VariantConfigurationTypeConverter.class,
+ category = "config",
+ help = "Variant configuration type for packaging the resources."
+ + " Acceptible values DEFAULT, LIBRARY, TEST")
+ public VariantConfiguration.Type packageType;
+
+ @Option(name = "densities",
+ defaultValue = "",
+ converter = CommaSeparatedOptionListConverter.class,
+ category = "config",
+ 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",
+ help = "Custom java package to generate the R symbols files.")
+ public String packageForR;
+
+ @Option(name = "applicationId",
+ defaultValue = "null",
+ category = "config",
+ help = "Custom application id (package manifest) for the packaged manifest.")
+ public String applicationId;
+
+ @Option(name = "versionName",
+ defaultValue = "null",
+ category = "config",
+ help = "Version name to stamp into the packaged manifest.")
+ public String versionName;
+
+ @Option(name = "versionCode",
+ defaultValue = "-1",
+ 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 Options options;
+
+ public static void main(String[] args) {
+ final Stopwatch timer = Stopwatch.createStarted();
+ OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
+ optionsParser.parseAndExitUponError(args);
+ options = optionsParser.getOptions(Options.class);
+ FileSystem fileSystem = FileSystems.getDefault();
+ Path working = fileSystem.getPath("").toAbsolutePath();
+ Path mergedAssets = working.resolve("merged_assets");
+ Path mergedResources = working.resolve("merged_resources");
+
+ final AndroidResourceProcessor resourceProcessor =
+ new AndroidResourceProcessor(STD_LOGGER);
+
+ final AndroidSdkTools sdkTools = new AndroidSdkTools(options.apiVersion,
+ options.aapt,
+ options.annotationJar,
+ options.adb,
+ options.zipAlign,
+ options.androidJar,
+ STD_LOGGER);
+ try {
+ LOGGER.fine(String.format("Setup finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ final ImmutableList<DirectoryModifier> modifiers = ImmutableList.of(
+ new PackedResourceTarExpander(working.resolve("expanded"), working),
+ new FileDeDuplicator(Hashing.murmur3_128(), working.resolve("deduplicated"), working));
+
+ final AndroidBuilder builder = sdkTools.createAndroidBuilder();
+
+ final MergedAndroidData mergedData = resourceProcessor.mergeData(
+ options.primaryData,
+ options.data,
+ mergedResources,
+ mergedAssets,
+ modifiers,
+ useAaptCruncher() ? builder.getAaptCruncher() : null,
+ true);
+
+ LOGGER.info(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ final Path filteredResources = fileSystem.getPath("resources-filtered");
+ final Path densityManifest = fileSystem.getPath("manifest-filtered/AndroidManifest.xml");
+ final DensityFilteredAndroidData filteredData = mergedData.filter(
+ new DensitySpecificResourceFilter(options.densities, filteredResources, working),
+ new DensitySpecificManifestProcessor(options.densities, densityManifest));
+ LOGGER.info(
+ String.format("Density filtering finished at %sms",
+ timer.elapsed(TimeUnit.MILLISECONDS)));
+ resourceProcessor.processResources(
+ builder,
+ options.packageType,
+ options.debug,
+ options.packageForR,
+ new FlagAaptOptions(),
+ options.resourceConfigs,
+ options.applicationId,
+ options.versionCode,
+ options.versionName,
+ filteredData,
+ options.data,
+ working.resolve("manifest"),
+ options.generatedSourcePath,
+ options.packagePath,
+ options.proguardOutput);
+ LOGGER.fine(String.format("appt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ if (options.srcJarOutput != null) {
+ resourceProcessor.createSrcJar(options.generatedSourcePath, options.srcJarOutput);
+ }
+ if (options.rOutput != null) {
+ resourceProcessor.copyRToOutput(options.generatedSourcePath, options.rOutput);
+ }
+ if (options.symbolsTxtOut != null) {
+ resourceProcessor.copyRToOutput(options.generatedSourcePath, options.symbolsTxtOut);
+ }
+ LOGGER.fine(String.format("Packaging finished at %sms",
+ timer.elapsed(TimeUnit.MILLISECONDS)));
+ } catch (MergingException e) {
+ LOGGER.log(java.util.logging.Level.SEVERE, "Error during merging resources", e);
+ System.exit(1);
+ } catch (IOException | InterruptedException | LoggedErrorException e) {
+ LOGGER.log(java.util.logging.Level.SEVERE, "Error during processing resources", e);
+ System.exit(2);
+ } catch (Exception e) {
+ LOGGER.log(java.util.logging.Level.SEVERE, "Unexpected", e);
+ System.exit(3);
+ }
+ LOGGER.info(String.format("Resources processed in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ // AOSP code can leave dangling threads.
+ System.exit(0);
+ }
+
+ private static boolean useAaptCruncher() {
+ // If the value was set, use that.
+ if (options.useAaptCruncher != TriState.AUTO) {
+ return options.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;
+ }
+ return null;
+ }
+
+ @Override
+ public String getIgnoreAssets() {
+ if (!options.assetsToIgnore.isEmpty()) {
+ return Joiner.on(":").join(options.assetsToIgnore);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean getFailOnMissingConfigEntry() {
+ return false;
+ }
+ }
+}
+