Search in sources :

Example 1 with Entry

use of com.android.zipflinger.Entry in project bundletool by google.

the class ModuleSplitSerializer method serialize.

/**
 * Serializes module splits on disk under {@code outputDirectory}.
 *
 * <p>Returns {@link ApkDescription} for each serialized split keyed by relative path of module
 * split.
 */
public ImmutableMap<ZipPath, ApkDescription> serialize(Path outputDirectory, ImmutableMap<ZipPath, ModuleSplit> splitsByRelativePath) {
    // Prepare original splits by:
    // * signing embedded APKs
    // * injecting manifest and resource table as module entries.
    ImmutableList<ModuleSplit> preparedSplits = splitsByRelativePath.values().stream().map(apkSigner::signEmbeddedApks).map(ModuleSplitSerializer::injectManifestAndResourceTableAsEntries).collect(toImmutableList());
    try (SerializationFilesManager filesManager = new SerializationFilesManager()) {
        // Convert module splits to binary format and apply uncompressed globs specified in
        // BundleConfig. We do it in this order because as specified in documentation the matching
        // for uncompressed globs is done against paths in final APKs.
        ImmutableList<ModuleSplit> binarySplits = aapt2ResourceConverter.convert(preparedSplits, filesManager).stream().map(this::applyUncompressedGlobsAndUncompressedNativeLibraries).collect(toImmutableList());
        // Build a pack from entries which may be compressed inside final APKs. 'May be
        // compressed' means that for these entries we will decide later should they be compressed
        // or not based on whether we gain enough savings from compression.
        ModuleEntriesPack maybeCompressedEntriesPack = buildCompressedEntriesPack(filesManager.getCompressedResourceEntriesPackPath(), filesManager.getCompressedEntriesPackPath(), binarySplits);
        // Build a pack with entries that are uncompressed in final APKs: force uncompressed entries
        // + entries that have very low compression ratio.
        ModuleEntriesPack uncompressedEntriesPack = buildUncompressedEntriesPack(filesManager.getUncompressedEntriesPackPath(), binarySplits, maybeCompressedEntriesPack);
        // Now content of all binary apks is already moved to compressed/uncompressed packs. Delete
        // them to free space.
        filesManager.closeAndRemoveBinaryApks();
        // Merge two packs together, so we have all entries for final APKs inside one pack. If the
        // same entry is in both packs we prefer uncompressed one, because it means this entry
        // has very low compression ratio, it makes no sense to put it in compressed form.
        ModuleEntriesPack allEntriesPack = maybeCompressedEntriesPack.mergeWith(uncompressedEntriesPack);
        // Serialize and sign final APKs.
        ImmutableList<ListenableFuture<ApkDescription>> apkDescriptions = Streams.zip(splitsByRelativePath.keySet().stream(), binarySplits.stream(), (relativePath, split) -> executorService.submit(() -> serializeAndSignSplit(outputDirectory, relativePath, split, allEntriesPack, uncompressedEntriesPack))).collect(toImmutableList());
        return ConcurrencyUtils.waitForAll(apkDescriptions).stream().collect(toImmutableMap(apk -> ZipPath.create(apk.getPath()), identity()));
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
Also used : PathMatcher(com.android.tools.build.bundletool.model.utils.PathMatcher) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) ZipPath(com.android.tools.build.bundletool.model.ZipPath) ApkListener(com.android.tools.build.bundletool.model.ApkListener) Inject(javax.inject.Inject) ApkSerializerHelper.toApkEntryPath(com.android.tools.build.bundletool.io.ApkSerializerHelper.toApkEntryPath) ImmutableList(com.google.common.collect.ImmutableList) ApkDescription(com.android.bundle.Commands.ApkDescription) ZipArchive(com.android.zipflinger.ZipArchive) Version(com.android.tools.build.bundletool.model.version.Version) ByteSource(com.google.common.io.ByteSource) Path(java.nio.file.Path) ImmutableMap(com.google.common.collect.ImmutableMap) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Collection(java.util.Collection) ApkSerializerHelper.requiresAapt2Conversion(com.android.tools.build.bundletool.io.ApkSerializerHelper.requiresAapt2Conversion) IOException(java.io.IOException) Deflater(java.util.zip.Deflater) VerboseLogs(com.android.tools.build.bundletool.commands.BuildApksModule.VerboseLogs) Streams(com.google.common.collect.Streams) Entry(com.android.zipflinger.Entry) ZipSource(com.android.zipflinger.ZipSource) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) UncheckedIOException(java.io.UncheckedIOException) ImmutableMap.toImmutableMap(com.google.common.collect.ImmutableMap.toImmutableMap) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) BundleConfig(com.android.bundle.Config.BundleConfig) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) NO_DEFAULT_UNCOMPRESS_EXTENSIONS(com.android.tools.build.bundletool.model.version.VersionGuardedFeature.NO_DEFAULT_UNCOMPRESS_EXTENSIONS) Function.identity(java.util.function.Function.identity) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) Comparator(java.util.Comparator) FileUtils(com.android.tools.build.bundletool.model.utils.files.FileUtils) ListeningExecutorService(com.google.common.util.concurrent.ListeningExecutorService) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException)

Example 2 with Entry

use of com.android.zipflinger.Entry in project bundletool by google.

the class ModuleSplitSerializer method shouldUncompressBecauseOfLowRatio.

/**
 * Whether module entry should be put in uncompressed form because savings for the entry is low.
 */
private boolean shouldUncompressBecauseOfLowRatio(ModuleEntry moduleEntry, ModuleEntriesPack compressedPack) {
    Entry entry = compressedPack.getZipEntry(moduleEntry);
    long compressedSize = entry.getCompressedSize();
    long uncompressedSize = entry.getUncompressedSize();
    // Copying logic from aapt2: require at least 10% gains in savings.
    if (moduleEntry.getPath().startsWith("res")) {
        return compressedSize + compressedSize / 10 > uncompressedSize;
    }
    return compressedSize >= uncompressedSize;
}
Also used : Entry(com.android.zipflinger.Entry) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry)

Example 3 with Entry

use of com.android.zipflinger.Entry in project bundletool by google.

the class ModuleSplitSerializer method serializeSplit.

private void serializeSplit(Path outputPath, ModuleSplit split, ModuleEntriesPack allEntriesPack, ModuleEntriesPack uncompressedEntriesPack) {
    FileUtils.createDirectories(outputPath.getParent());
    try (ZipArchive archive = new ZipArchive(outputPath)) {
        ImmutableMap<ZipPath, ModuleEntry> moduleEntriesByName = split.getEntries().stream().collect(toImmutableMap(entry -> toApkEntryPath(entry.getPath()), entry -> entry, // e.g. base/assets/foo and base/root/assets/foo.
        (a, b) -> b));
        // Sorting entries by name for determinism.
        ImmutableList<ModuleEntry> sortedEntries = ImmutableList.sortedCopyOf(Comparator.comparing(e -> toApkEntryPath(e.getPath())), moduleEntriesByName.values());
        ZipSource zipSource = allEntriesPack.select(sortedEntries, entry -> toApkEntryPath(entry.getPath(), /* binaryApk= */
        true).toString(), entry -> alignmentForEntry(entry, uncompressedEntriesPack));
        archive.add(zipSource);
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
Also used : PathMatcher(com.android.tools.build.bundletool.model.utils.PathMatcher) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) ZipPath(com.android.tools.build.bundletool.model.ZipPath) ApkListener(com.android.tools.build.bundletool.model.ApkListener) Inject(javax.inject.Inject) ApkSerializerHelper.toApkEntryPath(com.android.tools.build.bundletool.io.ApkSerializerHelper.toApkEntryPath) ImmutableList(com.google.common.collect.ImmutableList) ApkDescription(com.android.bundle.Commands.ApkDescription) ZipArchive(com.android.zipflinger.ZipArchive) Version(com.android.tools.build.bundletool.model.version.Version) ByteSource(com.google.common.io.ByteSource) Path(java.nio.file.Path) ImmutableMap(com.google.common.collect.ImmutableMap) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Collection(java.util.Collection) ApkSerializerHelper.requiresAapt2Conversion(com.android.tools.build.bundletool.io.ApkSerializerHelper.requiresAapt2Conversion) IOException(java.io.IOException) Deflater(java.util.zip.Deflater) VerboseLogs(com.android.tools.build.bundletool.commands.BuildApksModule.VerboseLogs) Streams(com.google.common.collect.Streams) Entry(com.android.zipflinger.Entry) ZipSource(com.android.zipflinger.ZipSource) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) UncheckedIOException(java.io.UncheckedIOException) ImmutableMap.toImmutableMap(com.google.common.collect.ImmutableMap.toImmutableMap) ModuleSplit(com.android.tools.build.bundletool.model.ModuleSplit) BundleConfig(com.android.bundle.Config.BundleConfig) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) NO_DEFAULT_UNCOMPRESS_EXTENSIONS(com.android.tools.build.bundletool.model.version.VersionGuardedFeature.NO_DEFAULT_UNCOMPRESS_EXTENSIONS) Function.identity(java.util.function.Function.identity) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) Comparator(java.util.Comparator) FileUtils(com.android.tools.build.bundletool.model.utils.files.FileUtils) ListeningExecutorService(com.google.common.util.concurrent.ListeningExecutorService) ZipSource(com.android.zipflinger.ZipSource) SpecialModuleEntry(com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ZipArchive(com.android.zipflinger.ZipArchive) UncheckedIOException(java.io.UncheckedIOException) ZipPath(com.android.tools.build.bundletool.model.ZipPath) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException)

Example 4 with Entry

use of com.android.zipflinger.Entry in project bundletool by google.

the class ZipFlingerApkSerializer method writeProtoApk.

/**
 * Writes an APK for aapt2 to convert to binary.
 *
 * <p>This APK contains only files that aapt2 will convert from proto to binary, and the files
 * needed for the conversion to succeed (e.g. all resources referenced in the resource table).
 *
 * <p>All files are left uncompressed to save aapt2 from re-compressing all the entries it
 * generated since we prefer having control over which compression is used. This is effectively a
 * tradeoff between local storage and CPU.
 *
 * <p>No entry is 4-byte aligned since it's only a temporary APK for aapt2 conversion which won't
 * be actually stored or served to any device.
 */
private void writeProtoApk(ModuleSplit split, Path protoApkPath, TempDirectory tempDir) throws IOException {
    try (ZipArchive apkWriter = new ZipArchive(protoApkPath)) {
        apkWriter.add(new BytesSource(split.getAndroidManifest().getManifestRoot().getProto().toByteArray(), MANIFEST_FILENAME, NO_COMPRESSION.getValue()));
        if (split.getResourceTable().isPresent()) {
            BytesSource bytesSource = new BytesSource(split.getResourceTable().get().toByteArray(), SpecialModuleEntry.RESOURCE_TABLE.getPath().toString(), NO_COMPRESSION.getValue());
            bytesSource.align(4);
            apkWriter.add(bytesSource);
        }
        Map<String, Entry> bundleEntriesByName = bundleZipReader.getEntries();
        ZipEntrySourceFactory sourceFactory = new ZipEntrySourceFactory(bundleZipReader, tempDir);
        for (ModuleEntry moduleEntry : split.getEntries()) {
            ZipPath pathInApk = ApkSerializerHelper.toApkEntryPath(moduleEntry.getPath());
            if (!requiresAapt2Conversion(pathInApk)) {
                continue;
            }
            if (moduleEntry.getBundleLocation().isPresent()) {
                ZipPath pathInBundle = moduleEntry.getBundleLocation().get().entryPathInBundle();
                Entry entry = bundleEntriesByName.get(pathInBundle.toString());
                checkNotNull(entry, "Could not find entry '%s'.", pathInBundle);
                apkWriter.add(sourceFactory.create(entry, pathInApk, NO_COMPRESSION));
            } else {
                apkWriter.add(new BytesSource(moduleEntry.getContent().read(), pathInApk.toString(), NO_COMPRESSION.getValue()));
            }
        }
    }
}
Also used : BytesSource(com.android.zipflinger.BytesSource) 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 5 with Entry

use of com.android.zipflinger.Entry in project bundletool by google.

the class AppBundleRecompressor method recompressAppBundle.

public void recompressAppBundle(File inputFile, File outputFile) {
    try (ZipReader zipReader = ZipReader.createFromFile(inputFile.toPath());
        ZipArchive newBundle = new ZipArchive(outputFile);
        TempDirectory tempDirectory = new TempDirectory(getClass().getSimpleName())) {
        ZipEntrySourceFactory sourceFactory = new ZipEntrySourceFactory(zipReader, tempDirectory);
        List<ListenableFuture<ZipEntrySource>> sources = new ArrayList<>();
        BundleConfig bundleConfig = extractBundleConfig(zipReader);
        ImmutableSet<String> uncompressedAssetsModules = extractModulesWithUncompressedAssets(zipReader, bundleConfig);
        CompressionManager compressionManager = new CompressionManager(bundleConfig, uncompressedAssetsModules);
        for (Entry entry : zipReader.getEntries().values()) {
            CompressionLevel compressionLevel = compressionManager.getCompressionLevel(entry);
            // parallelization there either.
            if (compressionLevel.equals(SAME_AS_SOURCE) || compressionLevel.equals(NO_COMPRESSION) || entry.getUncompressedSize() < LARGE_ENTRY_SIZE_THRESHOLD_BYTES) {
                sources.add(immediateFuture(sourceFactory.create(entry, compressionLevel)));
            } else {
                sources.add(executor.submit(() -> sourceFactory.create(entry, compressionLevel)));
            }
        }
        // as they're ready.
        for (ListenableFuture<ZipEntrySource> sourceFuture : Futures.inCompletionOrder(sources)) {
            ZipEntrySource source = Futures.getUnchecked(sourceFuture);
            if (source.getCompressionLevel().isCompressed() && source.getCompressedSize() >= source.getUncompressedSize()) {
                // No benefit in compressing, leave the file uncompressed.
                newBundle.add(sourceFactory.create(source.getEntry(), NO_COMPRESSION));
            } else {
                newBundle.add(source);
            }
        }
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
Also used : ArrayList(java.util.ArrayList) ZipArchive(com.android.zipflinger.ZipArchive) ZipReader(com.android.tools.build.bundletool.io.ZipReader) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) ZipEntrySourceFactory(com.android.tools.build.bundletool.io.ZipEntrySourceFactory) TempDirectory(com.android.tools.build.bundletool.io.TempDirectory) BundleConfig(com.android.bundle.Config.BundleConfig) Entry(com.android.zipflinger.Entry) CompressionLevel(com.android.tools.build.bundletool.model.CompressionLevel) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) ZipEntrySource(com.android.tools.build.bundletool.io.ZipEntrySource)

Aggregations

Entry (com.android.zipflinger.Entry)8 SpecialModuleEntry (com.android.tools.build.bundletool.model.BundleModule.SpecialModuleEntry)5 ModuleEntry (com.android.tools.build.bundletool.model.ModuleEntry)5 ZipArchive (com.android.zipflinger.ZipArchive)5 IOException (java.io.IOException)5 UncheckedIOException (java.io.UncheckedIOException)5 BundleConfig (com.android.bundle.Config.BundleConfig)4 ZipPath (com.android.tools.build.bundletool.model.ZipPath)4 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)4 ApkDescription (com.android.bundle.Commands.ApkDescription)3 VerboseLogs (com.android.tools.build.bundletool.commands.BuildApksModule.VerboseLogs)3 ApkSerializerHelper.requiresAapt2Conversion (com.android.tools.build.bundletool.io.ApkSerializerHelper.requiresAapt2Conversion)3 ApkListener (com.android.tools.build.bundletool.model.ApkListener)3 ModuleSplit (com.android.tools.build.bundletool.model.ModuleSplit)3 PathMatcher (com.android.tools.build.bundletool.model.utils.PathMatcher)3 FileUtils (com.android.tools.build.bundletool.model.utils.files.FileUtils)3 Version (com.android.tools.build.bundletool.model.version.Version)3 NO_DEFAULT_UNCOMPRESS_EXTENSIONS (com.android.tools.build.bundletool.model.version.VersionGuardedFeature.NO_DEFAULT_UNCOMPRESS_EXTENSIONS)3 ApkSerializerHelper.toApkEntryPath (com.android.tools.build.bundletool.io.ApkSerializerHelper.toApkEntryPath)2 EntryNotFoundException (com.android.tools.build.bundletool.io.ZipReader.EntryNotFoundException)2