[Gradle] KotlinSingleTargetExtension: Implement lateinit target as Future
It is not obvious from 'KotlinSingleTargetExtension' that calling into 'target' is unsafe and it might throw. Ordering code (especially with Android) is hard: It is not clear when the target will become available. The Future (and forAllTargets implementation) is intended to make working with targets generically more safe. KT-61634
This commit is contained in:
committed by
Space Team
parent
77376f0d28
commit
eba13b5b91
+40
-43
@@ -17,15 +17,21 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_BUILD_TOOLS_API_IMPL
|
||||
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.CoroutineStart.Undispatched
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrSingleTargetPreset
|
||||
import org.jetbrains.kotlin.gradle.tasks.CompileUsingKotlinDaemon
|
||||
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.castIsolatedKotlinPluginClassLoaderAware
|
||||
import org.jetbrains.kotlin.gradle.utils.configureExperimentalTryK2
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.tooling.core.HasMutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.MutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
@@ -180,17 +186,21 @@ open class KotlinProjectExtension @Inject constructor(project: Project) : Kotlin
|
||||
|
||||
abstract class KotlinSingleTargetExtension<TARGET : KotlinTarget>(project: Project) : KotlinProjectExtension(project) {
|
||||
abstract val target: TARGET
|
||||
|
||||
internal abstract val targetFuture: Future<TARGET>
|
||||
fun target(body: Action<TARGET>) = body.execute(target)
|
||||
}
|
||||
|
||||
abstract class KotlinSingleJavaTargetExtension(project: Project) : KotlinSingleTargetExtension<KotlinWithJavaTarget<*, *>>(project)
|
||||
|
||||
abstract class KotlinJvmProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
|
||||
override lateinit var target: KotlinWithJavaTarget<KotlinJvmOptions, KotlinJvmCompilerOptions>
|
||||
internal set
|
||||
override val target: KotlinWithJavaTarget<KotlinJvmOptions, KotlinJvmCompilerOptions>
|
||||
get() = targetFuture.getOrThrow()
|
||||
|
||||
open fun target(body: KotlinWithJavaTarget<KotlinJvmOptions, KotlinJvmCompilerOptions>.() -> Unit) = target.run(body)
|
||||
override val targetFuture = CompletableFuture<KotlinWithJavaTarget<KotlinJvmOptions, KotlinJvmCompilerOptions>>()
|
||||
|
||||
open fun target(body: KotlinWithJavaTarget<KotlinJvmOptions, KotlinJvmCompilerOptions>.() -> Unit) {
|
||||
project.launch(Undispatched) { targetFuture.await().body() }
|
||||
}
|
||||
|
||||
val compilerOptions: KotlinJvmCompilerOptions = project.objects
|
||||
.newInstance(KotlinJvmCompilerOptionsDefault::class.java)
|
||||
@@ -206,20 +216,16 @@ abstract class KotlinJvmProjectExtension(project: Project) : KotlinSingleJavaTar
|
||||
}
|
||||
|
||||
abstract class Kotlin2JsProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
|
||||
private lateinit var _target: KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>
|
||||
|
||||
override val target: KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>
|
||||
get() {
|
||||
if (!::_target.isInitialized) throw IllegalStateException("Extension target is not initialized!")
|
||||
|
||||
return _target
|
||||
if (!targetFuture.isCompleted) throw IllegalStateException("Extension target is not initialized!")
|
||||
return targetFuture.getOrThrow()
|
||||
}
|
||||
|
||||
internal fun setTarget(target: KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>) {
|
||||
_target = target
|
||||
override val targetFuture = CompletableFuture<KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>>()
|
||||
open fun target(body: KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>.() -> Unit) {
|
||||
project.launch(Undispatched) { targetFuture.await().body() }
|
||||
}
|
||||
|
||||
open fun target(body: KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>.() -> Unit) = target.run(body)
|
||||
}
|
||||
|
||||
abstract class KotlinJsProjectExtension(project: Project) :
|
||||
@@ -227,45 +233,31 @@ abstract class KotlinJsProjectExtension(project: Project) :
|
||||
KotlinJsCompilerTypeHolder {
|
||||
lateinit var irPreset: KotlinJsIrSingleTargetPreset
|
||||
|
||||
private val targetSetObservers = mutableListOf<(KotlinJsTargetDsl?) -> Unit>()
|
||||
@Deprecated("Use js() instead", ReplaceWith("js()"))
|
||||
override val target: KotlinJsTargetDsl
|
||||
get() = targetFuture.lenient.getOrNull() ?: js()
|
||||
|
||||
@Deprecated("Because only IR compiler is left, no more necessary to know about compiler type in properties")
|
||||
override val compilerTypeFromProperties: KotlinJsCompilerType? = null
|
||||
|
||||
// target is public property
|
||||
// Users can write kotlin.target and it should work
|
||||
// So call of target should init default configuration
|
||||
@Deprecated("Use `target` instead", ReplaceWith("target"))
|
||||
var _target: KotlinJsTargetDsl? = null
|
||||
private set(value) {
|
||||
field = value
|
||||
targetSetObservers.forEach { it(value) }
|
||||
}
|
||||
override val targetFuture = CompletableFuture<KotlinJsTargetDsl>()
|
||||
|
||||
fun registerTargetObserver(observer: (KotlinJsTargetDsl?) -> Unit) {
|
||||
targetSetObservers.add(observer)
|
||||
}
|
||||
|
||||
@Deprecated("Use js() instead", ReplaceWith("js()"))
|
||||
@Suppress("DEPRECATION")
|
||||
override val target: KotlinJsTargetDsl
|
||||
get() {
|
||||
if (_target == null) {
|
||||
js {}
|
||||
}
|
||||
return _target!!
|
||||
project.launch(Undispatched) {
|
||||
observer(targetFuture.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun jsInternal(
|
||||
compiler: KotlinJsCompilerType? = null,
|
||||
body: KotlinJsTargetDsl.() -> Unit,
|
||||
): KotlinJsTargetDsl {
|
||||
if (_target == null) {
|
||||
if (!targetFuture.isCompleted) {
|
||||
val target: KotlinJsTargetDsl = irPreset
|
||||
.createTargetInternal("js")
|
||||
|
||||
this._target = target
|
||||
this.targetFuture.complete(target)
|
||||
|
||||
target.project.components.addAll(target.components)
|
||||
}
|
||||
@@ -317,26 +309,31 @@ abstract class KotlinJsProjectExtension(project: Project) :
|
||||
)
|
||||
@Suppress("DEPRECATION")
|
||||
fun getTargets(): NamedDomainObjectContainer<KotlinTarget>? =
|
||||
_target?.let { target ->
|
||||
targetFuture.lenient.getOrNull()?.let { target ->
|
||||
target.project.container(KotlinTarget::class.java)
|
||||
.apply { add(target) }
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KotlinCommonProjectExtension(project: Project) : KotlinSingleJavaTargetExtension(project) {
|
||||
override lateinit var target: KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>
|
||||
internal set
|
||||
override val target: KotlinWithJavaTarget<*, *> get() = targetFuture.getOrThrow()
|
||||
override val targetFuture =
|
||||
CompletableFuture<KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>>()
|
||||
|
||||
open fun target(
|
||||
body: KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>.() -> Unit,
|
||||
) = target.run(body)
|
||||
) = project.launch(Undispatched) {
|
||||
targetFuture.await().body()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KotlinAndroidProjectExtension(project: Project) : KotlinSingleTargetExtension<KotlinAndroidTarget>(project) {
|
||||
override lateinit var target: KotlinAndroidTarget
|
||||
internal set
|
||||
override val target: KotlinAndroidTarget get() = targetFuture.getOrThrow()
|
||||
override val targetFuture = CompletableFuture<KotlinAndroidTarget>()
|
||||
|
||||
open fun target(body: KotlinAndroidTarget.() -> Unit) = target.run(body)
|
||||
open fun target(body: KotlinAndroidTarget.() -> Unit) = project.launch(Undispatched) {
|
||||
targetFuture.await().body()
|
||||
}
|
||||
|
||||
val compilerOptions: KotlinJvmCompilerOptions = project.objects
|
||||
.newInstance(KotlinJvmCompilerOptionsDefault::class.java)
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ internal open class Kotlin2JsPlugin(
|
||||
}
|
||||
) as KotlinWithJavaTarget<KotlinJsOptions, KotlinJsCompilerOptions>
|
||||
|
||||
(project.kotlinExtension as Kotlin2JsProjectExtension).setTarget(target)
|
||||
(project.kotlinExtension as Kotlin2JsProjectExtension).targetFuture.complete(target)
|
||||
super.apply(project)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -38,7 +38,7 @@ internal open class KotlinAndroidPlugin(
|
||||
project
|
||||
).also { target ->
|
||||
val kotlinAndroidExtension = project.kotlinExtension as KotlinAndroidProjectExtension
|
||||
kotlinAndroidExtension.target = target
|
||||
kotlinAndroidExtension.targetFuture.complete(target)
|
||||
project.configureCompilerOptionsForTarget(
|
||||
kotlinAndroidExtension.compilerOptions,
|
||||
target.compilerOptions
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ internal open class KotlinCommonPlugin(
|
||||
}
|
||||
}
|
||||
) as KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>
|
||||
(project.kotlinExtension as KotlinCommonProjectExtension).target = target
|
||||
(project.kotlinExtension as KotlinCommonProjectExtension).targetFuture.complete(target)
|
||||
|
||||
super.apply(project)
|
||||
}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ internal open class KotlinJvmPlugin(
|
||||
disambiguationClassifier = null // don't add anything to the task names
|
||||
}
|
||||
val kotlinExtension = project.kotlinExtension as KotlinJvmProjectExtension
|
||||
kotlinExtension.target = target
|
||||
kotlinExtension.targetFuture.complete(target)
|
||||
|
||||
super.apply(project)
|
||||
|
||||
|
||||
+1
-2
@@ -319,8 +319,7 @@ abstract class AbstractKotlinJsPluginWrapper : KotlinBasePluginWrapper() {
|
||||
get() = KotlinJsProjectExtension::class
|
||||
|
||||
override fun whenBuildEvaluated(project: Project) = project.runProjectConfigurationHealthCheck {
|
||||
val isJsTargetUninitialized = (project.kotlinExtension as KotlinJsProjectExtension)
|
||||
._target == null
|
||||
val isJsTargetUninitialized = !(project.kotlinExtension as KotlinJsProjectExtension).targetFuture.isCompleted
|
||||
|
||||
if (isJsTargetUninitialized) {
|
||||
throw GradleException(
|
||||
|
||||
+1
-2
@@ -38,8 +38,7 @@ open class KotlinJsPlugin: Plugin<Project> {
|
||||
}
|
||||
|
||||
project.runProjectConfigurationHealthCheckWhenEvaluated {
|
||||
@Suppress("DEPRECATION")
|
||||
if (kotlinExtension._target == null) {
|
||||
if (!kotlinExtension.targetFuture.isCompleted) {
|
||||
project.logger.warn(
|
||||
"""
|
||||
Please initialize the Kotlin/JS target. Use:
|
||||
|
||||
+4
-2
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.gradle.utils
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.CoroutineStart.Undispatched
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.launch
|
||||
|
||||
internal val KotlinProjectExtension.targets: Iterable<KotlinTarget>
|
||||
get() = when (this) {
|
||||
@@ -20,8 +22,8 @@ internal val KotlinProjectExtension.targets: Iterable<KotlinTarget>
|
||||
|
||||
internal fun KotlinProjectExtension.forAllTargets(action: (target: KotlinTarget) -> Unit) {
|
||||
when (this) {
|
||||
is KotlinSingleTargetExtension<*> -> action(this.target)
|
||||
is KotlinSingleTargetExtension<*> -> project.launch(Undispatched) { action(targetFuture.await()) }
|
||||
is KotlinMultiplatformExtension -> targets.all(action)
|
||||
else -> error("Unexpected 'kotlin' extension $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user