[Gradle] jvm().withJava(): Fix Zombie compilation instance returned from create()

^KT-60462 Verification Pending
This commit is contained in:
Sebastian Sellmair
2023-07-15 13:03:50 +02:00
committed by Space Team
parent 0945968751
commit 0feecebc07
6 changed files with 93 additions and 25 deletions
@@ -286,4 +286,4 @@ internal abstract class AbstractKotlinPlugin(
}
}
}
}
}
@@ -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<T : AbstractKotlinCompile<*>>(
protected val javaSourceSet: SourceSet?
get() = when (val compilation = compilationInfo.safeAs<KotlinCompilationInfo.TCS>()?.origin) {
is KotlinWithJavaCompilation<*, *> -> compilation.javaSourceSet
is KotlinJvmCompilation -> compilation.javaSourceSet.getOrNull()
is KotlinJvmCompilation -> compilation.javaSourceSet
else -> null
}
@@ -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<JavaCompile> = 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<SourceSet> = 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'")
@@ -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
@@ -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<Unit> get() = withJavaEnabledFutureImpl
private val withJavaEnabledFutureImpl = CompletableFuture<Unit>()
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) {
@@ -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)
}
}
}