Search in sources :

Example 6 with TargetedAssetsDirectory

use of com.android.bundle.Files.TargetedAssetsDirectory in project bundletool by google.

the class AssetsDimensionSplitterFactory method createSplitter.

/**
 * Creates a {@link ModuleSplitSplitter} capable of splitting on a given Asset targeting
 * dimension, with, optionally, a dimension to be removed from asset paths.
 *
 * @param <T> the proto buffer message class representing the splitting targeting dimension.
 * @param dimensionGetter function that extracts the sub-message representing a targeting
 *     dimension.
 * @param targetingSetter function that creates a split targeting that will be merged with the
 *     targeting of the input {@link ModuleSplit}.
 * @param hasTargeting predicate to test if the input {@link ModuleSplit} is already targeting on
 *     the dimension of this splitter.
 * @param targetingDimensionToRemove If not empty, the targeting for this dimension will be
 * removed from asset paths (i.e: suffixes like #tcf_xxx will be removed from paths).
 * @return {@link ModuleSplitSplitter} for a given dimension functions.
 */
public static <T extends Message> ModuleSplitSplitter createSplitter(Function<AssetsDirectoryTargeting, T> dimensionGetter, Function<T, ApkTargeting> targetingSetter, Predicate<ApkTargeting> hasTargeting, Optional<TargetingDimension> targetingDimensionToRemove) {
    return new ModuleSplitSplitter() {

        @Override
        public ImmutableCollection<ModuleSplit> split(ModuleSplit split) {
            checkArgument(!hasTargeting.test(split.getApkTargeting()), "Split is already targeting the splitting dimension.");
            return split.getAssetsConfig().map(assetsConfig -> splitAssetsDirectories(assetsConfig, split)).orElse(ImmutableList.of(split)).stream().map(moduleSplit -> moduleSplit.isMasterSplit() ? moduleSplit : removeAssetsTargeting(moduleSplit)).collect(toImmutableList());
        }

        private ModuleSplit removeAssetsTargeting(ModuleSplit split) {
            return targetingDimensionToRemove.isPresent() ? SuffixStripper.createForDimension(targetingDimensionToRemove.get()).removeAssetsTargeting(split) : split;
        }

        private ImmutableList<ModuleSplit> splitAssetsDirectories(Assets assets, ModuleSplit split) {
            Multimap<T, TargetedAssetsDirectory> directoriesMap = Multimaps.filterKeys(Multimaps.index(assets.getDirectoryList(), targetedDirectory -> dimensionGetter.apply(targetedDirectory.getTargeting())), not(this::isDefaultTargeting));
            ImmutableList.Builder<ModuleSplit> splitsBuilder = new ImmutableList.Builder<>();
            // Generate config splits.
            directoriesMap.asMap().entrySet().forEach(entry -> {
                ImmutableList<ModuleEntry> entries = listEntriesFromDirectories(entry.getValue(), split);
                if (entries.isEmpty()) {
                    return;
                }
                ModuleSplit.Builder modifiedSplit = split.toBuilder();
                modifiedSplit.setEntries(entries).setApkTargeting(generateTargeting(split.getApkTargeting(), entry.getKey())).setMasterSplit(false).addMasterManifestMutator(withSplitsRequired(true));
                splitsBuilder.add(modifiedSplit.build());
            });
            // Ensure that master split (even an empty one) always exists.
            ModuleSplit defaultSplit = getDefaultAssetsSplit(split, splitsBuilder.build());
            if (defaultSplit.isMasterSplit() || !defaultSplit.getEntries().isEmpty()) {
                splitsBuilder.add(defaultSplit);
            }
            return splitsBuilder.build();
        }

        private ModuleSplit getDefaultAssetsSplit(ModuleSplit inputSplit, ImmutableList<ModuleSplit> configSplits) {
            ImmutableSet<ModuleEntry> claimedEntries = configSplits.stream().map(ModuleSplit::getEntries).flatMap(Collection::stream).collect(toImmutableSet());
            return inputSplit.toBuilder().setEntries(inputSplit.getEntries().stream().filter(not(claimedEntries::contains)).collect(toImmutableList())).build();
        }

        private boolean isDefaultTargeting(T splittingDimensionTargeting) {
            return splittingDimensionTargeting.equals(splittingDimensionTargeting.getDefaultInstanceForType());
        }

        private ApkTargeting generateTargeting(ApkTargeting splitTargeting, T extraTargeting) {
            if (isDefaultTargeting(extraTargeting)) {
                return splitTargeting;
            }
            return splitTargeting.toBuilder().mergeFrom(targetingSetter.apply(extraTargeting)).build();
        }

        private ImmutableList<ModuleEntry> listEntriesFromDirectories(Collection<TargetedAssetsDirectory> directories, ModuleSplit moduleSplit) {
            return directories.stream().map(targetedAssetsDirectory -> ZipPath.create(targetedAssetsDirectory.getPath())).flatMap(moduleSplit::getEntriesInDirectory).collect(toImmutableList());
        }
    };
}
Also used : ApkTargeting(com.android.bundle.Targeting.ApkTargeting) ZipPath(com.android.tools.build.bundletool.model.ZipPath) ImmutableCollection(com.google.common.collect.ImmutableCollection) Multimap(com.google.common.collect.Multimap) TargetingDimension(com.android.tools.build.bundletool.model.targeting.TargetingDimension) Multimaps(com.google.common.collect.Multimaps) ManifestMutator.withSplitsRequired(com.android.tools.build.bundletool.model.ManifestMutator.withSplitsRequired) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) SameTargetingMerger(com.android.tools.build.bundletool.mergers.SameTargetingMerger) Predicates.not(com.google.common.base.Predicates.not) ImmutableList(com.google.common.collect.ImmutableList) AssetsDirectoryTargeting(com.android.bundle.Targeting.AssetsDirectoryTargeting) Function(com.google.common.base.Function) ImmutableSet(com.google.common.collect.ImmutableSet) Predicate(java.util.function.Predicate) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Collection(java.util.Collection) ImmutableCollectors.toImmutableSet(com.android.utils.ImmutableCollectors.toImmutableSet) SuffixStripper(com.android.tools.build.bundletool.shards.SuffixStripper) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) Message(com.google.protobuf.Message) Optional(java.util.Optional) TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) Assets(com.android.bundle.Files.Assets) TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) ApkTargeting(com.android.bundle.Targeting.ApkTargeting) ImmutableList(com.google.common.collect.ImmutableList) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) Assets(com.android.bundle.Files.Assets) ImmutableCollection(com.google.common.collect.ImmutableCollection) Collection(java.util.Collection)

Example 7 with TargetedAssetsDirectory

use of com.android.bundle.Files.TargetedAssetsDirectory in project bundletool by google.

the class SameTargetingMergerTest method assetsConfigMerging_mergeAssetsWithIntersection.

@Test
public void assetsConfigMerging_mergeAssetsWithIntersection() throws Exception {
    ModuleSplit moduleSplit = createModuleSplitBuilder().setEntries(ImmutableList.of(createModuleEntryForFile("assets/some_assets/file.txt", DUMMY_CONTENT))).setApkTargeting(ApkTargeting.getDefaultInstance()).setAssetsConfig(Assets.newBuilder().addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_assets").setTargeting(createAssetsDirectoryLanguageTargeting("de")).build()).build()).build();
    ModuleSplit moduleSplit2 = createModuleSplitBuilder().setEntries(ImmutableList.of(createModuleEntryForFile("assets/some_assets/file.txt", DUMMY_CONTENT), createModuleEntryForFile("assets/some_other_assets/file.txt", DUMMY_CONTENT))).setApkTargeting(ApkTargeting.getDefaultInstance()).setAssetsConfig(Assets.newBuilder().addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_assets").setTargeting(createAssetsDirectoryLanguageTargeting("de")).build()).addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_other_assets").build()).build()).build();
    ImmutableCollection<ModuleSplit> splits = new SameTargetingMerger().merge(ImmutableList.of(moduleSplit, moduleSplit2));
    assertThat(splits).hasSize(1);
    ModuleSplit masterSplit = splits.iterator().next();
    assertThat(masterSplit.getAssetsConfig()).isPresent();
    assertThat(masterSplit.getAssetsConfig().get().getDirectoryList().stream().map(TargetedAssetsDirectory::getPath)).containsExactly("assets/some_assets", "assets/some_other_assets");
}
Also used : TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) Test(org.junit.Test)

Example 8 with TargetedAssetsDirectory

use of com.android.bundle.Files.TargetedAssetsDirectory in project bundletool by google.

the class ModuleSplitsToShardMergerTest method mergeSingleShard_mergeAssetsWithIntersection.

@Test
public void mergeSingleShard_mergeAssetsWithIntersection() throws Exception {
    ModuleSplit baseModuleSplit = createModuleSplitBuilder().setEntries(ImmutableList.of(createModuleEntryForFile("assets/some_assets/file.txt", DUMMY_CONTENT))).setAssetsConfig(Assets.newBuilder().addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_assets").setTargeting(createAssetsDirectoryLanguageTargeting("de")).build()).build()).build();
    ModuleSplit featureModuleSplit = createModuleSplitBuilder().setModuleName(BundleModuleName.create(FEATURE_MODULE_NAME)).setEntries(ImmutableList.of(createModuleEntryForFile("assets/some_assets/file.txt", DUMMY_CONTENT), createModuleEntryForFile("assets/some_other_assets/file.txt", DUMMY_CONTENT))).setAssetsConfig(Assets.newBuilder().addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_assets").setTargeting(createAssetsDirectoryLanguageTargeting("de")).build()).addDirectory(TargetedAssetsDirectory.newBuilder().setPath("assets/some_other_assets").build()).build()).build();
    ModuleSplit merged = splitsToShardMerger.mergeSingleShard(ImmutableList.of(baseModuleSplit, featureModuleSplit), createCache());
    assertThat(extractPaths(merged.getEntries())).containsExactly("assets/some_assets/file.txt", "assets/some_other_assets/file.txt");
    assertThat(merged.getAssetsConfig()).isPresent();
    assertThat(merged.getAssetsConfig().get().getDirectoryList().stream().map(TargetedAssetsDirectory::getPath)).containsExactly("assets/some_assets", "assets/some_other_assets");
}
Also used : TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) Test(org.junit.Test)

Example 9 with TargetedAssetsDirectory

use of com.android.bundle.Files.TargetedAssetsDirectory 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);
}
Also used : BundleModuleName(com.android.tools.build.bundletool.model.BundleModuleName) TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) ApkTargeting(com.android.bundle.Targeting.ApkTargeting) HashMap(java.util.HashMap) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) AndroidManifest(com.android.tools.build.bundletool.model.AndroidManifest) ZipPath(com.android.tools.build.bundletool.model.ZipPath) ResourceTable(com.android.aapt.Resources.ResourceTable)

Example 10 with TargetedAssetsDirectory

use of com.android.bundle.Files.TargetedAssetsDirectory 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();
}
Also used : ApexImages(com.android.bundle.Files.ApexImages) BundleModuleName(com.android.tools.build.bundletool.model.BundleModuleName) TargetedAssetsDirectory(com.android.bundle.Files.TargetedAssetsDirectory) NativeLibraries(com.android.bundle.Files.NativeLibraries) HashMap(java.util.HashMap) ImmutableList(com.google.common.collect.ImmutableList) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) AndroidManifest(com.android.tools.build.bundletool.model.AndroidManifest) ApexEmbeddedApkConfig(com.android.bundle.Config.ApexEmbeddedApkConfig) VariantTargeting(com.android.bundle.Targeting.VariantTargeting) ResourceTable(com.android.aapt.Resources.ResourceTable)

Aggregations

TargetedAssetsDirectory (com.android.bundle.Files.TargetedAssetsDirectory)10 ModuleSplit (com.android.tools.build.bundletool.model.ModuleSplit)7 ModuleEntry (com.android.tools.build.bundletool.model.ModuleEntry)5 ZipPath (com.android.tools.build.bundletool.model.ZipPath)5 Assets (com.android.bundle.Files.Assets)4 Test (org.junit.Test)4 ResourceTable (com.android.aapt.Resources.ResourceTable)2 ApkTargeting (com.android.bundle.Targeting.ApkTargeting)2 AssetsDirectoryTargeting (com.android.bundle.Targeting.AssetsDirectoryTargeting)2 AndroidManifest (com.android.tools.build.bundletool.model.AndroidManifest)2 BundleModuleName (com.android.tools.build.bundletool.model.BundleModuleName)2 ImmutableList (com.google.common.collect.ImmutableList)2 ImmutableSet (com.google.common.collect.ImmutableSet)2 HashMap (java.util.HashMap)2 ApexEmbeddedApkConfig (com.android.bundle.Config.ApexEmbeddedApkConfig)1 ApexImages (com.android.bundle.Files.ApexImages)1 NativeLibraries (com.android.bundle.Files.NativeLibraries)1 VariantTargeting (com.android.bundle.Targeting.VariantTargeting)1 SameTargetingMerger (com.android.tools.build.bundletool.mergers.SameTargetingMerger)1 BundleModule (com.android.tools.build.bundletool.model.BundleModule)1