diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt index f9fb671cd9f..f68e69f4b7e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt @@ -286,4 +286,4 @@ internal abstract class AbstractKotlinPlugin( } } } -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSetProcessor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSetProcessor.kt index eb5dfa7e8d5..93b29279605 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSetProcessor.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSetProcessor.kt @@ -15,7 +15,6 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar -import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile @@ -41,7 +40,7 @@ internal abstract class KotlinSourceSetProcessor>( protected val javaSourceSet: SourceSet? get() = when (val compilation = compilationInfo.safeAs()?.origin) { is KotlinWithJavaCompilation<*, *> -> compilation.javaSourceSet - is KotlinJvmCompilation -> compilation.javaSourceSet.getOrNull() + is KotlinJvmCompilation -> compilation.javaSourceSet else -> null } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilation.kt index 33a609520fe..9ed864b017a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilation.kt @@ -19,9 +19,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationI import org.jetbrains.kotlin.gradle.plugin.variantImplementationFactory import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask -import org.jetbrains.kotlin.gradle.utils.LenientFuture -import org.jetbrains.kotlin.gradle.utils.future -import org.jetbrains.kotlin.gradle.utils.lenient import javax.inject.Inject open class KotlinJvmCompilation @Inject internal constructor( @@ -67,17 +64,19 @@ open class KotlinJvmCompilation @Inject internal constructor( * will be enabled after call to this method. */ internal val compileJavaTaskProviderSafe: Provider = target.project.providers - .provider { javaSourceSet.getOrNull() } + .provider { javaSourceSet } .flatMap { javaSourceSet -> - @Suppress("RedundantRequireNotNullCall") // IDE and CLI disagree here checkNotNull(javaSourceSet) project.tasks.named(javaSourceSet.compileJavaTaskName, JavaCompile::class.java) } - internal val javaSourceSet: LenientFuture = target.project.future { - target.withJavaEnabledFuture.await() - target.project.javaSourceSets.maybeCreate(compilationName) - }.lenient + internal val javaSourceSet + get() = if (target.withJavaEnabled) maybeCreateJavaSourceSet() else null + + internal fun maybeCreateJavaSourceSet(): SourceSet { + check(target.withJavaEnabled) + return target.project.javaSourceSets.maybeCreate(compilationName) + } override val processResourcesTaskName: String get() = compilation.processResourcesTaskName ?: error("Missing 'processResourcesTaskName'") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilationFactory.kt index 703aa558025..0df48ebef62 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmCompilationFactory.kt @@ -6,12 +6,10 @@ @file:Suppress("PackageDirectoryMismatch") // Old package for compatibility package org.jetbrains.kotlin.gradle.plugin.mpp -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.* +import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinJvmCompilationAssociator import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinJvmCompilerOptionsFactory import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget -import org.jetbrains.kotlin.gradle.utils.* open class KotlinJvmCompilationFactory internal constructor( override val target: KotlinJvmTarget diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt index 3614822f96c..b9dd9bcbf9e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt @@ -34,9 +34,8 @@ import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRunDsl import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRunDslImpl import org.jetbrains.kotlin.gradle.targets.jvm.tasks.registerMainRunTask import org.jetbrains.kotlin.gradle.tasks.withType -import org.jetbrains.kotlin.gradle.utils.* -import org.jetbrains.kotlin.gradle.utils.CompletableFuture import org.jetbrains.kotlin.gradle.utils.Future +import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull import org.jetbrains.kotlin.gradle.utils.future import org.jetbrains.kotlin.utils.addToStdlib.cast @@ -95,13 +94,6 @@ abstract class KotlinJvmTarget @Inject constructor( mainRun.await()?.configure() } - /** - * Future that will complete once [withJava] is called and java support for this target - * was established. - */ - internal val withJavaEnabledFuture: Future get() = withJavaEnabledFutureImpl - private val withJavaEnabledFutureImpl = CompletableFuture() - var withJavaEnabled = false private set @@ -184,7 +176,9 @@ abstract class KotlinJvmTarget @Inject constructor( disableJavaPluginTasks(javaSourceSets) } - withJavaEnabledFutureImpl.complete(Unit) + compilations.all { compilation -> + compilation.maybeCreateJavaSourceSet() + } } private fun disableJavaPluginTasks(javaSourceSet: SourceSetContainer) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/regressionTests/KT60462WithJavaZombieCompilationTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/regressionTests/KT60462WithJavaZombieCompilationTest.kt new file mode 100644 index 00000000000..d553decbd1a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/regressionTests/KT60462WithJavaZombieCompilationTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.regressionTests + +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest +import kotlin.test.Test +import kotlin.test.assertNull +import kotlin.test.assertSame + +class KT60462WithJavaZombieCompilationTest { + + /** + * Regression was introduced by: + * + * ``` + * [Gradle] Ensure java source sets being created eagerly for 'withJava' Sebastian Sellmair* 07.07.23, 20:49 + * 817e3de8f546e34974b89fef0f4f93b425e7e607 + * ``` + * + * The commit was ensuring that jvm compilations will create their associated java source sets + * as eager as possible. The solution chosen in the commit was that already the construction of the compilation + * will spawn a coroutine that waits for the `withJavaEnabled` callback to create the java source set. + * + * However, a buildscript like + * + * ```kotlin + * kotlin { + * jvm().withJava() + * val customCompilation = jvm().compilations.create("custom") + * // ^ + * // Zombie + * } + * ``` + * + * would therefore try to create the java source set right in the constructor call of the 'custom' compilation. + * This would have triggered a listener on `javaSourceSets.all {}` which would ensure that all + * java source sets have a corresponding kotlin compilation created. + * + * Since the current stack is currently inside the constructor of the first compilation, the + * used `compilations.maybeCreate` would trigger the creation of another custom compilation. + * + * The initial buildscript call creating the initial custom compilation will therefore return a Zombie instance + * ```kotlin + * kotlin { + * val customCompilation = jvm().compilations.create("custom") + * customCompilation != jvm().compilations.getByName("custom") + * // ^ ^ + * // Zombie Real instance created by the javaSourceSets.all listener + * } + * ``` + */ + @Test + fun `test - custom compilation`() = buildProjectWithMPP().runLifecycleAwareTest { + multiplatformExtension.jvm().apply { + withJava() + + var instanceUsedForConfigureBlock: KotlinJvmCompilation? = null + + val instanceReturnedFromCreate = compilations.create("custom") { instance -> + assertNull(instanceUsedForConfigureBlock) + instanceUsedForConfigureBlock = instance + } + + val instanceReturnedFromGet = compilations.getByName("custom") + + assertSame(instanceReturnedFromCreate, instanceUsedForConfigureBlock) + assertSame(instanceReturnedFromGet, instanceUsedForConfigureBlock) + } + } +}