From 3bee55145b72191d4d6423c5cdef6c728cb52dbd Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 3 Jun 2016 19:42:27 +0000 Subject: Enable the AndroidResourceProcessingAction to accept splits. These splits will be passed through to aapt, which will create additional resource packages (and reduce the set found in the base ap_) as a result. -- MOS_MIGRATED_REVID=123995947 --- .../devtools/build/android/AaptCommandBuilder.java | 219 ++++++++++++++++----- .../android/AndroidResourceProcessingAction.java | 3 +- .../build/android/AndroidResourceProcessor.java | 46 +++-- .../build/android/ResourceShrinkerAction.java | 1 + 4 files changed, 203 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java index d1f5a54dcb..b3c6747640 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java +++ b/src/tools/android/java/com/google/devtools/build/android/AaptCommandBuilder.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.android; +import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -20,33 +21,53 @@ import com.android.builder.core.VariantConfiguration.Type; import com.android.sdklib.repository.FullRevision; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; +/** + * Builder for AAPT command lines, with support for making flags conditional on build tools version + * and variant type. + */ class AaptCommandBuilder { - private final Path aapt; - private final String command; - private final List flags = new ArrayList<>(); - private final FullRevision buildToolsVersion; - private final Type variantType; - - AaptCommandBuilder( - Path aapt, @Nullable FullRevision buildToolsVersion, Type variantType, String command) { - this.aapt = aapt; + private final ImmutableList.Builder flags = new ImmutableList.Builder<>(); + private FullRevision buildToolsVersion; + private Type variantType; + + public AaptCommandBuilder(Path aapt) { + flags.add(aapt.toString()); + } + + /** Sets the build tools version to be used for {@link #whenVersionIsAtLeast}. */ + AaptCommandBuilder forBuildToolsVersion(@Nullable FullRevision buildToolsVersion) { + Preconditions.checkState( + this.buildToolsVersion == null, "A build tools version was already specified."); this.buildToolsVersion = buildToolsVersion; + return this; + } + + /** Sets the variant type to be used for {@link #whenVariantIs}. */ + AaptCommandBuilder forVariantType(Type variantType) { + Preconditions.checkNotNull(variantType); + Preconditions.checkState(this.variantType == null, "A variant type was already specified."); this.variantType = variantType; - this.command = command; + return this; } - AaptCommandBuilder add(String flag) { + /** Adds a single flag to the builder. */ + public AaptCommandBuilder add(String flag) { flags.add(flag); return this; } - AaptCommandBuilder add(String flag, @Nullable String value) { + /** + * Adds a flag to the builder, along with a string value. The two will be added as different words + * in the final command line. If the value is {@code null}, neither the flag nor the value will + * be added. + */ + public AaptCommandBuilder add(String flag, @Nullable String value) { + Preconditions.checkNotNull(flag); if (!Strings.isNullOrEmpty(value)) { flags.add(flag); flags.add(value); @@ -54,70 +75,168 @@ class AaptCommandBuilder { return this; } - AaptCommandBuilder add(String flag, @Nullable Path path) { + /** + * Adds a flag to the builder, along with a path value. The path will be converted to a string + * using {@code toString}, then the flag and the path will be added to the final command line as + * different words. If the value is {@code null}, neither the flag nor the path will be added. + * + * @see #add(String,String) + */ + public AaptCommandBuilder add(String flag, @Nullable Path path) { + Preconditions.checkNotNull(flag); if (path != null) { add(flag, path.toString()); } return this; } - AaptCommandBuilder addRepeated(String flag, Collection values) { + /** + * Adds a flag to the builder multiple times, once for each value in the given collection. + * {@code null} values will be skipped. If the collection is empty, nothing will be added. + * The values will be added in the source collection's iteration order. + * + *

ex. If {@code flag} is {@code "-0"} and {@code values} contains the values + * {@code "png"}, {@code null}, and {@code "gif"}, then four words will be added to the final + * command line: {@code "-0", "png", "-0", "gif"}. + */ + public AaptCommandBuilder addRepeated(String flag, Collection values) { + Preconditions.checkNotNull(flag); for (String value : values) { add(flag, value); } return this; } - - AaptCommandBuilder maybeAdd(String flag, boolean condition) { + /** Adds the next flag to the builder only if the condition is true. */ + public ConditionalAaptCommandBuilder when(boolean condition) { if (condition) { - add(flag); + return new SuccessfulConditionCommandBuilder(this); + } else { + return new FailedConditionCommandBuilder(this); } - return this; } - AaptCommandBuilder maybeAdd(String flag, Path directory, boolean condition) { - if (condition) { - add(flag, directory); - } - return this; + /** Adds the next flag to the builder only if the variant type is the passed-in type. */ + public ConditionalAaptCommandBuilder whenVariantIs(Type type) { + Preconditions.checkNotNull(type); + return when(variantType == type); } - AaptCommandBuilder maybeAdd(String flag, @Nullable Path path, FullRevision requiredVersion) { - if (buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0) { - add(flag, path); - } - return this; + /** + * Adds the next flag to the builder only if the build tools version is unspecified or is + * greater than or equal to the given version. + */ + public ConditionalAaptCommandBuilder whenVersionIsAtLeast(FullRevision requiredVersion) { + Preconditions.checkNotNull(requiredVersion); + return when(buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0); } - AaptCommandBuilder maybeAdd(String flag, FullRevision requiredVersion) { - if (buildToolsVersion == null || buildToolsVersion.compareTo(requiredVersion) >= 0) { - add(flag); - } - return this; + /** Assembles the full command line as a list. */ + public List build() { + return flags.build(); } - AaptCommandBuilder maybeAdd(String flag, Type variant) { - if (variantType == variant) { - add(flag); - } - return this; + /** + * Wrapper for potentially adding flags to an AaptCommandBuilder based on a conditional. + */ + public interface ConditionalAaptCommandBuilder { + /** + * Adds a single flag to the builder if the condition was true. + * + * @see AaptCommandBuilder#add(String) + */ + AaptCommandBuilder thenAdd(String flag); + + /** + * Adds a single flag and associated string value to the builder if the value is non-null and + * the condition was true. + * + * @see AaptCommandBuilder#add(String,String) + */ + AaptCommandBuilder thenAdd(String flag, @Nullable String value); + + /** + * Adds a single flag and associated path value to the builder if the value is non-null and the + * condition was true. + * + * @see AaptCommandBuilder#add(String,Path) + */ + AaptCommandBuilder thenAdd(String flag, @Nullable Path value); + + /** + * Adds the values in the collection to the builder, each preceded by the given flag, + * if the collection was non-empty and the condition was true. + * + * @see AaptCommandBuilder#addRepeated(String,Collection) + */ + AaptCommandBuilder thenAddRepeated(String flag, Collection values); } - AaptCommandBuilder maybeAdd(String flag, @Nullable String value, Type variant) { - if (variantType == variant) { - add(flag, value); + /** + * Forwarding implementation of ConditionalAaptCommandBuilder returned when a condition is true. + */ + private static class SuccessfulConditionCommandBuilder implements ConditionalAaptCommandBuilder { + private final AaptCommandBuilder originalCommandBuilder; + + public SuccessfulConditionCommandBuilder(AaptCommandBuilder originalCommandBuilder) { + this.originalCommandBuilder = originalCommandBuilder; + } + + @Override + public AaptCommandBuilder thenAdd(String flag) { + return originalCommandBuilder.add(flag); + } + + @Override + public AaptCommandBuilder thenAdd(String flag, @Nullable String value) { + return originalCommandBuilder.add(flag, value); + } + + @Override + public AaptCommandBuilder thenAdd(String flag, @Nullable Path value) { + return originalCommandBuilder.add(flag, value); + } + + @Override + public AaptCommandBuilder thenAddRepeated(String flag, Collection values) { + return originalCommandBuilder.addRepeated(flag, values); } - return this; } - List build() { - return ImmutableList - .builder() - .add(aapt.toString()) - .add(command) - .addAll(flags) - .build(); + /** + * Null implementation of ConditionalAaptCommandBuilder returned when a condition is false. + */ + private static class FailedConditionCommandBuilder implements ConditionalAaptCommandBuilder { + private final AaptCommandBuilder originalCommandBuilder; + + public FailedConditionCommandBuilder(AaptCommandBuilder originalCommandBuilder) { + this.originalCommandBuilder = originalCommandBuilder; + } + + @Override + public AaptCommandBuilder thenAdd(String flag) { + Preconditions.checkNotNull(flag); + return originalCommandBuilder; + } + + @Override + public AaptCommandBuilder thenAdd(String flag, @Nullable String value) { + Preconditions.checkNotNull(flag); + return originalCommandBuilder; + } + + @Override + public AaptCommandBuilder thenAdd(String flag, @Nullable Path value) { + Preconditions.checkNotNull(flag); + return originalCommandBuilder; + } + + @Override + public AaptCommandBuilder thenAddRepeated(String flag, Collection values) { + Preconditions.checkNotNull(flag); + Preconditions.checkNotNull(values); + return originalCommandBuilder; + } } } 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 29112c97ca..373f1612e1 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 @@ -174,7 +174,7 @@ public class AndroidResourceProcessingAction { defaultValue = "", converter = CommaSeparatedOptionListConverter.class, category = "config", - help = "A list densities to filter the resource drawables by.") + help = "A list of densities to filter the resource drawables by.") public List densities; @Option(name = "packageForR", @@ -287,6 +287,7 @@ public class AndroidResourceProcessingAction { options.packageForR, new FlagAaptOptions(aaptConfigOptions), aaptConfigOptions.resourceConfigs, + aaptConfigOptions.splits, processedManifestData, data, generatedSources, 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 03709b7234..33aa697b8e 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 @@ -25,6 +25,7 @@ import com.google.common.collect.Maps; 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.ColonSeparatedOptionListConverter; import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; @@ -169,6 +170,14 @@ public class AndroidResourceProcessor { category = "config", help = "A list of resource config filters to pass to aapt.") public List resourceConfigs; + + @Option(name = "splits", + defaultValue = "", + converter = ColonSeparatedOptionListConverter.class, + category = "config", + help = "A list of splits to pass to aapt, separated by colons." + + " Each split is a list of qualifiers separated by commas.") + public List splits; } /** @@ -326,6 +335,7 @@ public class AndroidResourceProcessor { String customPackageForR, AaptOptions aaptOptions, Collection resourceConfigs, + Collection splits, MergedAndroidData primaryData, List dependencyData, Path sourceOut, @@ -348,43 +358,49 @@ public class AndroidResourceProcessor { } AaptCommandBuilder commandBuilder = - new AaptCommandBuilder(aapt, buildToolsVersion, variantType, "package") - // If the logger is verbose, set aapt to be verbose - .maybeAdd("-v", stdLogger.getLevel() == StdLogger.Level.VERBOSE) + new AaptCommandBuilder(aapt) + .forBuildToolsVersion(buildToolsVersion) + .forVariantType(variantType) + // first argument is the command to be executed, "package" + .add("package") + // If the logger is verbose, set aapt to be verbose + .when(stdLogger.getLevel() == StdLogger.Level.VERBOSE).thenAdd("-v") // Overwrite existing files, if they exist. .add("-f") // Resources are precrunched in the merge process. .add("--no-crunch") // Do not automatically generate versioned copies of vector XML resources. - .maybeAdd("--no-version-vectors", new FullRevision(23)) + .whenVersionIsAtLeast(new FullRevision(23)).thenAdd("--no-version-vectors") // Add the android.jar as a base input. .add("-I", androidJar) // Add the manifest for validation. .add("-M", androidManifest.toAbsolutePath()) // Maybe add the resources if they exist - .maybeAdd("-S", resourceDir, Files.isDirectory(resourceDir)) + .when(Files.isDirectory(resourceDir)).thenAdd("-S", resourceDir) // Maybe add the assets if they exist - .maybeAdd("-A", assetsDir, Files.isDirectory(assetsDir)) + .when(Files.isDirectory(assetsDir)).thenAdd("-A", assetsDir) // Outputs - .maybeAdd("-m", sourceOut != null) - .maybeAdd("-J", prepareOutputPath(sourceOut), sourceOut != null) - .maybeAdd("--output-text-symbols", prepareOutputPath(sourceOut), sourceOut != null) - .maybeAdd("-F", packageOut, packageOut != null) + .when(sourceOut != null).thenAdd("-m") + .add("-J", prepareOutputPath(sourceOut)) + .add("--output-text-symbols", prepareOutputPath(sourceOut)) + .add("-F", packageOut) .add("-G", proguardOut) - .maybeAdd("-D", mainDexProguardOut, new FullRevision(24)) + .whenVersionIsAtLeast(new FullRevision(24)).thenAdd("-D", mainDexProguardOut) .add("-P", publicResourcesOut) - .maybeAdd("--debug-mode", debug) + .when(debug).thenAdd("--debug-mode") .add("--custom-package", customPackageForR) // If it is a library, do not generate final java ids. - .maybeAdd("--non-constant-id", VariantConfiguration.Type.LIBRARY) + .whenVariantIs(VariantConfiguration.Type.LIBRARY).thenAdd("--non-constant-id") .add("--ignore-assets", aaptOptions.getIgnoreAssets()) - .maybeAdd("--error-on-missing-config-entry", aaptOptions.getFailOnMissingConfigEntry()) + .when(aaptOptions.getFailOnMissingConfigEntry()).thenAdd("--error-on-missing-config-entry") // Never compress apks. .add("-0", "apk") // Add custom no-compress extensions. .addRepeated("-0", aaptOptions.getNoCompress()) // Filter by resource configuration type. - .add("-c", Joiner.on(',').join(resourceConfigs)); + .add("-c", Joiner.on(',').join(resourceConfigs)) + // Split APKs if any splits were specified. + .whenVersionIsAtLeast(new FullRevision(23)).thenAddRepeated("--split", splits); new CommandLineRunner(stdLogger).runCmdLine(commandBuilder.build(), null); diff --git a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java index 86ae74d81d..69317a135b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction.java @@ -211,6 +211,7 @@ public class ResourceShrinkerAction { null /* packageForR */, new FlagAaptOptions(aaptConfigOptions), aaptConfigOptions.resourceConfigs, + ImmutableList.of() /* splits */, new MergedAndroidData( shrunkResources, resourceFiles.resolve("assets"), options.primaryManifest), ImmutableList.of() /* libraries */, -- cgit v1.2.3