Skip to content

Commit

Permalink
Prepare for release 0.12.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
plecesne committed Dec 20, 2019
1 parent 1c41e70 commit 8f922cf
Show file tree
Hide file tree
Showing 82 changed files with 2,697 additions and 679 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.11.0](https://github.com/google/bundletool/releases)
Latest release: [0.12.0](https://github.com/google/bundletool/releases)
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ shadowJar {
exclude 'com.android.bundle.**'
// Aapt protos.
exclude 'com.android.aapt.**'
// String constants in classes.
// For some reason, the Shadow plug-in seems to rename strings in classes too!
exclude 'com.android.vending.splits'
exclude 'com.android.vending.splits.required'
exclude 'com.android.dynamic.apk.fused.modules'
}
}

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.11.0
release_version = 0.12.0
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileDoesNotExist;
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileExistsAndExecutable;
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileExistsAndReadable;
import static com.android.tools.build.bundletool.model.version.VersionGuardedFeature.RESOURCES_REFERENCED_IN_MANIFEST_TO_MASTER_SPLIT;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
Expand Down Expand Up @@ -178,10 +179,7 @@ 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, getSuffixStrippings(bundleConfig))
.generateSplits(
modulesToFuse,
appBundle.getBundleMetadata(),
Expand Down Expand Up @@ -286,7 +284,7 @@ private ImmutableList<ModuleSplit> generateSplitApks(AppBundle appBundle) throws
Version bundleVersion = Version.of(appBundle.getBundleConfig().getBundletool().getVersion());
ApkGenerationConfiguration.Builder apkGenerationConfiguration =
getCommonSplitApkGenerationConfiguration(appBundle);
if (!bundleVersion.isOlderThan(Version.of("0.8.1"))) {
if (RESOURCES_REFERENCED_IN_MANIFEST_TO_MASTER_SPLIT.enabledForVersion(bundleVersion)) {
// Make sure that resources reachable from the manifest of the base module will be
// represented in the master split (by at least one config). This prevents the app
// from crashing too soon (before reaching Application#onCreate), in case when only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,23 @@
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkDirectoryExists;
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileExistsAndExecutable;
import static com.android.tools.build.bundletool.model.utils.files.FilePreconditions.checkFileExistsAndReadable;
import static com.google.common.base.Preconditions.checkArgument;

import com.android.bundle.Commands.BuildApksResult;
import com.android.bundle.Devices.DeviceSpec;
import com.android.tools.build.bundletool.commands.CommandHelp.CommandDescription;
import com.android.tools.build.bundletool.commands.CommandHelp.FlagDescription;
import com.android.tools.build.bundletool.device.AdbRunner;
import com.android.tools.build.bundletool.device.AdbServer;
import com.android.tools.build.bundletool.device.ApksInstaller;
import com.android.tools.build.bundletool.device.Device;
import com.android.tools.build.bundletool.device.Device.InstallOptions;
import com.android.tools.build.bundletool.device.DeviceAnalyzer;
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.model.exceptions.CommandExecutionException;
import com.android.tools.build.bundletool.model.utils.DefaultSystemEnvironmentProvider;
import com.android.tools.build.bundletool.model.utils.ResultUtils;
import com.android.tools.build.bundletool.model.utils.SdkToolsLocator;
import com.android.tools.build.bundletool.model.utils.SystemEnvironmentProvider;
import com.google.auto.value.AutoValue;
Expand All @@ -54,6 +58,9 @@ public abstract class InstallApksCommand {
private static final Flag<String> DEVICE_ID_FLAG = Flag.string("device-id");
private static final Flag<ImmutableSet<String>> MODULES_FLAG = Flag.stringSet("modules");
private static final Flag<Boolean> ALLOW_DOWNGRADE_FLAG = Flag.booleanFlag("allow-downgrade");
private static final Flag<String> PUSH_SPLITS_FLAG = Flag.string("push-splits-to");
private static final Flag<Boolean> CLEAR_PUSH_PATH_FLAG = Flag.booleanFlag("clear-push-path");
private static final Flag<Boolean> ALLOW_TEST_ONLY_FLAG = Flag.booleanFlag("allow-test-only");

private static final String ANDROID_SERIAL_VARIABLE = "ANDROID_SERIAL";

Expand All @@ -70,10 +77,19 @@ public abstract class InstallApksCommand {

public abstract boolean getAllowDowngrade();

public abstract Optional<String> getPushSplitsPath();

public abstract boolean getClearPushPath();

public abstract boolean getAllowTestOnly();

abstract AdbServer getAdbServer();

public static Builder builder() {
return new AutoValue_InstallApksCommand.Builder().setAllowDowngrade(false);
return new AutoValue_InstallApksCommand.Builder()
.setAllowDowngrade(false)
.setClearPushPath(false)
.setAllowTestOnly(false);
}

/** Builder for the {@link InstallApksCommand}. */
Expand All @@ -92,6 +108,12 @@ public abstract static class Builder {
/** The caller is responsible for the lifecycle of the {@link AdbServer}. */
public abstract Builder setAdbServer(AdbServer adbServer);

public abstract Builder setPushSplitsPath(String pushSplitsPath);

public abstract Builder setClearPushPath(boolean clearPushPath);

public abstract Builder setAllowTestOnly(boolean allowTestOnly);

public abstract InstallApksCommand build();
}

Expand Down Expand Up @@ -123,6 +145,9 @@ public static InstallApksCommand fromFlags(

Optional<ImmutableSet<String>> modules = MODULES_FLAG.getValue(flags);
Optional<Boolean> allowDowngrade = ALLOW_DOWNGRADE_FLAG.getValue(flags);
Optional<String> pushSplits = PUSH_SPLITS_FLAG.getValue(flags);
Optional<Boolean> clearPushPath = CLEAR_PUSH_PATH_FLAG.getValue(flags);
Optional<Boolean> allowTestOnly = ALLOW_TEST_ONLY_FLAG.getValue(flags);

flags.checkNoUnknownFlags();

Expand All @@ -131,6 +156,7 @@ public static InstallApksCommand fromFlags(
deviceSerialName.ifPresent(command::setDeviceId);
modules.ifPresent(command::setModules);
allowDowngrade.ifPresent(command::setAllowDowngrade);
allowTestOnly.ifPresent(command::setAllowTestOnly);

return command.build();
}
Expand All @@ -153,17 +179,73 @@ public void execute() {
extractApksCommand.setOutputDirectory(tempDirectory.getPath());
}
getModules().ifPresent(extractApksCommand::setModules);
ImmutableList<Path> extractedApks = extractApksCommand.build().execute();
final ImmutableList<Path> extractedApks = extractApksCommand.build().execute();

ApksInstaller installer = new ApksInstaller(adbServer);
AdbRunner adbRunner = new AdbRunner(adbServer);
InstallOptions installOptions =
InstallOptions.builder().setAllowDowngrade(getAllowDowngrade()).build();
InstallOptions.builder()
.setAllowDowngrade(getAllowDowngrade())
.setAllowTestOnly(getAllowTestOnly())
.build();

if (getDeviceId().isPresent()) {
installer.installApks(extractedApks, installOptions, getDeviceId().get());
adbRunner.run(
device -> device.installApks(extractedApks, installOptions), getDeviceId().get());
} else {
installer.installApks(extractedApks, installOptions);
adbRunner.run(device -> device.installApks(extractedApks, installOptions));
}

pushSplits(deviceSpec, extractApksCommand.build(), adbRunner);
}
}

private void pushSplits(
DeviceSpec baseSpec, ExtractApksCommand baseExtractCommand, AdbRunner adbRunner) {
if (!getPushSplitsPath().isPresent()) {
return;
}

ExtractApksCommand.Builder extractApksCommand = ExtractApksCommand.builder();
extractApksCommand.setApksArchivePath(baseExtractCommand.getApksArchivePath());
baseExtractCommand.getOutputDirectory().ifPresent(extractApksCommand::setOutputDirectory);

// We want to push all modules...
extractApksCommand.setModules(ImmutableSet.of(ExtractApksCommand.ALL_MODULES_SHORTCUT));
// ... and all languages
BuildApksResult toc = ResultUtils.readTableOfContents(getApksArchivePath());
ImmutableSet<String> targetedLanguages = ResultUtils.getAllTargetedLanguages(toc);
final ImmutableList<Path> extractedApksForPush =
extractApksCommand
.setDeviceSpec(baseSpec.toBuilder().addAllSupportedLocales(targetedLanguages).build())
.build()
.execute();

Device.PushOptions.Builder pushOptions =
Device.PushOptions.builder()
.setDestinationPath(getPushSplitsPath().get())
.setClearDestinationPath(getClearPushPath());

// We're going to need the package name later for pushing to relative paths
// (i.e. inside the app's private directory)
if (!getPushSplitsPath().get().startsWith("/")) {
String packageName = toc.getPackageName();
if (packageName.isEmpty()) {
throw new CommandExecutionException(
"Unable to determine the package name of the base APK. If your APK "
+ "set was produced using an older version of bundletool, please "
+ "regenerate it. Alternatively, you can try again with an "
+ "absolute path for --push-splits-to, pointing to a location "
+ "that is writeable by the shell user, e.g. /sdcard/...");
}
pushOptions.setPackageName(packageName);
}

if (getDeviceId().isPresent()) {
adbRunner.run(
device -> device.pushApks(extractedApksForPush, pushOptions.build()),
getDeviceId().get());
} else {
adbRunner.run(device -> device.pushApks(extractedApksForPush, pushOptions.build()));
}
}

Expand All @@ -174,6 +256,20 @@ private void validateInput() {
checkFileExistsAndReadable(getApksArchivePath());
}
checkFileExistsAndExecutable(getAdbPath());
if (getClearPushPath()) {
checkArgument(
getPushSplitsPath().isPresent(),
"--%s only applies when --%s is set.",
CLEAR_PUSH_PATH_FLAG.getName(),
PUSH_SPLITS_FLAG.getName());
}
getPushSplitsPath()
.ifPresent(
path ->
checkArgument(
!path.isEmpty(),
"The value of the flag --%s cannot be empty.",
PUSH_SPLITS_FLAG.getName()));
}

public static CommandHelp help() {
Expand Down Expand Up @@ -239,6 +335,14 @@ public static CommandHelp help() {
+ "value of this flag is ignored if the device receives a standalone APK.",
ExtractApksCommand.ALL_MODULES_SHORTCUT)
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(ALLOW_TEST_ONLY_FLAG.getName())
.setOptional(true)
.setDescription(
"If set, apps with 'android:testOnly=true' set in their manifest can also be"
+ " deployed")
.build())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,29 @@

import static com.google.common.collect.ImmutableList.toImmutableList;

import com.android.tools.build.bundletool.device.Device.InstallOptions;
import com.android.tools.build.bundletool.model.exceptions.CommandExecutionException;
import com.android.tools.build.bundletool.model.exceptions.DeviceNotFoundException;
import com.android.tools.build.bundletool.model.exceptions.DeviceNotFoundException.TooManyDevicesMatchedException;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Predicate;

/** Responsible for installing the APKs. */
public class ApksInstaller {
/** Responsible for running actions on a connected device. */
public class AdbRunner {

private final AdbServer adbServer;

/** Initializes the instance. Expects the {@link AdbServer} to be initialized. */
public ApksInstaller(AdbServer adbServer) {
public AdbRunner(AdbServer adbServer) {
this.adbServer = adbServer;
}

/** Attempts to install the given APKs to the only connected device. */
public void installApks(ImmutableList<Path> apkPaths, InstallOptions installOptions) {
/** Attempts to run the given action on the only connected device. */
public void run(Consumer<Device> deviceAction) {
try {
installApks(apkPaths, installOptions, Predicates.alwaysTrue());
run(deviceAction, Predicates.alwaysTrue());
} catch (TooManyDevicesMatchedException e) {
throw CommandExecutionException.builder()
.withMessage("Expected to find one connected device, but found %d.", e.getMatchedNumber())
Expand All @@ -55,11 +54,10 @@ public void installApks(ImmutableList<Path> apkPaths, InstallOptions installOpti
}
}

/** Attempts to install the given APKs to a device with a given serial number. */
public void installApks(
ImmutableList<Path> apkPaths, InstallOptions installOptions, String deviceId) {
/** Attempts to run the given action on a device with a given serial number. */
public void run(Consumer<Device> deviceAction, String deviceId) {
try {
installApks(apkPaths, installOptions, device -> device.getSerialNumber().equals(deviceId));
run(deviceAction, device -> device.getSerialNumber().equals(deviceId));
} catch (DeviceNotFoundException e) {
throw CommandExecutionException.builder()
.withMessage("Expected to find one connected device with serial number '%s'.", deviceId)
Expand All @@ -68,8 +66,7 @@ public void installApks(
}
}

private void installApks(
ImmutableList<Path> apkPaths, InstallOptions installOptions, Predicate<Device> deviceFilter) {
private void run(Consumer<Device> deviceAction, Predicate<Device> deviceFilter) {
try {
ImmutableList<Device> matchedDevices =
adbServer.getDevices().stream().filter(deviceFilter).collect(toImmutableList());
Expand All @@ -79,7 +76,7 @@ private void installApks(
throw new TooManyDevicesMatchedException(matchedDevices.size());
}

installOnDevice(apkPaths, installOptions, matchedDevices.get(0));
deviceAction.accept(matchedDevices.get(0));

} catch (TimeoutException e) {
throw CommandExecutionException.builder()
Expand All @@ -88,9 +85,4 @@ private void installApks(
.build();
}
}

private void installOnDevice(
ImmutableList<Path> apkPaths, InstallOptions installOptions, Device device) {
device.installApks(apkPaths, installOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.android.tools.build.bundletool.model.utils.ModuleDependenciesUtils.addModuleDependencies;
import static com.android.tools.build.bundletool.model.utils.ModuleDependenciesUtils.buildAdjacencyMap;
import static com.android.tools.build.bundletool.model.version.VersionGuardedFeature.NEW_DELIVERY_TYPE_MANIFEST_TAG;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

Expand Down Expand Up @@ -212,7 +213,7 @@ private ImmutableSet<String> buildModulesDeliveredInstallTime(

private boolean willBeDeliveredInstallTime(ModuleMetadata moduleMetadata, Version bundleVersion) {
boolean installTime =
bundleVersion.isNewerThan(Version.of("0.10.1"))
NEW_DELIVERY_TYPE_MANIFEST_TAG.enabledForVersion(bundleVersion)
? moduleMetadata.getDeliveryType().equals(DeliveryType.INSTALL_TIME)
: !moduleMetadata.getOnDemandDeprecated();

Expand Down
Loading

0 comments on commit 8f922cf

Please sign in to comment.