Search in sources :

Example 1 with ZipArchive

use of com.android.zipflinger.ZipArchive 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 2 with ZipArchive

use of com.android.zipflinger.ZipArchive 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 3 with ZipArchive

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

the class ApkSetWriter method zip.

/**
 * Creates ApkSet writer which stores all splits as ZIP archive.
 */
static ApkSetWriter zip(Path tempDirectory, Path outputFile) {
    return new ApkSetWriter() {

        @Override
        public Path getSplitsDirectory() {
            return tempDirectory;
        }

        @Override
        public void writeApkSet(BuildApksResult toc) throws IOException {
            Stream<ApkDescription> apks = toc.getVariantList().stream().flatMap(variant -> variant.getApkSetList().stream()).flatMap(apkSet -> apkSet.getApkDescriptionList().stream());
            Stream<ApkDescription> assets = toc.getAssetSliceSetList().stream().flatMap(assetSliceSet -> assetSliceSet.getApkDescriptionList().stream());
            ImmutableSet<String> apkRelativePaths = Stream.concat(apks, assets).map(ApkDescription::getPath).sorted().collect(toImmutableSet());
            zipApkSet(apkRelativePaths, toc.toByteArray());
        }

        @Override
        public void writeApkSet(BuildSdkApksResult toc) throws IOException {
            Stream<ApkDescription> apks = toc.getVariantList().stream().flatMap(variant -> variant.getApkSetList().stream()).flatMap(apkSet -> apkSet.getApkDescriptionList().stream());
            ImmutableSet<String> apkRelativePaths = apks.map(ApkDescription::getPath).sorted().collect(toImmutableSet());
            zipApkSet(apkRelativePaths, toc.toByteArray());
        }

        private void zipApkSet(ImmutableSet<String> apkRelativePaths, byte[] tocBytes) throws IOException {
            try (ZipArchive zipArchive = new ZipArchive(outputFile)) {
                zipArchive.add(new BytesSource(tocBytes, TABLE_OF_CONTENTS_FILE, Deflater.NO_COMPRESSION));
                for (String relativePath : apkRelativePaths) {
                    zipArchive.add(new LargeFileSource(getSplitsDirectory().resolve(relativePath), /* tmpStorage= */
                    null, relativePath, Deflater.NO_COMPRESSION));
                }
            }
        }
    };
}
Also used : TABLE_OF_CONTENTS_FILE(com.android.tools.build.bundletool.model.utils.FileNames.TABLE_OF_CONTENTS_FILE) ImmutableSet(com.google.common.collect.ImmutableSet) Files(java.nio.file.Files) BuildApksResult(com.android.bundle.Commands.BuildApksResult) BytesSource(com.android.zipflinger.BytesSource) LargeFileSource(com.android.zipflinger.LargeFileSource) IOException(java.io.IOException) Deflater(java.util.zip.Deflater) Stream(java.util.stream.Stream) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) ApkDescription(com.android.bundle.Commands.ApkDescription) ZipArchive(com.android.zipflinger.ZipArchive) BuildSdkApksResult(com.android.bundle.Commands.BuildSdkApksResult) Path(java.nio.file.Path) BuildSdkApksResult(com.android.bundle.Commands.BuildSdkApksResult) BytesSource(com.android.zipflinger.BytesSource) ApkDescription(com.android.bundle.Commands.ApkDescription) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) BuildApksResult(com.android.bundle.Commands.BuildApksResult) ZipArchive(com.android.zipflinger.ZipArchive) LargeFileSource(com.android.zipflinger.LargeFileSource)

Example 4 with ZipArchive

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

the class ModuleEntriesPack method mergeWith.

/**
 * Merges two packs into a single one.
 *
 * <p>Prefers to merge smaller pack into bigger one. Removes zip file of unused pack.
 */
public ModuleEntriesPack mergeWith(ModuleEntriesPack anotherPack) {
    checkArgument(Collections.disjoint(namePrefixes, anotherPack.namePrefixes), "Both packs contain the same name prefix.");
    try {
        long thisSize = Files.size(zipMap.getPath());
        long anotherSize = Files.size(anotherPack.zipMap.getPath());
        ModuleEntriesPack to = thisSize > anotherSize ? this : anotherPack;
        ModuleEntriesPack from = thisSize > anotherSize ? anotherPack : this;
        try (ZipArchive archive = new ZipArchive(to.zipMap.getPath())) {
            archive.add(ZipSource.selectAll(from.zipMap.getPath()));
        }
        Files.delete(from.zipMap.getPath());
        IdentityHashMap<ModuleEntry, String> mergedNames = Streams.concat(entryNameByModuleEntry.entrySet().stream(), anotherPack.entryNameByModuleEntry.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b, Maps::newIdentityHashMap));
        return new ModuleEntriesPack(Sets.union(to.namePrefixes, from.namePrefixes).immutableCopy(), ZipMap.from(to.zipMap.getPath()), mergedNames);
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
Also used : ImmutableSet(com.google.common.collect.ImmutableSet) IdentityHashMap(java.util.IdentityHashMap) Files(java.nio.file.Files) IOException(java.io.IOException) Streams(com.google.common.collect.Streams) Entry(com.android.zipflinger.Entry) ZipSource(com.android.zipflinger.ZipSource) Maps(com.google.common.collect.Maps) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) UncheckedIOException(java.io.UncheckedIOException) ZipMap(com.android.zipflinger.ZipMap) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) ImmutableList(com.google.common.collect.ImmutableList) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) Map(java.util.Map) ZipArchive(com.android.zipflinger.ZipArchive) ToLongFunction(java.util.function.ToLongFunction) Collections(java.util.Collections) ModuleEntry(com.android.tools.build.bundletool.model.ModuleEntry) ZipArchive(com.android.zipflinger.ZipArchive) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) IdentityHashMap(java.util.IdentityHashMap) ZipMap(com.android.zipflinger.ZipMap) Map(java.util.Map)

Example 5 with ZipArchive

use of com.android.zipflinger.ZipArchive 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

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