use of com.android.tools.build.bundletool.model.BundleModuleName in project bundletool by google.
the class FusingAndroidManifestMergerTest method mergeFeatureActivitiesIntoBaseManifest.
@Test
@Theory
public void mergeFeatureActivitiesIntoBaseManifest(FusingAndroidManifestMerger.Mode mode) {
SetMultimap<BundleModuleName, AndroidManifest> manifests = createManifests(androidManifest("com.testapp", withCustomThemeActivity("activity1", BASE_THEME_REF_ID), withCustomThemeActivity("activity2", BASE_THEME_REF_ID), withCustomThemeActivity("activity3", BASE_THEME_REF_ID)), androidManifestForFeature("com.testapp.feature1", withCustomThemeActivity("activity1", FEATURE1_THEME_REF_ID)), androidManifestForFeature("com.testapp.feature2", withCustomThemeActivity("activity3", FEATURE2_THEME_REF_ID)));
AndroidManifest merged = createMerger(mode).merge(manifests);
Map<String, Integer> refIdByActivity = Maps.transformValues(merged.getActivitiesByName(), activity -> activity.getAndroidAttribute(THEME_RESOURCE_ID).get().getValueAsRefId());
assertThat(merged.getPackageName()).isEqualTo("com.testapp");
assertThat(refIdByActivity).containsExactly("activity1", FEATURE1_THEME_REF_ID, "activity2", BASE_THEME_REF_ID, "activity3", FEATURE2_THEME_REF_ID).inOrder();
}
use of com.android.tools.build.bundletool.model.BundleModuleName in project bundletool by google.
the class FusingAndroidManifestMergerTest method duplicateManifest_throws.
@Test
public void duplicateManifest_throws() {
SetMultimap<BundleModuleName, AndroidManifest> manifests = ImmutableSetMultimap.of(BASE_MODULE_NAME, AndroidManifest.create(androidManifest("com.testapp1")), BASE_MODULE_NAME, AndroidManifest.create(androidManifest("com.testapp2")));
CommandExecutionException exception = assertThrows(CommandExecutionException.class, () -> createMerger(Mode.REPLACE).merge(manifests));
assertThat(exception).hasMessageThat().isEqualTo("Expected exactly one base module manifest, but found 2.");
}
use of com.android.tools.build.bundletool.model.BundleModuleName in project bundletool by google.
the class ApkSerializerManager method serializeApks.
@VisibleForTesting
ImmutableList<Variant> serializeApks(Path outputDirectory, GeneratedApks generatedApks, Optional<DeviceSpec> deviceSpec) {
validateInput(generatedApks, apkBuildMode);
// Running with system APK mode generates a fused APK and additional unmatched language splits.
// To avoid filtering of unmatched language splits we skip device filtering for system mode.
Predicate<ModuleSplit> deviceFilter = deviceSpec.isPresent() && !apkBuildMode.equals(SYSTEM) ? new ApkMatcher(addDefaultDeviceTierIfNecessary(deviceSpec.get()))::matchesModuleSplitByTargeting : alwaysTrue();
ImmutableListMultimap<VariantKey, ModuleSplit> splitsByVariant = generatedApks.getAllApksGroupedByOrderedVariants();
// Assign the variant numbers to each variant present.
AtomicInteger variantNumberCounter = new AtomicInteger(firstVariantNumber);
ImmutableMap<VariantKey, Integer> variantNumberByVariantKey = splitsByVariant.keySet().stream().collect(toImmutableMap(identity(), unused -> variantNumberCounter.getAndIncrement()));
// 1. Remove APKs not matching the device spec.
// 2. Modify the APKs based on the ApkModifier.
// 3. Serialize all APKs in parallel.
// Modifies the APK using APK modifier, then returns a map by extracting the variant
// of APK first and later clearing out its variant targeting.
ImmutableListMultimap<VariantKey, ModuleSplit> finalSplitsByVariant = splitsByVariant.entries().stream().filter(keyModuleSplitEntry -> deviceFilter.test(keyModuleSplitEntry.getValue())).collect(groupingBySortedKeys(Entry::getKey, entry -> clearVariantTargeting(modifyApk(entry.getValue(), variantNumberByVariantKey.get(entry.getKey())))));
// After variant targeting of APKs are cleared, there might be duplicate APKs
// which are removed and the distinct APKs are then serialized in parallel.
ImmutableBiMap<ZipPath, ModuleSplit> splitsByRelativePath = finalSplitsByVariant.values().stream().distinct().collect(toImmutableBiMap(apkPathManager::getApkPath, identity()));
ImmutableMap<ZipPath, ApkDescription> apkDescriptionsByRelativePath = apkSerializer.serialize(outputDirectory, splitsByRelativePath);
// Build the result proto.
ImmutableList.Builder<Variant> variants = ImmutableList.builder();
for (VariantKey variantKey : finalSplitsByVariant.keySet()) {
Variant.Builder variant = Variant.newBuilder().setVariantNumber(variantNumberByVariantKey.get(variantKey)).setTargeting(variantKey.getVariantTargeting());
Multimap<BundleModuleName, ModuleSplit> splitsByModuleName = finalSplitsByVariant.get(variantKey).stream().collect(groupingBySortedKeys(ModuleSplit::getModuleName));
for (BundleModuleName moduleName : splitsByModuleName.keySet()) {
variant.addApkSet(ApkSet.newBuilder().setModuleMetadata(bundle.getModule(moduleName).getModuleMetadata()).addAllApkDescription(splitsByModuleName.get(moduleName).stream().map(split -> splitsByRelativePath.inverse().get(split)).map(apkDescriptionsByRelativePath::get).collect(toImmutableList())));
}
variants.add(variant.build());
}
return variants.build();
}
use of com.android.tools.build.bundletool.model.BundleModuleName 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.BundleModuleName 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);
}
Aggregations