blob: 8458d0a5a8181c33e0143e4a048d0d35fc7bc594 [file] [log] [blame]
/*
* Copyright (C) 2012 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 static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.PROJECT;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.FEATURE_APPLICATION_ID_DECLARATION;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.MANIFEST;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.METADATA_BASE_MODULE_DECLARATION;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.METADATA_FEATURE_MANIFEST;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.NAVIGATION_JSON;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.METADATA_VALUES;
import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH;
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.GradleVariantConfiguration;
import com.android.build.gradle.internal.dependency.ArtifactCollectionWithExtraArtifact.ExtraComponentIdentifier;
import com.android.build.gradle.internal.scope.ApkData;
import com.android.build.gradle.internal.scope.BuildArtifactsHolder;
import com.android.build.gradle.internal.scope.BuildElements;
import com.android.build.gradle.internal.scope.BuildOutput;
import com.android.build.gradle.internal.scope.ExistingBuildElements;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.ModuleMetadata;
import com.android.build.gradle.internal.tasks.TaskInputHelper;
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction;
import com.android.build.gradle.internal.tasks.featuresplit.FeatureSetMetadata;
import com.android.build.gradle.internal.tasks.manifest.ManifestHelperKt;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.options.BooleanOption;
import com.android.builder.core.VariantType;
import com.android.builder.dexing.DexingType;
import com.android.builder.model.ApiVersion;
import com.android.manifmerger.ManifestMerger2;
import com.android.manifmerger.ManifestMerger2.Invoker.Feature;
import com.android.manifmerger.ManifestProvider;
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.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.gradle.api.GradleException;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ArtifactCollection;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCollection;
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.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.internal.component.local.model.OpaqueComponentArtifactIdentifier;
/** A task that processes the manifest */
@CacheableTask
public abstract class ProcessApplicationManifest extends ManifestProcessorTask {
private Supplier<String> minSdkVersion;
private Supplier<String> targetSdkVersion;
private Supplier<Integer> maxSdkVersion;
private ArtifactCollection manifests;
private ArtifactCollection featureManifests;
private FileCollection microApkManifest;
private FileCollection packageManifest;
private Supplier<EnumSet<Feature>> optionalFeatures;
private final RegularFileProperty mainManifest;
private final Property<String> packageOverride;
private final ListProperty<File> manifestOverlays;
private final MapProperty<String, Object> manifestPlaceholders;
private boolean isHybridVariantType;
private String buildTypeName;
private FileCollection navigationJsons;
// supplier to read the file above to get the feature name for the current project.
@Nullable private Supplier<String> featureNameSupplier = null;
@Inject
public ProcessApplicationManifest(ObjectFactory objectFactory) {
super(objectFactory);
mainManifest = objectFactory.fileProperty();
packageOverride = objectFactory.property(String.class);
manifestOverlays = objectFactory.listProperty(File.class);
manifestPlaceholders = objectFactory.mapProperty(String.class, Object.class);
}
@Override
protected void doFullTaskAction() throws IOException {
// read the output of the compatible screen manifest.
BuildElements compatibleScreenManifests =
ExistingBuildElements.from(
InternalArtifactType.COMPATIBLE_SCREEN_MANIFEST,
getCompatibleScreensManifest().get().getAsFile());
ModuleMetadata moduleMetadata = null;
if (packageManifest != null && !packageManifest.isEmpty()) {
moduleMetadata = ModuleMetadata.load(packageManifest.getSingleFile());
boolean isDebuggable = optionalFeatures.get().contains(Feature.DEBUGGABLE);
if (moduleMetadata.getDebuggable() != isDebuggable) {
String moduleType = isHybridVariantType ? "Instant App Feature" : "Dynamic Feature";
String errorMessage =
String.format(
"%1$s '%2$s' (build type '%3$s') %4$s debuggable,\n"
+ "and the corresponding build type in the base "
+ "application %5$s debuggable.\n"
+ "Recommendation: \n"
+ " in %6$s\n"
+ " set android.buildTypes.%3$s.debuggable = %7$s",
moduleType,
getProject().getPath(),
buildTypeName,
isDebuggable ? "is" : "is not",
moduleMetadata.getDebuggable() ? "is" : "is not",
getProject().getBuildFile(),
moduleMetadata.getDebuggable() ? "true" : "false");
throw new InvalidUserDataException(errorMessage);
}
}
@Nullable BuildOutput compatibleScreenManifestForSplit;
ImmutableList.Builder<BuildOutput> mergedManifestOutputs = ImmutableList.builder();
ImmutableList.Builder<BuildOutput> metadataFeatureMergedManifestOutputs =
ImmutableList.builder();
ImmutableList.Builder<BuildOutput> bundleManifestOutputs = ImmutableList.builder();
ImmutableList.Builder<BuildOutput> instantAppManifestOutputs = ImmutableList.builder();
List<File> navJsons =
navigationJsons == null
? Collections.emptyList()
: Lists.newArrayList(navigationJsons);
// FIX ME : multi threading.
for (ApkData apkData : ExistingBuildElements.loadApkList(getApkList().get().getAsFile())) {
compatibleScreenManifestForSplit = compatibleScreenManifests.element(apkData);
File manifestOutputFile =
new File(
getManifestOutputDirectory().get().getAsFile(),
FileUtils.join(apkData.getDirName(), ANDROID_MANIFEST_XML));
File metadataFeatureManifestOutputFile =
FileUtils.join(
getMetadataFeatureManifestOutputDirectory().get().getAsFile(),
apkData.getDirName(),
ANDROID_MANIFEST_XML);
File bundleManifestOutputFile =
FileUtils.join(
getBundleManifestOutputDirectory().get().getAsFile(),
apkData.getDirName(),
ANDROID_MANIFEST_XML);
File instantAppManifestOutputFile =
getInstantAppManifestOutputDirectory().isPresent()
? FileUtils.join(
getInstantAppManifestOutputDirectory().get().getAsFile(),
apkData.getDirName(),
ANDROID_MANIFEST_XML)
: null;
MergingReport mergingReport =
ManifestHelperKt.mergeManifestsForApplication(
mainManifest.getAsFile().get(),
manifestOverlays.get(),
computeFullProviderList(compatibleScreenManifestForSplit),
navJsons,
getFeatureName(),
moduleMetadata == null
? packageOverride.getOrNull()
: moduleMetadata.getApplicationId(),
moduleMetadata == null
? apkData.getVersionCode()
: Integer.parseInt(moduleMetadata.getVersionCode()),
moduleMetadata == null
? apkData.getVersionName()
: moduleMetadata.getVersionName(),
getMinSdkVersion(),
getTargetSdkVersion(),
getMaxSdkVersion(),
manifestOutputFile.getAbsolutePath(),
// no aapt friendly merged manifest file necessary for applications.
null /* aaptFriendlyManifestOutputFile */,
metadataFeatureManifestOutputFile.getAbsolutePath(),
bundleManifestOutputFile.getAbsolutePath(),
instantAppManifestOutputFile != null
? instantAppManifestOutputFile.getAbsolutePath()
: null,
ManifestMerger2.MergeType.APPLICATION,
manifestPlaceholders.get(),
getOptionalFeatures(),
getReportFile().get().getAsFile(),
LoggerWrapper.getLogger(ProcessApplicationManifest.class));
XmlDocument mergedXmlDocument =
mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
outputMergeBlameContents(mergingReport, getMergeBlameFile().get().getAsFile());
ImmutableMap<String, String> properties =
mergedXmlDocument != null
? ImmutableMap.of(
"packageId",
mergedXmlDocument.getPackageName(),
"split",
mergedXmlDocument.getSplitName(),
SdkConstants.ATTR_MIN_SDK_VERSION,
mergedXmlDocument.getMinSdkVersion())
: ImmutableMap.of();
mergedManifestOutputs.add(
new BuildOutput(
InternalArtifactType.MERGED_MANIFESTS,
apkData,
manifestOutputFile,
properties));
metadataFeatureMergedManifestOutputs.add(
new BuildOutput(
InternalArtifactType.METADATA_FEATURE_MANIFEST,
apkData,
metadataFeatureManifestOutputFile));
bundleManifestOutputs.add(
new BuildOutput(
InternalArtifactType.BUNDLE_MANIFEST,
apkData,
bundleManifestOutputFile,
properties));
if (instantAppManifestOutputFile != null) {
instantAppManifestOutputs.add(
new BuildOutput(
InternalArtifactType.INSTANT_APP_MANIFEST,
apkData,
instantAppManifestOutputFile,
properties));
}
}
new BuildElements(mergedManifestOutputs.build())
.save(getManifestOutputDirectory());
new BuildElements(metadataFeatureMergedManifestOutputs.build())
.save(getMetadataFeatureManifestOutputDirectory());
new BuildElements(bundleManifestOutputs.build()).save(
getBundleManifestOutputDirectory());
if (getInstantAppManifestOutputDirectory().isPresent()) {
new BuildElements(instantAppManifestOutputs.build())
.save(getInstantAppManifestOutputDirectory());
}
}
@Nullable
@Override
@Internal
public File getAaptFriendlyManifestOutputFile() {
return null;
}
@Optional
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public RegularFileProperty getMainManifest() {
return mainManifest;
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public ListProperty<File> getManifestOverlays() {
return manifestOverlays;
}
@Input
@Optional
public Property<String> getPackageOverride() {
return packageOverride;
}
/**
* 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;
}
private List<ManifestProvider> computeProviders(
@NonNull Set<ResolvedArtifactResult> artifacts) {
List<ManifestProvider> providers = Lists.newArrayListWithCapacity(artifacts.size());
for (ResolvedArtifactResult artifact : artifacts) {
File directory = artifact.getFile();
BuildElements splitOutputs =
ExistingBuildElements.from(
InternalArtifactType.METADATA_FEATURE_MANIFEST, directory);
if (splitOutputs.isEmpty()) {
throw new GradleException("Could not load manifest from " + directory);
}
providers.add(
new CreationAction.ManifestProviderImpl(
splitOutputs.iterator().next().getOutputFile(),
getArtifactName(artifact)));
}
return providers;
}
/**
* Compute the final list of providers based on the manifest file collection and the other
* providers.
*
* @return the list of providers.
*/
private List<ManifestProvider> computeFullProviderList(
@Nullable BuildOutput compatibleScreenManifestForSplit) {
final Set<ResolvedArtifactResult> artifacts = manifests.getArtifacts();
List<ManifestProvider> providers = Lists.newArrayListWithCapacity(artifacts.size() + 2);
for (ResolvedArtifactResult artifact : artifacts) {
providers.add(
new CreationAction.ManifestProviderImpl(
artifact.getFile(), getArtifactName(artifact)));
}
if (microApkManifest != null) {
// this is now always present if embedding is enabled, but it doesn't mean
// anything got embedded so the file may not run (the file path exists and is
// returned by the FC but the file doesn't exist.
File microManifest = microApkManifest.getSingleFile();
if (microManifest.isFile()) {
providers.add(
new CreationAction.ManifestProviderImpl(
microManifest, "Wear App sub-manifest"));
}
}
if (compatibleScreenManifestForSplit != null) {
providers.add(
new CreationAction.ManifestProviderImpl(
compatibleScreenManifestForSplit.getOutputFile(),
"Compatible-Screens sub-manifest"));
}
if (getAutoNamespacedManifests().isPresent()) {
// We do not have resolved artifact results here, we need to find the artifact name
// based on the file name.
File directory = getAutoNamespacedManifests().get().getAsFile();
Preconditions.checkState(
directory.isDirectory(),
"Auto namespaced manifests should be a directory.",
directory);
for (File autoNamespacedManifest : Preconditions.checkNotNull(directory.listFiles())) {
providers.add(
new CreationAction.ManifestProviderImpl(
autoNamespacedManifest,
getNameFromAutoNamespacedManifest(autoNamespacedManifest)));
}
}
if (featureManifests != null) {
providers.addAll(computeProviders(featureManifests.getArtifacts()));
}
return providers;
}
@NonNull
@Internal
private static String getNameFromAutoNamespacedManifest(@NonNull File manifest) {
final String manifestSuffix = "_AndroidManifest.xml";
String fileName = manifest.getName();
// Get the ID based on the file name generated by the [AutoNamespaceDependenciesTask]. It is
// the sanitized name, but should be enough.
if (!fileName.endsWith(manifestSuffix)) {
throw new RuntimeException(
"Invalid auto-namespaced manifest file: " + manifest.getAbsolutePath());
}
return fileName.substring(0, fileName.length() - manifestSuffix.length());
}
// TODO put somewhere else?
@NonNull
@Internal
public static String getArtifactName(@NonNull ResolvedArtifactResult artifact) {
ComponentIdentifier id = artifact.getId().getComponentIdentifier();
if (id instanceof ProjectComponentIdentifier) {
return ((ProjectComponentIdentifier) id).getProjectPath();
} else if (id instanceof ModuleComponentIdentifier) {
ModuleComponentIdentifier mID = (ModuleComponentIdentifier) id;
return mID.getGroup() + ":" + mID.getModule() + ":" + mID.getVersion();
} else if (id instanceof OpaqueComponentArtifactIdentifier) {
// this is the case for local jars.
// FIXME: use a non internal class.
return id.getDisplayName();
} else if (id instanceof ExtraComponentIdentifier) {
return id.getDisplayName();
} else {
throw new RuntimeException("Unsupported type of ComponentIdentifier");
}
}
@Input
@Optional
public String getMinSdkVersion() {
return minSdkVersion.get();
}
@Input
@Optional
public String getTargetSdkVersion() {
return targetSdkVersion.get();
}
@Input
@Optional
public Integer getMaxSdkVersion() {
return maxSdkVersion.get();
}
/** Not an input, see {@link #getOptionalFeaturesString()}. */
@Internal
public EnumSet<Feature> getOptionalFeatures() {
return optionalFeatures.get();
}
/** Synthetic input for {@link #getOptionalFeatures()} */
@Input
public List<String> getOptionalFeaturesString() {
return getOptionalFeatures().stream().map(Enum::toString).collect(Collectors.toList());
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getManifests() {
return manifests.getArtifactFiles();
}
@Optional
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getNavigationJsons() {
return navigationJsons;
}
@InputFiles
@Optional
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getFeatureManifests() {
if (featureManifests == null) {
return null;
}
return featureManifests.getArtifactFiles();
}
@InputFiles
@Optional
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getMicroApkManifest() {
return microApkManifest;
}
@InputFiles
@Optional
@PathSensitive(PathSensitivity.RELATIVE)
public abstract DirectoryProperty getCompatibleScreensManifest();
@InputFiles
@Optional
@PathSensitive(PathSensitivity.RELATIVE)
public FileCollection getPackageManifest() {
return packageManifest;
}
@Input
@Optional
public String getFeatureName() {
return featureNameSupplier != null ? featureNameSupplier.get() : null;
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
@Optional
public abstract DirectoryProperty getAutoNamespacedManifests();
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getApkList();
public static class CreationAction
extends VariantTaskCreationAction<ProcessApplicationManifest> {
protected final boolean isAdvancedProfilingOn;
public CreationAction(
@NonNull VariantScope scope,
// TODO : remove this variable and find ways to access it from scope.
boolean isAdvancedProfilingOn) {
super(scope);
this.isAdvancedProfilingOn = isAdvancedProfilingOn;
}
@NonNull
@Override
public String getName() {
return getVariantScope().getTaskName("process", "Manifest");
}
@NonNull
@Override
public Class<ProcessApplicationManifest> getType() {
return ProcessApplicationManifest.class;
}
@Override
public void preConfigure(@NonNull String taskName) {
super.preConfigure(taskName);
VariantType variantType = getVariantScope().getType();
Preconditions.checkState(!variantType.isTestComponent());
BuildArtifactsHolder artifacts = getVariantScope().getArtifacts();
artifacts.republish(
InternalArtifactType.MERGED_MANIFESTS, InternalArtifactType.MANIFEST_METADATA);
}
@Override
public void handleProvider(
@NonNull TaskProvider<? extends ProcessApplicationManifest> taskProvider) {
super.handleProvider(taskProvider);
getVariantScope().getTaskContainer().setProcessManifestTask(taskProvider);
BuildArtifactsHolder artifacts = getVariantScope().getArtifacts();
artifacts
.producesDir(
InternalArtifactType.MERGED_MANIFESTS,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ManifestProcessorTask::getManifestOutputDirectory,
"");
artifacts
.producesDir(
InternalArtifactType.INSTANT_APP_MANIFEST,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ManifestProcessorTask::getInstantAppManifestOutputDirectory,
"");
artifacts.producesFile(
InternalArtifactType.MANIFEST_MERGE_BLAME_FILE,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessApplicationManifest::getMergeBlameFile,
"manifest-merger-blame-"
+ getVariantScope().getVariantConfiguration().getBaseName()
+ "-report.txt");
artifacts.producesDir(
InternalArtifactType.METADATA_FEATURE_MANIFEST,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessApplicationManifest::getMetadataFeatureManifestOutputDirectory,
"metadata-feature");
artifacts.producesDir(
InternalArtifactType.BUNDLE_MANIFEST,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessApplicationManifest::getBundleManifestOutputDirectory,
"bundle-manifest");
getVariantScope()
.getArtifacts()
.producesFile(
InternalArtifactType.MANIFEST_MERGE_REPORT,
BuildArtifactsHolder.OperationType.INITIAL,
taskProvider,
ProcessApplicationManifest::getReportFile,
FileUtils.join(
getVariantScope().getGlobalScope().getOutputsDir(),
"logs")
.getAbsolutePath(),
"manifest-merger-"
+ getVariantScope().getVariantConfiguration().getBaseName()
+ "-report.txt");
}
@Override
public void configure(@NonNull ProcessApplicationManifest task) {
super.configure(task);
getVariantScope()
.getArtifacts()
.setTaskInputToFinalProduct(
InternalArtifactType.CHECK_MANIFEST_RESULT,
task.getCheckManifestResult());
final BaseVariantData variantData = getVariantScope().getVariantData();
final GradleVariantConfiguration config = variantData.getVariantConfiguration();
GlobalScope globalScope = getVariantScope().getGlobalScope();
VariantType variantType = getVariantScope().getType();
Project project = globalScope.getProject();
// This includes the dependent libraries.
task.manifests =
getVariantScope().getArtifactCollection(RUNTIME_CLASSPATH, ALL, MANIFEST);
// Also include rewritten auto-namespaced manifests if there are any
if (variantType
.isBaseModule() // TODO(b/112251836): Auto namespacing for dynamic features.
&& getVariantScope()
.getGlobalScope()
.getExtension()
.getAaptOptions()
.getNamespaced()
&& getVariantScope()
.getGlobalScope()
.getProjectOptions()
.get(BooleanOption.CONVERT_NON_NAMESPACED_DEPENDENCIES)) {
getVariantScope()
.getArtifacts()
.setTaskInputToFinalProduct(
InternalArtifactType.NAMESPACED_MANIFESTS,
task.getAutoNamespacedManifests());
}
// optional manifest files too.
if (getVariantScope().getTaskContainer().getMicroApkTask() != null
&& config.getBuildType().isEmbedMicroApp()) {
task.microApkManifest = project.files(getVariantScope().getMicroApkManifestFile());
}
BuildArtifactsHolder artifacts = getVariantScope().getArtifacts();
artifacts.setTaskInputToFinalProduct(
InternalArtifactType.COMPATIBLE_SCREEN_MANIFEST,
task.getCompatibleScreensManifest());
task.minSdkVersion =
TaskInputHelper.memoize(
() -> {
ApiVersion minSdk = config.getMergedFlavor().getMinSdkVersion();
return minSdk == null ? null : minSdk.getApiString();
});
task.targetSdkVersion =
TaskInputHelper.memoize(
() -> {
ApiVersion targetSdk =
config.getMergedFlavor().getTargetSdkVersion();
return targetSdk == null ? null : targetSdk.getApiString();
});
task.maxSdkVersion =
TaskInputHelper.memoize(config.getMergedFlavor()::getMaxSdkVersion);
task.optionalFeatures =
TaskInputHelper.memoize(
() -> getOptionalFeatures(getVariantScope(), isAdvancedProfilingOn));
artifacts.setTaskInputToFinalProduct(InternalArtifactType.APK_LIST, task.getApkList());
// set optional inputs per module type
if (variantType.isBaseModule()) {
task.packageManifest =
getVariantScope()
.getArtifactFileCollection(
METADATA_VALUES, PROJECT, METADATA_BASE_MODULE_DECLARATION);
task.featureManifests =
getVariantScope()
.getArtifactCollection(
METADATA_VALUES, PROJECT, METADATA_FEATURE_MANIFEST);
} else if (variantType.isFeatureSplit()) {
task.featureNameSupplier =
FeatureSetMetadata.getInstance()
.getFeatureNameSupplierForTask(getVariantScope(), task);
task.packageManifest =
getVariantScope()
.getArtifactFileCollection(
COMPILE_CLASSPATH,
PROJECT,
FEATURE_APPLICATION_ID_DECLARATION);
}
if (!getVariantScope()
.getGlobalScope()
.getExtension()
.getAaptOptions()
.getNamespaced()) {
task.navigationJsons =
project.files(
getVariantScope()
.getArtifacts()
.getFinalProduct(InternalArtifactType.NAVIGATION_JSON),
getVariantScope()
.getArtifactFileCollection(
RUNTIME_CLASSPATH, ALL, NAVIGATION_JSON));
}
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));
task.isHybridVariantType = config.getType().isHybrid();
task.buildTypeName = config.getBuildType().getName();
// TODO: here in the "else" block should be the code path for the namespaced pipeline
}
/**
* Implementation of AndroidBundle that only contains a manifest.
*
* This is used to pass to the merger manifest snippet that needs to be added during
* merge.
*/
public static class ManifestProviderImpl implements ManifestProvider {
@NonNull
private final File manifest;
@NonNull
private final String name;
public ManifestProviderImpl(@NonNull File manifest, @NonNull String name) {
this.manifest = manifest;
this.name = name;
}
@NonNull
@Override
public File getManifest() {
return manifest;
}
@NonNull
@Override
public String getName() {
return name;
}
}
}
private static EnumSet<Feature> getOptionalFeatures(
VariantScope variantScope, boolean isAdvancedProfilingOn) {
List<Feature> features = new ArrayList<>();
VariantType variantType = variantScope.getType();
if (variantType.isHybrid()) {
features.add(Feature.TARGET_SANDBOX_VERSION);
}
if (variantType.isFeatureSplit()) {
features.add(Feature.ADD_FEATURE_SPLIT_ATTRIBUTE);
features.add(Feature.CREATE_FEATURE_MANIFEST);
}
if (variantType.isDynamicFeature()) {
features.add(Feature.STRIP_MIN_SDK_FROM_FEATURE_MANIFEST);
}
if (variantType.isHybrid()) {
features.add(Feature.ADD_INSTANT_APP_FEATURE_SPLIT_INFO);
}
if (!variantType.isHybrid()) {
features.add(Feature.ADD_INSTANT_APP_MANIFEST);
}
if (variantType.isBaseModule() || variantType.isFeatureSplit()) {
features.add(Feature.CREATE_BUNDLETOOL_MANIFEST);
}
if (variantType.isDynamicFeature()) {
// create it for dynamic-features and base modules that are not hybrid base features.
// hybrid features already contain the split name.
features.add(Feature.ADD_SPLIT_NAME_TO_BUNDLETOOL_MANIFEST);
}
if (variantScope.isTestOnly()) {
features.add(Feature.TEST_ONLY);
}
if (variantScope.getVariantConfiguration().getBuildType().isDebuggable()) {
features.add(Feature.DEBUGGABLE);
if (isAdvancedProfilingOn) {
features.add(Feature.ADVANCED_PROFILING);
}
}
if (variantScope.getVariantConfiguration().getDexingType() == DexingType.LEGACY_MULTIDEX) {
if (variantScope
.getGlobalScope()
.getProjectOptions()
.get(BooleanOption.USE_ANDROID_X)) {
features.add(Feature.ADD_ANDROIDX_MULTIDEX_APPLICATION_IF_NO_NAME);
} else {
features.add(Feature.ADD_SUPPORT_MULTIDEX_APPLICATION_IF_NO_NAME);
}
}
if (variantScope
.getGlobalScope()
.getProjectOptions()
.get(BooleanOption.ENFORCE_UNIQUE_PACKAGE_NAMES)) {
features.add(Feature.ENFORCE_UNIQUE_PACKAGE_NAME);
}
return features.isEmpty() ? EnumSet.noneOf(Feature.class) : EnumSet.copyOf(features);
}
}