Add prototype against the new AGP API

* Add a separate Gradle source set, that is compiled against the new
  versions of AGP and Gradle

* Separate the public AGP API usages from the old internal API usages
  in KotlinPlugin.kt -- see AbstractAndroidProjectHandler

* Implement Android25ProjectHandler with the new AGP API, wired it
  to KotlinAndroidPlugin through reflection

(cherry picked from commit e6dbf54)

(cherry picked from commit 88e3d4b)
This commit is contained in:
Sergey Igushkin
2017-05-05 19:17:26 +03:00
committed by Alexey Tsvetkov
parent a5620454fa
commit eb71dee9cc
4 changed files with 344 additions and 145 deletions
@@ -19,6 +19,13 @@ repositories {
jcenter() jcenter()
mavenLocal() mavenLocal()
maven { url 'http://repository.jetbrains.com/utils/' } maven { url 'http://repository.jetbrains.com/utils/' }
// todo: remove this once the AGP artifacts get published
maven { url project.property("googlePreviewRepositoryPath") } // path to the local unpacked preview repo
}
configurations {
agp25CompileOnly
} }
dependencies { dependencies {
@@ -37,6 +44,10 @@ dependencies {
compileOnly 'org.codehaus.groovy:groovy-all:2.3.9' compileOnly 'org.codehaus.groovy:groovy-all:2.3.9'
compileOnly 'org.jetbrains.kotlin:gradle-api:2.2' compileOnly 'org.jetbrains.kotlin:gradle-api:2.2'
agp25CompileOnly 'com.android.tools.build:gradle:2.4.0-dev'
agp25CompileOnly 'org.codehaus.groovy:groovy-all:2.3.9'
agp25CompileOnly gradleApi()
testCompile "org.jetbrains.kotlin:kotlin-build-common-test:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-build-common-test:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
} }
@@ -54,12 +65,21 @@ sourceSets.main.compileClasspath += files(groovyClassesDir)
compileGroovy.classpath = sourceSets.main.compileClasspath - files(groovyClassesDir) compileGroovy.classpath = sourceSets.main.compileClasspath - files(groovyClassesDir)
sourceSets.main.java.srcDirs += sourceSets.main.kotlin.srcDirs sourceSets.main.java.srcDirs += sourceSets.main.kotlin.srcDirs
sourceSets {
agp25 {
compileClasspath += configurations.compile + configurations.agp25CompileOnly + sourceSets.main.output
}
}
processResources { processResources {
expand(project.properties) expand(project.properties)
} }
jar.dependsOn agp25Classes
jar { jar {
from compileGroovy.destinationDir from compileGroovy.destinationDir
from sourceSets.agp25.output.classesDir
} }
test.executable = "${JDK_18}/bin/java" test.executable = "${JDK_18}/bin/java"
@@ -0,0 +1,74 @@
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.*
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.SourceKind
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import com.android.builder.model.SourceProvider
import org.gradle.api.Project
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File
@Suppress("unused")
class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools)
: AbstractAndroidProjectHandler<BaseVariant>(kotlinConfigurationTools) {
override fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
project.plugins.all { plugin ->
val androidExtension = project.extensions.getByName("android")
var testedExtension: TestedExtension? = null
when (plugin.javaClass) {
AppPlugin::class.java -> {
(androidExtension as AppExtension).applicationVariants.all(action)
testedExtension = androidExtension
}
LibraryPlugin::class.java -> {
(androidExtension as LibraryExtension).libraryVariants.all(action)
testedExtension = androidExtension
}
TestPlugin::class.java -> {
(androidExtension as TestExtension).applicationVariants.all(action)
}
}
testedExtension?.apply {
testVariants.all(action)
unitTestVariants.all(action)
}
}
}
override fun getSourceProviders(variantData: BaseVariant): Iterable<SourceProvider> =
variantData.sourceSets
override fun getAllJavaSources(variantData: BaseVariant): Iterable<File> =
variantData.getSourceFolders(SourceKind.JAVA).map { it.dir }
override fun getVariantName(variant: BaseVariant): String = variant.name
override fun checkVariant(variant: BaseVariant) = Unit
override fun getTestedVariantData(variantData: BaseVariant): BaseVariant? = when (variantData) {
is TestVariant -> variantData.testedVariant
is UnitTestVariant -> variantData.testedVariant as? BaseVariant
else -> null
}
override fun getJavaTask(variantData: BaseVariant): AbstractCompile? =
@Suppress("DEPRECATION") // There is always a Java compile task -- the deprecation was for Jack
variantData.javaCompile
override fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariant, javaSourceDirectory: File) =
variantData.addJavaSourceFoldersToModel(javaSourceDirectory)
override fun configureMultiProjectIc(project: Project,
variantData: BaseVariant,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile,
kotlinAfterJavaTask: KotlinCompile?) {
//todo: No easy solution because of the absence of the output information in library modules
// Though it is affordable not to implement this for the first previews, because the impact is tolerable
// to some degree -- the dependent projects will rebuild non-incrementally when a library project changes
}
}
@@ -4,14 +4,12 @@ import com.android.build.gradle.BaseExtension
import com.android.build.gradle.BasePlugin import com.android.build.gradle.BasePlugin
import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.build.gradle.internal.variant.TestVariantData
import com.android.builder.model.SourceProvider import com.android.builder.model.SourceProvider
import groovy.lang.Closure import groovy.lang.Closure
import org.apache.tools.ant.util.ReflectUtil.newInstance
import org.gradle.api.InvalidUserDataException import org.gradle.api.InvalidUserDataException
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.ProjectConfigurationException
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
@@ -33,7 +31,6 @@ import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import java.util.* import java.util.*
@@ -339,11 +336,7 @@ internal open class KotlinAndroidPlugin(
private val kotlinGradleBuildServices: KotlinGradleBuildServices private val kotlinGradleBuildServices: KotlinGradleBuildServices
) : Plugin<Project> { ) : Plugin<Project> {
private val log = Logging.getLogger(this.javaClass)
override fun apply(project: Project) { override fun apply(project: Project) {
val ext = project.extensions.getByName("android") as BaseExtension
val version = loadAndroidPluginVersion() val version = loadAndroidPluginVersion()
if (version != null) { if (version != null) {
val minimalVersion = "1.1.0" val minimalVersion = "1.1.0"
@@ -352,15 +345,80 @@ internal open class KotlinAndroidPlugin(
} }
} }
val kotlinTools = KotlinConfigurationTools(
kotlinSourceSetProvider,
tasksProvider,
kotlinPluginVersion,
kotlinGradleBuildServices)
val legacyVersionThreshold = "2.4.0"
val variantProcessor = if (compareVersionNumbers(version, legacyVersionThreshold) < 0)
LegacyAndroidAndroidProjectHandler(kotlinTools)
else
newInstance(
Class.forName("org.jetbrains.kotlin.gradle.plugin.Android25ProjectHandler"),
arrayOf(kotlinTools.javaClass), arrayOf(kotlinTools)) as AbstractAndroidProjectHandler<*>
variantProcessor.handleProject(project)
}
}
class KotlinConfigurationTools internal constructor(val kotlinSourceSetProvider: KotlinSourceSetProvider,
val kotlinTasksProvider: KotlinTasksProvider,
val kotlinPluginVersion: String,
val kotlinGradleBuildServices: KotlinGradleBuildServices)
/** Part of Android configuration, that works only with the old public API.
* @see [LegacyAndroidAndroidProjectHandler] that is implemented with the old internal API and [AndroidGradle25VariantProcessor] that works
* with the new public API */
abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationTools: KotlinConfigurationTools) {
protected val artifactDifferenceRegistryProvider get() =
kotlinConfigurationTools.kotlinGradleBuildServices.artifactDifferenceRegistryProvider
private val logger = Logging.getLogger(this.javaClass)
private fun Project.tryGetSingleArtifact(variantData: BaseVariantData<*>): File? {
val log = logger
log.kotlinDebug { "Trying to determine single artifact for project $path" }
val outputs = variantData.outputs
if (outputs.size != 1) {
log.kotlinDebug { "Output count != 1 for variant: ${outputs.map { it.outputFile.relativeTo(rootDir).path }.joinToString()}" }
return null
}
return variantData.outputs.first().outputFile
}
protected abstract fun forEachVariant(project: Project, action: (V) -> Unit): Unit
protected abstract fun getSourceProviders(variantData: V): Iterable<SourceProvider>
protected abstract fun getAllJavaSources(variantData: V): Iterable<File>
protected abstract fun getVariantName(variant: V): String
protected abstract fun checkVariant(variant: V): Unit
protected abstract fun getTestedVariantData(variantData: V): V?
protected abstract fun getJavaTask(variantData: V): AbstractCompile?
protected abstract fun addJavaSourceDirectoryToVariantModel(variantData: V, javaSourceDirectory: File): Unit
protected abstract fun configureMultiProjectIc(project: Project,
variantData: V,
javaTask: AbstractCompile,
kotlinTask: KotlinCompile,
kotlinAfterJavaTask: KotlinCompile?)
fun handleProject(project: Project) {
val ext = project.extensions.getByName("android") as BaseExtension
val aptConfigurations = hashMapOf<String, Configuration>() val aptConfigurations = hashMapOf<String, Configuration>()
ext.sourceSets.all { sourceSet -> ext.sourceSets.all { sourceSet ->
log.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet") logger.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet")
val kotlinSourceSet = kotlinSourceSetProvider.create(sourceSet.name) val kotlinSourceSet = kotlinConfigurationTools.kotlinSourceSetProvider.create(sourceSet.name)
kotlinSourceSet.kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin"))) kotlinSourceSet.kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin")))
sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet) sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
aptConfigurations.put(sourceSet.name, project.createAptConfiguration(sourceSet.name, kotlinPluginVersion)) aptConfigurations.put(sourceSet.name, project.createAptConfiguration(sourceSet.name, kotlinConfigurationTools.kotlinPluginVersion))
} }
val kotlinOptions = KotlinJvmOptionsImpl() val kotlinOptions = KotlinJvmOptionsImpl()
@@ -372,164 +430,129 @@ internal open class KotlinAndroidPlugin(
project.afterEvaluate { project -> project.afterEvaluate { project ->
if (project != null) { if (project != null) {
val plugin = (project.plugins.findPlugin("android") val plugin = (project.plugins.findPlugin("android")
?: project.plugins.findPlugin("android-library") ?: project.plugins.findPlugin("android-library")
?: project.plugins.findPlugin("com.android.test")) as BasePlugin ?: project.plugins.findPlugin("com.android.test")) as BasePlugin
val subpluginEnvironment = loadSubplugins(project)
val variantManager = AndroidGradleWrapper.getVariantDataManager(plugin) forEachVariant(project) {
processVariantData(variantManager.variantDataList, project, processVariant(it, project, ext, plugin, aptConfigurations, kotlinOptions,
ext, plugin, aptConfigurations, kotlinOptions) kotlinConfigurationTools.kotlinTasksProvider, subpluginEnvironment)
}
} }
} }
} }
private fun getTestedVariantData(variantData: BaseVariantData<*>): BaseVariantData<*>? = private fun processVariant(variantData: V,
((variantData as? TestVariantData)?.testedVariantData as? BaseVariantData<*>) project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin,
aptConfigurations: Map<String, Configuration>,
rootKotlinOptions: KotlinJvmOptionsImpl,
tasksProvider: KotlinTasksProvider, subpluginEnvironment: SubpluginEnvironment) {
private fun processVariantData( checkVariant(variantData)
variantDataList: List<BaseVariantData<out BaseVariantOutputData>>,
project: Project,
androidExt: BaseExtension,
androidPlugin: BasePlugin,
aptConfigurations: Map<String, Configuration>,
rootKotlinOptions: KotlinJvmOptionsImpl
) {
val logger = project.logger
val subpluginEnvironment = loadSubplugins(project)
for (variantData in variantDataList) { val variantDataName = getVariantName(variantData)
if (AndroidGradleWrapper.isJackEnabled(variantData)) { logger.kotlinDebug("Process variant [$variantDataName]")
throw ProjectConfigurationException(
"Kotlin Gradle plugin does not support the deprecated Jack toolchain.\n" + val testedVariantData = getTestedVariantData(variantData)
"Disable Jack or revert to Kotlin Gradle plugin version 1.1.1.", null) val isAndroidTestVariant = variantDataName.endsWith("androidTest", ignoreCase = true) &&
testedVariantData != null
val javaTask = getJavaTask(variantData)
if (javaTask == null) {
logger.info("KOTLIN: javaTask is missing for $variantDataName, so Kotlin files won't be compiled for it")
return
}
val kotlinTaskName = "compile${variantDataName.capitalize()}Kotlin"
// todo: Investigate possibility of creating and configuring kotlinTask before evaluation
val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, variantDataName)
kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions
// store kotlin classes in separate directory. They will serve as class-path to java compiler
kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName")
kotlinTask.description = "Compiles the $variantDataName kotlin."
kotlinTask.setDependsOn(javaTask.dependsOn)
val isKapt3Enabled = Kapt3GradleSubplugin.isEnabled(project)
val aptFiles = arrayListOf<File>()
if (!isKapt3Enabled) {
var hasAnyKaptDependency: Boolean = false
for (provider in getSourceProviders(variantData)) {
val aptConfiguration = aptConfigurations[(provider as AndroidSourceSet).name]
// Ignore if there's only an annotation processor wrapper in dependencies (added by default)
if (aptConfiguration != null && aptConfiguration.allDependencies.size > 1) {
javaTask.dependsOn(aptConfiguration.buildDependencies)
aptFiles.addAll(aptConfiguration.resolve())
hasAnyKaptDependency = true
}
} }
val variantDataName = variantData.name if (!hasAnyKaptDependency) {
logger.kotlinDebug("Process variant [$variantDataName]")
val testedVariantData = getTestedVariantData(variantData)
val isAndroidTestVariant = variantDataName.endsWith("androidTest", ignoreCase = true) &&
testedVariantData != null
val javaTask = AndroidGradleWrapper.getJavaTask(variantData)
if (javaTask == null) {
logger.info("KOTLIN: javaTask is missing for $variantDataName, so Kotlin files won't be compiled for it")
continue
}
val kotlinTaskName = "compile${variantDataName.capitalize()}Kotlin"
// todo: Investigate possibility of creating and configuring kotlinTask before evaluation
val kotlinTask = tasksProvider.createKotlinJVMTask(project, kotlinTaskName, variantData.name)
kotlinTask.parentKotlinOptionsImpl = rootKotlinOptions
// store kotlin classes in separate directory. They will serve as class-path to java compiler
kotlinTask.destinationDir = File(project.buildDir, "tmp/kotlin-classes/$variantDataName")
kotlinTask.description = "Compiles the $variantDataName kotlin."
kotlinTask.setDependsOn(javaTask.dependsOn)
val isKapt3Enabled = Kapt3GradleSubplugin.isEnabled(project)
val aptFiles = arrayListOf<File>()
if (!isKapt3Enabled) {
var hasAnyKaptDependency: Boolean = false
for (provider in variantData.sourceProviders) {
val aptConfiguration = aptConfigurations[(provider as AndroidSourceSet).name]
// Ignore if there's only an annotation processor wrapper in dependencies (added by default)
if (aptConfiguration != null && aptConfiguration.allDependencies.size > 1) {
javaTask.dependsOn(aptConfiguration.buildDependencies)
aptFiles.addAll(aptConfiguration.resolve())
hasAnyKaptDependency = true
}
}
if (!hasAnyKaptDependency) {
removeAnnotationProcessingPluginClasspathEntry(kotlinTask)
}
} else {
removeAnnotationProcessingPluginClasspathEntry(kotlinTask) removeAnnotationProcessingPluginClasspathEntry(kotlinTask)
} }
} else {
val appliedPlugins = subpluginEnvironment.addSubpluginOptions( removeAnnotationProcessingPluginClasspathEntry(kotlinTask)
project, kotlinTask, javaTask, variantData, null)
kotlinTask.mapClasspath {
javaTask.classpath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
}
var kotlinAfterJavaTask: KotlinCompile? = null
if (javaTask is JavaCompile && aptFiles.isNotEmpty() && !isKapt3Enabled) {
val (aptOutputDir, aptWorkingDir) = project.getAptDirsForSourceSet(variantDataName)
variantData.addJavaSourceFoldersToModel(aptOutputDir)
val kaptManager = AnnotationProcessingManager(kotlinTask, javaTask, variantDataName,
aptFiles.toSet(), aptOutputDir, aptWorkingDir, variantData)
kotlinAfterJavaTask = project.initKapt(kotlinTask, javaTask, kaptManager,
variantDataName, rootKotlinOptions, subpluginEnvironment, tasksProvider)
}
for (task in listOfNotNull(kotlinTask, kotlinAfterJavaTask)) {
configureSources(task, variantData)
}
if (isAndroidTestVariant) {
// Android Gradle plugin bypasses the Gradle finalizedBy for its tasks in some cases, and
// the Kotlin classes may not be copied for the tested variant. Make sure they are.
kotlinTask.dependsOn(syncOutputTaskName(testedVariantData!!.name))
}
appliedPlugins
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { configureSources(it, variantData) }
configureJavaTask(kotlinTask, javaTask, logger)
createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, variantDataName)
if ((kotlinAfterJavaTask ?: kotlinTask).incremental) {
val artifactFile = project.tryGetSingleArtifact(variantData)
val artifactDifferenceRegistryProvider = ArtifactDifferenceRegistryProviderAndroidWrapper(
kotlinGradleBuildServices.artifactDifferenceRegistryProvider,
{ AndroidGradleWrapper.getJarToAarMapping(variantData) }
)
configureMultiProjectIncrementalCompilation(project, kotlinTask, javaTask, kotlinAfterJavaTask,
artifactDifferenceRegistryProvider, artifactFile)
}
} }
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, variantData, null)
kotlinTask.mapClasspath {
javaTask.classpath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
}
var kotlinAfterJavaTask: KotlinCompile? = null
if (javaTask is JavaCompile && aptFiles.isNotEmpty() && !isKapt3Enabled) {
val (aptOutputDir, aptWorkingDir) = project.getAptDirsForSourceSet(variantDataName)
addJavaSourceDirectoryToVariantModel(variantData, aptOutputDir)
val kaptManager = AnnotationProcessingManager(kotlinTask, javaTask, variantDataName,
aptFiles.toSet(), aptOutputDir, aptWorkingDir, variantData)
kotlinAfterJavaTask = project.initKapt(kotlinTask, javaTask, kaptManager,
variantDataName, rootKotlinOptions, subpluginEnvironment, tasksProvider)
}
for (task in listOfNotNull(kotlinTask, kotlinAfterJavaTask)) {
configureSources(task, variantData)
}
if (isAndroidTestVariant) {
// Android Gradle plugin bypasses the Gradle finalizedBy for its tasks in some cases, and
// the Kotlin classes may not be copied for the tested variant. Make sure they are.
kotlinTask.dependsOn(syncOutputTaskName(getVariantName(testedVariantData!!)))
}
appliedPlugins.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { configureSources(it, variantData) }
//todo Wire tasks and classpaths with the new API
configureJavaTask(kotlinTask, javaTask, logger)
createSyncOutputTask(project, kotlinTask, javaTask, kotlinAfterJavaTask, variantDataName)
configureMultiProjectIc(project, variantData, javaTask, kotlinTask, kotlinAfterJavaTask)
} }
private fun configureSources(compileTask: AbstractCompile, private fun configureSources(compileTask: AbstractCompile, variantData: V) {
variantData: BaseVariantData<out BaseVariantOutputData>) {
val logger = compileTask.project.logger val logger = compileTask.project.logger
for (provider in variantData.sourceProviders) { for (provider in getSourceProviders(variantData)) {
val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue val kotlinSourceSet = provider.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet ?: continue
compileTask.source(kotlinSourceSet.kotlin) compileTask.source(kotlinSourceSet.kotlin)
} }
for (javaSrcDir in AndroidGradleWrapper.getJavaSources(variantData)) { for (javaSrcDir in getAllJavaSources(variantData)) {
compileTask.source(javaSrcDir) compileTask.source(javaSrcDir)
logger.kotlinDebug("Source directory $javaSrcDir was added to kotlin source for ${compileTask.name}") logger.kotlinDebug("Source directory $javaSrcDir was added to kotlin source for ${compileTask.name}")
} }
} }
private fun Project.tryGetSingleArtifact(variantData: BaseVariantData<*>): File? {
val log = logger
log.kotlinDebug { "Trying to determine single artifact for project $path" }
val outputs = variantData.outputs
if (outputs.size != 1) {
log.kotlinDebug { "Output count != 1 for variant: ${outputs.map { it.outputFile.relativeTo(rootDir).path }.joinToString() }" }
return null
}
return variantData.outputs.first().outputFile
}
private val BaseVariantData<*>.sourceProviders: List<SourceProvider>
get() = variantConfiguration.sortedSourceProviders
} }
private fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractCompile, logger: Logger) { private fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractCompile, logger: Logger) {
@@ -0,0 +1,82 @@
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.BasePlugin
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.build.gradle.internal.variant.TestVariantData
import com.android.builder.model.SourceProvider
import org.gradle.api.Project
import org.gradle.api.ProjectConfigurationException
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper
import java.io.File
internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools)
: AbstractAndroidProjectHandler<BaseVariantData<out BaseVariantOutputData>>(kotlinConfigurationTools) {
override fun getSourceProviders(variantData: BaseVariantData<out BaseVariantOutputData>): Iterable<SourceProvider> =
variantData.sourceProviders
override fun getAllJavaSources(variantData: BaseVariantData<out BaseVariantOutputData>): Iterable<File> =
AndroidGradleWrapper.getJavaSources(variantData)
override fun forEachVariant(project: Project, action: (BaseVariantData<out BaseVariantOutputData>) -> Unit) {
val plugin = (project.plugins.findPlugin("android")
?: project.plugins.findPlugin("android-library")
?: project.plugins.findPlugin("com.android.test")) as BasePlugin
val variantManager = AndroidGradleWrapper.getVariantDataManager(plugin)
variantManager.variantDataList.forEach(action)
}
override fun getVariantName(variant: BaseVariantData<out BaseVariantOutputData>): String = variant.name
override fun checkVariant(variant: BaseVariantData<out BaseVariantOutputData>): Unit {
if (AndroidGradleWrapper.isJackEnabled(variant)) {
throw ProjectConfigurationException(
"Kotlin Gradle plugin does not support the deprecated Jack toolchain.\n" +
"Disable Jack or revert to Kotlin Gradle plugin version 1.1.1.", null)
}
}
override fun getJavaTask(variantData: BaseVariantData<out BaseVariantOutputData>): AbstractCompile? =
AndroidGradleWrapper.getJavaTask(variantData)
override fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariantData<out BaseVariantOutputData>,
javaSourceDirectory: File) =
variantData.addJavaSourceFoldersToModel(javaSourceDirectory)
override fun configureMultiProjectIc(project: Project, variantData: BaseVariantData<out BaseVariantOutputData>, javaTask: AbstractCompile, kotlinTask: KotlinCompile, kotlinAfterJavaTask: KotlinCompile?) {
if ((kotlinAfterJavaTask ?: kotlinTask).incremental) {
val artifactFile = project.tryGetSingleArtifact(variantData)
val artifactDifferenceRegistryProvider = ArtifactDifferenceRegistryProviderAndroidWrapper(
artifactDifferenceRegistryProvider,
{ AndroidGradleWrapper.getJarToAarMapping(variantData) }
)
configureMultiProjectIncrementalCompilation(project, kotlinTask, javaTask, kotlinAfterJavaTask,
artifactDifferenceRegistryProvider, artifactFile)
}
}
override fun getTestedVariantData(variantData: BaseVariantData<*>): BaseVariantData<*>? =
((variantData as? TestVariantData)?.testedVariantData as? BaseVariantData<*>)
private fun Project.tryGetSingleArtifact(variantData: BaseVariantData<*>): File? {
val log = logger
log.kotlinDebug { "Trying to determine single artifact for project $path" }
val outputs = variantData.outputs
if (outputs.size != 1) {
log.kotlinDebug { "Output count != 1 for variant: ${outputs.map { it.outputFile.relativeTo(rootDir).path }.joinToString()}" }
return null
}
return variantData.outputs.first().outputFile
}
private val BaseVariantData<*>.sourceProviders: List<SourceProvider>
get() = variantConfiguration.sortedSourceProviders
}