diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle b/libraries/tools/kotlin-gradle-plugin/build.gradle index a21f0692e64..8f6b9c10a96 100644 --- a/libraries/tools/kotlin-gradle-plugin/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin/build.gradle @@ -19,6 +19,13 @@ repositories { jcenter() mavenLocal() 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 { @@ -37,6 +44,10 @@ dependencies { compileOnly 'org.codehaus.groovy:groovy-all:2.3.9' 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-test-junit:$kotlin_version" } @@ -54,12 +65,21 @@ sourceSets.main.compileClasspath += files(groovyClassesDir) compileGroovy.classpath = sourceSets.main.compileClasspath - files(groovyClassesDir) sourceSets.main.java.srcDirs += sourceSets.main.kotlin.srcDirs +sourceSets { + agp25 { + compileClasspath += configurations.compile + configurations.agp25CompileOnly + sourceSets.main.output + } +} + processResources { expand(project.properties) } +jar.dependsOn agp25Classes + jar { from compileGroovy.destinationDir + from sourceSets.agp25.output.classesDir } test.executable = "${JDK_18}/bin/java" diff --git a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt new file mode 100644 index 00000000000..d17cc24164a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt @@ -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(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 = + variantData.sourceSets + + override fun getAllJavaSources(variantData: BaseVariant): Iterable = + 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 + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 271db819364..8597099d0fe 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -4,14 +4,12 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.BasePlugin import com.android.build.gradle.api.AndroidSourceSet 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 groovy.lang.Closure +import org.apache.tools.ant.util.ReflectUtil.newInstance import org.gradle.api.InvalidUserDataException import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.ProjectConfigurationException import org.gradle.api.artifacts.Configuration import org.gradle.api.logging.Logger 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.utils.ParsedGradleVersion import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation -import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistryProviderAndroidWrapper import java.io.File import java.net.URL import java.util.* @@ -339,11 +336,7 @@ internal open class KotlinAndroidPlugin( private val kotlinGradleBuildServices: KotlinGradleBuildServices ) : Plugin { - private val log = Logging.getLogger(this.javaClass) - override fun apply(project: Project) { - val ext = project.extensions.getByName("android") as BaseExtension - val version = loadAndroidPluginVersion() if (version != null) { 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(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 + protected abstract fun getAllJavaSources(variantData: V): Iterable + 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() ext.sourceSets.all { sourceSet -> - log.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet") - val kotlinSourceSet = kotlinSourceSetProvider.create(sourceSet.name) + logger.kotlinDebug("Creating KotlinSourceSet for source set $sourceSet") + val kotlinSourceSet = kotlinConfigurationTools.kotlinSourceSetProvider.create(sourceSet.name) kotlinSourceSet.kotlin.srcDir(project.file(project.file("src/${sourceSet.name}/kotlin"))) 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() @@ -372,164 +430,129 @@ internal open class KotlinAndroidPlugin( project.afterEvaluate { project -> if (project != null) { val plugin = (project.plugins.findPlugin("android") - ?: project.plugins.findPlugin("android-library") - ?: project.plugins.findPlugin("com.android.test")) as BasePlugin + ?: project.plugins.findPlugin("android-library") + ?: project.plugins.findPlugin("com.android.test")) as BasePlugin + val subpluginEnvironment = loadSubplugins(project) - val variantManager = AndroidGradleWrapper.getVariantDataManager(plugin) - processVariantData(variantManager.variantDataList, project, - ext, plugin, aptConfigurations, kotlinOptions) + forEachVariant(project) { + processVariant(it, project, ext, plugin, aptConfigurations, kotlinOptions, + kotlinConfigurationTools.kotlinTasksProvider, subpluginEnvironment) + } } } } - private fun getTestedVariantData(variantData: BaseVariantData<*>): BaseVariantData<*>? = - ((variantData as? TestVariantData)?.testedVariantData as? BaseVariantData<*>) + private fun processVariant(variantData: V, + project: Project, + androidExt: BaseExtension, + androidPlugin: BasePlugin, + aptConfigurations: Map, + rootKotlinOptions: KotlinJvmOptionsImpl, + tasksProvider: KotlinTasksProvider, subpluginEnvironment: SubpluginEnvironment) { - private fun processVariantData( - variantDataList: List>, - project: Project, - androidExt: BaseExtension, - androidPlugin: BasePlugin, - aptConfigurations: Map, - rootKotlinOptions: KotlinJvmOptionsImpl - ) { - val logger = project.logger - val subpluginEnvironment = loadSubplugins(project) + checkVariant(variantData) - for (variantData in variantDataList) { - if (AndroidGradleWrapper.isJackEnabled(variantData)) { - 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) + val variantDataName = getVariantName(variantData) + logger.kotlinDebug("Process variant [$variantDataName]") + + val testedVariantData = getTestedVariantData(variantData) + 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() + + 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 - 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() - - 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 { + if (!hasAnyKaptDependency) { removeAnnotationProcessingPluginClasspathEntry(kotlinTask) } - - 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) - - 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) - } + } else { + removeAnnotationProcessingPluginClasspathEntry(kotlinTask) } + + 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, - variantData: BaseVariantData) { + private fun configureSources(compileTask: AbstractCompile, variantData: V) { 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 compileTask.source(kotlinSourceSet.kotlin) } - for (javaSrcDir in AndroidGradleWrapper.getJavaSources(variantData)) { + for (javaSrcDir in getAllJavaSources(variantData)) { compileTask.source(javaSrcDir) 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 - get() = variantConfiguration.sortedSourceProviders } private fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractCompile, logger: Logger) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt new file mode 100644 index 00000000000..ed2278aa1d4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/LegacyAndroidAndroidProjectHandler.kt @@ -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>(kotlinConfigurationTools) { + + override fun getSourceProviders(variantData: BaseVariantData): Iterable = + variantData.sourceProviders + + override fun getAllJavaSources(variantData: BaseVariantData): Iterable = + AndroidGradleWrapper.getJavaSources(variantData) + + override fun forEachVariant(project: Project, action: (BaseVariantData) -> 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): String = variant.name + + override fun checkVariant(variant: BaseVariantData): 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): AbstractCompile? = + AndroidGradleWrapper.getJavaTask(variantData) + + + override fun addJavaSourceDirectoryToVariantModel(variantData: BaseVariantData, + javaSourceDirectory: File) = + variantData.addJavaSourceFoldersToModel(javaSourceDirectory) + + override fun configureMultiProjectIc(project: Project, variantData: BaseVariantData, 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 + get() = variantConfiguration.sortedSourceProviders +} \ No newline at end of file