use of com.android.bundle.Config.BundleConfig in project bundletool by google.
the class BuildBundleCommandTest method bundleConfig_saved.
@Test
public void bundleConfig_saved() throws Exception {
Path module = createSimpleBaseModule();
// Any version supplied by the user is ignored and overwritten.
BundleConfig bundleConfigFromUser = BundleConfigBuilder.create().setVersion("0.0.0").build();
BundleConfig bundleConfigInBundle = new BundleConfigBuilder(bundleConfigFromUser).setVersion(BundleToolVersion.getCurrentVersion().toString()).build();
assertThat(bundleConfigFromUser.getBundletool().getVersion()).isNotEqualTo(bundleConfigInBundle.getBundletool().getVersion());
BuildBundleCommand.builder().setModulesPaths(ImmutableList.of(module)).setOutputPath(bundlePath).setBundleConfig(bundleConfigFromUser).build().execute();
try (ZipFile appBundleZip = new ZipFile(bundlePath.toFile())) {
AppBundle appBundle = AppBundle.buildFromZip(appBundleZip);
assertThat(appBundle.getBundleConfig()).isEqualTo(bundleConfigInBundle);
}
}
use of com.android.bundle.Config.BundleConfig in project bundletool by google.
the class AppBundleRecompressor method extractBundleConfig.
private static BundleConfig extractBundleConfig(ZipReader zipReader) {
// The bundle hasn't been validated yet, therefore selected validations need to be run manually.
if (!zipReader.getEntry(AppBundle.BUNDLE_CONFIG_FILE_NAME).isPresent()) {
throw InvalidBundleException.builder().withUserMessage("The archive doesn't seem to be an App Bundle, it is missing required file '%s'.", AppBundle.BUNDLE_CONFIG_FILE_NAME).build();
}
try (InputStream inputStream = zipReader.getUncompressedPayload(AppBundle.BUNDLE_CONFIG_FILE_NAME)) {
BundleConfig bundleConfig = BundleConfig.parseFrom(inputStream, ExtensionRegistryLite.getEmptyRegistry());
new BundleConfigValidator().validateCompression(bundleConfig.getCompression());
return bundleConfig;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
use of com.android.bundle.Config.BundleConfig 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);
}
}
use of com.android.bundle.Config.BundleConfig in project bundletool by google.
the class TextureCompressionFormatParityValidator method validateBundle.
@Override
public void validateBundle(AppBundle bundle) {
BundleConfig bundleConfig = bundle.getBundleConfig();
Optimizations optimizations = bundleConfig.getOptimizations();
List<SplitDimension> splitDimensions = optimizations.getSplitsConfig().getSplitDimensionList();
Optional<String> tcfDefaultSuffix = splitDimensions.stream().filter(dimension -> dimension.getValue().equals(Value.TEXTURE_COMPRESSION_FORMAT)).map(dimension -> dimension.getSuffixStripping().getDefaultSuffix()).collect(toOptional());
if (tcfDefaultSuffix.isPresent()) {
// Get the default texture compression format targeting, or an empty optional if fallback
// must be used.
Optional<TextureCompressionFormatTargeting> defaultTextureCompressionFormat = Optional.ofNullable(TextureCompressionUtils.TEXTURE_TO_TARGETING.get(tcfDefaultSuffix.get()));
validateFormatSupportedByAllModules(bundle, defaultTextureCompressionFormat);
}
}
use of com.android.bundle.Config.BundleConfig 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