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
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 d1f5a54..b3c6747 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.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<String> flags = new ArrayList<>();
- private final FullRevision buildToolsVersion;
- private final Type variantType;
+ private final ImmutableList.Builder<String> flags = new ImmutableList.Builder<>();
+ private FullRevision buildToolsVersion;
+ private Type variantType;
- AaptCommandBuilder(
- Path aapt, @Nullable FullRevision buildToolsVersion, Type variantType, String command) {
- this.aapt = aapt;
- this.buildToolsVersion = buildToolsVersion;
- this.variantType = variantType;
- this.command = command;
+ public AaptCommandBuilder(Path aapt) {
+ flags.add(aapt.toString());
}
- AaptCommandBuilder add(String flag) {
+ /** 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;
+ return this;
+ }
+
+ /** 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 @@
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<String> 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.
+ *
+ * <p>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<String> 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<String> 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<String>)
+ */
+ AaptCommandBuilder thenAddRepeated(String flag, Collection<String> 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;
}
- return this;
+
+ @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<String> values) {
+ return originalCommandBuilder.addRepeated(flag, values);
+ }
}
- List<String> build() {
- return ImmutableList
- .<String>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<String> 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 29112c9..373f161 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 @@
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<String> densities;
@Option(name = "packageForR",
@@ -287,6 +287,7 @@
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 03709b7..33aa697 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.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 @@
category = "config",
help = "A list of resource config filters to pass to aapt.")
public List<String> 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<String> splits;
}
/**
@@ -326,6 +335,7 @@
String customPackageForR,
AaptOptions aaptOptions,
Collection<String> resourceConfigs,
+ Collection<String> splits,
MergedAndroidData primaryData,
List<DependencyAndroidData> dependencyData,
Path sourceOut,
@@ -348,43 +358,49 @@
}
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 86ae74d..69317a1 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 @@
null /* packageForR */,
new FlagAaptOptions(aaptConfigOptions),
aaptConfigOptions.resourceConfigs,
+ ImmutableList.<String>of() /* splits */,
new MergedAndroidData(
shrunkResources, resourceFiles.resolve("assets"), options.primaryManifest),
ImmutableList.<DependencyAndroidData>of() /* libraries */,