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);
}
}
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()));
}
}
}
}
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));
}
}
}
};
}
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);
}
}
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);
}
}
Aggregations