Skip to content

Commit

Permalink
Prepare for release 1.10.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
Iurii Makhno committed Apr 25, 2022
1 parent 791d6cc commit f7f5fd2
Show file tree
Hide file tree
Showing 82 changed files with 2,284 additions and 2,073 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ https://developer.android.com/studio/command-line/bundletool

## Releases

Latest release: [1.9.1](https://github.com/google/bundletool/releases)
Latest release: [1.10.0](https://github.com/google/bundletool/releases)
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ configurations {
// The repackaging rules are defined in the "shadowJar" task below.
dependencies {
implementation "com.android.tools:common:30.1.0-alpha07"
implementation "com.android.tools:r8:2.2.64"
implementation "com.android.tools:r8:3.3.28"
implementation "com.android.tools.build:apksig:4.2.0-alpha13"
implementation "com.android.tools.ddms:ddmlib:30.1.0-alpha07"
implementation "com.android:zipflinger:7.1.0-alpha07"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 1.9.1
release_version = 1.10.0
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@ public static P7ZipCommand defaultP7ZipCommand(Path p7zipExecutable, int numThre
"a",
"-tzip",
"-mtc=off",
String.format("-mx=%d", numThreads),
"-mx=9",
String.format("-mmt=%d", numThreads),
"-bso0",
"-bsp0",
"-r",
outputPath.toAbsolutePath().normalize().toString(),
String.join(
File.pathSeparator,
inputDirectoryPath.toAbsolutePath().normalize().toString(),
"*"));
File.separator, inputDirectoryPath.toAbsolutePath().normalize().toString(), "*"));
new DefaultCommandExecutor()
.execute(command, CommandOptions.builder().setTimeout(Duration.ofMinutes(10)).build());
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.android.tools.build.bundletool.archive;

import static com.android.tools.build.bundletool.model.AndroidManifest.ANDROID_NAMESPACE_URI;
import static com.android.tools.build.bundletool.model.AndroidManifest.LAUNCHER_CATEGORY_NAME;
import static com.android.tools.build.bundletool.model.AndroidManifest.MAIN_ACTION_NAME;
import static com.google.common.base.Preconditions.checkNotNull;

import com.android.tools.build.bundletool.model.AndroidManifest;
Expand All @@ -37,8 +39,6 @@ public final class ArchivedAndroidManifestUtils {
"com.google.android.archive.ReactivateActivity";
public static final String HOLO_LIGHT_NO_ACTION_BAR_THEME =
"@android:style/Theme.Holo.Light.NoActionBar";
public static final String MAIN_ACTION_NAME = "android.intent.action.MAIN";
public static final String LAUNCHER_CATEGORY_NAME = "android.intent.category.LAUNCHER";

public static final String UPDATE_BROADCAST_RECEIVER_NAME =
"com.google.android.archive.UpdateBroadcastReceiver";
Expand Down Expand Up @@ -67,6 +67,7 @@ public static AndroidManifest createArchivedManifest(AndroidManifest manifest) {
manifest.getHasFragileUserData().ifPresent(editor::setHasFragileUserData);
manifest.getIsGame().ifPresent(editor::setIsGame);
manifest.getIcon().ifPresent(editor::setIcon);
manifest.getBanner().ifPresent(editor::setBanner);
if (manifest.hasLabelString()) {
manifest.getLabelString().ifPresent(editor::setLabelAsString);
}
Expand All @@ -77,10 +78,14 @@ public static AndroidManifest createArchivedManifest(AndroidManifest manifest) {
manifest.getFullBackupOnly().ifPresent(editor::setFullBackupOnly);
manifest.getFullBackupContent().ifPresent(editor::setFullBackupContent);
manifest.getDataExtractionRules().ifPresent(editor::setDataExtractionRules);
manifest.getRestrictedAccountType().ifPresent(editor::setRestrictedAccountType);
manifest.getRequiredAccountType().ifPresent(editor::setRequiredAccountType);
manifest.getLargeHeap().ifPresent(editor::setLargeHeap);
}

editor.copyPermissions(manifest);
editor.copyPermissionGroups(manifest);
editor.copyPermissionTrees(manifest);

editor.addActivity(createReactivateActivity());
editor.addReceiver(createUpdateBroadcastReceiver());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.android.tools.build.bundletool.archive;

import static com.android.tools.build.bundletool.model.version.VersionGuardedFeature.ARCHIVED_APK_GENERATION;
import static com.android.tools.build.bundletool.model.version.VersionGuardedFeature.STORE_ARCHIVE_ENABLED_BY_DEFAULT;
import static com.google.common.base.Preconditions.checkNotNull;

import com.android.aapt.Resources.ResourceTable;
Expand All @@ -31,6 +32,7 @@
import com.android.tools.build.bundletool.model.exceptions.InvalidCommandException;
import com.android.tools.build.bundletool.model.utils.ResourcesUtils;
import com.android.tools.build.bundletool.model.version.BundleToolVersion;
import com.android.tools.build.bundletool.model.version.Version;
import com.android.tools.build.bundletool.splitters.ResourceAnalyzer;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
Expand Down Expand Up @@ -78,9 +80,9 @@ public ModuleSplit generateArchivedApk(

private void validateRequest(AppBundle appBundle) {
checkNotNull(appBundle);

if (!ARCHIVED_APK_GENERATION.enabledForVersion(
BundleToolVersion.getVersionFromBundleConfig(appBundle.getBundleConfig()))) {
Version bundletoolVersion =
BundleToolVersion.getVersionFromBundleConfig(appBundle.getBundleConfig());
if (!ARCHIVED_APK_GENERATION.enabledForVersion(bundletoolVersion)) {
throw InvalidCommandException.builder()
.withInternalMessage(
String.format(
Expand All @@ -89,12 +91,22 @@ private void validateRequest(AppBundle appBundle) {
.build();
}

if (!appBundle.storeArchiveEnabled()) {
Optional<Boolean> storeArchiveConfig = appBundle.getStoreArchive();
boolean isStoreArchiveEnabledByDefault =
STORE_ARCHIVE_ENABLED_BY_DEFAULT.enabledForVersion(bundletoolVersion);
if (!storeArchiveConfig.orElse(isStoreArchiveEnabledByDefault)) {
throw InvalidCommandException.builder()
.withInternalMessage(
"Archived APK cannot be generated when Store Archive configuration is disabled.")
.build();
}

if (appBundle.getBaseModule().getAndroidManifest().isHeadless()) {
throw InvalidCommandException.builder()
.withInternalMessage(
"Archived APK can not be generated for applications without a launcher activity.")
.build();
}
}

private ResourceTable getArchivedResourceTable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static com.android.tools.build.bundletool.commands.BuildApksCommand.OutputFormat.APK_SET;
import static com.android.tools.build.bundletool.commands.BuildApksCommand.OutputFormat.DIRECTORY;
import static com.android.tools.build.bundletool.commands.CommandUtils.ANDROID_SERIAL_VARIABLE;
import static com.android.tools.build.bundletool.model.utils.BundleParser.getModulesZip;
import static com.android.tools.build.bundletool.model.utils.SdkToolsLocator.ANDROID_HOME_VARIABLE;
import static com.android.tools.build.bundletool.model.utils.SdkToolsLocator.SYSTEM_PATH_VARIABLE;
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileDoesNotExist;
Expand All @@ -31,8 +32,6 @@
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileHasExtension;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
import static java.util.function.Function.identity;

import com.android.apksig.SigningCertificateLineage;
import com.android.apksig.apk.ApkFormatException;
Expand All @@ -49,7 +48,6 @@
import com.android.tools.build.bundletool.flags.Flag;
import com.android.tools.build.bundletool.flags.ParsedFlags;
import com.android.tools.build.bundletool.io.TempDirectory;
import com.android.tools.build.bundletool.io.ZipReader;
import com.android.tools.build.bundletool.model.ApkListener;
import com.android.tools.build.bundletool.model.ApkModifier;
import com.android.tools.build.bundletool.model.AppBundle;
Expand All @@ -68,7 +66,6 @@
import com.android.tools.build.bundletool.model.utils.SystemEnvironmentProvider;
import com.android.tools.build.bundletool.model.utils.files.FileUtils;
import com.android.tools.build.bundletool.preprocessors.AppBundlePreprocessorManager;
import com.android.tools.build.bundletool.preprocessors.AppBundleRecompressor;
import com.android.tools.build.bundletool.preprocessors.DaggerAppBundlePreprocessorComponent;
import com.android.tools.build.bundletool.validation.AppBundleValidator;
import com.android.tools.build.bundletool.validation.SdkBundleValidator;
Expand All @@ -77,6 +74,7 @@
import com.google.common.base.Ascii;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
Expand Down Expand Up @@ -297,7 +295,7 @@ public static Builder builder() {
.setModules(ImmutableSet.of())
.setExtraValidators(ImmutableList.of())
.setSystemApkOptions(ImmutableSet.of())
.setEnableApkSerializerWithoutBundleRecompression(false)
.setEnableApkSerializerWithoutBundleRecompression(true)
.setRuntimeEnabledSdkBundlePaths(ImmutableSet.of());
}

Expand Down Expand Up @@ -513,7 +511,9 @@ public Builder setCreateApkSetArchive(boolean createApkSetArchive) {
* Sets package name of an app store that will be called by archived app to redownload the
* application.
*
* <p>PlayStore package is used by default.
* <p>Can only be used in ARCHIVE mode.
*
* <p>PlayStore package (com.android.vending) is used by default.
*/
public abstract Builder setAppStorePackageName(String appStorePackageName);

Expand Down Expand Up @@ -632,6 +632,15 @@ public BuildApksCommand build() {
UNIVERSAL.getLowerCaseName(), SYSTEM.getLowerCaseName())
.build();
}

if (command.getAppStorePackageName().isPresent()
&& !command.getApkBuildMode().equals(ARCHIVE)) {
throw InvalidCommandException.builder()
.withInternalMessage(
"Providing custom store package is only possible when running with '%s' mode flag.",
ARCHIVE.getLowerCaseName())
.build();
}
return command;
}
}
Expand Down Expand Up @@ -746,64 +755,43 @@ public Path execute() {
FileUtils.createDirectories(outputDirectory);
}

try (TempDirectory tempDir = new TempDirectory(getClass().getSimpleName())) {
Path bundlePath;
// The old APK serializer relies on the compression of entries in the App Bundle.
// Unfortunately, we don't know the compression level that was used when the bundle was built,
// so we re-compress all entries with our desired compression level.
// Exception is made when the device spec is specified, we only need a fraction of the
// entries, so re-compressing all entries would be a waste of CPU.
boolean recompressAppBundle =
!getDeviceSpec().isPresent() && !getEnableApkSerializerWithoutBundleRecompression();
if (recompressAppBundle) {
bundlePath = tempDir.getPath().resolve("recompressed.aab");
new AppBundleRecompressor(getExecutorService())
.recompressAppBundle(getBundlePath().toFile(), bundlePath.toFile());
} else {
bundlePath = getBundlePath();
}
try (TempDirectory tempDir = new TempDirectory(getClass().getSimpleName());
ZipFile bundleZip = new ZipFile(getBundlePath().toFile());
Closer closer = Closer.create()) {
AppBundleValidator bundleValidator = AppBundleValidator.create(getExtraValidators());
bundleValidator.validateFile(bundleZip);

AppBundle appBundle = AppBundle.buildFromZip(bundleZip);
bundleValidator.validate(appBundle);

ImmutableMap<String, SdkBundle> sdkBundles =
getValidatedSdkBundlesByPackageName(closer, tempDir);
validateSdkBundlesMatchAppBundleDependencies(appBundle, sdkBundles);

AppBundlePreprocessorManager appBundlePreprocessorManager =
DaggerAppBundlePreprocessorComponent.builder().setBuildApksCommand(this).build().create();
AppBundle preprocessedAppBundle = appBundlePreprocessorManager.processAppBundle(appBundle);

BuildApksManager buildApksManager =
DaggerBuildApksManagerComponent.builder()
.setBuildApksCommand(this)
.setTempDirectory(tempDir)
.setAppBundle(preprocessedAppBundle)
.build()
.create();
buildApksManager.execute();
} catch (ZipException e) {
throw InvalidBundleException.builder()
.withCause(e)
.withUserMessage("The App Bundle is not a valid zip file.")
.build();

try (ZipFile bundleZip = new ZipFile(bundlePath.toFile());
ZipReader zipReader = ZipReader.createFromFile(bundlePath);
Closer closer = Closer.create()) {
AppBundleValidator bundleValidator = AppBundleValidator.create(getExtraValidators());
bundleValidator.validateFile(bundleZip);

AppBundle appBundle = AppBundle.buildFromZip(bundleZip);
bundleValidator.validate(appBundle);

ImmutableMap<String, SdkBundle> sdkBundles = getValidatedSdkBundlesByPackageName(closer);
validateSdkBundlesMatchAppBundleDependencies(appBundle, sdkBundles);

AppBundlePreprocessorManager appBundlePreprocessorManager =
DaggerAppBundlePreprocessorComponent.builder()
.setBuildApksCommand(this)
.build()
.create();
AppBundle preprocessedAppBundle = appBundlePreprocessorManager.processAppBundle(appBundle);

BuildApksManager buildApksManager =
DaggerBuildApksManagerComponent.builder()
.setBuildApksCommand(this)
.setTempDirectory(tempDir)
.setAppBundle(preprocessedAppBundle)
.setZipReader(zipReader)
.setUseBundleCompression(recompressAppBundle)
.build()
.create();
buildApksManager.execute();
} catch (ZipException e) {
throw InvalidBundleException.builder()
.withCause(e)
.withUserMessage("The App Bundle is not a valid zip file.")
.build();
} finally {
if (isExecutorServiceCreatedByBundleTool()) {
getExecutorService().shutdown();
}
}
} catch (IOException e) {
throw new UncheckedIOException("An error occurred when processing the App Bundle.", e);
} finally {
if (isExecutorServiceCreatedByBundleTool()) {
getExecutorService().shutdown();
}
}

return getOutputFile();
Expand Down Expand Up @@ -847,24 +835,31 @@ private void validateInput() {
});
}

private ImmutableMap<String, SdkBundle> getValidatedSdkBundlesByPackageName(Closer closer)
throws IOException {
private ImmutableMap<String, SdkBundle> getValidatedSdkBundlesByPackageName(
Closer closer, TempDirectory tempDir) throws IOException {
SdkBundleValidator sdkBundleValidator = SdkBundleValidator.create();
ImmutableSet.Builder<ZipFile> sdkBundleZipsBuilder = ImmutableSet.builder();
for (Path sdkBundlePath : getRuntimeEnabledSdkBundlePaths()) {
sdkBundleZipsBuilder.add(closer.register(new ZipFile(sdkBundlePath.toFile())));
ImmutableListMultimap.Builder<String, SdkBundle> sdkBundlesPerPackageNameBuilder =
ImmutableListMultimap.builder();
ImmutableList<Path> sdkBundlePaths = getRuntimeEnabledSdkBundlePaths().asList();
for (int index = 0; index < sdkBundlePaths.size(); index++) {
Path sdkBundlePath = sdkBundlePaths.get(index);
ZipFile sdkBundleZip = closer.register(new ZipFile(sdkBundlePath.toFile()));
sdkBundleValidator.validateFile(sdkBundleZip);

ZipFile sdkModulesZip =
closer.register(getModulesZip(sdkBundleZip, tempDir.getPath().resolve("tmp" + index)));
sdkBundleValidator.validateModulesFile(sdkModulesZip);

// SdkBundle#getVersionCode is not used in `build-apks`. It does not matter what
// value we set here, so we are just setting 0.
SdkBundle sdkBundle =
SdkBundle.buildFromZip(sdkBundleZip, sdkModulesZip, /* versionCode= */ 0);
sdkBundlesPerPackageNameBuilder.put(sdkBundle.getPackageName(), sdkBundle);
}

ImmutableSet<ZipFile> sdkBundleZips = sdkBundleZipsBuilder.build();
sdkBundleZips.forEach(sdkBundleValidator::validateFile);

ImmutableMap<String, Collection<SdkBundle>> sdkBundlesPerPackageName =
sdkBundleZips.stream()
// SdkBundle#getVersionCode is not used in `build-apks`. It does not matter what
// value we set here, so we are just setting 0.
.map(sdkBundleZip -> SdkBundle.buildFromZip(sdkBundleZip, /* versionCode= */ 0))
.collect(toImmutableListMultimap(SdkBundle::getPackageName, identity()))
.asMap();
sdkBundlesPerPackageNameBuilder.build().asMap();

sdkBundlesPerPackageName
.entrySet()
.forEach(
Expand Down Expand Up @@ -901,15 +896,24 @@ private static void validateSdkBundlesMatchAppBundleDependencies(
RuntimeEnabledSdk sdkDependencyFromAppBundle =
appBundle.getRuntimeEnabledSdkDependencies().get(sdkPackageName);
SdkBundle sdkBundle = sdkBundles.get(sdkPackageName);
if (sdkDependencyFromAppBundle.getVersionMajor()
!= sdkBundle.getMajorVersionAsLong()) {
if (sdkDependencyFromAppBundle.getVersionMajor() != sdkBundle.getMajorVersion()) {
throw InvalidCommandException.builder()
.withInternalMessage(
"App bundle depends on SDK '%s' with major version '%d', but provided SDK"
+ " bundle has major version '%d'.",
sdkPackageName,
sdkDependencyFromAppBundle.getVersionMajor(),
sdkBundle.getMajorVersionAsLong())
sdkBundle.getMajorVersion())
.build();
}
if (sdkDependencyFromAppBundle.getVersionMinor() != sdkBundle.getMinorVersion()) {
throw InvalidCommandException.builder()
.withInternalMessage(
"App bundle depends on SDK '%s' with minor version '%d', but provided SDK"
+ " bundle has minor version '%d'.",
sdkPackageName,
sdkDependencyFromAppBundle.getVersionMinor(),
sdkBundle.getMinorVersion())
.build();
}
});
Expand Down Expand Up @@ -1319,7 +1323,8 @@ public static CommandHelp help() {
.setOptional(true)
.setDescription(
"Package name of an app store that will be called by archived app to redownload"
+ " the application. Play Store is called by default.")
+ " the application. Play Store is called by default."
+ " Can only be provided for ARCHIVE mode.")
.build())
.build();
}
Expand Down
Loading

0 comments on commit f7f5fd2

Please sign in to comment.