use of com.android.tools.build.bundletool.model.AndroidManifest in project bundletool by google.
the class BundleModuleMerger method mergeAndroidManifest.
private static void mergeAndroidManifest(Version bundletoolVersion, ImmutableSet<BundleModule> bundleModulesToFuse, BundleModule.Builder mergedBaseModule) {
HashMultimap<BundleModuleName, AndroidManifest> manifests = bundleModulesToFuse.stream().collect(toMultimap(BundleModule::getName, BundleModule::getAndroidManifest, HashMultimap::create));
AndroidManifestMerger manifestMerger = FUSE_APPLICATION_ELEMENTS_FROM_FEATURE_MANIFESTS.enabledForVersion(bundletoolVersion) ? fusingMergerApplicationElements() : fusingMergerOnlyReplaceActivities();
AndroidManifest mergedManifest = manifestMerger.merge(manifests);
mergedManifest = mergedManifest.toEditor().setFusedModuleNames(bundleModulesToFuse.stream().map(module -> module.getName().getName()).collect(toImmutableList())).save();
mergedBaseModule.setAndroidManifest(mergedManifest);
}
use of com.android.tools.build.bundletool.model.AndroidManifest in project bundletool by google.
the class FusingAndroidManifestMerger method mergeManifests.
private AndroidManifest mergeManifests(AndroidManifest baseManifest, List<AndroidManifest> featureManifests) {
// Gather all child elements of 'application' from all manifest. If element with the same name
// and type is presented in more than one manifest we give precedency to one in feature module.
// All feature manifests are sorted by feature module name in this method.
ImmutableListMultimap<ApplicationElementId, XmlProtoElement> applicationElements = gatherApplicationElementsManifests(ImmutableList.<AndroidManifest>builder().addAll(featureManifests).add(baseManifest).build(), elementsToMerge);
// This is optimization that allows to skip merging if there is no mergeable elements in
// feature modules.
long numberOfMergeableElementsInBase = baseManifest.getManifestRoot().getElement().getChildrenElements(AndroidManifest.APPLICATION_ELEMENT_NAME).flatMap(application -> application.getChildrenElements()).filter(element -> elementsToMerge.contains(element.getName())).count();
if (numberOfMergeableElementsInBase == applicationElements.size()) {
return baseManifest;
}
// Merge manifest elements with the same name and type based on specified mode.
ImmutableMap<ApplicationElementId, XmlProtoElement> mergedElements = applicationElements.keySet().stream().collect(toImmutableMap(Function.identity(), key -> mergeElements(key, applicationElements.get(key))));
ManifestEditor manifestEditor = baseManifest.toEditor();
XmlProtoElementBuilder applicationElement = manifestEditor.getRawProto().getOrCreateChildElement(AndroidManifest.APPLICATION_ELEMENT_NAME);
// Replace original elements from the base manifest with merged ones. This is done in a way to
// preserve original elements ordering and additional elements are added to the end.
Set<XmlProtoElement> replacedElements = Sets.newIdentityHashSet();
applicationElement.modifyChildElements(child -> stream(getCorrespondingElementFromMergedElements(child, mergedElements)).peek(replacedElements::add).map(element -> XmlProtoNodeBuilder.createElementNode(element.toBuilder())).collect(toOptional()).orElse(child));
mergedElements.values().stream().filter(not(replacedElements::contains)).forEach(element -> applicationElement.addChildElement(element.toBuilder()));
return manifestEditor.save();
}
use of com.android.tools.build.bundletool.model.AndroidManifest in project bundletool by google.
the class ModuleSplitsToShardMerger method mergeSingleShard.
/**
* Gets a list of splits, and merges them into a single standalone APK (aka shard).
*
* <p>Allows to customize split type {@code mergedSplitType} of merged shard and Android manifest
* merger {@code manifestMerger}.
*/
public ModuleSplit mergeSingleShard(ImmutableCollection<ModuleSplit> splitsOfShard, Map<ImmutableSet<ModuleEntry>, ImmutableList<Path>> mergedDexCache, SplitType mergedSplitType, AndroidManifestMerger manifestMerger) {
ListMultimap<BundleModuleName, ModuleEntry> dexFilesToMergeByModule = ArrayListMultimap.create();
// If multiple splits were generated from one module, we'll see the same manifest multiple
// times. The multimap filters out identical (module name, manifest) pairs by contract.
// All splits of a module should have the same manifest, so the following multimap should
// associate just one value with each key. This is checked explicitly for the base module
// because the manifest merger requires *single* base manifest.
SetMultimap<BundleModuleName, AndroidManifest> androidManifestsToMergeByModule = HashMultimap.create();
Map<ZipPath, ModuleEntry> mergedEntriesByPath = new HashMap<>();
Optional<ResourceTable> mergedResourceTable = Optional.empty();
Map<String, TargetedAssetsDirectory> mergedAssetsConfig = new HashMap<>();
ApkTargeting mergedSplitTargeting = ApkTargeting.getDefaultInstance();
for (ModuleSplit split : splitsOfShard) {
// Resource tables and Split targetings can be merged for each split individually as we go.
mergedResourceTable = mergeResourceTables(mergedResourceTable, split);
mergedSplitTargeting = mergeSplitTargetings(mergedSplitTargeting, split);
// Android manifests need to be merged later, globally for all splits.
androidManifestsToMergeByModule.put(split.getModuleName(), split.getAndroidManifest());
for (ModuleEntry entry : split.getEntries()) {
if (entry.getPath().startsWith(DEX_DIRECTORY)) {
// Dex files need to be merged later, globally for all splits.
dexFilesToMergeByModule.put(split.getModuleName(), entry);
} else {
mergeEntries(mergedEntriesByPath, split, entry);
}
}
split.getAssetsConfig().ifPresent(assetsConfig -> {
mergeTargetedAssetsDirectories(mergedAssetsConfig, assetsConfig.getDirectoryList());
});
}
AndroidManifest mergedAndroidManifest = manifestMerger.merge(androidManifestsToMergeByModule);
Collection<ModuleEntry> mergedDexFiles = mergeDexFilesAndCache(dexFilesToMergeByModule, mergedAndroidManifest, mergedDexCache);
// Record names of the modules this shard was fused from.
ImmutableList<String> fusedModuleNames = getUniqueModuleNames(splitsOfShard);
if (mergedSplitType.equals(SplitType.STANDALONE)) {
mergedAndroidManifest = mergedAndroidManifest.toEditor().setFusedModuleNames(fusedModuleNames).save();
}
// Construct the final shard.
return buildShard(mergedEntriesByPath.values(), mergedDexFiles, mergedSplitTargeting, mergedAndroidManifest, mergedResourceTable, mergedAssetsConfig, mergedSplitType);
}
use of com.android.tools.build.bundletool.model.AndroidManifest in project bundletool by google.
the class SameTargetingMerger method mergeSplits.
private ModuleSplit mergeSplits(ImmutableCollection<ModuleSplit> splits) {
ModuleSplit.Builder builder = ModuleSplit.builder();
ImmutableList.Builder<ModuleEntry> entries = ImmutableList.builder();
AndroidManifest mergedManifest = null;
ResourceTable mergedResourceTable = null;
NativeLibraries mergedNativeConfig = null;
Map<String, TargetedAssetsDirectory> mergedAssetsConfig = new HashMap<>();
ApexImages mergedApexConfig = null;
ImmutableList<ApexEmbeddedApkConfig> mergedApexEmbeddedApkConfigs = null;
BundleModuleName mergedModuleName = null;
Boolean mergedIsMasterSplit = null;
VariantTargeting mergedVariantTargeting = null;
for (ModuleSplit split : splits) {
mergedManifest = getSameValueOrNonNull(mergedManifest, split.getAndroidManifest()).orElseThrow(() -> new IllegalStateException("Encountered two distinct manifests while merging."));
if (split.getResourceTable().isPresent()) {
mergedResourceTable = getSameValueOrNonNull(mergedResourceTable, split.getResourceTable().get()).orElseThrow(() -> new IllegalStateException("Unsupported case: encountered two distinct resource tables while " + "merging."));
}
if (split.getNativeConfig().isPresent()) {
mergedNativeConfig = getSameValueOrNonNull(mergedNativeConfig, split.getNativeConfig().get()).orElseThrow(() -> new IllegalStateException("Encountered two distinct native configs while merging."));
}
if (split.getApexConfig().isPresent()) {
mergedApexConfig = getSameValueOrNonNull(mergedApexConfig, split.getApexConfig().get()).orElseThrow(() -> new IllegalStateException("Encountered two distinct apex configs while merging."));
}
mergedApexEmbeddedApkConfigs = getSameValueOrNonNull(mergedApexEmbeddedApkConfigs, split.getApexEmbeddedApkConfigs()).orElseThrow(() -> new IllegalStateException("Encountered two distinct apex embedded apk configs while merging."));
mergedModuleName = getSameValueOrNonNull(mergedModuleName, split.getModuleName()).orElseThrow(() -> new IllegalStateException("Encountered two distinct module names while merging."));
mergedIsMasterSplit = getSameValueOrNonNull(mergedIsMasterSplit, Boolean.valueOf(split.isMasterSplit())).orElseThrow(() -> 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.setApkTargeting(split.getApkTargeting());
split.getAssetsConfig().ifPresent(assetsConfig -> {
mergeTargetedAssetsDirectories(mergedAssetsConfig, assetsConfig.getDirectoryList());
});
}
if (mergedManifest != null) {
builder.setAndroidManifest(mergedManifest);
}
if (mergedResourceTable != null) {
builder.setResourceTable(mergedResourceTable);
}
if (mergedNativeConfig != null) {
builder.setNativeConfig(mergedNativeConfig);
}
if (!mergedAssetsConfig.isEmpty()) {
builder.setAssetsConfig(Assets.newBuilder().addAllDirectory(mergedAssetsConfig.values()).build());
}
if (mergedApexConfig != null) {
builder.setApexConfig(mergedApexConfig);
}
if (mergedApexEmbeddedApkConfigs != null) {
builder.setApexEmbeddedApkConfigs(mergedApexEmbeddedApkConfigs);
}
if (mergedModuleName != null) {
builder.setModuleName(mergedModuleName);
}
if (mergedIsMasterSplit != null) {
builder.setMasterSplit(mergedIsMasterSplit);
}
builder.setVariantTargeting(mergedVariantTargeting);
builder.setEntries(entries.build());
return builder.build();
}
use of com.android.tools.build.bundletool.model.AndroidManifest in project bundletool by google.
the class ManifestProtoUtilsTest method splitNameAttributeAddedToProvider.
@Test
public void splitNameAttributeAddedToProvider() {
AndroidManifest manifest = AndroidManifest.create(androidManifest("com.test.app", withSplitNameProvider("FooProvider", "foo")));
ImmutableList<XmlElement> providers = manifest.getManifestRoot().getElement().getChildElement("application").getChildrenElements(PROVIDER_ELEMENT_NAME).map(XmlProtoElement::getProto).collect(toImmutableList());
assertThat(providers).hasSize(1);
XmlElement providerElement = Iterables.getOnlyElement(providers);
assertThat(providerElement.getAttributeList()).containsExactly(xmlAttribute(ANDROID_NAMESPACE_URI, NAME_ATTRIBUTE_NAME, NAME_RESOURCE_ID, "FooProvider"), xmlAttribute(ANDROID_NAMESPACE_URI, SPLIT_NAME_ATTRIBUTE_NAME, SPLIT_NAME_RESOURCE_ID, "foo"));
}
Aggregations