Skip to content

Commit

Permalink
Prepare for release 0.13.4.
Browse files Browse the repository at this point in the history
  • Loading branch information
Iurii Makhno committed Mar 27, 2020
1 parent a325c37 commit 24e8e6e
Show file tree
Hide file tree
Showing 64 changed files with 1,834 additions and 941 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ https://developer.android.com/studio/command-line/bundletool

## Releases

Latest release: [0.13.3](https://github.com/google/bundletool/releases)
Latest release: [0.13.4](https://github.com/google/bundletool/releases)
13 changes: 12 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ apply plugin: "maven"
repositories {
jcenter()
google()
maven {
// We need to add this custom format repo because we use r8 version which is
// not available in gMaven yet. This repo can be removed once new version is
// uploaded to gMaven.
// Shrunk version of r8 is available there by the following path:
// https://storage.googleapis.com/r8-releases/raw/[revision]/r8lib.jar
ivy {
url "https://storage.googleapis.com/r8-releases/raw"
patternLayout {
artifact "[revision]/[artifact]lib.[ext]"
}
}
}

Expand Down Expand Up @@ -66,6 +74,9 @@ dependencies {
testCompile "org.junit.vintage:junit-vintage-engine:5.2.0"
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.2.0"
testCompile "org.junit.platform:junit-platform-runner:1.2.0"
testCompile("org.smali:dexlib2:2.3.4") {
exclude group: "com.google.guava", module: "guava"
}
}

def osName = System.getProperty("os.name").toLowerCase()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 0.13.3
release_version = 0.13.4
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ public final boolean isAnySystemMode() {
private static final Flag<Password> KEYSTORE_PASSWORD_FLAG = Flag.password("ks-pass");
private static final Flag<Password> KEY_PASSWORD_FLAG = Flag.password("key-pass");

// SourceStamp-related flags.
private static final Flag<Boolean> CREATE_STAMP_FLAG = Flag.booleanFlag("create-stamp");
private static final Flag<Path> STAMP_KEYSTORE_FLAG = Flag.path("stamp-ks");
private static final Flag<Password> STAMP_KEYSTORE_PASSWORD_FLAG = Flag.password("stamp-ks-pass");
private static final Flag<String> STAMP_KEY_ALIAS_FLAG = Flag.string("stamp-key-alias");
private static final Flag<Password> STAMP_KEY_PASSWORD_FLAG = Flag.password("stamp-key-pass");
private static final Flag<String> STAMP_SOURCE_FLAG = Flag.string("stamp-source");

private static final String APK_SET_ARCHIVE_EXTENSION = "apks";

Expand Down Expand Up @@ -181,6 +188,7 @@ ListeningExecutorService getExecutorService() {

public abstract Optional<PrintStream> getOutputPrintStream();

public abstract Optional<SourceStamp> getSourceStamp();

public static Builder builder() {
return new AutoValue_BuildApksCommand.Builder()
Expand Down Expand Up @@ -331,6 +339,11 @@ public Builder setExecutorService(ListeningExecutorService executorService) {
/** For command line, sets the {@link PrintStream} to use for outputting the warnings. */
public abstract Builder setOutputPrintStream(PrintStream outputPrintStream);

/**
* Provides a {@link SourceStamp} to be included in the
* generated APKs.
*/
public abstract Builder setSourceStamp(SourceStamp sourceStamp);

abstract BuildApksCommand autoBuild();

Expand Down Expand Up @@ -464,6 +477,7 @@ static BuildApksCommand fromFlags(
OPTIMIZE_FOR_FLAG.getValue(flags).ifPresent(buildApksCommand::setOptimizationDimensions);

populateSigningConfigurationFromFlags(buildApksCommand, flags, out, systemEnvironmentProvider);
populateSourceStampFromFlags(buildApksCommand, flags, out, systemEnvironmentProvider);

boolean connectedDeviceMode = CONNECTED_DEVICE_FLAG.getValue(flags).orElse(false);
CONNECTED_DEVICE_FLAG
Expand Down Expand Up @@ -732,6 +746,71 @@ public static CommandHelp help() {
+ " accessed by the Play Core API.",
InstallApksCommand.COMMAND_NAME)
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(CREATE_STAMP_FLAG.getName())
.setOptional(true)
.setDescription(
"If set, a stamp will be generated and embedded in the generated APKs.")
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(STAMP_KEYSTORE_FLAG.getName())
.setExampleValue("path/to/keystore")
.setOptional(true)
.setDescription(
"Path to the stamp keystore that should be used to sign the APK contents hash."
+ " If not set, the '%s' keystore will be tried if present. Otherwise, the"
+ " default debug keystore will be used if it exists. If a default debug"
+ " keystore is not found, the stamp will fail to get generated. If"
+ " set, the flag '%s' must also be set.",
KEYSTORE_FLAG, STAMP_KEY_ALIAS_FLAG)
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(STAMP_KEYSTORE_PASSWORD_FLAG.getName())
.setExampleValue("[pass|file]:value")
.setOptional(true)
.setDescription(
"Password of the stamp keystore to use to sign the APK contents hash. If"
+ " provided, must be prefixed with either 'pass:' (if the password is"
+ " passed in clear text, e.g. 'pass:qwerty') or 'file:' (if the password"
+ " is the first line of a file, e.g. 'file:/tmp/myPassword.txt'). If this"
+ " flag is not set, the password will be requested on the prompt.")
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(STAMP_KEY_ALIAS_FLAG.getName())
.setExampleValue("stamp-key-alias")
.setOptional(true)
.setDescription(
"Alias of the stamp key to use in the keystore to sign the APK contents hash."
+ " If not set, the '%s' key alias will be tried if present.",
KEY_ALIAS_FLAG)
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(STAMP_KEY_PASSWORD_FLAG.getName())
.setExampleValue("stamp-key-password")
.setOptional(true)
.setDescription(
"Password of the stamp key in the keystore to use to sign the APK contents"
+ " hash. if provided, must be prefixed with either 'pass:' (if the"
+ " password is passed in clear text, e.g. 'pass:qwerty') or 'file:' (if"
+ " the password is the first line of a file, e.g."
+ " 'file:/tmp/myPassword.txt'). If this flag is not set, the keystore"
+ " password will be tried. If that fails, the password will be requested"
+ " on the prompt.")
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(STAMP_SOURCE_FLAG.getName())
.setExampleValue("stamp-source")
.setOptional(true)
.setDescription(
"Name of source generating the stamp. For stores, it is their package names."
+ " For locally generated stamp, it is 'local'.")
.build())
.build();
}

Expand Down Expand Up @@ -778,4 +857,81 @@ private static void populateSigningConfigurationFromFlags(
}
}

private static void populateSourceStampFromFlags(
Builder buildApksCommand,
ParsedFlags flags,
PrintStream out,
SystemEnvironmentProvider systemEnvironmentProvider) {
boolean createStamp = CREATE_STAMP_FLAG.getValue(flags).orElse(false);
Optional<String> stampSource = STAMP_SOURCE_FLAG.getValue(flags);

if (!createStamp) {
return;
}

SourceStamp.Builder sourceStamp = SourceStamp.builder();

sourceStamp.setSigningConfiguration(
getStampSigningConfiguration(flags, out, systemEnvironmentProvider));
stampSource.ifPresent(sourceStamp::setSource);

buildApksCommand.setSourceStamp(sourceStamp.build());
}

private static SigningConfiguration getStampSigningConfiguration(
ParsedFlags flags, PrintStream out, SystemEnvironmentProvider systemEnvironmentProvider) {
// Signing-related flags.
Optional<Path> signingKeystorePath = KEYSTORE_FLAG.getValue(flags);
Optional<Password> signingKeystorePassword = KEYSTORE_PASSWORD_FLAG.getValue(flags);
Optional<String> signingKeyAlias = KEY_ALIAS_FLAG.getValue(flags);
Optional<Password> signingKeyPassword = KEY_PASSWORD_FLAG.getValue(flags);

// Stamp-related flags.
Optional<Path> stampKeystorePath = STAMP_KEYSTORE_FLAG.getValue(flags);
Optional<Password> stampKeystorePassword = STAMP_KEYSTORE_PASSWORD_FLAG.getValue(flags);
Optional<String> stampKeyAlias = STAMP_KEY_ALIAS_FLAG.getValue(flags);
Optional<Password> stampKeyPassword = STAMP_KEY_PASSWORD_FLAG.getValue(flags);

Path keystorePath = null;
Optional<Password> keystorePassword = Optional.empty();
if (stampKeystorePath.isPresent()) {
keystorePath = stampKeystorePath.get();
keystorePassword = stampKeystorePassword;
} else if (signingKeystorePath.isPresent()) {
keystorePath = signingKeystorePath.get();
keystorePassword = signingKeystorePassword;
}

if (keystorePath == null) {
// Try to use debug keystore if present.
Optional<SigningConfiguration> debugConfig =
DebugKeystoreUtils.getDebugSigningConfiguration(systemEnvironmentProvider);
if (debugConfig.isPresent()) {
out.printf(
"INFO: The stamp will be signed with the debug keystore found at '%s'.%n",
DebugKeystoreUtils.DEBUG_KEYSTORE_CACHE.getUnchecked(systemEnvironmentProvider).get());
return debugConfig.get();
} else {
throw new CommandExecutionException("No key was found to sign the stamp.");
}
}

String keyAlias = null;
Optional<Password> keyPassword = Optional.empty();
if (stampKeyAlias.isPresent()) {
keyAlias = stampKeyAlias.get();
keyPassword = stampKeyPassword;
} else if (signingKeyAlias.isPresent()) {
keyAlias = signingKeyAlias.get();
keyPassword = signingKeyPassword;
}

if (keyAlias == null) {
throw new CommandExecutionException(
"Flag --stamp-key-alias or --ks-key-alias are required when --stamp-ks or --ks are set.");
}

return SigningConfiguration.extractFromKeystore(
keystorePath, keyAlias, keystorePassword, keyPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.android.tools.build.bundletool.model.ModuleSplit;
import com.android.tools.build.bundletool.model.OptimizationDimension;
import com.android.tools.build.bundletool.model.SigningConfiguration;
import com.android.tools.build.bundletool.model.SourceStamp;
import com.android.tools.build.bundletool.model.exceptions.CommandExecutionException;
import com.android.tools.build.bundletool.model.targeting.AlternativeVariantTargetingPopulator;
import com.android.tools.build.bundletool.model.utils.SplitsXmlInjector;
Expand Down Expand Up @@ -118,7 +119,7 @@ public Path execute() {
}

try (ZipFile bundleZip = new ZipFile(command.getBundlePath().toFile())) {
executeWithZip(bundleZip, deviceSpec);
executeWithZip(bundleZip, deviceSpec, command.getSourceStamp());
} catch (IOException e) {
throw new UncheckedIOException(
String.format(
Expand All @@ -133,8 +134,10 @@ public Path execute() {
return command.getOutputFile();
}

private void executeWithZip(ZipFile bundleZip, Optional<DeviceSpec> deviceSpec)
private void executeWithZip(
ZipFile bundleZip, Optional<DeviceSpec> deviceSpec, Optional<SourceStamp> sourceStamp)
throws IOException {
Optional<String> stampSource = sourceStamp.map(SourceStamp::getSource);
AppBundleValidator bundleValidator = AppBundleValidator.create(command.getExtraValidators());

bundleValidator.validateFile(bundleZip);
Expand Down Expand Up @@ -162,17 +165,18 @@ private void executeWithZip(ZipFile bundleZip, Optional<DeviceSpec> deviceSpec)

// Split APKs
if (apksToGenerate.generateSplitApks()) {
generatedApksBuilder.setSplitApks(generateSplitApks(appBundle));
generatedApksBuilder.setSplitApks(generateSplitApks(appBundle, stampSource));
}

// Instant APKs
if (apksToGenerate.generateInstantApks()) {
generatedApksBuilder.setInstantApks(generateInstantApks(appBundle));
generatedApksBuilder.setInstantApks(generateInstantApks(appBundle, stampSource));
}

// Standalone APKs
if (apksToGenerate.generateStandaloneApks()) {
generatedApksBuilder.setStandaloneApks(generateStandaloneApks(tempDir, appBundle));
generatedApksBuilder.setStandaloneApks(
generateStandaloneApks(tempDir, appBundle, stampSource));
}

// Universal APK
Expand All @@ -183,7 +187,12 @@ private void executeWithZip(ZipFile bundleZip, Optional<DeviceSpec> deviceSpec)
? modulesToFuse(appBundle.getFeatureModules().values().asList())
: requestedModules.asList();
generatedApksBuilder.setStandaloneApks(
new ShardedApksGenerator(tempDir, bundleVersion, getSuffixStrippings(bundleConfig))
new ShardedApksGenerator(
tempDir,
bundleVersion,
/* strip64BitLibrariesFromShards= */ false,
getSuffixStrippings(bundleConfig),
stampSource)
.generateSplits(
modulesToFuse,
appBundle.getBundleMetadata(),
Expand Down Expand Up @@ -217,10 +226,13 @@ private void executeWithZip(ZipFile bundleZip, Optional<DeviceSpec> deviceSpec)
checkDeviceCompatibilityWithBundle(generatedApks, deviceSpec.get());
}

Optional<SigningConfiguration> stampSigningConfiguration =
sourceStamp.map(SourceStamp::getSigningConfiguration);
ApkSetBuilder apkSetBuilder =
createApkSetBuilder(
aapt2Command,
command.getSigningConfiguration(),
stampSigningConfiguration,
bundleVersion,
bundleConfig.getCompression(),
tempDir);
Expand Down Expand Up @@ -248,15 +260,17 @@ private void executeWithZip(ZipFile bundleZip, Optional<DeviceSpec> deviceSpec)
apkSetBuilder.writeTo(command.getOutputFile());
}

private ImmutableList<ModuleSplit> generateStandaloneApks(Path tempDir, AppBundle appBundle) {
private ImmutableList<ModuleSplit> generateStandaloneApks(
Path tempDir, AppBundle appBundle, Optional<String> stampSource) {
ImmutableList<BundleModule> allModules = getModulesForStandaloneApks(appBundle);
Version bundleVersion = Version.of(appBundle.getBundleConfig().getBundletool().getVersion());
ShardedApksGenerator shardedApksGenerator =
new ShardedApksGenerator(
tempDir,
bundleVersion,
shouldStrip64BitLibrariesFromShards(appBundle),
getSuffixStrippings(appBundle.getBundleConfig()));
getSuffixStrippings(appBundle.getBundleConfig()),
stampSource);
return appBundle.isApex()
? shardedApksGenerator.generateApexSplits(modulesToFuse(allModules))
: shardedApksGenerator.generateSplits(
Expand All @@ -273,7 +287,8 @@ private ImmutableList<ModuleSplit> generateAssetSlices(AppBundle appBundle) {
return assetSlicesGenerator.generateAssetSlices();
}

private ImmutableList<ModuleSplit> generateInstantApks(AppBundle appBundle) {
private ImmutableList<ModuleSplit> generateInstantApks(
AppBundle appBundle, Optional<String> stampSource) {
Version bundleVersion = Version.of(appBundle.getBundleConfig().getBundletool().getVersion());
ImmutableList<BundleModule> allFeatureModules = appBundle.getFeatureModules().values().asList();
ImmutableList<BundleModule> instantModules =
Expand All @@ -285,11 +300,13 @@ private ImmutableList<ModuleSplit> generateInstantApks(AppBundle appBundle) {
// only support one variant.
.setEnableDexCompressionSplitter(false)
.build();
return new SplitApksGenerator(instantModules, bundleVersion, instantApkGenerationConfiguration)
return new SplitApksGenerator(
instantModules, bundleVersion, instantApkGenerationConfiguration, stampSource)
.generateSplits();
}

private ImmutableList<ModuleSplit> generateSplitApks(AppBundle appBundle) throws IOException {
private ImmutableList<ModuleSplit> generateSplitApks(
AppBundle appBundle, Optional<String> stampSource) throws IOException {
Version bundleVersion = Version.of(appBundle.getBundleConfig().getBundletool().getVersion());
ApkGenerationConfiguration.Builder apkGenerationConfiguration =
getCommonSplitApkGenerationConfiguration(appBundle);
Expand All @@ -303,7 +320,8 @@ private ImmutableList<ModuleSplit> generateSplitApks(AppBundle appBundle) throws
}

ImmutableList<BundleModule> featureModules = appBundle.getFeatureModules().values().asList();
return new SplitApksGenerator(featureModules, bundleVersion, apkGenerationConfiguration.build())
return new SplitApksGenerator(
featureModules, bundleVersion, apkGenerationConfiguration.build(), stampSource)
.generateSplits();
}

Expand Down Expand Up @@ -346,16 +364,27 @@ private DeviceSpec getDeviceSpecFromConnectedDevice() {
private ApkSetBuilder createApkSetBuilder(
Aapt2Command aapt2Command,
Optional<SigningConfiguration> signingConfiguration,
Optional<SigningConfiguration> stampSigningConfiguration,
Version bundleVersion,
Compression compression,
Path tempDir) {
ApkPathManager apkPathmanager = new ApkPathManager();
SplitApkSerializer splitApkSerializer =
new SplitApkSerializer(
apkPathmanager, aapt2Command, signingConfiguration, bundleVersion, compression);
apkPathmanager,
aapt2Command,
signingConfiguration,
stampSigningConfiguration,
bundleVersion,
compression);
StandaloneApkSerializer standaloneApkSerializer =
new StandaloneApkSerializer(
apkPathmanager, aapt2Command, signingConfiguration, bundleVersion, compression);
apkPathmanager,
aapt2Command,
signingConfiguration,
stampSigningConfiguration,
bundleVersion,
compression);

if (!command.getCreateApkSetArchive()) {
return ApkSetBuilderFactory.createApkSetWithoutArchiveBuilder(
Expand Down
Loading

0 comments on commit 24e8e6e

Please sign in to comment.