Search in sources :

Example 31 with ModuleEntry

use of com.android.tools.build.bundletool.model.ModuleEntry in project bundletool by google.

the class ZipFlingerApkSerializer method writeToZipFile.

private void writeToZipFile(ModuleSplit split, Path outputPath, TempDirectory tempDir) throws IOException {
    checkFileDoesNotExist(outputPath);
    createParentDirectories(outputPath);
    split = apkSigner.signEmbeddedApks(split);
    // Write a Proto-APK with only files that aapt2 requires as part of the convert command.
    Path partialProtoApk = tempDir.getPath().resolve("proto.apk");
    writeProtoApk(split, partialProtoApk, tempDir);
    // Invoke aapt2 to convert files from proto to binary format.
    Path binaryApkPath = tempDir.getPath().resolve("binary.apk");
    if (enableSparseEncoding && split.getResourceTable().isPresent()) {
        Path interimApk = tempDir.getPath().resolve("interim.apk");
        aapt2.convertApkProtoToBinary(partialProtoApk, interimApk);
        aapt2.optimizeToSparseResourceTables(interimApk, binaryApkPath);
    } else {
        aapt2.convertApkProtoToBinary(partialProtoApk, binaryApkPath);
    }
    checkState(Files.exists(binaryApkPath), "No APK created by aapt2 convert command.");
    try (ZipArchive apkWriter = new ZipArchive(outputPath);
        ZipReader aapt2ApkReader = ZipReader.createFromFile(binaryApkPath)) {
        ImmutableMap<ZipPath, ModuleEntry> moduleEntriesByName = split.getEntries().stream().collect(toImmutableMap(entry -> ApkSerializerHelper.toApkEntryPath(entry.getPath()), entry -> entry, // e.g. base/assets/foo and base/root/assets/foo.
        (a, b) -> b));
        // Sorting entries by name for determinism.
        ImmutableSortedSet<ZipPath> sortedEntryNames = Stream.concat(aapt2ApkReader.getEntries().keySet().stream().map(ZipPath::create), moduleEntriesByName.keySet().stream()).collect(toImmutableSortedSet(naturalOrder()));
        ApkEntrySerializer apkEntrySerializer = new ApkEntrySerializer(apkWriter, aapt2ApkReader, split, tempDir);
        for (ZipPath pathInApk : sortedEntryNames) {
            Optional<Entry> aapt2Entry = aapt2ApkReader.getEntry(pathInApk.toString());
            if (aapt2Entry.isPresent()) {
                apkEntrySerializer.addAapt2Entry(pathInApk, aapt2Entry.get());
            } else {
                ModuleEntry moduleEntry = checkNotNull(moduleEntriesByName.get(pathInApk));
                apkEntrySerializer.addRegularEntry(pathInApk, moduleEntry);
            }
        }
    }
    apkSigner.signApk(outputPath, split);
}
Also used : Path(java.nio.file.Path) ZipPath(com.android.tools.build.bundletool.model.ZipPath) NO_ALIGNMENT(com.android.zipflinger.Source.NO_ALIGNMENT) Comparator.naturalOrder(java.util.Comparator.naturalOrder) Map(java.util.Map) ZipArchive(com.android.zipflinger.ZipArchive) Version(com.android.tools.build.bundletool.model.version.Version) NO_COMPRESSION_EXTENSIONS(com.android.tools.build.bundletool.io.ApkSerializerHelper.NO_COMPRESSION_EXTENSIONS) Path(java.nio.file.Path) ImmutableSortedSet.toImmutableSortedSet(com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Aapt2Command(com.android.tools.build.bundletool.androidtools.Aapt2Command) Preconditions.checkState(com.google.common.base.Preconditions.checkState) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) UncheckedIOException(java.io.UncheckedIOException) ImmutableMap.toImmutableMap(com.google.common.collect.ImmutableMap.toImmutableMap) Stream(java.util.stream.Stream) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) BundleConfig(com.android.bundle.Config.BundleConfig) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) UseBundleCompression(com.android.tools.build.bundletool.commands.BuildApksManagerComponent.UseBundleCompression) FilePreconditions.checkFileDoesNotExist(com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileDoesNotExist) Optional(java.util.Optional) SparseEncoding(com.android.bundle.Config.ResourceOptimizations.SparseEncoding) Pattern(java.util.regex.Pattern) ListeningExecutorService(com.google.common.util.concurrent.ListeningExecutorService) SAME_AS_SOURCE(com.android.tools.build.bundletool.model.CompressionLevel.SAME_AS_SOURCE) PathMatcher(com.android.tools.build.bundletool.model.utils.PathMatcher) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) ZipPath(com.android.tools.build.bundletool.model.ZipPath) BytesSource(com.android.zipflinger.BytesSource) BEST_COMPRESSION(com.android.tools.build.bundletool.model.CompressionLevel.BEST_COMPRESSION) ApkListener(com.android.tools.build.bundletool.model.ApkListener) Inject(javax.inject.Inject) ImmutableList(com.google.common.collect.ImmutableList) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) ApkDescription(com.android.bundle.Commands.ApkDescription) CompressionLevel(com.android.tools.build.bundletool.model.CompressionLevel) ImmutableSortedSet(com.google.common.collect.ImmutableSortedSet) MANIFEST_FILENAME(com.android.tools.build.bundletool.model.BundleModule.MANIFEST_FILENAME) Files(java.nio.file.Files) DEFAULT_COMPRESSION(com.android.tools.build.bundletool.model.CompressionLevel.DEFAULT_COMPRESSION) Preconditions.checkNotNull(com.google.common.base.Preconditions.checkNotNull) ApkSerializerHelper.requiresAapt2Conversion(com.android.tools.build.bundletool.io.ApkSerializerHelper.requiresAapt2Conversion) IOException(java.io.IOException) FileUtils.createParentDirectories(com.android.tools.build.bundletool.model.utils.files.FileUtils.createParentDirectories) VerboseLogs(com.android.tools.build.bundletool.commands.BuildApksModule.VerboseLogs) Entry(com.android.zipflinger.Entry) Maps(com.google.common.collect.Maps) NO_COMPRESSION(com.android.tools.build.bundletool.model.CompressionLevel.NO_COMPRESSION) NO_DEFAULT_UNCOMPRESS_EXTENSIONS(com.android.tools.build.bundletool.model.version.VersionGuardedFeature.NO_DEFAULT_UNCOMPRESS_EXTENSIONS) FileUtils(com.android.tools.build.bundletool.model.utils.files.FileUtils) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) Entry(com.android.zipflinger.Entry) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ZipArchive(com.android.zipflinger.ZipArchive) ZipPath(com.android.tools.build.bundletool.model.ZipPath)

Example 32 with ModuleEntry

use of com.android.tools.build.bundletool.model.ModuleEntry in project bundletool by google.

the class ZipFlingerAppBundleSerializer method addEntriesFromSourceBundles.

/**
 * Adds umodified entries to an archive, sourcing them from their original on-disk location.
 */
private static void addEntriesFromSourceBundles(ZipArchive archive, ImmutableListMultimap<BundleModule, ModuleEntry> entries) throws IOException {
    Map<Path, ZipSource> bundleSources = new HashMap<>();
    for (Map.Entry<BundleModule, ModuleEntry> moduleAndEntry : entries.entries()) {
        BundleModule module = moduleAndEntry.getKey();
        ModuleEntry moduleEntry = moduleAndEntry.getValue();
        ModuleEntryBundleLocation location = moduleEntry.getBundleLocation().orElseThrow(IllegalStateException::new);
        ZipPath entryFullPathInSourceBundle = location.entryPathInBundle();
        ZipPath moduleDir = ZipPath.create(module.getName().toString());
        ZipPath entryFullPathInDestBundle = moduleDir.resolve(moduleEntry.getPath());
        Path pathToBundle = location.pathToBundle();
        // We cannot use computeIfAbstent because new ZipSource may throw.
        ZipSource entrySource = bundleSources.containsKey(pathToBundle) ? bundleSources.get(pathToBundle) : new ZipSource(pathToBundle);
        bundleSources.putIfAbsent(pathToBundle, entrySource);
        entrySource.select(entryFullPathInSourceBundle.toString(), /* newName= */
        entryFullPathInDestBundle.toString());
    }
    for (ZipSource source : bundleSources.values()) {
        archive.add(source);
    }
}
Also used : ZipPath(com.android.tools.build.bundletool.model.ZipPath) Path(java.nio.file.Path) ZipSource(com.android.zipflinger.ZipSource) HashMap(java.util.HashMap) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ZipPath(com.android.tools.build.bundletool.model.ZipPath) ModuleEntryBundleLocation(com.android.tools.build.bundletool.model.ModuleEntry.ModuleEntryBundleLocation) HashMap(java.util.HashMap) Map(java.util.Map) BundleModule(com.android.tools.build.bundletool.model.BundleModule)

Example 33 with ModuleEntry

use of com.android.tools.build.bundletool.model.ModuleEntry 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 34 with ModuleEntry

use of com.android.tools.build.bundletool.model.ModuleEntry in project bundletool by google.

the class ModuleSplitsToShardMerger method mergeSingleApexShard.

@VisibleForTesting
ModuleSplit mergeSingleApexShard(ImmutableList<ModuleSplit> splitsOfShard) {
    checkState(!splitsOfShard.isEmpty(), "A shard is made of at least one split.");
    Map<ZipPath, ModuleEntry> mergedEntriesByPath = new HashMap<>();
    ApkTargeting splitTargeting = ApkTargeting.getDefaultInstance();
    for (ModuleSplit split : splitsOfShard) {
        // An APEX shard is made of one master split and one multi-Abi split, so we use the latter.
        splitTargeting = splitTargeting.hasMultiAbiTargeting() ? splitTargeting : split.getApkTargeting();
        for (ModuleEntry entry : split.getEntries()) {
            mergeEntries(mergedEntriesByPath, split, entry);
        }
    }
    ModuleSplit shard = buildShard(mergedEntriesByPath.values(), ImmutableList.of(), splitTargeting, // An APEX module is made of one module, so any manifest works.
    splitsOfShard.get(0).getAndroidManifest(), /* mergedResourceTable= */
    Optional.empty(), /* mergedAssetsConfig= */
    new HashMap<>(), /* mergedSplitType= */
    SplitType.STANDALONE);
    // Add the APEX config as it's used to identify APEX APKs.
    return shard.toBuilder().setApexConfig(splitsOfShard.get(0).getApexConfig().get()).setApexEmbeddedApkConfigs(splitsOfShard.get(0).getApexEmbeddedApkConfigs()).build();
}
Also used : 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) ZipPath(com.android.tools.build.bundletool.model.ZipPath) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 35 with ModuleEntry

use of com.android.tools.build.bundletool.model.ModuleEntry in project bundletool by google.

the class ModuleSplitsToShardMerger method mergeEntries.

private static void mergeEntries(Map<ZipPath, ModuleEntry> mergedEntriesByPath, ModuleSplit split, ModuleEntry entry) {
    ModuleEntry existingEntry = mergedEntriesByPath.putIfAbsent(entry.getPath(), entry);
    // Any conflicts of plain entries should be caught by bundle validations.
    checkState(existingEntry == null || existingEntry.equals(entry), "Module '%s' and some other module(s) contain entry '%s' with different contents.", split.getModuleName(), entry.getPath());
}
Also used : ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry)

Aggregations

ModuleEntry (com.android.tools.build.bundletool.model.ModuleEntry)41 ModuleSplit (com.android.tools.build.bundletool.model.ModuleSplit)22 ZipPath (com.android.tools.build.bundletool.model.ZipPath)18 ImmutableList (com.google.common.collect.ImmutableList)18 Test (org.junit.Test)13 BundleModule (com.android.tools.build.bundletool.model.BundleModule)12 ImmutableSet (com.google.common.collect.ImmutableSet)12 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)10 SpecialModuleEntry (com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry)8 BundleModuleBuilder (com.android.tools.build.bundletool.testing.BundleModuleBuilder)6 HashMap (java.util.HashMap)6 Optional (java.util.Optional)6 TargetedAssetsDirectory (com.android.bundle.Files.TargetedAssetsDirectory)5 ImmutableSet.toImmutableSet (com.google.common.collect.ImmutableSet.toImmutableSet)5 IOException (java.io.IOException)5 Map (java.util.Map)5 ResourceTable (com.android.aapt.Resources.ResourceTable)4 ApkTargeting (com.android.bundle.Targeting.ApkTargeting)4 BundleModuleName (com.android.tools.build.bundletool.model.BundleModuleName)4 ApkOptimizations (com.android.tools.build.bundletool.optimizations.ApkOptimizations)4