Decouple KaptGenerateStubsTask from related KotlinCompile task configuration

This should simplify setup and remove task circular dependencies.

Generally users should not be affected by it unless they are configuring
 KotlinCompile task explicetly and expect the same configuration will be
  present on KaptGenerateStubsTask.

^KT-54468 In Progress
This commit is contained in:
Yahor Berdnikau
2023-04-20 20:31:46 +02:00
committed by Space Team
parent ee1517cd6a
commit 3ee089bb48
10 changed files with 142 additions and 132 deletions
@@ -1038,7 +1038,7 @@ open class Kapt3IT : Kapt3BaseIT() {
} }
} }
@DisplayName("KT-52761: generated sources attached to compile task are also used by generate stubs task") @DisplayName("Generated sources attached to KotlinSourceSet are also used by generate stubs task")
@GradleTest @GradleTest
fun testGeneratedSourcesUsedInGenerateStubsTask(gradleVersion: GradleVersion) { fun testGeneratedSourcesUsedInGenerateStubsTask(gradleVersion: GradleVersion) {
project("generatedSources".withPrefix, gradleVersion) { project("generatedSources".withPrefix, gradleVersion) {
@@ -192,8 +192,8 @@ class UpToDateIT : KGPBaseTest() {
} }
override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) { override fun checkAfterRebuild(buildResult: BuildResult) = with(buildResult) {
assertTasksExecuted(":compileKotlin", ":kaptGenerateStubsKotlin") assertTasksExecuted(":compileKotlin")
assertTasksUpToDate(":kaptKotlin") assertTasksUpToDate(":kaptGenerateStubsKotlin", ":kaptKotlin")
} }
} }
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.* import org.jetbrains.kotlin.gradle.util.*
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import kotlin.io.path.appendText import kotlin.io.path.appendText
import kotlin.io.path.readText
import kotlin.io.path.writeText import kotlin.io.path.writeText
@DisplayName("android with kapt3 tests") @DisplayName("android with kapt3 tests")
@@ -8,8 +8,10 @@ repositories {
mavenCentral() mavenCentral()
} }
tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile.class) { sourceSets {
source("src/proto/generated") main {
kotlin.srcDir("src/proto/generated")
}
} }
tasks.withType(JavaCompile.class).configureEach { tasks.withType(JavaCompile.class).configureEach {
@@ -240,9 +240,10 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
kaptExtension.includeCompileClasspath kaptExtension.includeCompileClasspath
?: project.isIncludeCompileClasspath() ?: project.isIncludeCompileClasspath()
@Suppress("UNCHECKED_CAST")
val kotlinCompile: TaskProvider<KotlinCompile> val kotlinCompile: TaskProvider<KotlinCompile>
// Can't use just kotlinCompilation.compileKotlinTaskProvider, as the latter is not statically-known to be KotlinCompile // Can't use just kotlinCompilation.compileKotlinTaskProvider, as the latter is not statically-known to be KotlinCompile
get() = checkNotNull(project.locateTask(kotlinCompilation.compileKotlinTaskName)) get() = kotlinCompilation.compileTaskProvider as TaskProvider<KotlinCompile>
} }
override fun applyToCompilation( override fun applyToCompilation(
@@ -355,7 +356,7 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
private fun Kapt3SubpluginContext.createKaptKotlinTask( private fun Kapt3SubpluginContext.createKaptKotlinTask(
generateStubsTask: TaskProvider<KaptGenerateStubsTask> generateStubsTask: TaskProvider<KaptGenerateStubsTask>
): TaskProvider<out KaptTask> { ): TaskProvider<out KaptTask> {
val taskName = getKaptTaskName("kapt") val taskName = kotlinCompile.kaptTaskName
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val taskConfigAction = KaptWithoutKotlincConfig( val taskConfigAction = KaptWithoutKotlincConfig(
kotlinCompilation.project, kotlinCompilation.project,
@@ -432,10 +433,10 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
} }
private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): TaskProvider<KaptGenerateStubsTask> { private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): TaskProvider<KaptGenerateStubsTask> {
val kaptTaskName = getKaptTaskName("kaptGenerateStubs") val kaptTaskName = kotlinCompile.kaptGenerateStubsTaskName
val kaptTaskProvider = project.registerTask<KaptGenerateStubsTask>(kaptTaskName) val kaptTaskProvider = project.registerTask<KaptGenerateStubsTask>(kaptTaskName)
val taskConfig = KaptGenerateStubsConfig(kotlinCompilation, kotlinCompile, classesOutputDir) val taskConfig = KaptGenerateStubsConfig(kotlinCompilation)
taskConfig.configureTask { taskConfig.configureTask {
it.stubsDir.set(getKaptStubsDir()) it.stubsDir.set(getKaptStubsDir())
it.destinationDirectory.set(getKaptIncrementalDataDir()) it.destinationDirectory.set(getKaptIncrementalDataDir())
@@ -451,13 +452,6 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
return kaptTaskProvider return kaptTaskProvider
} }
private fun Kapt3SubpluginContext.getKaptTaskName(prefix: String): String {
// Replace compile*Kotlin to kapt*Kotlin
val baseName = kotlinCompile.name
assert(baseName.startsWith("compile"))
return baseName.replaceFirst("compile", prefix)
}
private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() { private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() {
javaCompile?.configure { javaCompileInstance -> javaCompile?.configure { javaCompileInstance ->
if (javaCompileInstance !is JavaCompile) if (javaCompileInstance !is JavaCompile)
@@ -491,6 +485,24 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
JetBrainsSubpluginArtifact(artifactId = KAPT_ARTIFACT_NAME) JetBrainsSubpluginArtifact(artifactId = KAPT_ARTIFACT_NAME)
} }
internal const val KAPT_GENERATE_STUBS_PREFIX = "kaptGenerateStubs"
internal const val KAPT_PREFIX = "kapt"
internal val TaskProvider<out KotlinJvmCompile>.kaptGenerateStubsTaskName
get() = getKaptTaskName(name, KAPT_GENERATE_STUBS_PREFIX)
internal val TaskProvider<out KotlinJvmCompile>.kaptTaskName
get() = getKaptTaskName(name, KAPT_PREFIX)
internal fun getKaptTaskName(
kotlinCompileName: String,
prefix: String
): String {
// Replace compile*Kotlin to kapt*Kotlin
assert(kotlinCompileName.startsWith("compile"))
return kotlinCompileName.replaceFirst("compile", prefix)
}
internal fun buildKaptSubpluginOptions( internal fun buildKaptSubpluginOptions(
kaptExtension: KaptExtension, kaptExtension: KaptExtension,
project: Project, project: Project,
@@ -25,7 +25,6 @@ import org.gradle.work.Incremental
import org.gradle.work.NormalizeLineEndings import org.gradle.work.NormalizeLineEndings
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsDefault import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsDefault
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsHelper import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsHelper
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext
@@ -88,6 +87,9 @@ abstract class KaptGenerateStubsTask @Inject constructor(
@get:Internal @get:Internal
override val androidLayoutResources: FileCollection = objectFactory.fileCollection() override val androidLayoutResources: FileCollection = objectFactory.fileCollection()
@get:Internal
abstract val kotlinCompileDestinationDirectory: DirectoryProperty
override val incrementalProps: List<FileCollection> override val incrementalProps: List<FileCollection>
get() = listOf( get() = listOf(
sources, sources,
@@ -97,18 +99,10 @@ abstract class KaptGenerateStubsTask @Inject constructor(
classpathSnapshotProperties.classpathSnapshot classpathSnapshotProperties.classpathSnapshot
) )
@get:Internal
internal abstract val compileTaskCompilerOptions: Property<KotlinJvmCompilerOptions>
override fun createCompilerArguments(context: CreateCompilerArgumentsContext) = context.create<K2JVMCompilerArguments> { override fun createCompilerArguments(context: CreateCompilerArgumentsContext) = context.create<K2JVMCompilerArguments> {
primitive { args -> primitive { args ->
args.allowNoSourceFiles = true args.allowNoSourceFiles = true
KotlinJvmCompilerOptionsHelper.fillCompilerArguments(compileTaskCompilerOptions.get(), args)
// Workaround for freeCompiler args duplication when they were configured for both this task
// and linked KotlinCompile task with the same values. For now linked KotlinCompile task
// freeCompilerArgs is used as convention for this task freeCompilerArgs
args.freeArgs = emptyList()
KotlinJvmCompilerOptionsHelper.fillCompilerArguments(compilerOptions, args) KotlinJvmCompilerOptionsHelper.fillCompilerArguments(compilerOptions, args)
overrideArgsUsingTaskModuleNameWithWarning(args) overrideArgsUsingTaskModuleNameWithWarning(args)
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.KaptGenerateStubsConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.whenKaptEnabled import org.jetbrains.kotlin.gradle.utils.whenKaptEnabled
@@ -34,7 +35,14 @@ internal class Kotlin2JvmSourceSetProcessor(
taskName, taskName,
compilationInfo.compilerOptions.options as KotlinJvmCompilerOptions, compilationInfo.compilerOptions.options as KotlinJvmCompilerOptions,
configAction configAction
) ).also { kotlinTask ->
// Configuring here to not interfere with 'kotlin-android' plugin configuration for 'libraries' input
KaptGenerateStubsConfig.configureLibraries(
project,
kotlinTask,
{ compilationInfo.compileDependencyFiles }
)
}
} }
override fun doTargetSpecificProcessing() { override fun doTargetSpecificProcessing() {
@@ -59,6 +67,12 @@ internal class Kotlin2JvmSourceSetProcessor(
kotlinTask.configure { kotlinCompile -> kotlinTask.configure { kotlinCompile ->
kotlinCompile.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory }) kotlinCompile.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory })
} }
KaptGenerateStubsConfig.wireJavaAndKotlinOutputs(
project,
javaTask,
kotlinTask
)
} }
if (sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) { if (sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
@@ -9,6 +9,8 @@ import org.gradle.api.Project
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.internal.KAPT_GENERATE_STUBS_PREFIX
import org.jetbrains.kotlin.gradle.internal.getKaptTaskName
import org.jetbrains.kotlin.gradle.plugin.HasCompilerOptions import org.jetbrains.kotlin.gradle.plugin.HasCompilerOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
@@ -52,16 +54,20 @@ internal fun addSourcesToKotlinCompileTask(
// The `commonSourceSet` is passed to the compiler as-is, converted with toList // The `commonSourceSet` is passed to the compiler as-is, converted with toList
commonSourceSet.from( commonSourceSet.from(
Callable<Any> { if (addAsCommonSources.value) sources else emptyList<Any>() } { if (addAsCommonSources.value) sources else emptyList<Any>() }
) )
} }
project.tasks project.tasks
// To configure a task that may have not yet been created at this point, use 'withType-matching-configureEach`:
.withType(AbstractKotlinCompile::class.java) .withType(AbstractKotlinCompile::class.java)
.matching { it.name == taskName }
.configureEach { compileKotlinTask -> .configureEach { compileKotlinTask ->
compileKotlinTask.configureAction() val compileTaskName = compileKotlinTask.name
// We also should configure related Kapt* tasks as they are not pickup configuration from
// related KotlinJvmCompile to avoid circular task dependencies
val kaptGenerateStubsTaskName = getKaptTaskName(compileTaskName, KAPT_GENERATE_STUBS_PREFIX)
if (compileTaskName == taskName || kaptGenerateStubsTaskName == taskName) {
compileKotlinTask.configureAction()
}
} }
} }
@@ -26,7 +26,7 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.android.KotlinAndroidSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.android.findKotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.android.findKotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.KaptGenerateStubsConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
@@ -46,7 +47,6 @@ import java.io.File
import java.io.ObjectInputStream import java.io.ObjectInputStream
import java.io.ObjectOutputStream import java.io.ObjectOutputStream
import java.io.Serializable import java.io.Serializable
import java.util.concurrent.Callable
internal class AndroidProjectHandler( internal class AndroidProjectHandler(
private val kotlinTasksProvider: KotlinTasksProvider private val kotlinTasksProvider: KotlinTasksProvider
@@ -186,13 +186,22 @@ internal class AndroidProjectHandler(
task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variantDataName")) task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variantDataName"))
task.description = "Compiles the $variantDataName kotlin." task.description = "Compiles the $variantDataName kotlin."
} }
tasksProvider.registerKotlinJVMTask( val kotlinTask = tasksProvider.registerKotlinJVMTask(
project, project,
compilation.compileKotlinTaskName, compilation.compileKotlinTaskName,
compilation.compilerOptions.options as KotlinJvmCompilerOptions, compilation.compilerOptions.options as KotlinJvmCompilerOptions,
configAction configAction
) )
// Need to move it into afterEvaluate, so it will be executed after KaptGenerateStubsConfig config actions
// Otherwise build will fail within AbstractKotlinCompileConfig trying to modify value with 'disallowChanges()' state
project.afterEvaluate {
KaptGenerateStubsConfig.configureUseModuleDetection(
project,
kotlinTask
) { value(true).disallowChanges() }
}
// Register the source only after the task is created, because the task is required for that: // Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet) compilation.source(defaultSourceSet)
@@ -250,11 +259,23 @@ internal class AndroidProjectHandler(
kotlinTask.configure { kotlinTaskInstance -> kotlinTask.configure { kotlinTaskInstance ->
kotlinTaskInstance.libraries kotlinTaskInstance.libraries
.from(variantData.getCompileClasspath(preJavaClasspathKey)) .from(variantData.getCompileClasspath(preJavaClasspathKey))
.from(Callable { AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt) }) .from({ AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt) })
kotlinTaskInstance.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory }) kotlinTaskInstance.javaOutputDir.set(javaTask.flatMap { it.destinationDirectory })
} }
KaptGenerateStubsConfig.configureLibraries(
project,
kotlinTask,
variantData.getCompileClasspath(preJavaClasspathKey),
{ AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt) }
)
KaptGenerateStubsConfig.wireJavaAndKotlinOutputs(
project,
javaTask,
kotlinTask
)
// Find the classpath entries that come from the tested variant and register them as the friend paths, lazily // Find the classpath entries that come from the tested variant and register them as the friend paths, lazily
val originalArtifactCollection = variantData.getCompileClasspathArtifacts(preJavaClasspathKey) val originalArtifactCollection = variantData.getCompileClasspathArtifacts(preJavaClasspathKey)
val testedVariantDataIsNotNull = getTestedVariantData(variantData) != null val testedVariantDataIsNotNull = getTestedVariantData(variantData) != null
@@ -306,7 +327,7 @@ internal class AndroidProjectHandler(
project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName) project.addExtendsFromRelation(name, compilation.runtimeDependencyConfigurationName)
} }
val buildTypeAttrValue = project.objects.named(BuildTypeAttr::class.java, variant.buildType.name) val buildTypeAttrValue = project.objects.named<BuildTypeAttr>(variant.buildType.name)
listOf(compilation.compileDependencyConfigurationName, compilation.runtimeDependencyConfigurationName).forEach { listOf(compilation.compileDependencyConfigurationName, compilation.runtimeDependencyConfigurationName).forEach {
project.configurations.findByName(it)?.attributes?.attribute(Attribute.of(BuildTypeAttr::class.java), buildTypeAttrValue) project.configurations.findByName(it)?.attributes?.attribute(Attribute.of(BuildTypeAttr::class.java), buildTypeAttrValue)
} }
@@ -6,11 +6,12 @@
package org.jetbrains.kotlin.gradle.tasks.configuration package org.jetbrains.kotlin.gradle.tasks.configuration
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.specs.Spec
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsHelper
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.internal.* import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_SUBPLUGIN_ID
@@ -19,66 +20,29 @@ import org.jetbrains.kotlin.gradle.plugin.KaptExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable import org.jetbrains.kotlin.gradle.utils.whenKaptEnabled
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.isParentOf
import java.io.File
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig<KaptGenerateStubsTask> { internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig<KaptGenerateStubsTask> {
constructor( constructor(
compilation: KotlinCompilation<*>, compilation: KotlinCompilation<*>
kotlinTaskProvider: TaskProvider<KotlinCompile>,
kaptClassesDir: File
) : super(KotlinCompilationInfo(compilation)) { ) : super(KotlinCompilationInfo(compilation)) {
configureFromExtension(project.extensions.getByType(KaptExtension::class.java)) configureFromExtension(project.extensions.getByType(KaptExtension::class.java))
configureTask { task ->
val kotlinCompileTask = kotlinTaskProvider.get() configureTask { kaptGenerateStubsTask ->
task.useModuleDetection.value(kotlinCompileTask.useModuleDetection).disallowChanges() // Syncing compiler options from related KotlinJvmCompile task
@Suppress("DEPRECATION") val jvmCompilerOptions = compilation.compilerOptions.options as KotlinJvmCompilerOptions
task.moduleName.value(kotlinCompileTask.moduleName).disallowChanges() KotlinJvmCompilerOptionsHelper.syncOptionsAsConvention(
task.libraries.from({ kotlinCompileTask.libraries - project.files(kaptClassesDir) }) from = jvmCompilerOptions,
task.compileTaskCompilerOptions.set(providers.provider { kotlinCompileTask.compilerOptions }) into = kaptGenerateStubsTask.compilerOptions
task.pluginOptions.addAll(kotlinCompileTask.pluginOptions)
task.compilerOptions.moduleName.convention(kotlinCompileTask.compilerOptions.moduleName)
task.compilerOptions.freeCompilerArgs.convention(kotlinCompileTask.compilerOptions.freeCompilerArgs)
// KotlinCompile will also have as input output from KaptGenerateStubTask and KaptTask
// We are filtering them to avoid failed UP-TO-DATE checks
val kaptJavaSourcesDir = Kapt3GradleSubplugin.getKaptGeneratedSourcesDir(
project,
compilation.compilationName
)
val kaptKotlinSourcesDir = Kapt3GradleSubplugin.getKaptGeneratedKotlinSourcesDir(
project,
compilation.compilationName
)
val destinationDirectory = task.destinationDirectory
val stubsDir = task.stubsDir
val kaptFilterSpec = KaptFilterSpec(destinationDirectory, stubsDir, kaptJavaSourcesDir, kaptKotlinSourcesDir)
// FileTree filtering approach fails with configuration cache until Gradle 7.5 leading to failed UP-TO-DATE checks
val kaptFilter = if (shouldUseFileTreeKaptFilter) {
FileTreeKaptInputsFilter(kaptFilterSpec)
} else {
FileCollectionKaptInputsFilter(kaptFilterSpec)
}
task.source(
kaptFilter.filtered(kotlinCompileTask.javaSources),
kaptFilter.filtered(kotlinCompileTask.sources),
) )
} }
} }
constructor(project: Project, ext: KotlinTopLevelExtension, kaptExtension: KaptExtension) : super(project, ext) { constructor(project: Project, ext: KotlinTopLevelExtension, kaptExtension: KaptExtension) : super(project, ext) {
configureFromExtension(kaptExtension) configureFromExtension(kaptExtension)
configureTask { task ->
task.compileTaskCompilerOptions.set(
providers.provider { task.compilerOptions }
)
}
} }
private fun configureFromExtension(kaptExtension: KaptExtension) { private fun configureFromExtension(kaptExtension: KaptExtension) {
@@ -118,53 +82,51 @@ internal class KaptGenerateStubsConfig : BaseKotlinCompileConfig<KaptGenerateStu
} }
} }
private abstract class CachingKaptInputsFilter { companion object {
private val filterCache = ConcurrentHashMap<File, Boolean>() internal fun wireJavaAndKotlinOutputs(
abstract fun filtered(fileCollection: FileCollection): FileCollection project: Project,
javaCompileTask: TaskProvider<out AbstractCompile>,
protected fun isSatisfiedBy(file: File) = filterCache[file] ?: predicate(file).also { filterCache[file] = it } kotlinCompileTask: TaskProvider<out KotlinJvmCompile>
) {
abstract fun predicate(file: File): Boolean project.whenKaptEnabled {
} val kaptGenerateStubsTaskName = getKaptTaskName(kotlinCompileTask.name, KAPT_GENERATE_STUBS_PREFIX)
project.tasks.withType<KaptGenerateStubsTask>().configureEach { task ->
// Drop `isEmptyDirectory` check after min supported Gradle version will be bumped to 6.8 if (task.name == kaptGenerateStubsTaskName) {
// It will be covered by '@IgnoreEmptyDirectories' input annotation task.javaOutputDir.set(javaCompileTask.flatMap { it.destinationDirectory })
private class FileCollectionKaptInputsFilter(val spec: KaptFilterSpec) : CachingKaptInputsFilter() { task.kotlinCompileDestinationDirectory.set(kotlinCompileTask.flatMap { it.destinationDirectory })
override fun filtered(fileCollection: FileCollection): FileCollection { }
return fileCollection.filter(::isSatisfiedBy) }
}
override fun predicate(file: File) = !file.isEmptyDirectory && spec.isSatisfiedBy(file)
private val File.isEmptyDirectory: Boolean
get() = with(toPath()) {
Files.isDirectory(this) && !Files.list(this).use { it.findFirst().isPresent }
} }
}
private val shouldUseFileTreeKaptFilter
get() = isGradleVersionAtLeast(7, 5) || !isConfigurationCacheAvailable(project.gradle)
private class FileTreeKaptInputsFilter(val spec: KaptFilterSpec) : CachingKaptInputsFilter() {
override fun filtered(fileCollection: FileCollection): FileCollection {
return fileCollection.asFileTree.matching { it.include { elem -> isSatisfiedBy(elem.file) } }
} }
override fun predicate(file: File) = spec.isSatisfiedBy(file) internal fun configureLibraries(
} project: Project,
kotlinCompileTask: TaskProvider<out KotlinJvmCompile>,
vararg paths: Any
) {
project.whenKaptEnabled {
val kaptGenerateStubsTaskName = getKaptTaskName(kotlinCompileTask.name, KAPT_GENERATE_STUBS_PREFIX)
project.tasks.withType<KaptGenerateStubsTask>().configureEach { task ->
if (task.name == kaptGenerateStubsTaskName) {
task.libraries.from(paths)
}
}
}
}
private class KaptFilterSpec( internal fun configureUseModuleDetection(
private val destinationDirectory: DirectoryProperty, project: Project,
private val stubsDir: DirectoryProperty, kotlinCompileTask: TaskProvider<out KotlinJvmCompile>,
private val kaptJavaSourcesDir: File, config: Property<Boolean>.() -> Unit
private val kaptKotlinSourcesDir: File, ) {
) : Spec<File> { project.whenKaptEnabled {
override fun isSatisfiedBy(element: File) = element.isSourceRootAllowed() val kaptGenerateStubsTaskName = getKaptTaskName(kotlinCompileTask.name, KAPT_GENERATE_STUBS_PREFIX)
project.tasks.withType<KaptGenerateStubsTask>().configureEach { task ->
private fun File.isSourceRootAllowed(): Boolean = if (task.name == kaptGenerateStubsTaskName) {
!destinationDirectory.get().asFile.isParentOf(this) && config(task.useModuleDetection)
!stubsDir.asFile.get().isParentOf(this) && }
!kaptJavaSourcesDir.isParentOf(this) && }
!kaptKotlinSourcesDir.isParentOf(this) }
}
} }
} }