Skip to content

Instantly share code, notes, and snippets.

@benjaminRomano
Created November 22, 2019 19:54
Show Gist options
  • Save benjaminRomano/2df1a1c2bd47ae55317066f44364a4c6 to your computer and use it in GitHub Desktop.
Save benjaminRomano/2df1a1c2bd47ae55317066f44364a4c6 to your computer and use it in GitHub Desktop.
Getting App Size per module using Bundletool
diff --git a/src/main/java/com/android/tools/build/bundletool/commands/GetSizeCommand.java b/src/main/java/com/android/tools/build/bundletool/commands/GetSizeCommand.java
index 80ca234..438bb5b 100755
--- a/src/main/java/com/android/tools/build/bundletool/commands/GetSizeCommand.java
+++ b/src/main/java/com/android/tools/build/bundletool/commands/GetSizeCommand.java
@@ -96,6 +96,7 @@ public abstract class GetSizeCommand implements GetSizeRequest {
private static final Flag<Path> DEVICE_SPEC_FLAG = Flag.path("device-spec");
private static final Flag<ImmutableSet<String>> MODULES_FLAG = Flag.stringSet("modules");
private static final Flag<Boolean> INSTANT_FLAG = Flag.booleanFlag("instant");
+ private static final Flag<Boolean> EXACT_FLAG = Flag.booleanFlag("exact");
private static final Flag<ImmutableSet<Dimension>> DIMENSIONS_FLAG =
Flag.enumSet("dimensions", Dimension.class);
private static final Joiner COMMA_JOINER = Joiner.on(',');
@@ -121,10 +122,15 @@ public abstract class GetSizeCommand implements GetSizeRequest {
@Override
public abstract boolean getInstant();
+ /** Gets whether only explicit modules should used for size calculation */
+ @Override
+ public abstract boolean getExact();
+
public static Builder builder() {
return new AutoValue_GetSizeCommand.Builder()
.setDeviceSpec(DeviceSpec.getDefaultInstance())
.setInstant(false)
+ .setExact(false)
.setDimensions(ImmutableSet.of());
}
@@ -151,6 +157,14 @@ public abstract class GetSizeCommand implements GetSizeRequest {
*/
public abstract Builder setInstant(boolean instant);
+ /**
+ * Sets whether to only use modules explicitly specified in app size calculation
+ *
+ * <p>The default is {@code false}. If this is set to {@code true}, only the modules
+ * specified by modules flag be used in size calculation
+ */
+ public abstract Builder setExact(boolean exact);
+
/** Sets the sub-command of the get-size command, e.g. total. */
public abstract Builder setGetSizeSubCommand(GetSizeSubcommand getSizeSubcommand);
@@ -162,6 +176,7 @@ public abstract class GetSizeCommand implements GetSizeRequest {
Optional<Path> deviceSpecPath = DEVICE_SPEC_FLAG.getValue(flags);
Optional<ImmutableSet<String>> modules = MODULES_FLAG.getValue(flags);
Optional<Boolean> instant = INSTANT_FLAG.getValue(flags);
+ Optional<Boolean> exact = EXACT_FLAG.getValue(flags);
ImmutableSet<Dimension> dimensions = DIMENSIONS_FLAG.getValue(flags).orElse(ImmutableSet.of());
flags.checkNoUnknownFlags();
@@ -183,6 +198,8 @@ public abstract class GetSizeCommand implements GetSizeRequest {
instant.ifPresent(command::setInstant);
+ exact.ifPresent(command::setExact);
+
if (dimensions.contains(Dimension.ALL)) {
dimensions = SUPPORTED_DIMENSIONS;
}
@@ -305,6 +322,14 @@ public abstract class GetSizeCommand implements GetSizeRequest {
"When set, APKs of the instant modules will be considered instead of the "
+ "installable APKs. Defaults to false.")
.build())
+ .addFlag(
+ FlagDescription.builder()
+ .setFlagName(EXACT_FLAG.getName())
+ .setOptional(true)
+ .setDescription(
+ "When set, only APKs with modules explicitly defined will" +
+ "be considered for size calculation. Defaults to false.")
+ .build())
.build();
}
diff --git a/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java b/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java
index aa7c58a..afc83f8 100755
--- a/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java
+++ b/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java
@@ -54,8 +54,21 @@ public class ApkMatcher {
private final ModuleMatcher moduleMatcher;
private final VariantMatcher variantMatcher;
+ /**
+ * Only include modules explicitly specified by requestedModuleNames
+ */
+ private final boolean exact;
+
public ApkMatcher(DeviceSpec deviceSpec) {
- this(deviceSpec, Optional.empty(), /* matchInstant= */ false);
+ this(deviceSpec, Optional.empty(), /* matchInstant= */ false, false);
+ }
+
+ public ApkMatcher(
+ DeviceSpec deviceSpec,
+ Optional<ImmutableSet<String>> requestedModuleNames,
+ boolean matchInstant
+ ) {
+ this(deviceSpec, requestedModuleNames, matchInstant, false);
}
/**
@@ -65,10 +78,14 @@ public class ApkMatcher {
public ApkMatcher(
DeviceSpec deviceSpec,
Optional<ImmutableSet<String>> requestedModuleNames,
- boolean matchInstant) {
+ boolean matchInstant,
+ boolean exact) {
checkArgument(
!requestedModuleNames.isPresent() || !requestedModuleNames.get().isEmpty(),
"Set of requested split modules cannot be empty.");
+ checkArgument(
+ !exact || requestedModuleNames.isPresent(),
+ "Exact mode requires providing one or more module names");
SdkVersionMatcher sdkVersionMatcher = new SdkVersionMatcher(deviceSpec);
AbiMatcher abiMatcher = new AbiMatcher(deviceSpec);
MultiAbiMatcher multiAbiMatcher = new MultiAbiMatcher(deviceSpec);
@@ -81,6 +98,7 @@ public class ApkMatcher {
sdkVersionMatcher, abiMatcher, multiAbiMatcher, screenDensityMatcher, languageMatcher);
this.requestedModuleNames = requestedModuleNames;
this.matchInstant = matchInstant;
+ this.exact = exact;
this.moduleMatcher = new ModuleMatcher(sdkVersionMatcher, deviceFeatureMatcher);
this.variantMatcher =
new VariantMatcher(
@@ -137,7 +155,9 @@ public class ApkMatcher {
addModuleDependencies(requestedModuleName, moduleDependenciesMap, dependencyModules);
}
- if (matchInstant) {
+ if (exact) {
+ return requestedModuleNames.get()::contains;
+ } else if (matchInstant) {
return dependencyModules::contains;
} else {
return Predicates.or(
diff --git a/src/main/java/com/android/tools/build/bundletool/device/VariantTotalSizeAggregator.java b/src/main/java/com/android/tools/build/bundletool/device/VariantTotalSizeAggregator.java
index a68dfb4..c6d5df6 100755
--- a/src/main/java/com/android/tools/build/bundletool/device/VariantTotalSizeAggregator.java
+++ b/src/main/java/com/android/tools/build/bundletool/device/VariantTotalSizeAggregator.java
@@ -224,7 +224,8 @@ public class VariantTotalSizeAggregator {
screenDensityTargeting,
languageTargeting),
getSizeRequest.getModules(),
- getSizeRequest.getInstant())
+ getSizeRequest.getInstant(),
+ getSizeRequest.getExact())
.getMatchingApksFromVariant(variant, bundleVersion));
minSizeByConfiguration.merge(configuration, compressedSize, Math::min);
diff --git a/src/main/java/com/android/tools/build/bundletool/model/GetSizeRequest.java b/src/main/java/com/android/tools/build/bundletool/model/GetSizeRequest.java
index 7567ce4..fc7a493 100755
--- a/src/main/java/com/android/tools/build/bundletool/model/GetSizeRequest.java
+++ b/src/main/java/com/android/tools/build/bundletool/model/GetSizeRequest.java
@@ -40,4 +40,7 @@ public interface GetSizeRequest {
/** Gets whether instant APKs should be used for size calculation. */
boolean getInstant();
+
+ /** Gets whether only explicit modules should used for size calculation */
+ boolean getExact();
}
@benjaminRomano
Copy link
Author

benjaminRomano commented Nov 22, 2019

With this patch, it is now possible to request app size for specific module(s) using the --exact flag:
bundletool get-size total --apks=output.apks --modules=my_feature --exact --dimensions=ALL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment