blob: 8d8037476b9de8209af3e9e9a2c4dec1ef6426da [file] [log] [blame]
/*
* Copyright (C) 2014 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.build.gradle.tasks;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.build.OutputFile;
import com.android.build.api.artifact.BuildableArtifact;
import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.internal.core.VariantConfiguration;
import com.android.build.gradle.internal.packaging.IncrementalPackagerBuilder;
import com.android.build.gradle.internal.pipeline.StreamFilter;
import com.android.build.gradle.internal.scope.ApkData;
import com.android.build.gradle.internal.scope.BuildElementsTransformParams;
import com.android.build.gradle.internal.scope.BuildElementsTransformRunnable;
import com.android.build.gradle.internal.scope.ExistingBuildElements;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.scope.MutableTaskContainer;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.AndroidBuilderTask;
import com.android.build.gradle.internal.tasks.SigningConfigMetadata;
import com.android.build.gradle.internal.tasks.Workers;
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction;
import com.android.builder.files.IncrementalRelativeFileSets;
import com.android.builder.files.RelativeFile;
import com.android.builder.internal.packaging.IncrementalPackager;
import com.android.ide.common.resources.FileStatus;
import com.android.ide.common.workers.WorkerExecutorFacade;
import com.android.sdklib.AndroidVersion;
import com.android.utils.FileUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import javax.inject.Inject;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.tooling.BuildException;
import org.gradle.workers.WorkerExecutor;
/** Package a abi dimension specific split APK */
public class PackageSplitAbi extends AndroidBuilderTask {
private BuildableArtifact processedAbiResources;
private File outputDirectory;
private boolean jniDebuggable;
private FileCollection signingConfig;
private FileCollection jniFolders;
private AndroidVersion minSdkVersion;
private File incrementalDir;
private Collection<String> aaptOptionsNoCompress;
private Set<String> splits;
private final WorkerExecutorFacade workers;
@Inject
public PackageSplitAbi(WorkerExecutor workerExecutor) {
workers = Workers.INSTANCE.getWorker(workerExecutor);
}
@InputFiles
public BuildableArtifact getProcessedAbiResources() {
return processedAbiResources;
}
@OutputDirectory
public File getOutputDirectory() {
return outputDirectory;
}
@Input
public Set<String> getSplits() {
return splits;
}
@Input
public boolean isJniDebuggable() {
return jniDebuggable;
}
@InputFiles
public FileCollection getSigningConfig() {
return signingConfig;
}
@InputFiles
public FileCollection getJniFolders() {
return jniFolders;
}
@Input
public int getMinSdkVersion() {
return minSdkVersion.getFeatureLevel();
}
@Input
public Collection<String> getNoCompressExtensions() {
return aaptOptionsNoCompress != null ? aaptOptionsNoCompress : Collections.emptyList();
}
@TaskAction
protected void doFullTaskAction() throws IOException {
FileUtils.cleanOutputDir(incrementalDir);
ExistingBuildElements.from(
InternalArtifactType.ABI_PROCESSED_SPLIT_RES, processedAbiResources)
.transform(
workers,
PackageSplitAbiTransformRunnable.class,
(apkInfo, input) ->
new PackageSplitAbiTransformParams(apkInfo, input, this))
.into(InternalArtifactType.ABI_PACKAGED_SPLIT, outputDirectory);
}
private static class PackageSplitAbiTransformRunnable extends BuildElementsTransformRunnable {
@Inject
public PackageSplitAbiTransformRunnable(@NonNull PackageSplitAbiTransformParams params) {
super(params);
}
@Override
public void run() {
PackageSplitAbiTransformParams params = (PackageSplitAbiTransformParams) getParams();
try (IncrementalPackager pkg =
new IncrementalPackagerBuilder(IncrementalPackagerBuilder.ApkFormat.FILE)
.withOutputFile(params.getOutput())
.withSigning(
SigningConfigMetadata.Companion.load(params.signingConfigFile),
params.minSdkVersion)
.withCreatedBy(params.createdBy)
// .withManifest(manifest)
.withAaptOptionsNoCompress(params.aaptOptionsNoCompress)
.withIntermediateDir(params.incrementalDir)
.withKeepTimestampsInApk(params.keepTimestampsInApk)
.withDebuggableBuild(params.isJniDebuggable)
.withJniDebuggableBuild(params.isJniDebuggable)
.withAcceptedAbis(ImmutableSet.of(params.apkInfo.getFilterName()))
.build()) {
ImmutableMap<RelativeFile, FileStatus> nativeLibs =
IncrementalRelativeFileSets.fromZipsAndDirectories(params.jniFolders);
pkg.updateNativeLibraries(nativeLibs);
ImmutableMap<RelativeFile, FileStatus> androidResources =
IncrementalRelativeFileSets.fromZip(params.input);
pkg.updateAndroidResources(androidResources);
} catch (IOException e) {
throw new BuildException(e.getMessage(), e);
}
}
}
private static class PackageSplitAbiTransformParams extends BuildElementsTransformParams {
private final File input;
private final ApkData apkInfo;
private final File output;
private final File incrementalDir;
private final File signingConfigFile;
private final String createdBy;
private final Collection<String> aaptOptionsNoCompress;
private final Set<File> jniFolders;
private final boolean keepTimestampsInApk;
private final boolean isJniDebuggable;
private final int minSdkVersion;
PackageSplitAbiTransformParams(ApkData apkInfo, File input, PackageSplitAbi task) {
this.apkInfo = apkInfo;
this.input = input;
output =
new File(
task.outputDirectory,
getApkName(
apkInfo,
(String)
task.getProject()
.getProperties()
.get("archivesBaseName"),
task.signingConfig != null));
incrementalDir = task.incrementalDir;
signingConfigFile = SigningConfigMetadata.Companion.getOutputFile(task.signingConfig);
createdBy = task.getBuilder().getCreatedBy();
aaptOptionsNoCompress = task.aaptOptionsNoCompress;
jniFolders = task.getJniFolders().getFiles();
keepTimestampsInApk = AndroidGradleOptions.keepTimestampsInApk(task.getProject());
isJniDebuggable = task.jniDebuggable;
minSdkVersion = task.getMinSdkVersion();
}
@NonNull
@Override
public File getOutput() {
return output;
}
}
private static String getApkName(
final ApkData apkData, String archivesBaseName, boolean isSigned) {
String apkName = archivesBaseName + "-" + apkData.getBaseName();
return apkName + (isSigned ? "" : "-unsigned") + SdkConstants.DOT_ANDROID_PACKAGE;
}
// ----- CreationAction -----
public static class CreationAction extends VariantTaskCreationAction<PackageSplitAbi> {
private File outputDirectory;
public CreationAction(VariantScope scope) {
super(scope);
}
@Override
@NonNull
public String getName() {
return getVariantScope().getTaskName("package", "SplitAbi");
}
@Override
@NonNull
public Class<PackageSplitAbi> getType() {
return PackageSplitAbi.class;
}
@Override
public void preConfigure(@NonNull String taskName) {
super.preConfigure(taskName);
outputDirectory =
getVariantScope()
.getArtifacts()
.appendArtifact(
InternalArtifactType.ABI_PACKAGED_SPLIT, taskName, "out");
}
@Override
public void handleProvider(@NonNull TaskProvider<? extends PackageSplitAbi> taskProvider) {
super.handleProvider(taskProvider);
getVariantScope().getTaskContainer().setPackageSplitAbiTask(taskProvider);
}
@Override
public void configure(@NonNull PackageSplitAbi task) {
super.configure(task);
VariantScope scope = getVariantScope();
VariantConfiguration config = scope.getVariantConfiguration();
task.processedAbiResources =
scope.getArtifacts()
.getFinalArtifactFiles(InternalArtifactType.ABI_PROCESSED_SPLIT_RES);
task.signingConfig = scope.getSigningConfigFileCollection();
task.outputDirectory = outputDirectory;
task.minSdkVersion = config.getMinSdkVersion();
task.incrementalDir = scope.getIncrementalDir(task.getName());
task.aaptOptionsNoCompress =
scope.getGlobalScope().getExtension().getAaptOptions().getNoCompress();
task.jniDebuggable = config.getBuildType().isJniDebuggable();
task.jniFolders =
scope.getTransformManager()
.getPipelineOutputAsFileCollection(StreamFilter.NATIVE_LIBS);
task.jniDebuggable = config.getBuildType().isJniDebuggable();
task.splits = scope.getVariantData().getFilters(OutputFile.FilterType.ABI);
MutableTaskContainer taskContainer = scope.getTaskContainer();
if (taskContainer.getExternalNativeBuildTask() != null) {
task.dependsOn(taskContainer.getExternalNativeBuildTask());
}
}
}
}