Skip to content

Commit

Permalink
Prepare for release 0.6.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hitish Chappidi committed Sep 13, 2018
1 parent 9a749b7 commit 628ae23
Show file tree
Hide file tree
Showing 34 changed files with 874 additions and 184 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Read more about the App Bundle format and Bundletool's usage at

## Releases

Latest release: [0.5.1](https://github.com/google/bundletool/releases)
Latest release: [0.6.0](https://github.com/google/bundletool/releases)
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 0.5.1
release_version = 0.6.0
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import com.android.tools.build.bundletool.exceptions.ValidationException;
import com.android.tools.build.bundletool.io.TempFiles;
import com.android.tools.build.bundletool.model.Aapt2Command;
import com.android.tools.build.bundletool.model.ApkListener;
import com.android.tools.build.bundletool.model.ApkModifier;
import com.android.tools.build.bundletool.model.OptimizationDimension;
import com.android.tools.build.bundletool.model.SigningConfiguration;
import com.android.tools.build.bundletool.splitters.DexCompressionSplitter;
import com.android.tools.build.bundletool.splitters.NativeLibrariesCompressionSplitter;
import com.android.tools.build.bundletool.utils.EnvironmentVariableProvider;
import com.android.tools.build.bundletool.utils.SdkToolsLocator;
Expand Down Expand Up @@ -118,6 +120,8 @@ ListeningExecutorService getExecutorService() {
abstract boolean isExecutorServiceCreatedByBundleTool();


public abstract Optional<ApkListener> getApkListener();

public abstract Optional<ApkModifier> getApkModifier();

public abstract Optional<Integer> getFirstVariantNumber();
Expand Down Expand Up @@ -214,6 +218,13 @@ public Builder setExecutorService(ListeningExecutorService executorService) {
abstract Builder setExecutorServiceCreatedByBundleTool(boolean value);


/**
* Provides an {@link ApkListener} that will be notified at defined stages of APK creation.
*
* <p>The {@link ApkListener} must be thread-safe.
*/
public abstract Builder setApkListener(ApkListener apkListener);

/**
* Provides an {@link ApkModifier} that will be invoked just before the APKs are finalized,
* serialized on disk and signed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.android.tools.build.bundletool.commands;

import static com.android.tools.build.bundletool.utils.TargetingProtoUtils.sdkVersionFrom;
import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileDoesNotExist;
import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileExistsAndExecutable;
import static com.android.tools.build.bundletool.utils.files.FilePreconditions.checkFileExistsAndReadable;
Expand All @@ -28,19 +29,20 @@
import com.android.bundle.Config.Compression;
import com.android.bundle.Devices.DeviceSpec;
import com.android.bundle.Targeting.ApkTargeting;
import com.android.bundle.Targeting.SdkVersion;
import com.android.bundle.Targeting.SdkVersionTargeting;
import com.android.bundle.Targeting.VariantTargeting;
import com.android.tools.build.bundletool.device.AdbServer;
import com.android.tools.build.bundletool.device.DeviceAnalyzer;
import com.android.tools.build.bundletool.device.DeviceSpecParser;
import com.android.tools.build.bundletool.exceptions.CommandExecutionException;
import com.android.tools.build.bundletool.io.ApkPathManager;
import com.android.tools.build.bundletool.io.ApkSerializerManager;
import com.android.tools.build.bundletool.io.ApkSetBuilderFactory;
import com.android.tools.build.bundletool.io.ApkSetBuilderFactory.ApkSetBuilder;
import com.android.tools.build.bundletool.io.SplitApkSerializer;
import com.android.tools.build.bundletool.io.StandaloneApkSerializer;
import com.android.tools.build.bundletool.model.Aapt2Command;
import com.android.tools.build.bundletool.model.ApkListener;
import com.android.tools.build.bundletool.model.ApkModifier;
import com.android.tools.build.bundletool.model.AppBundle;
import com.android.tools.build.bundletool.model.BundleMetadata;
Expand Down Expand Up @@ -165,7 +167,6 @@ public Path execute(Path tempDir) {
generateStandaloneApks(
modulesForFusing,
appBundle.getBundleMetadata(),
command.getGenerateOnlyUniversalApk(),
tempDir,
apkOptimizations,
bundleVersion));
Expand All @@ -181,6 +182,7 @@ public Path execute(Path tempDir) {
appBundle,
apkSetBuilder,
command.getExecutorService(),
command.getApkListener().orElse(ApkListener.NO_OP),
command.getApkModifier().orElse(ApkModifier.NO_OP),
command.getFirstVariantNumber().orElse(0));
ImmutableList<Variant> allVariantsWithTargeting;
Expand Down Expand Up @@ -231,10 +233,12 @@ private ApkSetBuilder createApkSetBuilder(
Optional<SigningConfiguration> signingConfiguration,
Compression compression,
Path tempDir) {
ApkPathManager apkPathmanager = new ApkPathManager();
SplitApkSerializer splitApkSerializer =
new SplitApkSerializer(aapt2Command, signingConfiguration, compression);
new SplitApkSerializer(apkPathmanager, aapt2Command, signingConfiguration, compression);
StandaloneApkSerializer standaloneApkSerializer =
new StandaloneApkSerializer(aapt2Command, signingConfiguration, compression);
new StandaloneApkSerializer(
apkPathmanager, aapt2Command, signingConfiguration, compression);

return ApkSetBuilderFactory.createApkSetBuilder(
splitApkSerializer, standaloneApkSerializer, tempDir);
Expand Down Expand Up @@ -290,7 +294,6 @@ private ImmutableList<ModuleSplit> generateSplitApks(
private ImmutableList<ModuleSplit> generateStandaloneApks(
ImmutableList<BundleModule> modules,
BundleMetadata bundleMetadata,
boolean isUniversalApk,
Path tempDir,
ApkOptimizations apkOptimizations,
Version bundleVersion) {
Expand All @@ -299,16 +302,12 @@ private ImmutableList<ModuleSplit> generateStandaloneApks(
new BundleSharder(tempDir, bundleVersion)
.shardBundle(modules, apkOptimizations.getSplitDimensions(), bundleMetadata);

return standaloneApks
.stream()
return standaloneApks.stream()
.map(
moduleSplit ->
moduleSplit
.toBuilder()
.setVariantTargeting(
isUniversalApk
? VariantTargeting.getDefaultInstance()
: standaloneApkVariantTargeting(moduleSplit))
.setVariantTargeting(standaloneApkVariantTargeting(moduleSplit))
.setSplitType(SplitType.STANDALONE)
.build())
.collect(toImmutableList());
Expand All @@ -335,14 +334,14 @@ private static VariantTargeting standaloneApkVariantTargeting(ModuleSplit standa
if (apkTargeting.hasScreenDensityTargeting()) {
variantTargeting.setScreenDensityTargeting(apkTargeting.getScreenDensityTargeting());
}
// If a standalone variant was generated, then we may have also generated a splits variant with
// some SDK targeting (splits run only on Android L+). When we later compute alternative
// targeting across all variants, we don't allow some variants to have, and some variants not to
// have targeting for a dimension (SDK in this case). That is why we need to set the default
// instance of SDK targeting for the standalone variants.
variantTargeting.setSdkVersionTargeting(
SdkVersionTargeting.newBuilder().addValue(SdkVersion.getDefaultInstance()));
variantTargeting.setSdkVersionTargeting(sdkVersionTargeting(standaloneApk));

return variantTargeting.build();
}

private static SdkVersionTargeting sdkVersionTargeting(ModuleSplit moduleSplit) {
return SdkVersionTargeting.newBuilder()
.addValue(sdkVersionFrom(moduleSplit.getAndroidManifest().getEffectiveMinSdkVersion()))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.tools.build.bundletool.io;

import static java.util.stream.Collectors.toList;

import com.android.tools.build.bundletool.model.ModuleSplit;
import com.android.tools.build.bundletool.model.ModuleSplit.SplitType;
import com.android.tools.build.bundletool.model.ZipPath;
import com.google.common.base.Joiner;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.concurrent.GuardedBy;

/**
* Associates {@link ModuleSplit} with a file name ensuring there will be no path conflicts in the
* APK Set.
*
* <ul>
* <li>Standalone APKs will have the format: "standalones/standalone-x86-hdpi.apk"
* <li>Split APKs will have the format: "splits/moduleName-master.apk",
* "splits/moduleName-x86.apk", etc.
* <li>Instant APKs will have the format: "instant/instant-moduleName-master.apk",
* "instant/instant-moduleName-x86.apk", etc.
* </ul>
*
* <p>If the name of an APK conflicts with a previously generated APK, a unique number is appended
* to its name, e.g. "splits/moduleName-x86_2.apk".
*
* <p>The class is thread-safe.
*/
public class ApkPathManager {

private static final Joiner NAME_PARTS_JOINER = Joiner.on('-');

/** Paths of APKs that have already been allocated. */
@GuardedBy("this")
private final Set<ZipPath> usedPaths = new HashSet<>();

/**
* Returns a unique file path for the given ModuleSplit.
*
* <p>Note that calling this method twice for the same object will return a different result since
* each returned value is unique.
*/
public ZipPath getApkPath(ModuleSplit moduleSplit) {
String moduleName = moduleSplit.getModuleName().getName();
String targetingSuffix = getTargetingSuffix(moduleSplit);

ZipPath directory;
String apkFileName;
switch (moduleSplit.getSplitType()) {
case SPLIT:
directory = ZipPath.create("splits");
apkFileName = buildName(moduleName, targetingSuffix);
break;
case INSTANT:
directory = ZipPath.create("instant");
apkFileName = buildName("instant", moduleName, targetingSuffix);
break;
case STANDALONE:
directory = ZipPath.create("standalones");
apkFileName = buildName("standalone", targetingSuffix);
break;
default:
throw new IllegalStateException("Unrecognized split type: " + moduleSplit.getSplitType());
}

return findAndClaimUnusedPath(directory, apkFileName, /* fileExtension= */ ".apk");
}

/**
* Iterates over the given {@code proposedName} by suffixing the name with an increasing integer
* until an unused path is found, then returns this path.
*/
private synchronized ZipPath findAndClaimUnusedPath(
ZipPath directory, String proposedName, String fileExtension) {
ZipPath apkPath = directory.resolve(proposedName + fileExtension);

int serialNumber = 1;
while (usedPaths.contains(apkPath)) {
serialNumber++;
String fileName = String.format("%s_%d", proposedName, serialNumber);
apkPath = directory.resolve(fileName + fileExtension);
}
usedPaths.add(apkPath);

return apkPath;
}

private static String getTargetingSuffix(ModuleSplit moduleSplit) {
return moduleSplit.isMasterSplit() && !moduleSplit.getSplitType().equals(SplitType.STANDALONE)
? "master"
: moduleSplit.getSuffix();
}

private static String buildName(String... nameParts) {
return NAME_PARTS_JOINER.join(
Arrays.stream(nameParts).filter(s -> !s.isEmpty()).collect(toList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.android.bundle.Targeting.VariantTargeting;
import com.android.tools.build.bundletool.device.ApkMatcher;
import com.android.tools.build.bundletool.io.ApkSetBuilderFactory.ApkSetBuilder;
import com.android.tools.build.bundletool.model.ApkListener;
import com.android.tools.build.bundletool.model.ApkModifier;
import com.android.tools.build.bundletool.model.ApkModifier.ApkDescription.ApkType;
import com.android.tools.build.bundletool.model.AppBundle;
Expand All @@ -58,6 +59,7 @@
public class ApkSerializerManager {

private final ListeningExecutorService executorService;
private final ApkListener apkListener;
private final ApkModifier apkModifier;
private final int firstVariantNumber;
private final AppBundle appBundle;
Expand All @@ -67,11 +69,13 @@ public ApkSerializerManager(
AppBundle appBundle,
ApkSetBuilder apkSetBuilder,
ListeningExecutorService executorService,
ApkListener apkListener,
ApkModifier apkModifier,
int firstVariantNumber) {
this.appBundle = appBundle;
this.apkSetBuilder = apkSetBuilder;
this.executorService = executorService;
this.apkListener = apkListener;
this.apkModifier = apkModifier;
this.firstVariantNumber = firstVariantNumber;
}
Expand Down Expand Up @@ -112,7 +116,7 @@ private ImmutableList<Variant> serializeApks(
// 1. Remove APKs not matching the device spec.
// 2. Modify the APKs based on the ApkModifier.
// 3. Serialize all APKs in parallel.
ApkSerializer apkSerializer = new ApkSerializer(isUniversalApk);
ApkSerializer apkSerializer = new ApkSerializer(apkListener, isUniversalApk);

// Modifies the APK using APK modifier, then returns a map by extracting the variant
// of APK first and later clearing out its variant targeting.
Expand Down Expand Up @@ -228,24 +232,36 @@ public int compareTo(VariantKey o) {
}

private final class ApkSerializer {
private final ApkListener apkListener;
private final boolean isUniversalApk;

public ApkSerializer(boolean isUniversalApk) {
public ApkSerializer(ApkListener apkListener, boolean isUniversalApk) {
this.apkListener = apkListener;
this.isUniversalApk = isUniversalApk;
}

public ApkDescription serialize(ModuleSplit split) {
ApkDescription apkDescription;
switch (split.getSplitType()) {
case INSTANT:
return apkSetBuilder.addInstantApk(split);
apkDescription = apkSetBuilder.addInstantApk(split);
break;
case SPLIT:
return apkSetBuilder.addSplitApk(split);
apkDescription = apkSetBuilder.addSplitApk(split);
break;
case STANDALONE:
return isUniversalApk
? apkSetBuilder.addStandaloneUniversalApk(split)
: apkSetBuilder.addStandaloneApk(split);
apkDescription =
isUniversalApk
? apkSetBuilder.addStandaloneUniversalApk(split)
: apkSetBuilder.addStandaloneApk(split);
break;
default:
throw new IllegalStateException("Unexpected splitType: " + split.getSplitType());
}
throw new IllegalStateException("Unexpected splitType: " + split.getSplitType());

apkListener.onApkFinalized(apkDescription);

return apkDescription;
}
}
}
Loading

0 comments on commit 628ae23

Please sign in to comment.