From 4dbc6803ba6e623ba6e874cb62d04ca0740efe59 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Sat, 6 Jun 2020 00:23:19 +0300 Subject: [PATCH] Refactor the kapt Gradle plugin to use the new plugins API and TCA --- .../kapt/Kapt3KotlinGradleSubplugin.kt | 497 ++++++++++-------- .../internal/kapt/KaptWithoutKotlincTask.kt | 4 +- .../kotlin/gradle/internal/subpluginUtils.kt | 2 +- .../kotlin/gradle/plugin/KotlinPlugin.kt | 7 +- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 2 + .../gradle/tasks/kotlinCompileTaskData.kt | 5 +- ...kotlin.gradle.plugin.KotlinGradleSubplugin | 1 - 7 files changed, 292 insertions(+), 226 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index df7994110aa..7746bfcefb2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -7,44 +7,56 @@ package org.jetbrains.kotlin.gradle.internal import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.AndroidSourceSet -import com.android.builder.model.SourceProvider +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.api.SourceKind import com.intellij.openapi.util.SystemInfo import org.gradle.api.GradleException -import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.Usage -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.TaskDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile import org.gradle.process.CommandLineArgumentProvider import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry -import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir -import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedKotlinSourcesDir -import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedSourcesDir -import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureArtifactTransform import org.jetbrains.kotlin.gradle.model.builder.KaptModelBuilder import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.locateTask +import org.jetbrains.kotlin.gradle.tasks.registerTask import java.io.ByteArrayOutputStream import java.io.File import java.io.ObjectOutputStream import java.util.* +import java.util.concurrent.Callable import javax.inject.Inject // apply plugin: 'kotlin-kapt' -class Kapt3GradleSubplugin @Inject internal constructor(private val registry: ToolingModelBuilderRegistry) : Plugin { - companion object { - fun isEnabled(project: Project) = project.plugins.findPlugin(Kapt3GradleSubplugin::class.java) != null +class Kapt3GradleSubplugin @Inject internal constructor(private val registry: ToolingModelBuilderRegistry) : + KotlinCompilerPluginSupportPlugin { + override fun apply(project: Project) { + project.extensions.create("kapt", KaptExtension::class.java) + + project.configurations.create(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { + project.getKotlinPluginVersion()?.let { kotlinPluginVersion -> + val kaptDependency = getPluginArtifact().run { "$groupId:$artifactId:$kotlinPluginVersion" } + dependencies.add(project.dependencies.create(kaptDependency)) + } ?: throw GradleException("Kotlin plugin should be enabled before 'kotlin-kapt'") + } + + registry.register(KaptModelBuilder()) + } + + companion object { @JvmStatic fun getKaptGeneratedClassesDir(project: Project, sourceSetName: String) = File(project.buildDir, "tmp/kapt3/classes/$sourceSetName") @@ -56,41 +68,7 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To @JvmStatic fun getKaptGeneratedKotlinSourcesDir(project: Project, sourceSetName: String) = File(project.buildDir, "generated/source/kaptKotlin/$sourceSetName") - } - override fun apply(project: Project) { - project.extensions.create("kapt", KaptExtension::class.java) - - Kapt3KotlinGradleSubplugin().run { - project.configurations.create(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { - project.getKotlinPluginVersion()?.let { kotlinPluginVersion -> - val kaptDependency = getPluginArtifact().run { "$groupId:$artifactId:$kotlinPluginVersion" } - dependencies.add(project.dependencies.create(kaptDependency)) - } ?: throw GradleException("Kotlin plugin should be enabled before 'kotlin-kapt'") - } - } - registry.register(KaptModelBuilder()) - } -} - -abstract class KaptVariantData(val variantData: T) { - abstract val name: String - abstract val sourceProviders: Iterable - abstract fun addJavaSourceFoldersToModel(generatedFilesDir: File) - abstract val annotationProcessorOptions: Map? - abstract fun registerGeneratedJavaSource( - project: Project, - kaptTask: KaptTask, - javaTask: AbstractCompile - ) - - open val annotationProcessorOptionProviders: List<*> - get() = emptyList() -} - -// Subplugin for the Kotlin Gradle plugin -class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { - companion object { private val VERBOSE_OPTION_NAME = "kapt.verbose" private val USE_WORKER_API = "kapt.use.worker.api" private val INFO_AS_WARNINGS = "kapt.info.as.warnings" @@ -139,7 +117,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(SourceSet.MAIN_SOURCE_SET_NAME) fun createAptConfigurationIfNeeded(project: Project, sourceSetName: String): Configuration { - val configurationName = Kapt3KotlinGradleSubplugin.getKaptConfigurationName(sourceSetName) + val configurationName = getKaptConfigurationName(sourceSetName) project.configurations.findByName(configurationName)?.let { return it } val aptConfiguration = project.configurations.create(configurationName).apply { @@ -148,7 +126,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) } - if (aptConfiguration.name != Kapt3KotlinGradleSubplugin.MAIN_KAPT_CONFIGURATION_NAME) { + if (aptConfiguration.name != MAIN_KAPT_CONFIGURATION_NAME) { // The main configuration can be created after the current one. We should handle this case val mainConfiguration = findMainKaptConfiguration(project) ?: createAptConfigurationIfNeeded(project, SourceSet.MAIN_SOURCE_SET_NAME) @@ -158,11 +136,13 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { return aptConfiguration } + + fun isEnabled(project: Project) = + project.plugins.any { it is Kapt3GradleSubplugin } } - private val kotlinToKaptGenerateStubsTasksMap = mutableMapOf() - - override fun isApplicable(project: Project, task: AbstractCompile) = task is KotlinCompile && Kapt3GradleSubplugin.isEnabled(project) + override fun isApplicable(kotlinCompilation: KotlinCompilation<*>) = + (kotlinCompilation.platformType == KotlinPlatformType.jvm || kotlinCompilation.platformType == KotlinPlatformType.androidJvm) private fun Kapt3SubpluginContext.getKaptStubsDir() = temporaryKaptDirectory("stubs") @@ -178,13 +158,12 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { return dir } - private inner class Kapt3SubpluginContext( + internal inner class Kapt3SubpluginContext( val project: Project, - val kotlinCompile: KotlinCompile, - val javaCompile: AbstractCompile?, - val kaptVariantData: KaptVariantData<*>?, + val javaCompile: TaskProvider?, + val variantData: Any?, val sourceSetName: String, - val kotlinCompilation: KotlinCompilation<*>?, + val kotlinCompilation: KotlinCompilation<*>, val kaptExtension: KaptExtension, val kaptClasspathConfigurations: List ) { @@ -193,19 +172,18 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val classesOutputDir = getKaptGeneratedClassesDir(project, sourceSetName) val includeCompileClasspath = kaptExtension.includeCompileClasspath - ?: Kapt3KotlinGradleSubplugin.includeCompileClasspath(project) + ?: includeCompileClasspath(project) ?: true + + val kotlinCompile: TaskProvider + // Can't use just kotlinCompilation.compileKotlinTaskProvider, as the latter is not statically-known to be KotlinCompile + get() = checkNotNull(project.locateTask(kotlinCompilation.compileKotlinTaskName)) } - override fun apply( - project: Project, - kotlinCompile: KotlinCompile, - javaCompile: AbstractCompile?, - variantData: Any?, - androidProjectHandler: Any?, - kotlinCompilation: KotlinCompilation<*>? - ): List { - assert((variantData != null) xor (kotlinCompilation != null)) + override fun applyToCompilation( + kotlinCompilation: KotlinCompilation<*> + ): Provider> { + val project = kotlinCompilation.target.project val buildDependencies = arrayListOf() val kaptConfigurations = arrayListOf() @@ -217,17 +195,14 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } } - val kaptVariantData = variantData as? KaptVariantData<*> + val androidVariantData: BaseVariant? = (kotlinCompilation as? KotlinJvmAndroidCompilation)?.androidVariant - val sourceSetName = if (kaptVariantData != null) { - for (provider in kaptVariantData.sourceProviders) { + val sourceSetName = if (androidVariantData != null) { + for (provider in androidVariantData.sourceSets) { handleSourceSet((provider as AndroidSourceSet).name) } - - kaptVariantData.name + androidVariantData.name } else { - if (kotlinCompilation == null) error("In non-Android projects, Kotlin compilation should not be null") - handleSourceSet(kotlinCompilation.compilationName) kotlinCompilation.compilationName } @@ -236,75 +211,103 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val nonEmptyKaptConfigurations = kaptConfigurations.filter { it.dependencies.isNotEmpty() } + val javaCompileOrNull = findJavaTaskForKotlinCompilation(kotlinCompilation) + val context = Kapt3SubpluginContext( - project, kotlinCompile, javaCompile, - kaptVariantData, sourceSetName, kotlinCompilation, kaptExtension, nonEmptyKaptConfigurations + project, javaCompileOrNull, + androidVariantData, sourceSetName, kotlinCompilation, kaptExtension, nonEmptyKaptConfigurations ) - val kaptGenerateStubsTask = context.createKaptGenerateStubsTask() - val kaptTask = context.createKaptKotlinTask(useWorkerApi = project.isUseWorkerApi()) + val kaptGenerateStubsTaskProvider: TaskProvider = context.createKaptGenerateStubsTask() + val kaptTaskProvider: TaskProvider = context.createKaptKotlinTask(useWorkerApi = project.isUseWorkerApi()) - kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray()) + kaptGenerateStubsTaskProvider.configure { kaptGenerateStubsTask -> + kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray()) - kaptGenerateStubsTask.dependsOn(*buildDependencies.toTypedArray()) - kaptGenerateStubsTask.dependsOn(*kotlinCompile.dependsOn.toTypedArray()) + kaptGenerateStubsTask.dependsOn(*buildDependencies.toTypedArray()) + kaptGenerateStubsTask.dependsOn( + project.provider { + kotlinCompilation.compileKotlinTask.dependsOn.filter { it !is TaskProvider<*> || it.name != kaptTaskProvider.name } + } + ) - kaptTask.dependsOn(kaptGenerateStubsTask) - kotlinCompile.dependsOn(kaptTask) + if (androidVariantData != null) { + kaptGenerateStubsTask.inputs.files( + Callable { + // Avoid circular dependency: the stubs task need the Java sources, but the Java sources generated by Kapt should be + // excluded, as the Kapt tasks depend on the stubs ones, and having them in the input would lead to a cycle + val kaptJavaOutput = kaptTaskProvider.get().destinationDir + androidVariantData.getSourceFolders(SourceKind.JAVA).filter { it.dir != kaptJavaOutput } + } + ).withPathSensitivity(PathSensitivity.RELATIVE) + } + } + + kaptTaskProvider.configure { kaptTask -> + kaptTask.dependsOn(kaptGenerateStubsTaskProvider) + } + context.kotlinCompile.configure { it.dependsOn(kaptTaskProvider) } /** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */ - return emptyList() - } - - override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List { - val kaptGenerateStubsTask = kotlinToKaptGenerateStubsTasksMap[kotlinCompile] - return if (kaptGenerateStubsTask == null) emptyList() else listOf(kaptGenerateStubsTask) + return project.provider { emptyList() } } // This method should be called no more than once for each Kapt3SubpluginContext - private fun Kapt3SubpluginContext.buildOptions(aptMode: String, javacOptions: Map): List { - val pluginOptions = mutableListOf() - - val generatedFilesDir = getKaptGeneratedSourcesDir(project, sourceSetName) - kaptVariantData?.addJavaSourceFoldersToModel(generatedFilesDir) - - pluginOptions += SubpluginOption("aptMode", aptMode) + private fun Kapt3SubpluginContext.buildOptions( + aptMode: String, + javacOptions: Provider> + ): Provider> { disableAnnotationProcessingInJavaTask() - pluginOptions += FilesSubpluginOption("sources", listOf(generatedFilesDir)) - pluginOptions += FilesSubpluginOption("classes", listOf(getKaptGeneratedClassesDir(project, sourceSetName))) + return project.provider { + val pluginOptions = mutableListOf() - pluginOptions += FilesSubpluginOption("incrementalData", listOf(getKaptIncrementalDataDir())) + val generatedFilesDir = getKaptGeneratedSourcesDir(project, sourceSetName) + KaptWithAndroid.androidVariantData(this)?.addJavaSourceFoldersToModel(generatedFilesDir) - val annotationProcessors = kaptExtension.processors - if (annotationProcessors.isNotEmpty()) { - pluginOptions += SubpluginOption("processors", annotationProcessors) + pluginOptions += SubpluginOption("aptMode", aptMode) + + pluginOptions += FilesSubpluginOption("sources", listOf(generatedFilesDir)) + pluginOptions += FilesSubpluginOption("classes", listOf(getKaptGeneratedClassesDir(project, sourceSetName))) + + pluginOptions += FilesSubpluginOption("incrementalData", listOf(getKaptIncrementalDataDir())) + + val annotationProcessors = kaptExtension.processors + if (annotationProcessors.isNotEmpty()) { + pluginOptions += SubpluginOption("processors", annotationProcessors) + } + + kotlinSourcesOutputDir.mkdirs() + + val apOptions = getAPOptions().get() + + pluginOptions += CompositeSubpluginOption( + "apoptions", + lazy { encodeList(apOptions.associate { it.key to it.value }) }, + apOptions + ) + + pluginOptions += SubpluginOption("javacArguments", encodeList(javacOptions.get())) + + pluginOptions += SubpluginOption("includeCompileClasspath", includeCompileClasspath.toString()) + + addMiscOptions(pluginOptions) + + pluginOptions } - - kotlinSourcesOutputDir.mkdirs() - - val apOptions = getAPOptions() - - pluginOptions += CompositeSubpluginOption("apoptions", lazy { encodeList(apOptions.associate { it.key to it.value }) }, apOptions) - - pluginOptions += SubpluginOption("javacArguments", encodeList(javacOptions)) - - pluginOptions += SubpluginOption("includeCompileClasspath", includeCompileClasspath.toString()) - - addMiscOptions(pluginOptions) - - return pluginOptions } - private fun Kapt3SubpluginContext.getAPOptions(): List { - val androidPlugin = kaptVariantData?.let { + private fun Kapt3SubpluginContext.getAPOptions(): Provider> = project.provider { + val androidVariantData = KaptWithAndroid.androidVariantData(this) + + val androidPlugin = androidVariantData?.let { project.extensions.findByName("android") as? BaseExtension } - val androidOptions = kaptVariantData?.annotationProcessorOptions ?: emptyMap() + val androidOptions = androidVariantData?.annotationProcessorOptions ?: emptyMap() val apOptionsFromProviders = - kaptVariantData?.annotationProcessorOptionProviders + androidVariantData?.annotationProcessorOptionProviders ?.flatMap { (it as CommandLineArgumentProvider).asArguments() } .orEmpty() @@ -317,30 +320,44 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } val apOptionsPairsList: List> = - kaptExtension.getAdditionalArguments(project, kaptVariantData?.variantData, androidPlugin).toList() + + kaptExtension.getAdditionalArguments(project, androidVariantData, androidPlugin).toList() + androidOptions.toList() - return apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + + apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir)) + subluginOptionsFromProvidedApOptions } private fun Kapt3SubpluginContext.registerSubpluginOptions( - task: Task, - container: CompilerPluginOptions, - options: List + taskProvider: TaskProvider<*>, + optionsProvider: Provider> ) { - val compilerPluginId = getCompilerPluginId() - task.registerSubpluginOptionsAsInputs(compilerPluginId, options) + taskProvider.configure { taskInstance -> + val container = when (taskInstance) { + is KaptGenerateStubsTask -> taskInstance.pluginOptions + is KaptWithKotlincTask -> taskInstance.pluginOptions + is KaptWithoutKotlincTask -> taskInstance.processorOptions + else -> error("Unexpected task ${taskInstance.name} (${taskInstance.javaClass})") + } - for (option in options) { - container.addPluginArgument(compilerPluginId, option) + + val compilerPluginId = getCompilerPluginId() + + val options = optionsProvider.get() + + taskInstance.registerSubpluginOptionsAsInputs(compilerPluginId, options) + + for (option in options) { + container.addPluginArgument(compilerPluginId, option) + } } // Also register all the subplugin options from the Kotlin task: - project.afterEvaluate { - kotlinCompile.pluginOptions.subpluginOptionsByPluginId.forEach { (pluginId, options) -> - task.registerSubpluginOptionsAsInputs("kotlinCompile.$pluginId", options) + project.whenEvaluated { + taskProvider.configure { taskInstance -> + kotlinCompile.get().pluginOptions.subpluginOptionsByPluginId.forEach { (pluginId, options) -> + taskInstance.registerSubpluginOptionsAsInputs("kotlinCompile.$pluginId", options) + } } } } @@ -379,95 +396,111 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } } - private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): KaptTask { + private fun Kapt3SubpluginContext.createKaptKotlinTask(useWorkerApi: Boolean): TaskProvider { val taskClass = if (useWorkerApi) KaptWithoutKotlincTask::class.java else KaptWithKotlincTask::class.java val taskName = getKaptTaskName("kapt") - val kaptTask = project.tasks.create(taskName, taskClass) - kaptTask.useBuildCache = kaptExtension.useBuildCache - - kaptTask.kotlinCompileTask = kotlinCompile - - kaptTask.stubsDir = getKaptStubsDir() - kaptTask.destinationDir = sourcesOutputDir - kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir - kaptTask.classesDir = classesOutputDir - kaptTask.includeCompileClasspath = includeCompileClasspath - - kaptTask.isIncremental = project.isIncrementalKapt() - if (kaptTask.isIncremental) { - kaptTask.incAptCache = getKaptIncrementalAnnotationProcessingCache() - kaptTask.localState.register(kaptTask.incAptCache) + var classStructureIfIncremental: Configuration? = null + if (project.isIncrementalKapt()) { maybeRegisterTransform(project) - val classStructure = project.configurations.create("_classStructure${taskName}") + + classStructureIfIncremental = project.configurations.create("_classStructure${taskName}") // Wrap the `kotlinCompile.classpath` into a file collection, so that, if the classpath is represented by a configuration, // the configuration is not extended (via extendsFrom, which normally happens when one configuration is _added_ into another) // but is instead included as the (lazily) resolved files. This is needed because the class structure configuration doesn't have // the attributes that are potentially needed to resolve dependencies on MPP modules, and the classpath configuration does. - project.dependencies.add(classStructure.name, project.files(project.provider { kotlinCompile.classpath })) - - kaptTask.classpathStructure = classStructure.incoming.artifactView { viewConfig -> - viewConfig.attributes.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE) - }.files + project.dependencies.add(classStructureIfIncremental.name, project.files(project.provider { kotlinCompile.get().classpath })) } - kotlinCompilation?.run { - output.apply { - addClassesDir { project.files(classesOutputDir).builtBy(kaptTask) } + val kaptTaskProvider = project.registerTask(taskName, taskClass, emptyList()) { kaptTask -> + kaptTask.useBuildCache = kaptExtension.useBuildCache + + kaptTask.kotlinCompileTask = kotlinCompilation.compileKotlinTaskProvider.get() as KotlinCompile + + kaptTask.stubsDir = getKaptStubsDir() + + kaptTask.destinationDir = sourcesOutputDir + kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir + kaptTask.classesDir = classesOutputDir + kaptTask.includeCompileClasspath = includeCompileClasspath + + kaptTask.isIncremental = project.isIncrementalKapt() + + if (kaptTask.isIncremental) { + kaptTask.incAptCache = getKaptIncrementalAnnotationProcessingCache() + kaptTask.localState.register(kaptTask.incAptCache) + + kaptTask.classpathStructure = classStructureIfIncremental!!.incoming.artifactView { viewConfig -> + viewConfig.attributes.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE) + }.files + + if (kaptTask is KaptWithKotlincTask) { + kaptTask.pluginOptions.addPluginArgument( + getCompilerPluginId(), + SubpluginOption("incrementalCache", kaptTask.incAptCache!!.absolutePath) + ) + } + } + + kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations + + KaptWithAndroid.androidVariantData(this)?.annotationProcessorOptionProviders?.let { + kaptTask.annotationProcessorOptionProviders.add(it) } } - kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir) + kotlinCompilation.output.apply { + addClassesDir { project.files(classesOutputDir).builtBy(kaptTaskProvider) } + } + + kotlinCompilation.compileKotlinTaskProvider.configure { + it as SourceTask + it.source(sourcesOutputDir, kotlinSourcesOutputDir) + } if (javaCompile != null) { - if (kaptVariantData != null) { - kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile) + val androidVariantData = KaptWithAndroid.androidVariantData(this) + if (androidVariantData != null) { + KaptWithAndroid.registerGeneratedJavaSourceForAndroid(this, project, androidVariantData, kaptTaskProvider) } else { - registerGeneratedJavaSource(kaptTask, javaCompile) + registerGeneratedJavaSource(kaptTaskProvider, javaCompile) } } - kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations - - kaptVariantData?.annotationProcessorOptionProviders?.let { - kaptTask.annotationProcessorOptionProviders.add(it) + val dslJavacOptions: Provider> = project.provider { + kaptExtension.getJavacOptions().toMutableMap().also { result -> + if (javaCompile != null && "-source" !in result && "--source" !in result && "--release" !in result) { + val sourceOptionKey = if (SystemInfo.isJavaVersionAtLeast(12, 0, 0)) { + "--source" + } else { + "-source" + } + result[sourceOptionKey] = javaCompile.get().sourceCompatibility + } + } } - val dslJavacOptions = kaptExtension.getJavacOptions().toMutableMap() - if (javaCompile != null && "-source" !in dslJavacOptions && "--source" !in dslJavacOptions && "--release" !in dslJavacOptions) { - val sourceOptionKey = if (SystemInfo.isJavaVersionAtLeast(12, 0, 0)) { - "--source" - } else { - "-source" - } - dslJavacOptions[sourceOptionKey] = javaCompile.sourceCompatibility - } - if (kaptTask is KaptWithKotlincTask) { - if (kaptTask.isIncremental) { - kaptTask.pluginOptions.addPluginArgument( - getCompilerPluginId(), - SubpluginOption("incrementalCache", kaptTask.incAptCache!!.absolutePath)) - } - + if (taskClass == KaptWithKotlincTask::class.java) { val subpluginOptions = buildOptions("apt", dslJavacOptions) - registerSubpluginOptions(kaptTask, kaptTask.pluginOptions, subpluginOptions) + registerSubpluginOptions(kaptTaskProvider, subpluginOptions) } - if (kaptTask is KaptWithoutKotlincTask) { - with(kaptTask) { - isVerbose = project.isKaptVerbose() - mapDiagnosticLocations = kaptExtension.mapDiagnosticLocations - annotationProcessorFqNames = kaptExtension.processors.split(',').filter { it.isNotEmpty() } - javacOptions = dslJavacOptions + if (taskClass == KaptWithoutKotlincTask::class.java) { + kaptTaskProvider.configure { + it as KaptWithoutKotlincTask + it.isVerbose = project.isKaptVerbose() + it.mapDiagnosticLocations = kaptExtension.mapDiagnosticLocations + it.annotationProcessorFqNames = kaptExtension.processors.split(',').filter { it.isNotEmpty() } + it.javacOptions = dslJavacOptions.get() } val subpluginOptions = getAPOptions() - registerSubpluginOptions(kaptTask, kaptTask.processorOptions, subpluginOptions) + registerSubpluginOptions(kaptTaskProvider, subpluginOptions) } - return kaptTask + return kaptTaskProvider } private fun maybeRegisterTransform(project: Project) { @@ -487,33 +520,35 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } } - private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): KaptGenerateStubsTask { + private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): TaskProvider { val kaptTaskName = getKaptTaskName("kaptGenerateStubs") - KotlinCompileTaskData.register(kaptTaskName, KotlinCompileTaskData.get(project, kotlinCompile.name).compilation).apply { + KotlinCompileTaskData.register(kaptTaskName, kotlinCompilation).apply { useModuleDetection.set(KotlinCompileTaskData.get(project, kotlinCompile.name).useModuleDetection) + destinationDir.set(project.provider { getKaptIncrementalDataDir() }) } - val kaptTask = project.tasks.create( - kaptTaskName, - KaptGenerateStubsTask::class.java - ) + val kaptTaskProvider = project.registerTask(kaptTaskName) { kaptTask -> + kaptTask.kotlinCompileTask = kotlinCompile.get() - kaptTask.kotlinCompileTask = kotlinCompile - kotlinToKaptGenerateStubsTasksMap[kotlinCompile] = kaptTask + kaptTask.stubsDir = getKaptStubsDir() + kaptTask.setDestinationDir { getKaptIncrementalDataDir() } + kaptTask.mapClasspath { kaptTask.kotlinCompileTask.classpath } + kaptTask.generatedSourcesDir = sourcesOutputDir - kaptTask.stubsDir = getKaptStubsDir() - kaptTask.setDestinationDir { getKaptIncrementalDataDir() } - kaptTask.mapClasspath { kotlinCompile.classpath } - kaptTask.generatedSourcesDir = sourcesOutputDir - PropertiesProvider(project).mapKotlinTaskProperties(kaptTask) + kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations - kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations + PropertiesProvider(project).mapKotlinTaskProperties(kaptTask) + } - val subpluginOptions = buildOptions("stubs", kaptExtension.getJavacOptions()) - registerSubpluginOptions(kaptTask, kaptTask.pluginOptions, subpluginOptions) + project.whenEvaluated { + addCompilationSourcesToExternalCompileTask(kotlinCompilation, kaptTaskProvider) + } - return kaptTask + val subpluginOptions = buildOptions("stubs", project.provider { kaptExtension.getJavacOptions() }) + registerSubpluginOptions(kaptTaskProvider, subpluginOptions) + + return kaptTaskProvider } private fun Kapt3SubpluginContext.getKaptTaskName(prefix: String): String { @@ -524,8 +559,11 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() { - (javaCompile as? JavaCompile)?.let { javaCompile -> - val options = javaCompile.options + javaCompile?.configure { javaCompileInstance -> + if (javaCompileInstance !is JavaCompile) + return@configure + + val options = javaCompileInstance.options // 'android-apt' (com.neenbedankt) adds a File instance to compilerArgs (List). // Although it's not our problem, we need to handle this case properly. val oldCompilerArgs: List = options.compilerArgs @@ -539,8 +577,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { // Filter out the argument providers that are related to annotation processing and therefore already used by Kapt. // This is done to avoid outputs intersections between Kapt and and javaCompile and make the up-to-date check for // javaCompile more granular as it does not perform annotation processing: - if (kaptVariantData != null) { - options.compilerArgumentProviders.removeAll(kaptVariantData.annotationProcessorOptionProviders) + KaptWithAndroid.androidVariantData(this)?.let { androidVariantData -> + options.compilerArgumentProviders.removeAll(androidVariantData.annotationProcessorOptionProviders) } } } @@ -552,11 +590,38 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { } private val artifactType = Attribute.of("artifactType", String::class.java) +// Don't reference the BaseVariant type in the Kapt plugin signatures, as those type references will fail to link when there's no Android +// Gradle plugin on the project's plugin classpath +private object KaptWithAndroid { + // Avoid loading the BaseVariant type at call sites and instead lazily load it when evaluation reaches it in the body using inline: + @Suppress("NOTHING_TO_INLINE") + inline fun androidVariantData(context: Kapt3GradleSubplugin.Kapt3SubpluginContext): BaseVariant? = context.variantData as? BaseVariant -internal fun registerGeneratedJavaSource(kaptTask: KaptTask, javaTask: AbstractCompile) { - val generatedJavaSources = javaTask.project.fileTree(kaptTask.destinationDir) - generatedJavaSources.include("**/*.java") - javaTask.source(generatedJavaSources) + @Suppress("NOTHING_TO_INLINE") + // Avoid loading the BaseVariant type at call sites and instead lazily load it when evaluation reaches it in the body using inline: + inline fun registerGeneratedJavaSourceForAndroid( + kapt3SubpluginContext: Kapt3GradleSubplugin.Kapt3SubpluginContext, + project: Project, + variantData: BaseVariant, + kaptTask: TaskProvider + ) { + val kaptSourceOutput = project.fileTree(kapt3SubpluginContext.sourcesOutputDir).builtBy(kaptTask) + kaptSourceOutput.include("**/*.java") + variantData.registerExternalAptJavaOutput(kaptSourceOutput) + kaptTask.configure { kaptTaskInstance -> + variantData.dataBindingDependencyArtifactsIfSupported?.let { dataBindingArtifacts -> + kaptTaskInstance.dependsOn(dataBindingArtifacts) + } + } + } +} + +internal fun registerGeneratedJavaSource(kaptTask: TaskProvider, javaTaskProvider: TaskProvider) { + javaTaskProvider.configure { javaTask -> + val generatedJavaSources = javaTask.project.fileTree(kaptTask.map { it.destinationDir }) + generatedJavaSources.include("**/*.java") + javaTask.source(generatedJavaSources) + } } internal fun Configuration.getNamedDependencies(): List = allDependencies.filter { it.group != null && it.name != null } @@ -583,7 +648,7 @@ internal fun checkAndroidAnnotationProcessorDependencyUsage(project: Project) { ANNOTATION_PROCESSOR -> "kapt" else -> { val configurationName = apConfigurationName.dropLast(ANNOTATION_PROCESSOR_CAP.length) - Kapt3KotlinGradleSubplugin.getKaptConfigurationName(configurationName) + Kapt3GradleSubplugin.getKaptConfigurationName(configurationName) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt index ec7e293d75e..d3eaa0c275b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithoutKotlincTask.kt @@ -9,7 +9,7 @@ import org.gradle.api.tasks.* import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.gradle.workers.IsolationMode import org.gradle.workers.WorkerExecutor -import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges import org.jetbrains.kotlin.gradle.plugin.CompositeSubpluginOption import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper @@ -49,7 +49,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor lateinit var javacOptions: Map private fun getAnnotationProcessorOptions(): Map { - val options = processorOptions.subpluginOptionsByPluginId[Kapt3KotlinGradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap() + val options = processorOptions.subpluginOptionsByPluginId[Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap() val result = mutableMapOf() for (option in options) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt index 4cfb9850246..57668f8be11 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt @@ -53,7 +53,7 @@ internal fun CompilerPluginOptions.withWrappedKaptOptions( val resultOptionsByPluginId: MutableMap> = subpluginOptionsByPluginId.toMutableMap() - resultOptionsByPluginId.compute(Kapt3KotlinGradleSubplugin.KAPT_SUBPLUGIN_ID) { _, kaptOptions -> + resultOptionsByPluginId.compute(Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID) { _, kaptOptions -> val changedFilesOption = changedFiles.map { SubpluginOption("changedFile", it.canonicalPath) } val classpathChangesOption = classpathChanges.map { SubpluginOption("classpathChange", it) } val processIncrementallyOption = SubpluginOption("processIncrementally", processIncrementally.toString()) 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 4f372d6114d..7e1fabf412c 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 @@ -33,8 +33,7 @@ import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.jvm.tasks.Jar import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.jetbrains.kotlin.gradle.dsl.* -import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin -import org.jetbrains.kotlin.gradle.internal.KaptVariantData +import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.model.builder.KotlinModelBuilder @@ -202,7 +201,7 @@ internal class Kotlin2JvmSourceSetProcessor( override fun doTargetSpecificProcessing() { ifKaptEnabled(project) { - Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName) + Kapt3GradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName) } ScriptingGradleSubplugin.configureForSourceSet(project, kotlinCompilation.compilationName) @@ -785,7 +784,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool sourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet) ifKaptEnabled(project) { - Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name) + Kapt3GradleSubplugin.createAptConfigurationIfNeeded(project, sourceSet.name) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index ec1903c4698..c8caf7b20ea 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformPluginBase import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName import org.jetbrains.kotlin.gradle.report.BuildReportMode @@ -251,6 +252,7 @@ abstract class AbstractKotlinCompile() : AbstractKo @get:Internal // takes part in the compiler arguments val friendPaths: Array by project.provider { taskData.compilation.run { + if (this !is AbstractKotlinCompilation<*>) return@run emptyArray() associateWithTransitiveClosure .flatMap { it.output.classesDirs } .plus(friendArtifacts) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/kotlinCompileTaskData.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/kotlinCompileTaskData.kt index 7773f03115d..1f46ac220dd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/kotlinCompileTaskData.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/kotlinCompileTaskData.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.tasks import org.gradle.api.Project import org.gradle.api.plugins.ExtraPropertiesExtension import org.gradle.api.provider.Property +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation import org.jetbrains.kotlin.gradle.utils.getValue import java.io.File @@ -15,7 +16,7 @@ import java.io.File internal open class KotlinCompileTaskData( val taskName: String, @field:Transient // cannot be serialized for Gradle Instant Execution, but actually is not needed when a task is deserialized - val compilation: AbstractKotlinCompilation<*>, + val compilation: KotlinCompilation<*>, val destinationDir: Property, val useModuleDetection: Property ) { @@ -40,7 +41,7 @@ internal open class KotlinCompileTaskData( fun register( taskName: String, - compilation: AbstractKotlinCompilation<*> + compilation: KotlinCompilation<*> ): KotlinCompileTaskData { val project = compilation.target.project val container = project.getTaskDataMap() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin b/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin index e6839bb85cc..426710b9b36 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin +++ b/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin @@ -1,3 +1,2 @@ org.jetbrains.kotlin.gradle.internal.AndroidSubplugin -org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin org.jetbrains.kotlin.gradle.scripting.internal.ScriptingKotlinGradleSubplugin \ No newline at end of file