blob: fd0b755024fde687b32ac1ccb97aed66e46d8215 [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
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.OutputFile
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.internal.BadPluginException
import com.android.build.gradle.internal.ConfigurationDependencies
import com.android.build.gradle.internal.LibraryCache
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.ProductFlavorData
import com.android.build.gradle.internal.SdkHandler
import com.android.build.gradle.internal.VariantManager
import com.android.build.gradle.internal.api.DefaultAndroidSourceSet
import com.android.build.gradle.internal.core.GradleVariantConfiguration
import com.android.build.gradle.internal.coverage.JacocoInstrumentTask
import com.android.build.gradle.internal.coverage.JacocoPlugin
import com.android.build.gradle.internal.coverage.JacocoReportTask
import com.android.build.gradle.internal.dependency.DependencyChecker
import com.android.build.gradle.internal.dependency.LibraryDependencyImpl
import com.android.build.gradle.internal.dependency.ManifestDependencyImpl
import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl
import com.android.build.gradle.internal.dependency.VariantDependencies
import com.android.build.gradle.internal.dsl.BuildType
import com.android.build.gradle.internal.dsl.BuildTypeFactory
import com.android.build.gradle.internal.dsl.GroupableProductFlavor
import com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
import com.android.build.gradle.internal.dsl.ProductFlavor
import com.android.build.gradle.internal.dsl.SigningConfig
import com.android.build.gradle.internal.dsl.SigningConfigFactory
import com.android.build.gradle.internal.model.ArtifactMetaDataImpl
import com.android.build.gradle.internal.model.JavaArtifactImpl
import com.android.build.gradle.internal.model.MavenCoordinatesImpl
import com.android.build.gradle.internal.model.ModelBuilder
import com.android.build.gradle.internal.model.SyncIssueImpl
import com.android.build.gradle.internal.model.SyncIssueKey
import com.android.build.gradle.internal.publishing.ApkPublishArtifact
import com.android.build.gradle.internal.tasks.AndroidReportTask
import com.android.build.gradle.internal.tasks.CheckManifest
import com.android.build.gradle.internal.tasks.DependencyReportTask
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestLibraryTask
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
import com.android.build.gradle.internal.tasks.GenerateApkDataTask
import com.android.build.gradle.internal.tasks.InstallVariantTask
import com.android.build.gradle.internal.tasks.OutputFileTask
import com.android.build.gradle.internal.tasks.PrepareDependenciesTask
import com.android.build.gradle.internal.tasks.PrepareLibraryTask
import com.android.build.gradle.internal.tasks.PrepareSdkTask
import com.android.build.gradle.internal.tasks.SigningReportTask
import com.android.build.gradle.internal.tasks.TestServerTask
import com.android.build.gradle.internal.tasks.UninstallTask
import com.android.build.gradle.internal.tasks.ValidateSigningTask
import com.android.build.gradle.internal.tasks.multidex.CreateMainDexList
import com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList
import com.android.build.gradle.internal.tasks.multidex.JarMergingTask
import com.android.build.gradle.internal.tasks.multidex.RetraceMainDexList
import com.android.build.gradle.internal.test.report.ReportType
import com.android.build.gradle.internal.variant.ApkVariantData
import com.android.build.gradle.internal.variant.ApkVariantOutputData
import com.android.build.gradle.internal.variant.ApplicationVariantData
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer
import com.android.build.gradle.internal.variant.LibraryVariantData
import com.android.build.gradle.internal.variant.TestVariantData
import com.android.build.gradle.internal.variant.TestedVariantData
import com.android.build.gradle.internal.variant.VariantFactory
import com.android.build.gradle.tasks.AidlCompile
import com.android.build.gradle.tasks.CompatibleScreensManifest
import com.android.build.gradle.tasks.Dex
import com.android.build.gradle.tasks.GenerateBuildConfig
import com.android.build.gradle.tasks.GenerateResValues
import com.android.build.gradle.tasks.GenerateSplitAbiRes
import com.android.build.gradle.tasks.JackTask
import com.android.build.gradle.tasks.JillTask
import com.android.build.gradle.tasks.Lint
import com.android.build.gradle.tasks.MergeAssets
import com.android.build.gradle.tasks.MergeManifests
import com.android.build.gradle.tasks.MergeResources
import com.android.build.gradle.tasks.NdkCompile
import com.android.build.gradle.tasks.PackageApplication
import com.android.build.gradle.tasks.PackageSplitAbi
import com.android.build.gradle.tasks.PackageSplitRes
import com.android.build.gradle.tasks.PreDex
import com.android.build.gradle.tasks.ProcessAndroidResources
import com.android.build.gradle.tasks.ProcessManifest
import com.android.build.gradle.tasks.ProcessTestManifest
import com.android.build.gradle.tasks.RenderscriptCompile
import com.android.build.gradle.tasks.ShrinkResources
import com.android.build.gradle.tasks.SplitZipAlign
import com.android.build.gradle.tasks.ZipAlign
import com.android.builder.core.AndroidBuilder
import com.android.builder.core.DefaultBuildType
import com.android.builder.core.VariantConfiguration
import com.android.builder.dependency.DependencyContainer
import com.android.builder.dependency.JarDependency
import com.android.builder.dependency.LibraryDependency
import com.android.builder.internal.compiler.JackConversionCache
import com.android.builder.internal.compiler.PreDexCache
import com.android.builder.internal.testing.SimpleTestCallable
import com.android.builder.model.AndroidArtifact
import com.android.builder.model.ApiVersion
import com.android.builder.model.ArtifactMetaData
import com.android.builder.model.JavaArtifact
import com.android.builder.model.SourceProvider
import com.android.builder.model.SourceProviderContainer
import com.android.builder.model.SyncIssue
import com.android.builder.sdk.SdkInfo
import com.android.builder.sdk.TargetInfo
import com.android.builder.testing.ConnectedDeviceProvider
import com.android.builder.testing.api.DeviceProvider
import com.android.builder.testing.api.TestServer
import com.android.ide.common.internal.ExecutorSingleton
import com.android.sdklib.AndroidTargetHash
import com.android.sdklib.SdkVersionInfo
import com.android.utils.ILogger
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.ImmutableSet
import com.google.common.collect.ListMultimap
import com.google.common.collect.Lists
import com.google.common.collect.Maps
import com.google.common.collect.Multimap
import com.google.common.collect.Sets
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownProjectException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.SelfResolvingDependency
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.artifacts.result.UnresolvedDependencyResult
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.internal.ConventionMapping
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.Logger
import org.gradle.api.plugins.BasePluginConvention
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.specs.Specs
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.internal.reflect.Instantiator
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.tooling.BuildException
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GUtil
import proguard.gradle.ProGuardTask
import java.util.jar.Attributes
import java.util.jar.Manifest
import java.util.regex.Pattern
import static com.android.SdkConstants.DOT_JAR
import static com.android.SdkConstants.EXT_ANDROID_PACKAGE
import static com.android.SdkConstants.EXT_JAR
import static com.android.SdkConstants.FN_ANDROID_MANIFEST_XML
import static com.android.builder.core.BuilderConstants.CONNECTED
import static com.android.builder.core.BuilderConstants.DEBUG
import static com.android.builder.core.BuilderConstants.DEVICE
import static com.android.builder.core.BuilderConstants.EXT_LIB_ARCHIVE
import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS
import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS
import static com.android.builder.core.BuilderConstants.FD_FLAVORS
import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL
import static com.android.builder.core.BuilderConstants.FD_REPORTS
import static com.android.builder.core.BuilderConstants.RELEASE
import static com.android.builder.core.VariantConfiguration.Type.ANDROID_TEST
import static com.android.builder.core.VariantConfiguration.Type.DEFAULT
import static com.android.builder.core.VariantConfiguration.Type.UNIT_TEST
import static com.android.builder.model.AndroidProject.FD_GENERATED
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
import static com.android.builder.model.AndroidProject.FD_OUTPUTS
import static com.android.builder.model.AndroidProject.PROPERTY_APK_LOCATION
import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY
import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_ALIAS
import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_PASSWORD
import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_FILE
import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_PASSWORD
import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_TYPE
import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN
import static java.io.File.separator
/**
* Base class for all Android plugins
*/
@CompileStatic
public abstract class BasePlugin {
public final static String DIR_BUNDLES = "bundles";
private static final String GRADLE_MIN_VERSION = "2.2"
public static final String GRADLE_TEST_VERSION = "2.2"
public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*");
private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
"com.android.build.gradle.overrideVersionCheck"
private static final String INSTALL_GROUP = "Install"
private static final String BUILD_GROUP = org.gradle.api.plugins.BasePlugin.BUILD_GROUP
public static File TEST_SDK_DIR;
public static final String FILE_JACOCO_AGENT = 'jacocoagent.jar'
public static final String DEFAULT_PROGUARD_CONFIG_FILE = 'proguard-android.txt'
protected Instantiator instantiator
private ToolingModelBuilderRegistry registry
protected JacocoPlugin jacocoPlugin
private BaseExtension extension
private VariantManager variantManager
protected boolean buildModelForIde
final Map<LibraryDependencyImpl, PrepareLibraryTask> prepareTaskMap = [:]
final Map<SigningConfig, ValidateSigningTask> validateSigningTaskMap = [:]
protected Project project
private LoggerWrapper loggerWrapper
protected SdkHandler sdkHandler
private AndroidBuilder androidBuilder
private String creator
private boolean hasCreatedTasks = false
private ProductFlavorData<ProductFlavor> defaultConfigData
/** @deprecated use syncIssue instead */
@Deprecated
private final Collection<String> unresolvedDependencies = Sets.newHashSet();
private final Map<SyncIssueKey, SyncIssue> syncIssues = Maps.newHashMap();
protected DefaultAndroidSourceSet mainSourceSet
protected DefaultAndroidSourceSet androidTestSourceSet
protected DefaultAndroidSourceSet unitTestSourceSet
protected PrepareSdkTask mainPreBuild
protected Task uninstallAll
protected Task assembleAndroidTest
protected Task deviceCheck
protected Task connectedCheck
protected Copy jacocoAgentTask
public Task lintCompile
protected Task lintAll
protected Task lintVital
protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
this.instantiator = instantiator
this.registry = registry
String pluginVersion = getLocalVersion()
if (pluginVersion != null) {
creator = "Android Gradle " + pluginVersion
} else {
creator = "Android Gradle"
}
}
protected abstract Class<? extends BaseExtension> getExtensionClass()
protected abstract VariantFactory getVariantFactory()
public Instantiator getInstantiator() {
return instantiator
}
public VariantManager getVariantManager() {
return variantManager
}
BaseExtension getExtension() {
return extension
}
protected void apply(Project project) {
this.project = project
doApply()
}
protected void doApply() {
configureProject()
createExtension()
createTasks()
}
protected void configureProject() {
buildModelForIde = isBuildModelForIde()
checkGradleVersion()
sdkHandler = new SdkHandler(project, logger)
androidBuilder = new AndroidBuilder(
project == project.rootProject ? project.name : project.path,
creator, logger, verbose)
project.apply plugin: JavaBasePlugin
project.apply plugin: JacocoPlugin
jacocoPlugin = project.plugins.getPlugin(JacocoPlugin)
// Register a builder for the custom tooling model
registry.register(new ModelBuilder());
project.tasks.getByName("assemble").description =
"Assembles all variants of all applications and secondary packages."
// call back on execution. This is called after the whole build is done (not
// after the current project is done).
// This is will be called for each (android) projects though, so this should support
// being called 2+ times.
project.gradle.buildFinished {
ExecutorSingleton.shutdown()
sdkHandler.unload()
PreDexCache.getCache().clear(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
logger)
JackConversionCache.getCache().clear(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
logger)
LibraryCache.getCache().unload()
}
project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
for (Task task : taskGraph.allTasks) {
if (task instanceof PreDex) {
PreDexCache.getCache().load(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
break;
} else if (task instanceof JillTask) {
JackConversionCache.getCache().load(
project.rootProject.file(
"${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
break;
}
}
}
}
private void createExtension() {
def buildTypeContainer = project.container(BuildType,
new BuildTypeFactory(instantiator, project, project.getLogger()))
def productFlavorContainer = project.container(GroupableProductFlavor,
new GroupableProductFlavorFactory(instantiator, project, project.getLogger()))
def signingConfigContainer = project.container(SigningConfig,
new SigningConfigFactory(instantiator))
extension = project.extensions.create('android', getExtensionClass(),
this, (ProjectInternal) project, instantiator,
buildTypeContainer, productFlavorContainer, signingConfigContainer,
this instanceof LibraryPlugin)
setBaseExtension(extension)
variantManager = new VariantManager(project, this, extension, getVariantFactory())
// map the whenObjectAdded callbacks on the containers.
signingConfigContainer.whenObjectAdded { SigningConfig signingConfig ->
variantManager.addSigningConfig((SigningConfig) signingConfig)
}
buildTypeContainer.whenObjectAdded { DefaultBuildType buildType ->
variantManager.addBuildType((BuildType) buildType)
}
productFlavorContainer.whenObjectAdded { GroupableProductFlavor productFlavor ->
variantManager.addProductFlavor(productFlavor)
}
// create default Objects, signingConfig first as its used by the BuildTypes.
signingConfigContainer.create(DEBUG)
buildTypeContainer.create(DEBUG)
buildTypeContainer.create(RELEASE)
// map whenObjectRemoved on the containers to throw an exception.
signingConfigContainer.whenObjectRemoved {
throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
}
buildTypeContainer.whenObjectRemoved {
throw new UnsupportedOperationException("Removing build types is not supported.")
}
productFlavorContainer.whenObjectRemoved {
throw new UnsupportedOperationException("Removing product flavors is not supported.")
}
}
private void createTasks() {
uninstallAll = project.tasks.create("uninstallAll")
uninstallAll.description = "Uninstall all applications."
uninstallAll.group = INSTALL_GROUP
deviceCheck = project.tasks.create("deviceCheck")
deviceCheck.description = "Runs all device checks using Device Providers and Test Servers."
deviceCheck.group = JavaBasePlugin.VERIFICATION_GROUP
connectedCheck = project.tasks.create("connectedCheck")
connectedCheck.description = "Runs all device checks on currently connected devices."
connectedCheck.group = JavaBasePlugin.VERIFICATION_GROUP
mainPreBuild = project.tasks.create("preBuild", PrepareSdkTask)
mainPreBuild.plugin = this
project.afterEvaluate {
createAndroidTasks(false)
}
}
private void setBaseExtension(@NonNull BaseExtension extension) {
mainSourceSet = (DefaultAndroidSourceSet) extension.sourceSets.create(extension.defaultConfig.name)
androidTestSourceSet = (DefaultAndroidSourceSet) extension.sourceSets.create(ANDROID_TEST.prefix)
unitTestSourceSet = (DefaultAndroidSourceSet) extension.sourceSets.create(UNIT_TEST.prefix)
defaultConfigData = new ProductFlavorData<ProductFlavor>(
extension.defaultConfig, mainSourceSet,
androidTestSourceSet, unitTestSourceSet, project)
}
private void checkGradleVersion() {
if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().gradleVersion).matches()) {
boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
File file = new File("gradle" + separator + "wrapper" + separator +
"gradle-wrapper.properties");
String errorMessage = String.format(
"Gradle version %s is required. Current version is %s. " +
"If using the gradle wrapper, try editing the distributionUrl in %s " +
"to gradle-%s-all.zip",
GRADLE_MIN_VERSION, project.getGradle().gradleVersion, file.getAbsolutePath(),
GRADLE_MIN_VERSION);
if (allowNonMatching) {
getLogger().warning(errorMessage)
getLogger().warning("As %s is set, continuing anyways.",
GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
} else {
throw new BuildException(errorMessage, null)
}
}
}
final void createAndroidTasks(boolean force) {
// get current plugins and look for the default Java plugin.
if (project.plugins.hasPlugin(JavaPlugin.class)) {
throw new BadPluginException(
"The 'java' plugin has been applied, but it is not compatible with the Android plugins.")
}
// don't do anything if the project was not initialized.
// Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
// This is because project don't get evaluated in the unit test setup.
// See AppPluginDslTest
if (!force && (!project.state.executed || project.state.failure != null) && TEST_SDK_DIR == null) {
return
}
if (hasCreatedTasks) {
return
}
hasCreatedTasks = true
// setup SDK repositories.
for (File file : sdkHandler.sdkLoader.repositories) {
project.repositories.maven { MavenArtifactRepository repo ->
repo.url = file.toURI()
}
}
variantManager.createAndroidTasks(getSigningOverride())
createReportTasks()
if (lintVital != null) {
project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
if (taskGraph.hasTask(lintAll)) {
lintVital.setEnabled(false)
}
}
}
}
private SigningConfig getSigningOverride() {
if (project.hasProperty(PROPERTY_SIGNING_STORE_FILE) &&
project.hasProperty(PROPERTY_SIGNING_STORE_PASSWORD) &&
project.hasProperty(PROPERTY_SIGNING_KEY_ALIAS) &&
project.hasProperty(PROPERTY_SIGNING_KEY_PASSWORD)) {
SigningConfig signingConfigDsl = new SigningConfig("externalOverride")
Map<String, ?> props = project.getProperties();
signingConfigDsl.setStoreFile(new File((String) props.get(PROPERTY_SIGNING_STORE_FILE)))
signingConfigDsl.setStorePassword((String) props.get(PROPERTY_SIGNING_STORE_PASSWORD));
signingConfigDsl.setKeyAlias((String) props.get(PROPERTY_SIGNING_KEY_ALIAS));
signingConfigDsl.setKeyPassword((String) props.get(PROPERTY_SIGNING_KEY_PASSWORD));
if (project.hasProperty(PROPERTY_SIGNING_STORE_TYPE)) {
signingConfigDsl.setStoreType((String) props.get(PROPERTY_SIGNING_STORE_TYPE))
}
return signingConfigDsl
}
return null
}
void checkTasksAlreadyCreated() {
if (hasCreatedTasks) {
throw new GradleException(
"Android tasks have already been created.\n" +
"This happens when calling android.applicationVariants,\n" +
"android.libraryVariants or android.testVariants.\n" +
"Once these methods are called, it is not possible to\n" +
"continue configuring the model.")
}
}
ProductFlavorData<ProductFlavor> getDefaultConfigData() {
return defaultConfigData
}
@Deprecated
Collection<String> getUnresolvedDependencies() {
return unresolvedDependencies
}
Map<SyncIssueKey, SyncIssue> getSyncIssues() {
return syncIssues
}
ILogger getLogger() {
if (loggerWrapper == null) {
loggerWrapper = new LoggerWrapper(project.logger)
}
return loggerWrapper
}
boolean isVerbose() {
return project.logger.isEnabled(LogLevel.INFO)
}
boolean isDebugLog() {
return project.logger.isEnabled(LogLevel.DEBUG)
}
void setAssembleAndroidTest(Task assembleTest) {
this.assembleAndroidTest = assembleTest
}
AndroidBuilder getAndroidBuilder() {
return androidBuilder
}
public File getSdkFolder() {
return sdkHandler.getSdkFolder()
}
public File getNdkFolder() {
return sdkHandler.getNdkFolder()
}
public SdkInfo getSdkInfo() {
return sdkHandler.getSdkInfo()
}
public List<File> getBootClasspath() {
ensureTargetSetup()
return androidBuilder.getBootClasspath()
}
public List<String> getBootClasspathAsStrings() {
ensureTargetSetup()
return androidBuilder.getBootClasspathAsStrings()
}
public List<BaseVariantData<? extends BaseVariantOutputData>> getVariantDataList() {
if (variantManager.getVariantDataList().isEmpty()) {
variantManager.populateVariantDataList(getSigningOverride())
}
return variantManager.getVariantDataList();
}
public void ensureTargetSetup() {
// check if the target has been set.
TargetInfo targetInfo = androidBuilder.getTargetInfo()
if (targetInfo == null) {
sdkHandler.initTarget(
extension.getCompileSdkVersion(),
extension.buildToolsRevision,
androidBuilder)
}
}
public void createMergeAppManifestsTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
VariantConfiguration config = variantData.variantConfiguration
com.android.builder.model.ProductFlavor mergedFlavor = config.mergedFlavor
ApplicationVariantData appVariantData = variantData as ApplicationVariantData
Set<String> screenSizes = appVariantData.getCompatibleScreens()
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated manifest
for (BaseVariantOutputData vod : variantData.outputs) {
final CompatibleScreensManifest csmTask =
(vod.getMainOutputFile().getFilter(OutputFile.DENSITY) != null
&& !screenSizes.isEmpty()) ?
createCompatibleScreensManifest(vod, screenSizes) :
null
// create final var inside the loop to ensure the closures will work.
final BaseVariantOutputData variantOutputData = vod
String outputName = variantOutputData.fullName.capitalize()
String outputDirName = variantOutputData.dirName
def processManifestTask = project.tasks.create(
"process${outputName}Manifest",
MergeManifests)
variantOutputData.manifestProcessorTask = processManifestTask
processManifestTask.plugin = this
processManifestTask.dependsOn variantData.prepareDependenciesTask
if (variantData.generateApkDataTask != null) {
processManifestTask.dependsOn variantData.generateApkDataTask
}
if (csmTask != null) {
processManifestTask.dependsOn csmTask
}
processManifestTask.variantConfiguration = config
if (variantOutputData instanceof ApkVariantOutputData) {
processManifestTask.variantOutputData = variantOutputData as ApkVariantOutputData
}
conventionMapping(processManifestTask).map("libraries") {
List<ManifestDependencyImpl> manifests =
getManifestDependencies(config.directLibraries)
if (variantData.generateApkDataTask != null) {
manifests.add(new ManifestDependencyImpl(
variantData.generateApkDataTask.getManifestFile(), []))
}
if (csmTask != null) {
manifests.add(
new ManifestDependencyImpl(csmTask.getManifestFile(), []))
}
return manifests
}
conventionMapping(processManifestTask).map("minSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
mergedFlavor.minSdkVersion?.apiString
}
conventionMapping(processManifestTask).map("targetSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
return mergedFlavor.targetSdkVersion?.apiString
}
conventionMapping(processManifestTask).map("maxSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return null
}
return mergedFlavor.maxSdkVersion
}
conventionMapping(processManifestTask).map("manifestOutputFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/manifests/full/" +
"${outputDirName}/AndroidManifest.xml")
}
String defaultLocation = "$project.buildDir/${FD_OUTPUTS}/apk"
String apkLocation = defaultLocation
if (project.hasProperty(PROPERTY_APK_LOCATION)) {
apkLocation = project.getProperties().get(PROPERTY_APK_LOCATION)
}
conventionMapping(processManifestTask).map("reportFile") {
project.file(
"$apkLocation/manifest-merger-${config.baseName}-report.txt")
}
}
}
private CompatibleScreensManifest createCompatibleScreensManifest(
@NonNull BaseVariantOutputData variantOutputData,
@NonNull Set<String> screenSizes) {
CompatibleScreensManifest csmTask = project.tasks.create(
"create${variantOutputData.fullName.capitalize()}CompatibleScreensManifest",
CompatibleScreensManifest)
csmTask.screenDensity = variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY)
csmTask.screenSizes = screenSizes
conventionMapping(csmTask).map("manifestFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/manifests/density/" +
"${variantOutputData.dirName}/AndroidManifest.xml")
}
return csmTask;
}
@CompileDynamic
private ConventionMapping conventionMapping(Task task) {
task.conventionMapping
}
public void createMergeLibManifestsTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@NonNull String manifestOutDir) {
VariantConfiguration config = variantData.variantConfiguration
// get single output for now.
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
def processManifest = project.tasks.create(
"process${variantData.variantConfiguration.fullName.capitalize()}Manifest",
ProcessManifest)
variantOutputData.manifestProcessorTask = processManifest
processManifest.plugin = this
processManifest.dependsOn variantData.prepareDependenciesTask
processManifest.variantConfiguration = config
com.android.builder.model.ProductFlavor mergedFlavor = config.mergedFlavor
conventionMapping(processManifest).map("minSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
return mergedFlavor.minSdkVersion?.apiString
}
conventionMapping(processManifest).map("targetSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
return mergedFlavor.targetSdkVersion?.apiString
}
conventionMapping(processManifest).map("maxSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return null
}
return mergedFlavor.maxSdkVersion
}
conventionMapping(processManifest).map("manifestOutputFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/${manifestOutDir}/" +
"${variantData.variantConfiguration.dirName}/AndroidManifest.xml")
}
}
protected void createProcessTestManifestTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@NonNull String manifestOurDir) {
def processTestManifestTask;
VariantConfiguration config = variantData.variantConfiguration
processTestManifestTask = project.tasks.create(
"process${variantData.variantConfiguration.fullName.capitalize()}Manifest",
ProcessTestManifest)
conventionMapping(processTestManifestTask).map("testManifestFile") {
config.getMainManifest()
}
conventionMapping(processTestManifestTask).map("tmpDir") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/${manifestOurDir}/tmp")
}
// get single output for now.
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
variantOutputData.manifestProcessorTask = processTestManifestTask
processTestManifestTask.dependsOn variantData.prepareDependenciesTask
processTestManifestTask.plugin = this
conventionMapping(processTestManifestTask).map("testApplicationId") {
config.applicationId
}
conventionMapping(processTestManifestTask).map("minSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
config.minSdkVersion?.apiString
}
conventionMapping(processTestManifestTask).map("targetSdkVersion") {
if (androidBuilder.isPreviewTarget()) {
return androidBuilder.getTargetCodename()
}
return config.targetSdkVersion?.apiString
}
conventionMapping(processTestManifestTask).map("testedApplicationId") {
config.testedApplicationId
}
conventionMapping(processTestManifestTask).map("instrumentationRunner") {
config.instrumentationRunner
}
conventionMapping(processTestManifestTask).map("handleProfiling") {
config.handleProfiling
}
conventionMapping(processTestManifestTask).map("functionalTest") {
config.functionalTest
}
conventionMapping(processTestManifestTask).map("libraries") {
getManifestDependencies(config.directLibraries)
}
conventionMapping(processTestManifestTask).map("manifestOutputFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/${manifestOurDir}/${variantData.variantConfiguration.dirName}/AndroidManifest.xml")
}
}
public void createRenderscriptTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
GradleVariantConfiguration config = variantData.variantConfiguration
// get single output for now.
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
def renderscriptTask = project.tasks.create(
"compile${variantData.variantConfiguration.fullName.capitalize()}Renderscript",
RenderscriptCompile)
variantData.renderscriptCompileTask = renderscriptTask
if (config.type.isForTesting()) {
renderscriptTask.dependsOn variantOutputData.manifestProcessorTask
} else {
renderscriptTask.dependsOn variantData.checkManifestTask
}
com.android.builder.model.ProductFlavor mergedFlavor = config.mergedFlavor
boolean ndkMode = config.renderscriptNdkModeEnabled
variantData.resourceGenTask.dependsOn renderscriptTask
// only put this dependency if rs will generate Java code
if (!ndkMode) {
variantData.sourceGenTask.dependsOn renderscriptTask
}
renderscriptTask.dependsOn variantData.prepareDependenciesTask
renderscriptTask.plugin = this
conventionMapping(renderscriptTask).map("targetApi") {
int targetApi = mergedFlavor.renderscriptTargetApi != null ?
mergedFlavor.renderscriptTargetApi : -1
ApiVersion apiVersion = config.getMinSdkVersion()
if (apiVersion != null) {
int minSdk = apiVersion.apiLevel
if (apiVersion.codename != null) {
minSdk = SdkVersionInfo.getApiByBuildCode(apiVersion.codename, true)
}
return targetApi > minSdk ? targetApi : minSdk
}
return targetApi
}
renderscriptTask.supportMode = config.renderscriptSupportModeEnabled
renderscriptTask.ndkMode = ndkMode
renderscriptTask.debugBuild = config.buildType.renderscriptDebuggable
renderscriptTask.optimLevel = config.buildType.renderscriptOptimLevel
conventionMapping(renderscriptTask).map("sourceDirs") { config.renderscriptSourceList }
conventionMapping(renderscriptTask).map("importDirs") { config.renderscriptImports }
conventionMapping(renderscriptTask).map("sourceOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/source/rs/${variantData.variantConfiguration.dirName}")
}
conventionMapping(renderscriptTask).map("resOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/res/rs/${variantData.variantConfiguration.dirName}")
}
conventionMapping(renderscriptTask).map("objOutputDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/rs/${variantData.variantConfiguration.dirName}/obj")
}
conventionMapping(renderscriptTask).map("libOutputDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/rs/${variantData.variantConfiguration.dirName}/lib")
}
conventionMapping(renderscriptTask).map("ndkConfig") { config.ndkConfig }
}
public void createMergeResourcesTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
final boolean process9Patch) {
MergeResources mergeResourcesTask = basicCreateMergeResourcesTask(
variantData,
"merge",
"$project.buildDir/${FD_INTERMEDIATES}/res/${variantData.variantConfiguration.dirName}",
true /*includeDependencies*/,
process9Patch)
variantData.mergeResourcesTask = mergeResourcesTask
}
public MergeResources basicCreateMergeResourcesTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@NonNull String taskNamePrefix,
@NonNull String outputLocation,
final boolean includeDependencies,
final boolean process9Patch) {
MergeResources mergeResourcesTask = project.tasks.create(
"$taskNamePrefix${variantData.variantConfiguration.fullName.capitalize()}Resources",
MergeResources)
mergeResourcesTask.dependsOn variantData.prepareDependenciesTask, variantData.resourceGenTask
mergeResourcesTask.plugin = this
mergeResourcesTask.incrementalFolder = project.file(
"$project.buildDir/${FD_INTERMEDIATES}/incremental/${taskNamePrefix}Resources/${variantData.variantConfiguration.dirName}")
mergeResourcesTask.process9Patch = process9Patch
conventionMapping(mergeResourcesTask).map("useNewCruncher") { extension.aaptOptions.useNewCruncher }
conventionMapping(mergeResourcesTask).map("inputResourceSets") {
def generatedResFolders = [ variantData.renderscriptCompileTask.getResOutputDir(),
variantData.generateResValuesTask.getResOutputDir() ]
if (variantData.generateApkDataTask != null) {
generatedResFolders.add(variantData.generateApkDataTask.getResOutputDir())
}
variantData.variantConfiguration.getResourceSets(generatedResFolders,
includeDependencies)
}
conventionMapping(mergeResourcesTask).map("outputDir") { project.file(outputLocation) }
return mergeResourcesTask
}
public void createMergeAssetsTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@Nullable String outputLocation,
final boolean includeDependencies) {
if (outputLocation == null) {
outputLocation = "$project.buildDir/${FD_INTERMEDIATES}/assets/${variantData.variantConfiguration.dirName}"
}
VariantConfiguration variantConfig = variantData.variantConfiguration
def mergeAssetsTask = project.tasks.create(
"merge${variantConfig.fullName.capitalize()}Assets",
MergeAssets)
variantData.mergeAssetsTask = mergeAssetsTask
mergeAssetsTask.dependsOn variantData.prepareDependenciesTask, variantData.assetGenTask
mergeAssetsTask.plugin = this
mergeAssetsTask.incrementalFolder =
project.file("$project.buildDir/${FD_INTERMEDIATES}/incremental/mergeAssets/${variantConfig.dirName}")
conventionMapping(mergeAssetsTask).map("inputAssetSets") {
def generatedAssets = []
if (variantData.copyApkTask != null) {
generatedAssets.add(variantData.copyApkTask.destinationDir)
}
variantConfig.getAssetSets(generatedAssets, includeDependencies)
}
conventionMapping(mergeAssetsTask).map("outputDir") { project.file(outputLocation) }
}
public void createBuildConfigTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
def generateBuildConfigTask = project.tasks.create(
"generate${variantData.variantConfiguration.fullName.capitalize()}BuildConfig",
GenerateBuildConfig)
variantData.generateBuildConfigTask = generateBuildConfigTask
VariantConfiguration variantConfiguration = variantData.variantConfiguration
variantData.sourceGenTask.dependsOn generateBuildConfigTask
if (variantConfiguration.type.isForTesting()) {
// in case of a test project, the manifest is generated so we need to depend
// on its creation.
// For test apps there should be a single output, so we get it.
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
generateBuildConfigTask.dependsOn variantOutputData.manifestProcessorTask
} else {
generateBuildConfigTask.dependsOn variantData.checkManifestTask
}
generateBuildConfigTask.plugin = this
conventionMapping(generateBuildConfigTask).map("buildConfigPackageName") {
variantConfiguration.originalApplicationId
}
conventionMapping(generateBuildConfigTask).map("appPackageName") {
variantConfiguration.applicationId
}
conventionMapping(generateBuildConfigTask).map("versionName") {
variantConfiguration.versionName
}
conventionMapping(generateBuildConfigTask).map("versionCode") {
variantConfiguration.versionCode
}
conventionMapping(generateBuildConfigTask).map("debuggable") {
variantConfiguration.buildType.isDebuggable()
}
conventionMapping(generateBuildConfigTask).map("buildTypeName") {
variantConfiguration.buildType.name
}
conventionMapping(generateBuildConfigTask).map("flavorName") {
variantConfiguration.flavorName
}
conventionMapping(generateBuildConfigTask).map("flavorNamesWithDimensionNames") {
variantConfiguration.flavorNamesWithDimensionNames
}
conventionMapping(generateBuildConfigTask).map("items") {
variantConfiguration.buildConfigItems
}
conventionMapping(generateBuildConfigTask).map("sourceOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/source/buildConfig/${variantData.variantConfiguration.dirName}")
}
}
public void createGenerateResValuesTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
GenerateResValues generateResValuesTask = project.tasks.create(
"generate${variantData.variantConfiguration.fullName.capitalize()}ResValues",
GenerateResValues)
variantData.generateResValuesTask = generateResValuesTask
variantData.resourceGenTask.dependsOn generateResValuesTask
VariantConfiguration variantConfiguration = variantData.variantConfiguration
generateResValuesTask.plugin = this
conventionMapping(generateResValuesTask).map("items") {
variantConfiguration.resValues
}
conventionMapping(generateResValuesTask).map("resOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/res/generated/${variantData.variantConfiguration.dirName}")
}
}
public void createProcessResTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
boolean generateResourcePackage) {
createProcessResTask(variantData,
"$project.buildDir/${FD_INTERMEDIATES}/symbols/${variantData.variantConfiguration.dirName}",
generateResourcePackage)
}
public void createProcessResTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@NonNull final String symbolLocation,
boolean generateResourcePackage) {
VariantConfiguration config = variantData.variantConfiguration
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated data.
for (BaseVariantOutputData vod : variantData.outputs) {
// create final var inside the loop to ensure the closures will work.
final BaseVariantOutputData variantOutputData = vod
String outputName = variantOutputData.fullName.capitalize()
String outputBaseName = variantOutputData.baseName
ProcessAndroidResources processResources = project.tasks.create(
"process${outputName}Resources",
ProcessAndroidResources)
variantOutputData.processResourcesTask = processResources
processResources.dependsOn variantOutputData.manifestProcessorTask,
variantData.mergeResourcesTask, variantData.mergeAssetsTask
processResources.plugin = this
if (variantData.getSplitHandlingPolicy() ==
BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
Set<String> filters = new HashSet<String>(getExtension().getSplits().getDensityFilters());
filters.addAll(getExtension().getSplits().getLanguageFilters());
filters = removeAllNullEntries(filters);
processResources.splits = filters;
}
// only generate code if the density filter is null, and if we haven't generated
// it yet (if you have abi + density splits, then several abi output will have no
// densityFilter)
if (variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY) == null
&& variantData.generateRClassTask == null) {
variantData.generateRClassTask = processResources
variantData.sourceGenTask.dependsOn processResources
processResources.enforceUniquePackageName = extension.getEnforceUniquePackageName()
conventionMapping(processResources).map("libraries") {
getTextSymbolDependencies(config.allLibraries)
}
conventionMapping(processResources).map("packageForR") {
config.originalApplicationId
}
// TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
conventionMapping(processResources).map("sourceOutputDir") {
project.file(
"$project.buildDir/${FD_GENERATED}/source/r/${config.dirName}")
}
conventionMapping(processResources).map("textSymbolOutputDir") {
project.file(symbolLocation)
}
if (config.buildType.isMinifyEnabled()) {
if (config.buildType.shrinkResources && config.useJack) {
displayWarning(logger, project,
"WARNING: shrinkResources does not yet work with useJack=true")
}
conventionMapping(processResources).map("proguardOutputFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/proguard-rules/${config.dirName}/aapt_rules.txt")
}
} else if (config.buildType.shrinkResources) {
displayWarning(logger, project,
"WARNING: To shrink resources you must also enable ProGuard")
}
}
conventionMapping(processResources).map("manifestFile") {
variantOutputData.manifestProcessorTask.manifestOutputFile
}
conventionMapping(processResources).map("resDir") {
variantData.mergeResourcesTask.outputDir
}
conventionMapping(processResources).map("assetsDir") {
variantData.mergeAssetsTask.outputDir
}
if (generateResourcePackage) {
conventionMapping(processResources).map("packageOutputFile") {
project.file(
"$project.buildDir/${FD_INTERMEDIATES}/res/resources-${outputBaseName}.ap_")
}
}
conventionMapping(processResources).map("type") { config.type }
conventionMapping(processResources).map("debuggable") { config.buildType.debuggable }
conventionMapping(processResources).map("aaptOptions") { extension.aaptOptions }
conventionMapping(processResources).map("pseudoLocalesEnabled") { config.buildType.pseudoLocalesEnabled }
conventionMapping(processResources).map("resourceConfigs") {
return config.mergedFlavor.resourceConfigurations
}
conventionMapping(processResources).map("preferredDensity") {
variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY)
}
}
}
/**
* Creates the split resources packages task if necessary. AAPT will produce split packages
* for all --split provided parameters. These split packages should be signed and moved
* unchanged to the APK build output directory.
* @param variantData the variant configuration.
*/
public void createSplitResourcesTasks(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
assert variantData.getSplitHandlingPolicy() ==
BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
VariantConfiguration config = variantData.variantConfiguration
Set<String> densityFilters =
removeAllNullEntries(getExtension().getSplits().getDensityFilters());
Set<String> abiFilters = removeAllNullEntries(getExtension().getSplits().getAbiFilters());
Set<String> languageFilters =
removeAllNullEntries(getExtension().getSplits().getLanguageFilters());
def outputs = variantData.outputs;
if (outputs.size() != 1) {
throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
"found " + outputs.size());
}
BaseVariantOutputData variantOutputData = outputs.get(0);
variantOutputData.packageSplitResourcesTask =
project.tasks.create("package${config.fullName.capitalize()}SplitResources",
PackageSplitRes);
variantOutputData.packageSplitResourcesTask.inputDirectory =
new File("$project.buildDir/${FD_INTERMEDIATES}/res")
variantOutputData.packageSplitResourcesTask.densitySplits = densityFilters
variantOutputData.packageSplitResourcesTask.languageSplits = languageFilters
variantOutputData.packageSplitResourcesTask.outputBaseName = config.baseName
variantOutputData.packageSplitResourcesTask.signingConfig =
(SigningConfig) config.signingConfig
variantOutputData.packageSplitResourcesTask.outputDirectory =
new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
variantOutputData.packageSplitResourcesTask.plugin = this
variantOutputData.packageSplitResourcesTask.dependsOn variantOutputData.processResourcesTask
SplitZipAlign zipAlign = project.tasks.create("zipAlign${config.fullName.capitalize()}SplitPackages", SplitZipAlign)
conventionMapping(zipAlign).map("zipAlignExe") {
String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
if (path != null) {
return new File(path)
}
return null
}
zipAlign.outputDirectory = new File("$project.buildDir/outputs/apk")
zipAlign.inputDirectory = variantOutputData.packageSplitResourcesTask.outputDirectory
zipAlign.outputBaseName = config.baseName
zipAlign.abiFilters = abiFilters
zipAlign.languageFilters = languageFilters
zipAlign.densityFilters = densityFilters
((ApkVariantOutputData) variantOutputData).splitZipAlign = zipAlign
zipAlign.dependsOn(variantOutputData.packageSplitResourcesTask)
}
public void createSplitAbiTasks(
@NonNull ApplicationVariantData variantData) {
assert variantData.getSplitHandlingPolicy() ==
BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
VariantConfiguration config = variantData.variantConfiguration
Set<String> filters = new HashSet<String>();
for (String abi : getExtension().getSplits().getAbiFilters()) {
if (abi != null) {
filters.add(abi);
}
}
if (filters.isEmpty()) {
return;
}
def outputs = variantData.outputs;
if (outputs.size() != 1) {
throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
"found " + outputs.size());
}
BaseVariantOutputData variantOutputData = outputs.get(0);
// first create the split APK resources.
GenerateSplitAbiRes generateSplitAbiRes = project.tasks.
create("generate${config.fullName.capitalize()}SplitAbiRes",
GenerateSplitAbiRes)
generateSplitAbiRes.plugin = this
generateSplitAbiRes.outputDirectory =
new File("$project.buildDir/${FD_INTERMEDIATES}/abi/${config.dirName}")
generateSplitAbiRes.splits = filters
generateSplitAbiRes.outputBaseName = config.baseName
generateSplitAbiRes.applicationId = config.getApplicationId()
generateSplitAbiRes.versionCode = config.getVersionCode()
generateSplitAbiRes.versionName = config.getVersionName()
generateSplitAbiRes.debuggable = {
config.buildType.debuggable }
conventionMapping(generateSplitAbiRes).map("aaptOptions") {
extension.aaptOptions
}
generateSplitAbiRes.dependsOn variantOutputData.processResourcesTask
// then package those resources witth the appropriate JNI libraries.
variantOutputData.packageSplitAbiTask =
project.tasks.create("package${config.fullName.capitalize()}SplitAbi",
PackageSplitAbi);
variantOutputData.packageSplitAbiTask
variantOutputData.packageSplitAbiTask.inputDirectory = generateSplitAbiRes.outputDirectory
variantOutputData.packageSplitAbiTask.splits = filters
variantOutputData.packageSplitAbiTask.outputBaseName = config.baseName
variantOutputData.packageSplitAbiTask.signingConfig =
(SigningConfig) config.signingConfig
variantOutputData.packageSplitAbiTask.outputDirectory =
new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
variantOutputData.packageSplitAbiTask.plugin = this
variantOutputData.packageSplitAbiTask.dependsOn generateSplitAbiRes
conventionMapping(variantOutputData.packageSplitAbiTask).map("jniFolders") {
getJniFolders(variantData);
}
conventionMapping(variantOutputData.packageSplitAbiTask).map("jniDebuggable") { config.buildType.jniDebuggable }
conventionMapping(variantOutputData.packageSplitAbiTask).map("packagingOptions") { extension.packagingOptions }
((ApkVariantOutputData) variantOutputData).splitZipAlign.dependsOn variantOutputData.packageSplitAbiTask
}
/**
* Calculate the list of folders that can contain jni artifacts for this variant.
* @param variantData the variant
* @return a potentially empty list of directories that exist or not and that may contains
* native resources.
*/
@NonNull
public Set<File> getJniFolders(@NonNull ApkVariantData variantData) {
VariantConfiguration config = variantData.variantConfiguration
// for now only the project's compilation output.
Set<File> set = Sets.newHashSet()
if (extension.getUseNewNativePlugin()) {
throw new RuntimeException("useNewNativePlugin is currently not supported.")
} else {
set.addAll(variantData.ndkCompileTask.soFolder)
}
set.addAll(variantData.renderscriptCompileTask.libOutputDir)
set.addAll(config.libraryJniFolders)
set.addAll(config.jniLibsList)
if (config.mergedFlavor.renderscriptSupportModeEnabled) {
File rsLibs = androidBuilder.getSupportNativeLibFolder()
if (rsLibs != null && rsLibs.isDirectory()) {
set.add(rsLibs);
}
}
return set
}
public void createProcessJavaResTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
VariantConfiguration variantConfiguration = variantData.variantConfiguration
Copy processResources = project.tasks.create(
"process${variantData.variantConfiguration.fullName.capitalize()}JavaRes",
ProcessResources);
variantData.processJavaResourcesTask = processResources
// set the input
processResources.from(((AndroidSourceSet) variantConfiguration.defaultSourceSet).resources.getSourceFiles())
if (!variantConfiguration.type.isForTesting()) {
processResources.from(
((AndroidSourceSet) variantConfiguration.buildTypeSourceSet).resources.getSourceFiles())
}
if (variantConfiguration.hasFlavors()) {
for (SourceProvider flavorSourceSet : variantConfiguration.flavorSourceProviders) {
processResources.from(((AndroidSourceSet) flavorSourceSet).resources.getSourceFiles())
}
}
conventionMapping(processResources).map("destinationDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/javaResources/${variantData.variantConfiguration.dirName}")
}
}
public void createAidlTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@Nullable File parcelableDir) {
VariantConfiguration variantConfiguration = variantData.variantConfiguration
def compileTask = project.tasks.create(
"compile${variantData.variantConfiguration.fullName.capitalize()}Aidl",
AidlCompile)
variantData.aidlCompileTask = compileTask
variantData.sourceGenTask.dependsOn compileTask
variantData.aidlCompileTask.dependsOn variantData.prepareDependenciesTask
compileTask.plugin = this
compileTask.incrementalFolder =
project.file("$project.buildDir/${FD_INTERMEDIATES}/incremental/aidl/${variantData.variantConfiguration.dirName}")
conventionMapping(compileTask).map("sourceDirs") { variantConfiguration.aidlSourceList }
conventionMapping(compileTask).map("importDirs") { variantConfiguration.aidlImports }
conventionMapping(compileTask).map("sourceOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/source/aidl/${variantData.variantConfiguration.dirName}")
}
compileTask.aidlParcelableDir = parcelableDir
}
public void createCompileTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@Nullable BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
def compileTask = project.tasks.create(
"compile${variantData.variantConfiguration.fullName.capitalize()}Java",
JavaCompile)
variantData.javaCompileTask = compileTask
variantData.compileTask.dependsOn variantData.javaCompileTask
optionalDependsOn(variantData.javaCompileTask, variantData.sourceGenTask)
compileTask.source = variantData.getJavaSources()
VariantConfiguration config = variantData.variantConfiguration
// if the tested variant is an app, add its classpath. For the libraries,
// it's done automatically since the classpath includes the library output as a normal
// dependency.
if (testedVariantData instanceof ApplicationVariantData) {
conventionMapping(compileTask).map("classpath") {
project.files(androidBuilder.getCompileClasspath(config)) + testedVariantData.javaCompileTask.classpath + testedVariantData.javaCompileTask.outputs.files
}
} else {
conventionMapping(compileTask).map("classpath") {
project.files(androidBuilder.getCompileClasspath(config))
}
}
// TODO - dependency information for the compile classpath is being lost.
// Add a temporary approximation
compileTask.dependsOn variantData.variantDependency.compileConfiguration.buildDependencies
conventionMapping(compileTask).map("destinationDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/classes/${variantData.variantConfiguration.dirName}")
}
conventionMapping(compileTask).map("dependencyCacheDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/dependency-cache/${variantData.variantConfiguration.dirName}")
}
configureLanguageLevel(compileTask)
compileTask.options.encoding = extension.compileOptions.encoding
// setup the boot classpath just before the task actually runs since this will
// force the sdk to be parsed.
compileTask.doFirst {
compileTask.options.bootClasspath = androidBuilder.getBootClasspathAsStrings().join(File.pathSeparator)
}
}
public void createGenerateMicroApkDataTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@NonNull Configuration config) {
GenerateApkDataTask task = project.tasks.create(
"handle${variantData.variantConfiguration.fullName.capitalize()}MicroApk",
GenerateApkDataTask)
variantData.generateApkDataTask = task
task.plugin = this
conventionMapping(task).map("resOutputDir") {
project.file("$project.buildDir/${FD_GENERATED}/res/microapk/${variantData.variantConfiguration.dirName}")
}
conventionMapping(task).map("apkFile") {
// only care about the first one. There shouldn't be more anyway.
config.getFiles().iterator().next()
}
conventionMapping(task).map("manifestFile") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/${FD_GENERATED}/manifests/microapk/${variantData.variantConfiguration.dirName}/${FN_ANDROID_MANIFEST_XML}")
}
conventionMapping(task).map("mainPkgName") {
variantData.variantConfiguration.getApplicationId()
}
conventionMapping(task).map("minSdkVersion") {
variantData.variantConfiguration.getMinSdkVersion().apiLevel
}
conventionMapping(task).map("targetSdkVersion") {
variantData.variantConfiguration.getTargetSdkVersion().apiLevel
}
task.dependsOn config
// the merge res task will need to run after this one.
variantData.resourceGenTask.dependsOn task
}
public void createNdkTasks(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
NdkCompile ndkCompile = project.tasks.create(
"compile${variantData.variantConfiguration.fullName.capitalize()}Ndk",
NdkCompile)
ndkCompile.dependsOn mainPreBuild
ndkCompile.plugin = this
variantData.ndkCompileTask = ndkCompile
variantData.compileTask.dependsOn variantData.ndkCompileTask
GradleVariantConfiguration variantConfig = variantData.variantConfiguration
if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
ndkCompile.ndkRenderScriptMode = true
ndkCompile.dependsOn variantData.renderscriptCompileTask
} else {
ndkCompile.ndkRenderScriptMode = false
}
conventionMapping(ndkCompile).map("sourceFolders") {
List<File> sourceList = variantConfig.jniSourceList
if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
sourceList.add(variantData.renderscriptCompileTask.sourceOutputDir)
}
return sourceList
}
conventionMapping(ndkCompile).map("generatedMakefile") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/Android.mk")
}
conventionMapping(ndkCompile).map("ndkConfig") { variantConfig.ndkConfig }
conventionMapping(ndkCompile).map("debuggable") {
variantConfig.buildType.jniDebuggable
}
conventionMapping(ndkCompile).map("objFolder") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/obj")
}
conventionMapping(ndkCompile).map("soFolder") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/lib")
}
}
/**
* Creates the tasks to build unit tests.
*
* @param variantData the test variant
*/
void createUnitTestVariantTasks(@NonNull TestVariantData variantData) {
BaseVariantData testedVariantData = variantData.getTestedVariantData() as BaseVariantData
createCompileAnchorTask(variantData)
createCompileTask(variantData, testedVariantData)
}
/**
* Creates the tasks to build android tests.
*
* @param variantData the test variant
*/
public void createAndroidTestVariantTasks(@NonNull TestVariantData variantData) {
BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
variantData.getTestedVariantData() as BaseVariantData<? extends BaseVariantOutputData>
// get single output for now (though this may always be the case for tests).
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
BaseVariantOutputData testedVariantOutputData = testedVariantData.outputs.get(0)
createAnchorTasks(variantData)
// Add a task to process the manifest
createProcessTestManifestTask(variantData, "manifests")
// Add a task to create the res values
createGenerateResValuesTask(variantData)
// Add a task to compile renderscript files.
createRenderscriptTask(variantData)
// Add a task to merge the resource folders
createMergeResourcesTask(variantData, true /*process9Patch*/)
// Add a task to merge the assets folders
createMergeAssetsTask(variantData, null /*default location*/, true /*includeDependencies*/)
if (testedVariantData.variantConfiguration.type == VariantConfiguration.Type.LIBRARY) {
// in this case the tested library must be fully built before test can be built!
if (testedVariantOutputData.assembleTask != null) {
variantOutputData.manifestProcessorTask.dependsOn testedVariantOutputData.assembleTask
variantData.mergeResourcesTask.dependsOn testedVariantOutputData.assembleTask
}
}
// Add a task to create the BuildConfig class
createBuildConfigTask(variantData)
// Add a task to generate resource source files
createProcessResTask(variantData, true /*generateResourcePackage*/)
// process java resources
createProcessJavaResTask(variantData)
createAidlTask(variantData, null /*parcelableDir*/)
// Add NDK tasks
if (!extension.getUseNewNativePlugin()) {
createNdkTasks(variantData)
}
// Add a task to compile the test application
if (variantData.getVariantConfiguration().useJack) {
createJackTask(variantData, testedVariantData);
} else{
createCompileTask(variantData, testedVariantData)
createPostCompilationTasks(variantData);
}
createPackagingTask(variantData, null /*assembleTask*/, false /*publishApk*/)
if (assembleAndroidTest != null) {
assembleAndroidTest.dependsOn variantOutputData.assembleTask
}
}
// TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
public void createLintCompileTask() {
lintCompile = project.tasks.create("compileLint", Task)
File outputDir = new File("$project.buildDir/${FD_INTERMEDIATES}/lint")
lintCompile.doFirst{
// create the directory for lint output if it does not exist.
if (!outputDir.exists()) {
boolean mkdirs = outputDir.mkdirs();
if (!mkdirs) {
throw new GradleException("Unable to create lint output directory.")
}
}
}
}
/** Is the given variant relevant for lint? */
private static boolean isLintVariant(
@NonNull BaseVariantData<? extends BaseVariantOutputData> baseVariantData) {
// Only create lint targets for variants like debug and release, not debugTest
VariantConfiguration config = baseVariantData.variantConfiguration
// TODO: re-enable with Jack when possible
return !config.getType().isForTesting() && !config.useJack;
}
// Add tasks for running lint on individual variants. We've already added a
// lint task earlier which runs on all variants.
public void createLintTasks() {
Lint lint = project.tasks.create("lint", Lint)
lint.description = "Runs lint on all variants."
lint.group = JavaBasePlugin.VERIFICATION_GROUP
lint.setPlugin(this)
project.tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn lint
lintAll = lint
int count = variantManager.getVariantDataList().size()
for (int i = 0 ; i < count ; i++) {
final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
variantManager.getVariantDataList().get(i)
if (!isLintVariant(baseVariantData)) {
continue;
}
// wire the main lint task dependency.
lint.dependsOn lintCompile
optionalDependsOn(lint, baseVariantData.javaCompileTask, baseVariantData.jackTask)
String variantName = baseVariantData.variantConfiguration.fullName
def capitalizedVariantName = variantName.capitalize()
Lint variantLintCheck = project.tasks.create("lint" + capitalizedVariantName, Lint)
variantLintCheck.dependsOn lintCompile
optionalDependsOn(variantLintCheck, baseVariantData.javaCompileTask, baseVariantData.jackTask)
// Note that we don't do "lint.dependsOn lintCheck"; the "lint" target will
// on its own run through all variants (and compare results), it doesn't delegate
// to the individual tasks (since it needs to coordinate data collection and
// reporting)
variantLintCheck.setPlugin(this)
variantLintCheck.setVariantName(variantName)
variantLintCheck.description = "Runs lint on the " + capitalizedVariantName + " build"
variantLintCheck.group = JavaBasePlugin.VERIFICATION_GROUP
}
}
private void createLintVitalTask(@NonNull ApkVariantData variantData) {
assert extension.lintOptions.checkReleaseBuilds
// TODO: re-enable with Jack when possible
if (!variantData.variantConfiguration.buildType.debuggable &&
!variantData.variantConfiguration.useJack) {
String variantName = variantData.variantConfiguration.fullName
def capitalizedVariantName = variantName.capitalize()
def taskName = "lintVital" + capitalizedVariantName
Lint lintReleaseCheck = project.tasks.create(taskName, Lint)
// TODO: Make this task depend on lintCompile too (resolve initialization order first)
optionalDependsOn(lintReleaseCheck, variantData.javaCompileTask)
lintReleaseCheck.setPlugin(this)
lintReleaseCheck.setVariantName(variantName)
lintReleaseCheck.setFatalOnly(true)
lintReleaseCheck.description = "Runs lint on just the fatal issues in the " +
capitalizedVariantName + " build"
variantData.assembleVariantTask.dependsOn lintReleaseCheck
lintVital = lintReleaseCheck
}
}
void createUnitTestTasks() {
Task topLevelTest = project.tasks.create(JavaPlugin.TEST_TASK_NAME)
topLevelTest.group = JavaBasePlugin.VERIFICATION_GROUP
variantDataList.findAll{it.variantConfiguration.type == UNIT_TEST}.each { loopVariantData ->
// Inner scope copy for the closures.
TestVariantData variantData = loopVariantData as TestVariantData
BaseVariantData testedVariantData = variantData.testedVariantData as BaseVariantData
if (variantData.variantConfiguration.useJack
|| testedVariantData.variantConfiguration.useJack) {
// Don't create unit test tasks when using Jack.
// TODO: Handle Jack somehow.
return
}
Test runTestsTask = project.tasks.create(
UNIT_TEST.prefix + testedVariantData.variantConfiguration.fullName.capitalize(),
Test)
runTestsTask.group = JavaBasePlugin.VERIFICATION_GROUP
fixTestTaskSources(runTestsTask)
runTestsTask.dependsOn variantData.javaCompileTask
JavaCompile testCompileTask = variantData.javaCompileTask
runTestsTask.testClassesDir = testCompileTask.destinationDir
runTestsTask.classpath = project.files(
testCompileTask.classpath,
testCompileTask.outputs.files)
// See https://issues.gradle.org/browse/GRADLE-1682
// TODO: Remove
runTestsTask.scanForTestClasses = false
runTestsTask.include "**/*Test.class"
// TODO: Use mockable JAR.
runTestsTask.doFirst {
runTestsTask.classpath = project.files(
androidBuilder.bootClasspath,
runTestsTask.classpath)
}
topLevelTest.dependsOn runTestsTask
}
Task check = project.tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME)
check.dependsOn topLevelTest
}
@CompileDynamic
private static void fixTestTaskSources(@NonNull Test testTask) {
// We are running in afterEvaluate, so the JavaBasePlugin has already added a
// callback to add test classes to the list of source files of the newly created task.
// The problem is that we haven't configured the test classes yet (JavaBasePlugin
// assumes all Test tasks are fully configured at this point), so we have to remove the
// "directory null" entry from source files and add the right value.
//
// This is an ugly hack, since we assume sourceFiles is an instance of
// DefaultConfigurableFileCollection.
testTask.inputs.sourceFiles.from.clear()
}
public void createConnectedCheckTasks(boolean hasFlavors, boolean isLibraryTest) {
List<AndroidReportTask> reportTasks = Lists.newArrayListWithExpectedSize(2)
List<DeviceProvider> providers = extension.deviceProviders
List<TestServer> servers = extension.testServers
Task mainConnectedTask = connectedCheck
String connectedRootName = "${CONNECTED}${ANDROID_TEST.suffix}"
// if more than one flavor, create a report aggregator task and make this the parent
// task for all new connected tasks.
if (hasFlavors) {
mainConnectedTask = project.tasks.create(connectedRootName, AndroidReportTask)
mainConnectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
mainConnectedTask.description = "Installs and runs instrumentation tests for all flavors on connected devices."
mainConnectedTask.reportType = ReportType.MULTI_FLAVOR
connectedCheck.dependsOn mainConnectedTask
conventionMapping(mainConnectedTask).map("resultsDir") {
String rootLocation = extension.testOptions.resultsDir != null ?
extension.testOptions.resultsDir : "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
}
conventionMapping(mainConnectedTask).map("reportsDir") {
String rootLocation = extension.testOptions.reportDir != null ?
extension.testOptions.reportDir :
"$project.buildDir/${FD_OUTPUTS}/$FD_REPORTS/$FD_ANDROID_TESTS"
project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
}
reportTasks.add(mainConnectedTask)
}
Task mainProviderTask = deviceCheck
// if more than one provider tasks, either because of several flavors, or because of
// more than one providers, then create an aggregate report tasks for all of them.
if (providers.size() > 1 || hasFlavors) {
mainProviderTask = project.tasks.create("${DEVICE}${ANDROID_TEST.suffix}",
AndroidReportTask)
mainProviderTask.group = JavaBasePlugin.VERIFICATION_GROUP
mainProviderTask.description = "Installs and runs instrumentation tests using all Device Providers."
mainProviderTask.reportType = ReportType.MULTI_FLAVOR
deviceCheck.dependsOn mainProviderTask
conventionMapping(mainProviderTask).map("resultsDir") {
String rootLocation = extension.testOptions.resultsDir != null ?
extension.testOptions.resultsDir : "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
}
conventionMapping(mainProviderTask).map("reportsDir") {
String rootLocation = extension.testOptions.reportDir != null ?
extension.testOptions.reportDir :
"$project.buildDir/${FD_OUTPUTS}/$FD_REPORTS/$FD_ANDROID_TESTS"
project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
}
reportTasks.add(mainProviderTask)
}
// Now look for the tested variant and create the check tasks for them.
// don't use an auto loop as we can't reuse baseVariantData or the closure lower
// gets broken.
int count = variantManager.getVariantDataList().size();
for (int i = 0 ; i < count ; i++) {
final BaseVariantData<? extends BaseVariantOutputData> baseVariantData = variantManager.getVariantDataList().get(i);
if (baseVariantData instanceof TestedVariantData) {
final TestVariantData testVariantData = ((TestedVariantData) baseVariantData).getTestVariantData(ANDROID_TEST)
if (testVariantData == null) {
continue
}
// get single output for now
BaseVariantOutputData variantOutputData = baseVariantData.outputs.get(0)
BaseVariantOutputData testVariantOutputData = testVariantData.outputs.get(0)
Class<? extends DeviceProviderInstrumentTestTask> taskClass = isLibraryTest ?
DeviceProviderInstrumentTestLibraryTask :
DeviceProviderInstrumentTestTask
String connectedTaskName = hasFlavors ?
"${connectedRootName}${baseVariantData.variantConfiguration.fullName.capitalize()}" :
connectedRootName
// create the check tasks for this test
// first the connected one.
DeviceProviderInstrumentTestTask connectedTask = createDeviceProviderInstrumentTestTask(
connectedTaskName,
"Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' on connected devices.",
taskClass,
testVariantData,
baseVariantData as BaseVariantData,
new ConnectedDeviceProvider(getSdkInfo().adb),
CONNECTED
)
mainConnectedTask.dependsOn connectedTask
testVariantData.connectedTestTask = connectedTask
if (baseVariantData.variantConfiguration.buildType.isTestCoverageEnabled()) {
def reportTask = project.tasks.create(
"create${baseVariantData.variantConfiguration.fullName.capitalize()}CoverageReport",
JacocoReportTask)
reportTask.reportName = baseVariantData.variantConfiguration.fullName
conventionMapping(reportTask).map("jacocoClasspath") {
project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
}
conventionMapping(reportTask).map("coverageFile") {
new File(connectedTask.getCoverageDir(), SimpleTestCallable.FILE_COVERAGE_EC)
}
conventionMapping(reportTask).map("classDir") {
if (baseVariantData.javaCompileTask != null) {
return baseVariantData.javaCompileTask.destinationDir
}
return baseVariantData.jackTask.destinationDir
}
conventionMapping(reportTask).map("sourceDir") { baseVariantData.getJavaSourceFoldersForCoverage() }
conventionMapping(reportTask).map("reportDir") {
project.file(
"$project.buildDir/${FD_OUTPUTS}/$FD_REPORTS/coverage/${baseVariantData.variantConfiguration.dirName}")
}
reportTask.dependsOn connectedTask
mainConnectedTask.dependsOn reportTask
}
// now the providers.
for (DeviceProvider deviceProvider : providers) {
DeviceProviderInstrumentTestTask providerTask = createDeviceProviderInstrumentTestTask(
hasFlavors ?
"${deviceProvider.name}${ANDROID_TEST.suffix}${baseVariantData.variantConfiguration.fullName.capitalize()}" :
"${deviceProvider.name}${ANDROID_TEST.suffix}",
"Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' using Provider '${deviceProvider.name.capitalize()}'.",
taskClass,
testVariantData,
baseVariantData as BaseVariantData,
deviceProvider,
"$DEVICE/$deviceProvider.name"
)
mainProviderTask.dependsOn providerTask
testVariantData.providerTestTaskList.add(providerTask)
if (!deviceProvider.isConfigured()) {
providerTask.enabled = false;
}
}
// now the test servers
// don't use an auto loop as it'll break the closure inside.
for (TestServer testServer : servers) {
def serverTask = project.tasks.create(
hasFlavors ?
"${testServer.name}${"upload".capitalize()}${baseVariantData.variantConfiguration.fullName}" :
"${testServer.name}${"upload".capitalize()}",
TestServerTask)
serverTask.description = "Uploads APKs for Build '${baseVariantData.variantConfiguration.fullName}' to Test Server '${testServer.name.capitalize()}'."
serverTask.group = JavaBasePlugin.VERIFICATION_GROUP
serverTask.dependsOn testVariantOutputData.assembleTask, variantOutputData.assembleTask
serverTask.testServer = testServer
conventionMapping(serverTask).map("testApk") { testVariantOutputData.outputFile }
if (!(baseVariantData instanceof LibraryVariantData)) {
conventionMapping(serverTask).map("testedApk") { variantOutputData.outputFile }
}
conventionMapping(serverTask).map("variantName") { baseVariantData.variantConfiguration.fullName }
deviceCheck.dependsOn serverTask
if (!testServer.isConfigured()) {
serverTask.enabled = false;
}
}
}
}
// If gradle is launched with --continue, we want to run all tests and generate an
// aggregate report (to help with the fact that we may have several build variants, or
// or several device providers).
// To do that, the report tasks must run even if one of their dependent tasks (flavor
// or specific provider tasks) fails, when --continue is used, and the report task is
// meant to run (== is in the task graph).
// To do this, we make the children tasks ignore their errors (ie they won't fail and
// stop the build).
if (!reportTasks.isEmpty() && project.gradle.startParameter.continueOnFailure) {
project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
for (AndroidReportTask reportTask : reportTasks) {
if (taskGraph.hasTask(reportTask)) {
reportTask.setWillRun()
}
}
}
}
}
private DeviceProviderInstrumentTestTask createDeviceProviderInstrumentTestTask(
@NonNull String taskName,
@NonNull String description,
@NonNull Class<? extends DeviceProviderInstrumentTestTask> taskClass,
@NonNull TestVariantData testVariantData,
@NonNull BaseVariantData<? extends BaseVariantOutputData> testedVariantData,
@NonNull DeviceProvider deviceProvider,
@NonNull String subFolder) {
// get single output for now for the test.
BaseVariantOutputData testVariantOutputData = testVariantData.outputs.get(0)
DeviceProviderInstrumentTestTask testTask = project.tasks.create(
taskName,
taskClass as Class<DeviceProviderInstrumentTestTask>)
testTask.description = description
testTask.group = JavaBasePlugin.VERIFICATION_GROUP
testTask.dependsOn testVariantOutputData.assembleTask, testedVariantData.assembleVariantTask
testTask.plugin = this
testTask.testVariantData = testVariantData
testTask.flavorName = testVariantData.variantConfiguration.flavorName.capitalize()
testTask.deviceProvider = deviceProvider
conventionMapping(testTask).map("resultsDir") {
String rootLocation = extension.testOptions.resultsDir != null ?
extension.testOptions.resultsDir :
"$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
String flavorFolder = testVariantData.variantConfiguration.flavorName
if (!flavorFolder.isEmpty()) {
flavorFolder = "$FD_FLAVORS/" + flavorFolder
}
project.file("$rootLocation/$subFolder/$flavorFolder")
}
conventionMapping(testTask).map("adbExec") {
return getSdkInfo().getAdb()
}
conventionMapping(testTask).map("reportsDir") {
String rootLocation = extension.testOptions.reportDir != null ?
extension.testOptions.reportDir :
"$project.buildDir/${FD_OUTPUTS}/$FD_REPORTS/$FD_ANDROID_TESTS"
String flavorFolder = testVariantData.variantConfiguration.flavorName
if (!flavorFolder.isEmpty()) {
flavorFolder = "$FD_FLAVORS/" + flavorFolder
}
project.file("$rootLocation/$subFolder/$flavorFolder")
}
conventionMapping(testTask).map("coverageDir") {
String rootLocation = "$project.buildDir/${FD_OUTPUTS}/code-coverage"
String flavorFolder = testVariantData.variantConfiguration.flavorName
if (!flavorFolder.isEmpty()) {
flavorFolder = "$FD_FLAVORS/" + flavorFolder
}
project.file("$rootLocation/$subFolder/$flavorFolder")
}
return testTask
}
/**
* Class to hold data to setup the many optional
* post-compilation steps.
*/
public static class PostCompilationData {
List<?> classGeneratingTask
List<?> libraryGeneratingTask
Closure<List<File>> inputFiles
Closure<File> inputDir
Closure<List<File>> inputLibraries
}
/**
* Creates the post-compilation tasks for the given Variant.
*
* These tasks create the dex file from the .class files, plus optional intermediary steps
* like proguard and jacoco
*
* @param variantData the variant data.
*/
public void createPostCompilationTasks(@NonNull ApkVariantData variantData) {
GradleVariantConfiguration config = variantData.variantConfiguration
boolean isTestForApp = config.type.isForTesting() &&
(variantData as TestVariantData).testedVariantData.variantConfiguration.type == DEFAULT
boolean isMinifyEnabled = config.isMinifyEnabled()
boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp
boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode()
File multiDexKeepProguard = config.getMultiDexKeepProguard()
File multiDexKeepFile = config.getMultiDexKeepFile()
boolean isTestCoverageEnabled = config.buildType.isTestCoverageEnabled() &&
!config.type.isForTesting()
// common dex task configuration
String dexTaskName = "dex${config.fullName.capitalize()}"
Dex dexTask = project.tasks.create(dexTaskName, Dex)
variantData.dexTask = dexTask
dexTask.plugin = this
conventionMapping(dexTask).map("outputFolder") {
project.file("${project.buildDir}/${FD_INTERMEDIATES}/dex/${config.dirName}")
}
dexTask.tmpFolder = project.file("$project.buildDir/${FD_INTERMEDIATES}/tmp/dex/${config.dirName}")
dexTask.dexOptions = extension.dexOptions
dexTask.multiDexEnabled = isMultiDexEnabled
dexTask.legacyMultiDexMode = isLegacyMultiDexMode
// dx doesn't work with receving --no-optimize in debug so we disable it for now.
dexTask.optimize = true //!variantData.variantConfiguration.buildType.debuggable
// data holding dependencies and input for the dex. This gets updated as new
// post-compilation steps are inserted between the compilation and dx.
PostCompilationData pcData = new PostCompilationData()
pcData.classGeneratingTask = [variantData.javaCompileTask]
pcData.libraryGeneratingTask = [variantData.variantDependency.packageConfiguration.buildDependencies]
pcData.inputFiles = {
variantData.javaCompileTask.outputs.files.files as List
}
pcData.inputDir = {
variantData.javaCompileTask.destinationDir
}
pcData.inputLibraries = {
androidBuilder.getPackagedJars(config) as List
}
// ---- Code Coverage first -----
if (isTestCoverageEnabled) {
pcData = createJacocoTask(config, variantData, pcData)
}
// ----- Minify next ----
if (isMinifyEnabled) {
// first proguard task.
BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
(variantData instanceof TestVariantData ? variantData.testedVariantData : null) as BaseVariantData
createProguardTasks(variantData, testedVariantData, pcData)
} else if ((extension.dexOptions.preDexLibraries && !isMultiDexEnabled) ||
(isMultiDexEnabled && !isLegacyMultiDexMode)) {
def preDexTaskName = "preDex${config.fullName.capitalize()}"
PreDex preDexTask = project.tasks.create(preDexTaskName, PreDex)
variantData.preDexTask = preDexTask
preDexTask.plugin = this
preDexTask.dexOptions = extension.dexOptions
preDexTask.multiDex = isMultiDexEnabled
conventionMapping(preDexTask).map("inputFiles", pcData.inputLibraries)
conventionMapping(preDexTask).map("outputFolder") {
project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/pre-dexed/${config.dirName}")
}
// update dependency.
optionalDependsOn(preDexTask, pcData.libraryGeneratingTask)
pcData.libraryGeneratingTask = [preDexTask] as List<Object>
// update inputs
if (isMultiDexEnabled) {
pcData.inputLibraries = { [] }
} else {
pcData.inputLibraries = {
project.fileTree(preDexTask.outputFolder).files as List
}
}
}
// ----- Multi-Dex support
if (isMultiDexEnabled && isLegacyMultiDexMode) {
if (!isMinifyEnabled) {
// create a task that will convert the output of the compilation
// into a jar. This is needed by the multi-dex input.
JarMergingTask jarMergingTask = project.tasks.create(
"packageAll${config.fullName.capitalize()}ClassesForMultiDex",
JarMergingTask)
conventionMapping(jarMergingTask).map("inputJars",pcData.inputLibraries)
conventionMapping(jarMergingTask).map("inputDir", pcData.inputDir)
jarMergingTask.jarFile = project.file(
"$project.buildDir/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/allclasses.jar")
// update dependencies
optionalDependsOn(jarMergingTask, pcData.classGeneratingTask)
optionalDependsOn(jarMergingTask, pcData.libraryGeneratingTask)
pcData.libraryGeneratingTask = [jarMergingTask]
pcData.classGeneratingTask = [jarMergingTask]
// Update the inputs
pcData.inputFiles = { [jarMergingTask.jarFile] }
pcData.inputDir = null
pcData.inputLibraries = { [] }
}
// ----------
// Create a task to collect the list of manifest entry points which are
// needed in the primary dex
CreateManifestKeepList manifestKeepListTask = project.tasks.create(
"collect${config.fullName.capitalize()}MultiDexComponents",
CreateManifestKeepList)
// since all the output have the same manifest, besides the versionCode,
// we can take any of the output and use that.
final BaseVariantOutputData output = variantData.outputs.get(0)
manifestKeepListTask.dependsOn output.manifestProcessorTask
conventionMapping(manifestKeepListTask).map("manifest") {
output.manifestProcessorTask.manifestOutputFile
}
manifestKeepListTask.proguardFile = multiDexKeepProguard
manifestKeepListTask.outputFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/manifest_keep.txt")
//variant.ext.collectMultiDexComponents = manifestKeepListTask
// ----------
// Create a proguard task to shrink the classes to manifest components
ProGuardTask proguardComponentsTask = createShrinkingProGuardTask(project,
"shrink${config.fullName.capitalize()}MultiDexComponents")
proguardComponentsTask.configuration(manifestKeepListTask.outputFile)
proguardComponentsTask.libraryjars( {
ensureTargetSetup()
File shrinkedAndroid = new File(getAndroidBuilder().getTargetInfo().buildTools.location, "lib${File.separatorChar}shrinkedAndroid.jar")
// TODO remove in 1.0
// STOPSHIP
if (!shrinkedAndroid.isFile()) {
shrinkedAndroid = new File(getAndroidBuilder().getTargetInfo().buildTools.location, "multidex${File.separatorChar}shrinkedAndroid.jar")
}
return shrinkedAndroid
})
proguardComponentsTask.injars(pcData.inputFiles.call().iterator().next())
File componentsJarFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/componentClasses.jar")
proguardComponentsTask.outjars(componentsJarFile)
proguardComponentsTask.printconfiguration(
"${project.buildDir}/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/components.flags")
// update dependencies
proguardComponentsTask.dependsOn manifestKeepListTask
optionalDependsOn(proguardComponentsTask, pcData.classGeneratingTask)
optionalDependsOn(proguardComponentsTask, pcData.libraryGeneratingTask)
// ----------
// Compute the full list of classes for the main dex file
CreateMainDexList createMainDexListTask = project.tasks.create(
"create${config.fullName.capitalize()}MainDexClassList",
CreateMainDexList)
createMainDexListTask.plugin = this
createMainDexListTask.dependsOn proguardComponentsTask
//createMainDexListTask.dependsOn { proguardMainDexTask }
def files = pcData.inputFiles
createMainDexListTask.allClassesJarFile = files().first()
conventionMapping(createMainDexListTask).map("componentsJarFile") { componentsJarFile }
// conventionMapping(createMainDexListTask).map("includeInMainDexJarFile") { mainDexJarFile }
createMainDexListTask.mainDexListFile = multiDexKeepFile
createMainDexListTask.outputFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/maindexlist.txt")
// update dependencies
dexTask.dependsOn createMainDexListTask
// ----------
// If proguard is on create a de-obfuscated list to aid debugging.
if (isMinifyEnabled) {
RetraceMainDexList retraceTask = project.tasks.create(
"retrace${config.fullName.capitalize()}MainDexClassList",
RetraceMainDexList)
retraceTask.dependsOn variantData.obfuscationTask, createMainDexListTask
conventionMapping(retraceTask).map("mainDexListFile") { createMainDexListTask.outputFile }
conventionMapping(retraceTask).map("mappingFile") { variantData.mappingFile }
retraceTask.outputFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/multi-dex/${config.dirName}/maindexlist_deobfuscated.txt")
dexTask.dependsOn retraceTask
}
// configure the dex task to receive the generated class list.
conventionMapping(dexTask).map("mainDexListFile") { createMainDexListTask.outputFile }
}
// ----- Dex Task ----
// dependencies, some of these could be null
optionalDependsOn(dexTask, pcData.classGeneratingTask)
optionalDependsOn(dexTask, pcData.libraryGeneratingTask,)
// inputs
if (pcData.inputDir != null) {
conventionMapping(dexTask).map("inputDir", pcData.inputDir)
} else {
conventionMapping(dexTask).map("inputFiles", pcData.inputFiles)
}
conventionMapping(dexTask).map("libraries", pcData.inputLibraries)
}
public PostCompilationData createJacocoTask(
@NonNull GradleVariantConfiguration config,
@NonNull BaseVariantData variantData,
@NonNull final PostCompilationData pcData) {
final JacocoInstrumentTask jacocoTask = project.tasks.create(
"instrument${config.fullName.capitalize()}", JacocoInstrumentTask)
conventionMapping(jacocoTask).map("jacocoClasspath") {
project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
}
// can't directly use the existing inputFiles closure as we need the dir instead :\
conventionMapping(jacocoTask).map("inputDir", pcData.inputDir)
conventionMapping(jacocoTask).map("outputDir") {
project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/coverage-instrumented-classes/${config.dirName}")
}
variantData.jacocoInstrumentTask = jacocoTask
Copy agentTask = getJacocoAgentTask()
jacocoTask.dependsOn agentTask
// update dependency.
PostCompilationData pcData2 = new PostCompilationData()
optionalDependsOn(jacocoTask, pcData.classGeneratingTask)
pcData2.classGeneratingTask = [jacocoTask]
pcData2.libraryGeneratingTask = [pcData.libraryGeneratingTask, agentTask]
// update inputs
pcData2.inputFiles = {
project.files(jacocoTask.getOutputDir()).files as List
}
pcData2.inputDir = {
jacocoTask.getOutputDir()
}
pcData2.inputLibraries = {
[pcData.inputLibraries.call(), [new File(agentTask.destinationDir, FILE_JACOCO_AGENT)]].flatten() as List
}
return pcData2
}
private static ProGuardTask createShrinkingProGuardTask(
@NonNull Project project,
@NonNull String name) {
ProGuardTask task = project.tasks.create(name, ProGuardTask)
task.dontobfuscate()
task.dontoptimize()
task.dontpreverify()
task.dontwarn()
task.forceprocessing()
return task;
}
public void createJackTask(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
@Nullable BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
GradleVariantConfiguration config = variantData.variantConfiguration
// ----- Create Jill tasks -----
JillTask jillRuntimeTask = project.tasks.create(
"jill${config.fullName.capitalize()}RuntimeLibraries",
JillTask)
jillRuntimeTask.plugin = this
jillRuntimeTask.dexOptions = extension.dexOptions
conventionMapping(jillRuntimeTask).map("inputLibs") {
getBootClasspath()
}
conventionMapping(jillRuntimeTask).map("outputFolder") {
project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/runtime")
}
// ----
JillTask jillPackagedTask = project.tasks.create(
"jill${config.fullName.capitalize()}PackagedLibraries",
JillTask)
jillPackagedTask.dependsOn variantData.variantDependency.packageConfiguration.buildDependencies
jillPackagedTask.plugin = this
jillPackagedTask.dexOptions = extension.dexOptions
conventionMapping(jillPackagedTask).map("inputLibs") {
androidBuilder.getPackagedJars(config)
}
conventionMapping(jillPackagedTask).map("outputFolder") {
project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/packaged")
}
// ----- Create Jack Task -----
JackTask compileTask = project.tasks.create(
"compile${config.fullName.capitalize()}Java",
JackTask)
variantData.jackTask = compileTask
variantData.jackTask.dependsOn variantData.sourceGenTask, jillRuntimeTask, jillPackagedTask
variantData.compileTask.dependsOn variantData.jackTask
// TODO - dependency information for the compile classpath is being lost.
// Add a temporary approximation
compileTask.dependsOn variantData.variantDependency.compileConfiguration.buildDependencies
compileTask.plugin = this
conventionMapping(compileTask).map("javaMaxHeapSize") { extension.dexOptions.getJavaMaxHeapSize() }
compileTask.source = variantData.getJavaSources()
compileTask.multiDexEnabled = config.isMultiDexEnabled()
compileTask.minSdkVersion = config.minSdkVersion.apiLevel
// if the tested variant is an app, add its classpath. For the libraries,
// it's done automatically since the classpath includes the library output as a normal
// dependency.
if (testedVariantData instanceof ApplicationVariantData) {
conventionMapping(compileTask).map("classpath") {
project.fileTree(jillRuntimeTask.outputFolder) + testedVariantData.jackTask.classpath + project.fileTree(testedVariantData.jackTask.jackFile)
}
} else {
conventionMapping(compileTask).map("classpath") {
project.fileTree(jillRuntimeTask.outputFolder)
}
}
conventionMapping(compileTask).map("packagedLibraries") {
project.fileTree(jillPackagedTask.outputFolder).files
}
conventionMapping(compileTask).map("destinationDir") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/dex/${config.dirName}")
}
conventionMapping(compileTask).map("jackFile") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/classes/${config.dirName}/classes.zip")
}
conventionMapping(compileTask).map("tempFolder") {
project.file("$project.buildDir/${FD_INTERMEDIATES}/tmp/jack/${config.dirName}")
}
if (config.isMinifyEnabled()) {
conventionMapping(compileTask).map("proguardFiles") {
// since all the output use the same resources, we can use the first output
// to query for a proguard file.
BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
List<File> proguardFiles = config.getProguardFiles(true /*includeLibs*/,
[extension.getDefaultProguardFile(DEFAULT_PROGUARD_CONFIG_FILE)])
File proguardResFile = variantOutputData.processResourcesTask.proguardOutputFile
if (proguardResFile != null) {
proguardFiles.add(proguardResFile)
}
// for tested app, we only care about their aapt config since the base
// configs are the same files anyway.
if (testedVariantData != null) {
// use single output for now.
proguardResFile = testedVariantData.outputs.get(0).processResourcesTask.proguardOutputFile
if (proguardResFile != null) {
proguardFiles.add(proguardResFile)
}
}
return proguardFiles
}
compileTask.mappingFile = variantData.mappingFile = project.file(
"${project.buildDir}/${FD_OUTPUTS}/mapping/${variantData.variantConfiguration.dirName}/mapping.txt")
}
configureLanguageLevel(compileTask)
}
/**
* Configures the source and target language level of a compile task. If the user has set it
* explicitly, we obey the setting. Otherwise we change the default language level based on the
* compile SDK version.
*
* <p>This method modifies extension.compileOptions, to propagate the language level to Studio.
*/
private void configureLanguageLevel(AbstractCompile compileTask) {
def compileOptions = extension.compileOptions
JavaVersion javaVersionToUse
Integer compileSdkLevel =
AndroidTargetHash.getVersionFromHash(extension.compileSdkVersion)?.apiLevel
switch (compileSdkLevel) {
case null: // Default to 1.6 if we fail to parse compile SDK version.
case 0..20:
javaVersionToUse = JavaVersion.VERSION_1_6
break
default:
javaVersionToUse = JavaVersion.VERSION_1_7
break
}
def jdkVersion = JavaVersion.toVersion(System.getProperty("java.specification.version"))
if (jdkVersion < javaVersionToUse) {
logger.info(
"Default language level for 'compileSdkVersion %d' is %s, but the " +
"JDK used is %s, so the JDK language level will be used.",
compileSdkLevel, javaVersionToUse, jdkVersion)
javaVersionToUse = jdkVersion
}
compileOptions.defaultJavaVersion = javaVersionToUse
conventionMapping(compileTask).map("sourceCompatibility") {
compileOptions.sourceCompatibility.toString()
}
conventionMapping(compileTask).map("targetCompatibility") {
compileOptions.targetCompatibility.toString()
}
}
/**
* Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
* @param variantData
* @param assembleTask an optional assembleTask to be used. If null a new one is created. The
* assembleTask is always set in the Variant.
* @param publishApk if true the generated APK gets published.
*/
public void createPackagingTask(
@NonNull ApkVariantData variantData,
Task assembleTask,
boolean publishApk) {
GradleVariantConfiguration config = variantData.variantConfiguration
boolean signedApk = variantData.isSigned()
BasePluginConvention convention = project.convention.findPlugin(BasePluginConvention)
String projectBaseName = convention.archivesBaseName
String defaultLocation = "$project.buildDir/${FD_OUTPUTS}/apk"
String apkLocation = defaultLocation
if (project.hasProperty(PROPERTY_APK_LOCATION)) {
apkLocation = project.getProperties().get(PROPERTY_APK_LOCATION)
}
SigningConfig sc = (SigningConfig) config.signingConfig
boolean multiOutput = variantData.outputs.size() > 1
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated data.
for (ApkVariantOutputData vod : variantData.outputs) {
// create final var inside the loop to ensure the closures will work.
final ApkVariantOutputData variantOutputData = vod
String outputName = variantOutputData.fullName
String outputBaseName = variantOutputData.baseName
// Add a task to generate application package
PackageApplication packageApp = project.tasks.
create("package${outputName.capitalize()}",
PackageApplication)
variantOutputData.packageApplicationTask = packageApp
packageApp.dependsOn variantOutputData.processResourcesTask, variantData.processJavaResourcesTask
optionalDependsOn(packageApp, variantData.dexTask, variantData.jackTask)
if (variantOutputData.packageSplitResourcesTask != null) {
packageApp.dependsOn variantOutputData.packageSplitResourcesTask
}
if (variantOutputData.packageSplitAbiTask != null) {
packageApp.dependsOn variantOutputData.packageSplitAbiTask
}
// Add dependencies on NDK tasks if NDK plugin is applied.
if (extension.getUseNewNativePlugin()) {
throw new RuntimeException("useNewNativePlugin is currently not supported.")
} else {
packageApp.dependsOn variantData.ndkCompileTask
}
packageApp.plugin = this
if (config.minifyEnabled && config.buildType.shrinkResources && !config.useJack) {
def shrinkTask = createShrinkResourcesTask(vod)
// When shrinking resources, rather than having the packaging task
// directly map to the packageOutputFile of ProcessAndroidResources,
// we insert the ShrinkResources task into the chain, such that its
// input is the ProcessAndroidResources packageOutputFile, and its
// output is what the PackageApplication task reads.
packageApp.dependsOn shrinkTask
conventionMapping(packageApp).map("resourceFile") {
shrinkTask.compressedResources
}
} else {
conventionMapping(packageApp).map("resourceFile") {
variantOutputData.processResourcesTask.packageOutputFile
}
}
conventionMapping(packageApp).map("dexFolder") {
if (variantData.dexTask != null) {
return variantData.dexTask.outputFolder
}
if (variantData.jackTask != null) {
return variantData.jackTask.getDestinationDir()
}
return null
}
conventionMapping(packageApp).map("dexedLibraries") {
if (config.isMultiDexEnabled() &&
!config.isLegacyMultiDexMode() &&
variantData.preDexTask != null) {
return project.fileTree(variantData.preDexTask.outputFolder).files
}
return Collections.<File>emptyList()
}
conventionMapping(packageApp).map("packagedJars") { androidBuilder.getPackagedJars(config) }
conventionMapping(packageApp).map("javaResourceDir") {
getOptionalDir(variantData.processJavaResourcesTask.destinationDir)
}
conventionMapping(packageApp).map("jniFolders") {
getJniFolders(variantData);
}
conventionMapping(packageApp).map("abiFilters") {
if (variantOutputData.getMainOutputFile().getFilter(OutputFile.ABI) != null) {
return ImmutableSet.of(variantOutputData.getMainOutputFile().getFilter(OutputFile.ABI))
}
return config.supportedAbis
}
conventionMapping(packageApp).map("jniDebugBuild") { config.buildType.jniDebuggable }
conventionMapping(packageApp).map("signingConfig") { sc }
if (sc != null) {
ValidateSigningTask validateSigningTask = validateSigningTaskMap.get(sc)
if (validateSigningTask == null) {
validateSigningTask =
project.tasks.create("validate${sc.name.capitalize()}Signing",
ValidateSigningTask)
validateSigningTask.plugin = this
validateSigningTask.signingConfig = sc
validateSigningTaskMap.put(sc, validateSigningTask)
}
packageApp.dependsOn validateSigningTask
}
String apkName = signedApk ?
"$projectBaseName-${outputBaseName}-unaligned.apk" :
"$projectBaseName-${outputBaseName}-unsigned.apk"
conventionMapping(packageApp).map("packagingOptions") { extension.packagingOptions }
conventionMapping(packageApp).map("outputFile") {
// if this is the final task then the location is
// the potentially overridden one.
if (!signedApk || !variantData.zipAlignEnabled) {
project.file("$apkLocation/${apkName}")
} else {
// otherwise default one.
project.file("$defaultLocation/${apkName}")
}
}
Task appTask = packageApp
OutputFileTask outputFileTask = packageApp
if (signedApk) {
if (variantData.zipAlignEnabled) {
// Add a task to zip align application package
def zipAlignTask = project.tasks.create(
"zipalign${outputName.capitalize()}",
ZipAlign)
variantOutputData.zipAlignTask = zipAlignTask
zipAlignTask.dependsOn packageApp
conventionMapping(zipAlignTask).map("inputFile") { packageApp.outputFile }
conventionMapping(zipAlignTask).map("outputFile") {
project.file(
"$apkLocation/$projectBaseName-${outputBaseName}.apk")
}
conventionMapping(zipAlignTask).map("zipAlignExe") {
String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
if (path != null) {
return new File(path)
}
return null
}
if (variantOutputData.splitZipAlign != null) {
zipAlignTask.dependsOn variantOutputData.splitZipAlign
}
appTask = zipAlignTask
outputFileTask = zipAlignTask
}
}
// Add an assemble task
if (multiOutput) {
// create a task for this output
variantOutputData.assembleTask = createAssembleTask(variantOutputData)
// figure out the variant assemble task if it's not present yet.
if (variantData.assembleVariantTask == null) {
if (assembleTask != null) {
variantData.assembleVariantTask = assembleTask
} else {
variantData.assembleVariantTask = createAssembleTask(variantData)
}
}
// variant assemble task depends on each output assemble task.
variantData.assembleVariantTask.dependsOn variantOutputData.assembleTask
} else {
// single output
if (assembleTask != null) {
variantData.assembleVariantTask = variantOutputData.assembleTask = assembleTask
} else {
variantData.assembleVariantTask =
variantOutputData.assembleTask = createAssembleTask(variantData)
}
}
if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
// in case we are not signing the resulting APKs and we have some pure splits
// we should manually copy them from the intermediate location to the final
// apk location unmodified.
Copy copyTask = project.tasks.create(
"copySplit${outputName.capitalize()}",
Copy)
copyTask.destinationDir = new File(apkLocation as String);
copyTask.from(variantOutputData.packageSplitResourcesTask.getOutputDirectory())
variantOutputData.assembleTask.dependsOn(copyTask)
copyTask.mustRunAfter(appTask)
}
variantOutputData.assembleTask.dependsOn appTask
if (publishApk) {
if (extension.defaultPublishConfig.equals(outputName)) {
// add the artifact that will be published
project.artifacts.add("default", new ApkPublishArtifact(
projectBaseName,
null,
outputFileTask))
}
// also publish the artifact with its full config name
if (extension.publishNonDefault) {
// classifier cannot just be the publishing config as we need
// to add the filters if needed.
String classifier = variantData.variantDependency.publishConfiguration.name
if (variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY) != null) {
classifier = "${classifier}-${variantOutputData.getMainOutputFile().getFilter(OutputFile.DENSITY)}"
}
if (variantOutputData.getMainOutputFile().getFilter(OutputFile.ABI) != null) {
classifier = "${classifier}-${variantOutputData.getMainOutputFile().getFilter(OutputFile.ABI)}"
}
project.artifacts.add(variantData.variantDependency.publishConfiguration.name,
new ApkPublishArtifact(
projectBaseName,
classifier,
outputFileTask))
}
}
}
// create install task for the variant Data. This will deal with finding the
// right output if there are more than one.
// Add a task to install the application package
if (signedApk) {
InstallVariantTask installTask = project.tasks.
create("install${config.fullName.capitalize()}",
InstallVariantTask)
installTask.description = "Installs the " + variantData.description
installTask.group = INSTALL_GROUP
installTask.plugin = this
installTask.variantData = variantData
conventionMapping(installTask).map("adbExe") { androidBuilder.sdkInfo?.adb }
installTask.dependsOn variantData.assembleVariantTask
variantData.installTask = installTask
}
if (extension.lintOptions.checkReleaseBuilds) {
createLintVitalTask(variantData)
}
// add an uninstall task
def uninstallTask = project.tasks.create(
"uninstall${variantData.variantConfiguration.fullName.capitalize()}",
UninstallTask)
uninstallTask.description = "Uninstalls the " + variantData.description
uninstallTask.group = INSTALL_GROUP
uninstallTask.variant = variantData
conventionMapping(uninstallTask).map("adbExe") { sdkHandler.sdkInfo?.adb }
variantData.uninstallTask = uninstallTask
uninstallAll.dependsOn uninstallTask
}
public Task createAssembleTask(
@NonNull BaseVariantOutputData variantOutputData) {
Task assembleTask = project.tasks.
create("assemble${variantOutputData.fullName.capitalize()}")
return assembleTask
}