blob: 11b7df280db284ac0e47f58ec16d078750170a60 [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.annotations.Nullable;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.build.gradle.internal.core.VariantConfiguration;
import com.android.build.gradle.internal.dsl.CoreBuildType;
import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.build.gradle.internal.scope.ApkData;
import com.android.build.gradle.internal.scope.BuildArtifactsHolder;
import com.android.build.gradle.internal.scope.BuildOutput;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.scope.OutputScope;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.TaskInputHelper;
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction;
import com.android.build.gradle.internal.tasks.manifest.ManifestHelperKt;
import com.android.builder.model.ApiVersion;
import com.android.builder.model.ProductFlavor;
import com.android.ide.common.workers.WorkerExecutorFacade;
import com.android.manifmerger.ManifestMerger2;
import com.android.manifmerger.MergingReport;
import com.android.manifmerger.XmlDocument;
import com.android.utils.FileUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.inject.Inject;
import org.apache.tools.ant.BuildException;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskProvider;
/** a Task that only merge a single manifest with its overlays. */
@CacheableTask
public abstract class ProcessLibraryManifest extends ManifestProcessorTask {
private Supplier<String> minSdkVersion;
private Supplier<String> targetSdkVersion;
private Supplier<Integer> maxSdkVersion;
private OutputScope outputScope;
private final RegularFileProperty manifestOutputFile;
private final RegularFileProperty mainManifest;
private final Property<String> packageOverride;
private final Property<Integer> versionCode;
private final Property<String> versionName;
private final ListProperty<File> manifestOverlays;
private final MapProperty<String, Object> manifestPlaceholders;
private boolean isNamespaced;
@Inject
public ProcessLibraryManifest(ObjectFactory objectFactory) {
super(objectFactory);
manifestOutputFile = objectFactory.fileProperty();
mainManifest = objectFactory.fileProperty();
packageOverride = objectFactory.property(String.class);
versionCode = objectFactory.property(Integer.class);
versionName = objectFactory.property(String.class);
manifestOverlays = objectFactory.listProperty(File.class);
manifestPlaceholders = objectFactory.mapProperty(String.class, Object.class);
}
@OutputFile
@NonNull
public RegularFileProperty getManifestOutputFile() {
return manifestOutputFile;
}
@Override
protected void doFullTaskAction() {
try (WorkerExecutorFacade workers = getWorkerFacadeWithWorkers()) {
DirectoryProperty manifestOutputDirectory = getManifestOutputDirectory();
DirectoryProperty aaptFriendlyManifestOutputDirectory =
getAaptFriendlyManifestOutputDirectory();
workers.submit(
ProcessLibRunnable.class,
new ProcessLibParams(
getAaptFriendlyManifestOutputFile(),
isNamespaced,
mainManifest.getAsFile().get(),
manifestOverlays.get(),
packageOverride.getOrNull(),
versionCode.get(),
versionName.getOrNull(),
getMinSdkVersion(),
getTargetSdkVersion(),
getMaxSdkVersion(),
manifestOutputFile.get().getAsFile(),
manifestPlaceholders.get(),
getReportFile().get().getAsFile(),
getMergeBlameFile().get().getAsFile(),
manifestOutputDirectory.isPresent()
? manifestOutputDirectory.get().getAsFile()
: null,
aaptFriendlyManifestOutputDirectory.isPresent()
? aaptFriendlyManifestOutputDirectory.get().getAsFile()
: null,
outputScope.getMainSplit()));
}
}
private static class ProcessLibParams implements Serializable {
@Nullable private final File aaptFriendlyManifestOutputFile;
private final boolean isNamespaced;
@NonNull private final File mainManifest;
@NonNull private final List<File> manifestOverlays;
@Nullable private final String packageOverride;
private final int versionCode;
@Nullable private final String versionName;
@Nullable private final String minSdkVersion;
@Nullable private final String targetSdkVersion;
@Nullable private final Integer maxSdkVersion;
@NonNull private final File manifestOutputFile;
@NonNull private final Map<String, Object> manifestPlaceholders;
@NonNull private final File reportFile;
@NonNull private final File mergeBlameFile;
@Nullable private final File manifestOutputDirectory;
@Nullable private final File aaptFriendlyManifestOutputDirectory;
@NonNull private final ApkData mainSplit;
private ProcessLibParams(
@Nullable File aaptFriendlyManifestOutputFile,
boolean isNamespaced,
@NonNull File mainManifest,
@NonNull List<File> manifestOverlays,
@Nullable String packageOverride,
int versionCode,
@Nullable String versionName,
@Nullable String minSdkVersion,
@Nullable String targetSdkVersion,
@Nullable Integer maxSdkVersion,
@NonNull File manifestOutputFile,
@NonNull Map<String, Object> manifestPlaceholders,
@NonNull File reportFile,
@NonNull File mergeBlameFile,
@Nullable File manifestOutputDirectory,
@Nullable File aaptFriendlyManifestOutputDirectory,
@NonNull ApkData mainSplit) {
this.aaptFriendlyManifestOutputFile = aaptFriendlyManifestOutputFile;
this.isNamespaced = isNamespaced;
this.mainManifest = mainManifest;
this.manifestOverlays = manifestOverlays;
this.packageOverride = packageOverride;
this.versionCode = versionCode;
this.versionName = versionName;
this.minSdkVersion = minSdkVersion;
this.targetSdkVersion = targetSdkVersion;
this.maxSdkVersion = maxSdkVersion;
this.manifestOutputFile = manifestOutputFile;
this.manifestPlaceholders = manifestPlaceholders;
this.reportFile = reportFile;
this.mergeBlameFile = mergeBlameFile;
this.manifestOutputDirectory = manifestOutputDirectory;
this.aaptFriendlyManifestOutputDirectory = aaptFriendlyManifestOutputDirectory;
this.mainSplit = mainSplit;
}
}
public static class ProcessLibRunnable implements Runnable {
@NonNull private final ProcessLibParams params;
@Inject
public ProcessLibRunnable(@NonNull ProcessLibParams params) {
this.params = params;
}
@Override
public void run() {
Collection<ManifestMerger2.Invoker.Feature> optionalFeatures =
params.isNamespaced
? Collections.singletonList(
ManifestMerger2.Invoker.Feature.FULLY_NAMESPACE_LOCAL_RESOURCES)
: Collections.emptyList();
MergingReport mergingReport =
ManifestHelperKt.mergeManifestsForApplication(
params.mainManifest,
params.manifestOverlays,
Collections.emptyList(),
Collections.emptyList(),
null,
params.packageOverride,
params.versionCode,
params.versionName,
params.minSdkVersion,
params.targetSdkVersion,
params.maxSdkVersion,
params.manifestOutputFile.getAbsolutePath(),
params.aaptFriendlyManifestOutputFile != null
? params.aaptFriendlyManifestOutputFile.getAbsolutePath()
: null,
null /* outInstantRunManifestLocation */,
null, /*outMetadataFeatureManifestLocation */
null /* outInstantAppManifestLocation */,
ManifestMerger2.MergeType.LIBRARY,
params.manifestPlaceholders,
optionalFeatures,
params.reportFile,
LoggerWrapper.getLogger(ProcessLibraryManifest.class));
XmlDocument mergedXmlDocument =
mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
try {
outputMergeBlameContents(mergingReport, params.mergeBlameFile);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
ImmutableMap<String, String> properties =
mergedXmlDocument != null
? ImmutableMap.of(
"packageId", mergedXmlDocument.getPackageName(),
"split", mergedXmlDocument.getSplitName())
: ImmutableMap.of();
try {
if (params.manifestOutputDirectory != null) {
new BuildOutput(
InternalArtifactType.MERGED_MANIFESTS,
params.mainSplit,
params.manifestOutputFile,
properties)
.save(params.manifestOutputDirectory);
}
if (params.aaptFriendlyManifestOutputDirectory != null) {
new BuildOutput(
InternalArtifactType.AAPT_FRIENDLY_MERGED_MANIFESTS,
params.mainSplit,
params.aaptFriendlyManifestOutputFile,
properties)
.save(params.aaptFriendlyManifestOutputDirectory);
}
} catch (IOException e) {
throw new BuildException("Exception while saving build metadata : ", e);
}
}
}
@Nullable
@Override
@Internal
public File getAaptFriendlyManifestOutputFile() {
Preconditions.checkNotNull(outputScope.getMainSplit());
return getAaptFriendlyManifestOutputDirectory().isPresent()
? FileUtils.join(
getAaptFriendlyManifestOutputDirectory().get().getAsFile(),
outputScope.getMainSplit().getDirName(),
SdkConstants.ANDROID_MANIFEST_XML)
: null;
}
@Input
@Optional
public String getMinSdkVersion() {
return minSdkVersion.get();
}
@Input
@Optional
public String getTargetSdkVersion() {
return targetSdkVersion.get();
}
@Input
@Optional
public Integer getMaxSdkVersion() {
return maxSdkVersion.get();
}
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public RegularFileProperty getMainManifest() {
return mainManifest;
}
@Input
@Optional
public Property<String> getPackageOverride() {
return packageOverride;
}
@Input
public Property<Integer> getVersionCode() {
return versionCode;
}
@Input
@Optional
public Property<String> getVersionName() {
return versionName;
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public ListProperty<File> getManifestOverlays() {
return manifestOverlays;
}
/**
* Returns a serialized version of our map of key value pairs for placeholder substitution.
*
* <p>This serialized form is only used by gradle to compare past and present tasks to determine
* whether a task need to be re-run or not.
*/
@Input
@Optional
public MapProperty<String, Object> getManifestPlaceholders() {
return manifestPlaceholders;
}
@Input
public String getMainSplitFullName() {
// This information is written to the build output's metadata file, so it needs to be
// annotated as @Input
return outputScope.getMainSplit().getFullName();
}
public static class CreationAction extends VariantTaskCreationAction<ProcessLibraryManifest> {
/**
* {@code EagerTaskCreationAction} for the library process manifest task.
*
* @param scope The library variant scope.
*/
public CreationAction(@NonNull VariantScope scope) {
super(scope);
}
@NonNull
@Override
public String getName() {
return getVariantScope().getTaskName("process", "Manifest");
}
@NonNull
@Override
public Class<ProcessLibraryManifest> getType() {
return ProcessLibraryManifest.class;
}
@Override
public void handleProvider(
@NonNull TaskProvider<? extends ProcessLibraryManifest> taskProvider) {
super.handleProvider(taskProvider);
getVariantScope().getTaskContainer().setProcessManifestTask(taskProvider);
BuildArtifactsHolder artifacts = getVariantScope().getArtifacts();
artifacts.producesDir(
InternalArtifactType.AAPT_FRIENDLY_MERGED_MANIFESTS,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ManifestProcessorTask::getAaptFriendlyManifestOutputDirectory,
"aapt");
artifacts.producesDir(
InternalArtifactType.MERGED_MANIFESTS,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ManifestProcessorTask::getManifestOutputDirectory,
"");
artifacts.producesFile(
InternalArtifactType.LIBRARY_MANIFEST,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessLibraryManifest::getManifestOutputFile,
SdkConstants.ANDROID_MANIFEST_XML);
artifacts.producesFile(
InternalArtifactType.MANIFEST_MERGE_BLAME_FILE,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessLibraryManifest::getMergeBlameFile,
"manifest-merger-blame-"
+ getVariantScope().getVariantConfiguration().getBaseName()
+ "-report.txt");
artifacts.producesFile(
InternalArtifactType.MANIFEST_MERGE_REPORT,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessLibraryManifest::getReportFile,
FileUtils.join(getVariantScope().getGlobalScope().getOutputsDir(), "logs")
.getAbsolutePath(),
"manifest-merger-"
+ getVariantScope().getVariantConfiguration().getBaseName()
+ "-report.txt");
}
@Override
public void configure(@NonNull ProcessLibraryManifest task) {
super.configure(task);
getVariantScope()
.getArtifacts()
.setTaskInputToFinalProduct(
InternalArtifactType.CHECK_MANIFEST_RESULT,
task.getCheckManifestResult());
VariantConfiguration<CoreBuildType, CoreProductFlavor, CoreProductFlavor> config =
getVariantScope().getVariantConfiguration();
final ProductFlavor mergedFlavor = config.getMergedFlavor();
task.minSdkVersion =
TaskInputHelper.memoize(
() -> {
ApiVersion minSdkVersion1 = mergedFlavor.getMinSdkVersion();
if (minSdkVersion1 == null) {
return null;
}
return minSdkVersion1.getApiString();
});
task.targetSdkVersion =
TaskInputHelper.memoize(
() -> {
ApiVersion targetSdkVersion = mergedFlavor.getTargetSdkVersion();
if (targetSdkVersion == null) {
return null;
}
return targetSdkVersion.getApiString();
});
task.maxSdkVersion = TaskInputHelper.memoize(mergedFlavor::getMaxSdkVersion);
task.outputScope = getVariantScope().getOutputScope();
task.isNamespaced =
getVariantScope()
.getGlobalScope()
.getExtension()
.getAaptOptions()
.getNamespaced();
task.versionName.set(task.getProject().provider(config::getVersionName));
task.versionCode.set(task.getProject().provider(config::getVersionCode));
task.packageOverride.set(task.getProject().provider(config::getApplicationId));
task.manifestPlaceholders.set(
task.getProject().provider(config::getManifestPlaceholders));
task.mainManifest.set(
task.getProject()
.provider(
() -> {
RegularFileProperty fileProp =
task.getProject().getObjects().fileProperty();
fileProp.set(config.getMainManifest());
return fileProp.get();
}));
task.manifestOverlays.set(task.getProject().provider(config::getManifestOverlays));
}
}
}