提交 b6d70365 编写于 作者: P Pierre Lecesne

Prepare for release 0.4.0.

上级 8feb7e3e
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**Bundletool version(s) affected**
Version: [e.g. 0.3.3]
**Stacktrace**
Copy all of the output of the command, including the stacktrace if visible.
**To Reproduce**
Steps to reproduce the behavior.
Include the full command being run as well as, if possible, artifacts the bug can be reproduced with.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Known workaround**
If you have found a workaround, please specify what it is.
**Environment:**
OS: [e.g. iOS 10.3.3]
**Additional context**
Add any other context about the problem here.
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**Bundletool version(s) affected**
Version: [e.g. 0.3.3]
**Stacktrace**
Copy all of the output of the command, including the stacktrace if visible.
**To Reproduce**
Steps to reproduce the behavior.
Include the full command being run as well as, if possible, artifacts the bug can be reproduced with.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Known workaround**
If you have found a workaround, please specify what it is.
**Environment:**
OS: [e.g. iOS 10.3.3]
**Additional context**
Add any other context about the problem here.
---
name: Custom issue
about: Give some feedback that's not a bug or a feature request
---
---
name: Custom issue
about: Give some feedback that's not a bug or a feature request
---
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. It's very inconvenient to have to do [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. It's very inconvenient to have to do [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
......@@ -24,5 +24,5 @@ Read more about the App Bundle format and Bundletool's usage at
## Releases
Latest release: [0.3.3](https://github.com/google/bundletool/releases)
Latest release: [0.4.0](https://github.com/google/bundletool/releases)
release_version = 0.3.3
release_version = 0.4.0
......@@ -61,6 +61,8 @@ import com.android.tools.build.bundletool.utils.flags.Flag;
import com.android.tools.build.bundletool.utils.flags.Flag.Password;
import com.android.tools.build.bundletool.utils.flags.ParsedFlags;
import com.android.tools.build.bundletool.validation.AppBundleValidator;
import com.android.tools.build.bundletool.version.BundleToolVersion;
import com.android.tools.build.bundletool.version.Version;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
......@@ -88,8 +90,8 @@ public abstract class BuildApksCommand {
private static final Flag<Path> BUNDLE_LOCATION_FLAG = Flag.path("bundle");
private static final Flag<Path> OUTPUT_FILE_FLAG = Flag.path("output");
private static final Flag<ImmutableList<OptimizationDimension>> OPTIMIZE_FOR_FLAG =
Flag.enumList("optimize-for", OptimizationDimension.class);
private static final Flag<ImmutableSet<OptimizationDimension>> OPTIMIZE_FOR_FLAG =
Flag.enumSet("optimize-for", OptimizationDimension.class);
private static final Flag<Path> AAPT2_PATH_FLAG = Flag.path("aapt2");
private static final Flag<Boolean> GENERATE_UNIVERSAL_APK_FLAG = Flag.booleanFlag("universal");
private static final Flag<Integer> MAX_THREADS_FLAG = Flag.positiveInteger("max-threads");
......@@ -211,10 +213,7 @@ public abstract class BuildApksCommand {
.ifPresent(
maxThreads ->
buildApksCommand.setExecutorService(createInternalExecutorService(maxThreads)));
OPTIMIZE_FOR_FLAG
.getValue(flags)
.ifPresent(
values -> buildApksCommand.setOptimizationDimensions(ImmutableSet.copyOf(values)));
OPTIMIZE_FOR_FLAG.getValue(flags).ifPresent(buildApksCommand::setOptimizationDimensions);
// Signing-related arguments.
Optional<Path> keystorePath = KEYSTORE_FLAG.getValue(flags);
......@@ -258,6 +257,7 @@ public abstract class BuildApksCommand {
bundleValidator.validate(appBundle);
BundleConfig bundleConfig = appBundle.getBundleConfig();
Version bundleVersion = BundleToolVersion.getVersionFromBundleConfig(bundleConfig);
ImmutableList<BundleModule> allModules =
ImmutableList.copyOf(appBundle.getModules().values());
......@@ -270,7 +270,7 @@ public abstract class BuildApksCommand {
getGenerateOnlyUniversalApk()
? ApkOptimizations.getOptimizationsForUniversalApk()
: new OptimizationsMerger()
.mergeWithDefaults(appBundle.getBundleConfig(), getOptimizationDimensions());
.mergeWithDefaults(bundleConfig, getOptimizationDimensions());
// Generate APK variants.
ImmutableList<Variant> splitApkVariants = ImmutableList.of();
......@@ -280,7 +280,8 @@ public abstract class BuildApksCommand {
boolean generateStandaloneApks = getGenerateOnlyUniversalApk() || targetsPreL(appBundle);
if (generateSplitApks) {
splitApkVariants = generateSplitApkVariants(allModules, apkSetBuilder, apkOptimizations);
splitApkVariants =
generateSplitApkVariants(allModules, apkSetBuilder, apkOptimizations, bundleVersion);
}
if (generateStandaloneApks) {
// Note: Universal APK is a special type of standalone, with no optimization dimensions.
......@@ -293,7 +294,8 @@ public abstract class BuildApksCommand {
getGenerateOnlyUniversalApk(),
tempDir,
apkSetBuilder,
apkOptimizations);
apkOptimizations,
bundleVersion);
}
// Populate alternative targeting based on targeting of all variants.
......@@ -349,12 +351,13 @@ public abstract class BuildApksCommand {
private ImmutableList<Variant> generateSplitApkVariants(
ImmutableList<BundleModule> modules,
ApkSetBuilder apkSetBuilder,
ApkOptimizations apkOptimizations) {
ApkOptimizations apkOptimizations,
Version bundleVersion) {
// For now we build just a single variant with hard-coded L+ targeting.
Variant.Builder variant = Variant.newBuilder().setTargeting(lPlusVariantTargeting());
for (BundleModule module : modules) {
ModuleSplitter moduleSplitter =
new ModuleSplitter(module, apkOptimizations.getSplitDimensions());
new ModuleSplitter(module, apkOptimizations.getSplitDimensions(), bundleVersion);
ImmutableList<ModuleSplit> splitApks = moduleSplitter.splitModule();
List<ApkDescription> apkDescriptions =
......@@ -382,10 +385,11 @@ public abstract class BuildApksCommand {
boolean isUniversalApk,
Path tempDir,
ApkSetBuilder apkSetBuilder,
ApkOptimizations apkOptimizations) {
ApkOptimizations apkOptimizations,
Version bundleVersion) {
ImmutableList<ModuleSplit> standaloneApks =
new BundleSharder(tempDir)
new BundleSharder(tempDir, bundleVersion)
.shardBundle(modules, apkOptimizations.getSplitDimensions(), bundleMetadata);
// Wait for all concurrent tasks to succeed, or any to fail.
......@@ -445,7 +449,7 @@ public abstract class BuildApksCommand {
}
private static VariantTargeting standaloneApkVariantTargeting(ModuleSplit standaloneApk) {
ApkTargeting apkTargeting = standaloneApk.getTargeting();
ApkTargeting apkTargeting = standaloneApk.getApkTargeting();
VariantTargeting.Builder variantTargeting = VariantTargeting.newBuilder();
if (apkTargeting.hasAbiTargeting()) {
......
......@@ -143,7 +143,7 @@ public abstract class ExtractApksCommand {
for (Path matchedApk : matchedApks) {
ZipEntry entry = apksArchive.getEntry(matchedApk.toString());
checkNotNull(entry);
Path extractedApkPath = getOutputDirectory().resolve(matchedApk);
Path extractedApkPath = getOutputDirectory().resolve(matchedApk.getFileName());
try (InputStream inputStream = BufferedIo.inputStream(apksArchive, entry);
OutputStream outputApk = BufferedIo.outputStream(extractedApkPath)) {
ByteStreams.copy(inputStream, outputApk);
......
......@@ -16,7 +16,6 @@
package com.android.tools.build.bundletool.device;
import com.android.bundle.Devices.DeviceSpec;
import com.android.ddmlib.IDevice.DeviceState;
import com.android.tools.build.bundletool.exceptions.CommandExecutionException;
......
......@@ -21,6 +21,7 @@ import static com.android.tools.build.bundletool.model.BundleModule.RESOURCES_PR
import static com.android.tools.build.bundletool.model.BundleModule.ROOT_DIRECTORY;
import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileDoesNotExist;
import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileHasExtension;
import static com.android.tools.build.bundletool.utils.files.FileUtils.createParentDirectories;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
......@@ -127,6 +128,8 @@ final class ApkSerializerHelper {
void writeToZipFile(ModuleSplit split, Path outputPath, Path tempDir) {
checkFileDoesNotExist(outputPath);
createParentDirectories(outputPath);
// Write a Proto-APK with only files that aapt2 requires as part of the convert command.
Path partialProtoApk = tempDir.resolve("proto.apk");
writeProtoApk(split, partialProtoApk);
......
......@@ -26,15 +26,17 @@ import com.android.tools.build.bundletool.model.Aapt2Command;
import com.android.tools.build.bundletool.model.BundleModuleName;
import com.android.tools.build.bundletool.model.ModuleSplit;
import com.android.tools.build.bundletool.model.SigningConfiguration;
import com.android.tools.build.bundletool.model.ZipPath;
import com.google.common.collect.Iterables;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
/** Serializes split APKs on disk. */
public class SplitApkSerializer {
public static final String SPLIT_APKS_SUB_DIR = "splits";
private final ApkSerializerHelper apkSerializerHelper;
public SplitApkSerializer(
......@@ -46,12 +48,16 @@ public class SplitApkSerializer {
public ApkDescription writeSplitToDisk(ModuleSplit split, Path outputDirectory) {
checkState(isDirectory(outputDirectory));
String apkFileName = getApkFileName(split, split.getModuleName());
apkSerializerHelper.writeToZipFile(split, outputDirectory.resolve(Paths.get(apkFileName)));
// Using ZipPath to ensure '/' path delimiter in the ApkDescription proto.
String apkFileRelPath = ZipPath.create(SPLIT_APKS_SUB_DIR).resolve(apkFileName).toString();
apkSerializerHelper.writeToZipFile(split, outputDirectory.resolve(apkFileRelPath));
return ApkDescription.newBuilder()
.setPath(apkFileName)
.setTargeting(split.getTargeting())
.setPath(apkFileRelPath)
.setTargeting(split.getApkTargeting())
.setSplitApkMetadata(
SplitApkMetadata.newBuilder()
.setSplitId(split.getAndroidManifest().get().getSplitId().orElse(""))
......
......@@ -22,6 +22,7 @@ import com.android.bundle.Config.Compression;
import com.android.tools.build.bundletool.model.Aapt2Command;
import com.android.tools.build.bundletool.model.ModuleSplit;
import com.android.tools.build.bundletool.model.SigningConfiguration;
import com.android.tools.build.bundletool.model.ZipPath;
import com.google.common.annotations.VisibleForTesting;
import java.nio.file.Path;
import java.util.Optional;
......@@ -29,6 +30,8 @@ import java.util.Optional;
/** Serializes standalone APKs to disk. */
public class StandaloneApkSerializer {
public static final String STANDALONE_APKS_SUB_DIR = "standalones";
private final ApkSerializerHelper apkSerializerHelper;
public StandaloneApkSerializer(
......@@ -42,26 +45,29 @@ public class StandaloneApkSerializer {
String suffix =
(standaloneSplit.getSuffix().isEmpty() ? "" : "-") + standaloneSplit.getSuffix();
String apkFileName = String.format("standalone%s.apk", suffix);
// Using ZipPath to ensure '/' path delimiter in the ApkDescription proto.
String apkFileRelPath = ZipPath.create(STANDALONE_APKS_SUB_DIR).resolve(apkFileName).toString();
return writeToDiskInternal(standaloneSplit, apkFileName, outputDirectory);
return writeToDiskInternal(standaloneSplit, outputDirectory, apkFileRelPath);
}
public ApkDescription writeToDiskAsUniversal(ModuleSplit standaloneSplit, Path outputDirectory) {
return writeToDiskInternal(standaloneSplit, "universal.apk", outputDirectory);
return writeToDiskInternal(
standaloneSplit, outputDirectory, /* apkFileRelPath= */ "universal.apk");
}
@VisibleForTesting
ApkDescription writeToDiskInternal(
ModuleSplit standaloneSplit, String apkFileName, Path outputDirectory) {
apkSerializerHelper.writeToZipFile(standaloneSplit, outputDirectory.resolve(apkFileName));
ModuleSplit standaloneSplit, Path outputDirectory, String apkFileRelPath) {
apkSerializerHelper.writeToZipFile(standaloneSplit, outputDirectory.resolve(apkFileRelPath));
return ApkDescription.newBuilder()
.setPath(apkFileName)
.setPath(apkFileRelPath)
.setStandaloneApkMetadata(
StandaloneApkMetadata.newBuilder()
.addAllFusedModuleName(
standaloneSplit.getAndroidManifest().get().getFusedModuleNames()))
.setTargeting(standaloneSplit.getTargeting())
.setTargeting(standaloneSplit.getApkTargeting())
.build();
}
}
......@@ -18,6 +18,7 @@ package com.android.tools.build.bundletool.manifest;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.NO_NAMESPACE_URI;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.findAttribute;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.findAttributeIgnoringNamespace;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.findAttributeWithName;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.findAttributeWithResourceId;
import static com.android.tools.build.bundletool.manifest.ProtoXmlHelper.findElementFromDirectChildren;
......@@ -39,6 +40,7 @@ import com.android.tools.build.bundletool.exceptions.manifest.ManifestSdkTargeti
import com.android.tools.build.bundletool.exceptions.manifest.ManifestValidationException;
import com.android.tools.build.bundletool.exceptions.manifest.ManifestVersionException.VersionCodeInvalidException;
import com.android.tools.build.bundletool.exceptions.manifest.ManifestVersionException.VersionCodeMissingException;
import com.android.tools.build.bundletool.version.Version;
import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
......@@ -167,15 +169,21 @@ public abstract class AndroidManifest {
.map(ProtoXmlHelper::getAttributeValueAsBoolean);
}
public Optional<Boolean> getIsModuleIncludedInFusing() {
public Optional<Boolean> getIsModuleIncludedInFusing(Version bundleToolVersion) {
XmlElement manifest = getExactlyOneElement(getManifestRoot(), "manifest");
return findElementFromDirectChildren(manifest, "module", DISTRIBUTION_NAMESPACE)
.flatMap(module -> findElementFromDirectChildren(module, "fusing", DISTRIBUTION_NAMESPACE))
.map(
fusing ->
findAttribute(fusing, NO_NAMESPACE_URI, "include")
.orElseThrow(() -> new FusingMissingIncludeAttribute(getSplitId())))
fusing -> {
if (bundleToolVersion.isOlderThan(Version.of("0.3.4-dev"))) {
return findAttributeIgnoringNamespace(fusing, "include")
.orElseThrow(() -> new FusingMissingIncludeAttribute(getSplitId()));
} else {
return findAttribute(fusing, DISTRIBUTION_NAMESPACE, "include")
.orElseThrow(() -> new FusingMissingIncludeAttribute(getSplitId()));
}
})
.map(ProtoXmlHelper::getAttributeValueAsBoolean);
}
......@@ -216,13 +224,20 @@ public abstract class AndroidManifest {
.collect(toImmutableList());
}
public Optional<Boolean> isOnDemandModule() {
public Optional<Boolean> isOnDemandModule(Version bundleToolVersion) {
XmlElement manifest = getExactlyOneElement(getManifestRoot(), "manifest");
Optional<XmlElement> moduleElement =
findElementFromDirectChildren(manifest, "module", DISTRIBUTION_NAMESPACE);
return moduleElement
.flatMap(el -> findAttribute(el, DISTRIBUTION_NAMESPACE, "onDemand"))
.flatMap(
el -> {
if (bundleToolVersion.isOlderThan(Version.of("0.3.4-dev"))) {
return findAttributeIgnoringNamespace(el, "onDemand");
} else {
return findAttribute(el, DISTRIBUTION_NAMESPACE, "onDemand");
}
})
.map(ProtoXmlHelper::getAttributeValueAsBoolean);
}
......
......@@ -63,6 +63,10 @@ public final class ProtoXmlHelper {
.findFirst();
}
public static Optional<XmlAttribute> findAttributeIgnoringNamespace(XmlElement el, String name) {
return el.getAttributeList().stream().filter(attributeWithName(name)).findFirst();
}
/**
* Returns the boolean value of the attribute.
*
......
......@@ -24,6 +24,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.android.aapt.Resources.ResourceTable;
import com.android.bundle.Targeting.ApkTargeting;
import com.android.bundle.Targeting.VariantTargeting;
import com.android.tools.build.bundletool.exceptions.CommandExecutionException;
import com.android.tools.build.bundletool.manifest.AndroidManifest;
import com.android.tools.build.bundletool.model.BundleMetadata;
......@@ -147,12 +148,14 @@ public class ModuleSplitsToShardMerger {
.addAll(mergedEntriesByPath.values())
.addAll(mergedDexFiles)
.build())
.setTargeting(mergedSplitTargeting)
.setApkTargeting(mergedSplitTargeting)
.setStandalone(true)
// We don't care about the following properties for shards. The values are set just to
// satisfy contract of @AutoValue.Builder.
// `nativeConfig` is optional and therefore not being set.
.setMasterSplit(false)
.setModuleName(SHARD_MODULE_NAME);
.setModuleName(SHARD_MODULE_NAME)
.setVariantTargeting(VariantTargeting.getDefaultInstance());
mergedResourceTable.ifPresent(shard::setResourceTable);
return shard.build();
}
......@@ -266,7 +269,7 @@ public class ModuleSplitsToShardMerger {
private ApkTargeting mergeSplitTargetings(ApkTargeting merged, ModuleSplit split) {
try {
return MergingUtils.mergeShardTargetings(merged, split.getTargeting());
return MergingUtils.mergeShardTargetings(merged, split.getApkTargeting());
} catch (CommandExecutionException | IllegalStateException e) {
throw CommandExecutionException.builder()
.withCause(e)
......
......@@ -17,10 +17,12 @@
package com.android.tools.build.bundletool.mergers;
import static com.android.tools.build.bundletool.mergers.MergingUtils.getSameValueOrNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import com.android.aapt.Resources.ResourceTable;
import com.android.bundle.Files.NativeLibraries;
import com.android.bundle.Targeting.ApkTargeting;
import com.android.bundle.Targeting.VariantTargeting;
import com.android.tools.build.bundletool.manifest.AndroidManifest;
import com.android.tools.build.bundletool.model.BundleModuleName;
import com.android.tools.build.bundletool.model.ModuleEntry;
......@@ -30,14 +32,21 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
/** Merges module splits together that have the same targeting. */
/**
* Merges module splits together that have the same targeting.
*
* <p>The current implementation assumes all splits belong to the same variant.
*/
public class SameTargetingMerger implements ModuleSplitMerger {
@Override
public ImmutableList<ModuleSplit> merge(ImmutableCollection<ModuleSplit> moduleSplits) {
checkArgument(
moduleSplits.stream().map(ModuleSplit::getVariantTargeting).distinct().count() == 1,
"SameTargetingMerger doesn't support merging splits from different variants.");
ImmutableList.Builder<ModuleSplit> result = ImmutableList.builder();
ImmutableListMultimap<ApkTargeting, ModuleSplit> splitsByTargeting =
Multimaps.index(moduleSplits, ModuleSplit::getTargeting);
Multimaps.index(moduleSplits, ModuleSplit::getApkTargeting);
for (ApkTargeting targeting : splitsByTargeting.keySet()) {
result.add(mergeSplits(splitsByTargeting.get(targeting)));
}
......@@ -52,6 +61,7 @@ public class SameTargetingMerger implements ModuleSplitMerger {
NativeLibraries mergedNativeConfig = null;
BundleModuleName mergedModuleName = null;
Boolean mergedIsMasterSplit = null;
VariantTargeting mergedVariantTargeting = null;
for (ModuleSplit split : splits) {
if (split.getAndroidManifest().isPresent()) {
......@@ -91,8 +101,14 @@ public class SameTargetingMerger implements ModuleSplitMerger {
() ->
new IllegalStateException(
"Encountered conflicting isMasterSplit flag values while merging."));
mergedVariantTargeting =
getSameValueOrNonNull(mergedVariantTargeting, split.getVariantTargeting())
.orElseThrow(
() ->
new IllegalStateException(
"Encountered conflicting variant targeting values while merging."));
entries.addAll(split.getEntries());
builder.setTargeting(split.getTargeting());
builder.setApkTargeting(split.getApkTargeting());
}
if (mergedManifest != null) {
......@@ -110,6 +126,7 @@ public class SameTargetingMerger implements ModuleSplitMerger {
if (mergedIsMasterSplit != null) {
builder.setMasterSplit(mergedIsMasterSplit);
}
builder.setVariantTargeting(mergedVariantTargeting);
builder.setEntries(entries.build());
return builder.build();
}
......
......@@ -67,7 +67,7 @@ public class AppBundle {
public static AppBundle buildFromZip(ZipFile bundleFile) {
BundleConfig bundleConfig = readBundleConfig(bundleFile);
return new AppBundle(
sanitize(extractModules(bundleFile), bundleConfig),
sanitize(extractModules(bundleFile, bundleConfig), bundleConfig),
bundleConfig,
readBundleMetadata(bundleFile));
}
......@@ -106,7 +106,8 @@ public class AppBundle {
return bundleMetadata;
}
private static Map<BundleModuleName, BundleModule> extractModules(ZipFile bundleFile) {
private static Map<BundleModuleName, BundleModule> extractModules(
ZipFile bundleFile, BundleConfig bundleConfig) {
Map<BundleModuleName, BundleModule.Builder> moduleBuilders = new HashMap<>();
Enumeration<? extends ZipEntry> entries = bundleFile.entries();
while (entries.hasMoreElements()) {
......@@ -146,6 +147,9 @@ public class AppBundle {
.build();
}
}
for (BundleModule.Builder value : moduleBuilders.values()) {
value.setBundleConfig(bundleConfig);
}
return Maps.transformValues(moduleBuilders, BundleModule.Builder::build);
}
......
......@@ -19,9 +19,11 @@ package com.android.tools.build.bundletool.model;
import com.android.aapt.Resources.ResourceTable;
import com.android.aapt.Resources.XmlNode;
import com.android.bundle.Commands.ModuleMetadata;
import com.android.bundle.Config.BundleConfig;
import com.android.bundle.Files.Assets;
import com.android.bundle.Files.NativeLibraries;
import com.android.tools.build.bundletool.manifest.AndroidManifest;
import com.android.tools.build.bundletool.version.BundleToolVersion;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
......@@ -57,6 +59,9 @@ public abstract class BundleModule {
public abstract BundleModuleName getName();
/** The version of Bundletool that built this module, taken from BundleConfig. */
public abstract BundleConfig getBundleConfig();
public abstract AndroidManifest getAndroidManifest();
public abstract Optional<ResourceTable> getResourceTable();
......@@ -89,7 +94,11 @@ public abstract class BundleModule {
public boolean isIncludedInFusing() {
// The following should never throw if the module/bundle has been validated.
return isBaseModule() || getAndroidManifest().getIsModuleIncludedInFusing().get();
return isBaseModule()
|| getAndroidManifest()
.getIsModuleIncludedInFusing(
BundleToolVersion.getVersionFromBundleConfig(getBundleConfig()))
.get();
}