[Gradle,JS] Return legacy target to Gradle plugin back

This commit is contained in:
Ilya Goncharov
2023-07-27 16:33:45 +02:00
committed by teamcity
parent fe9de6875a
commit 4c82206d3b
31 changed files with 1493 additions and 89 deletions
@@ -991,18 +991,18 @@ public final class org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType$Compa
}
public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder {
public abstract synthetic fun getBOTH ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract fun getBOTH ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract fun getCompilerTypeFromProperties ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract fun getDefaultJsCompilerType ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract fun getIR ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract synthetic fun getLEGACY ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public abstract fun getLEGACY ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
}
public final class org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder$DefaultImpls {
public static synthetic fun getBOTH (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public static fun getBOTH (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public static fun getDefaultJsCompilerType (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public static fun getIR (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public static synthetic fun getLEGACY (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public static fun getLEGACY (Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
}
public final class org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeKt {
@@ -12,17 +12,17 @@ interface KotlinJsCompilerTypeHolder {
val compilerTypeFromProperties: KotlinJsCompilerType?
val defaultJsCompilerType: KotlinJsCompilerType
get() = KotlinJsCompilerType.IR
get() = compilerTypeFromProperties ?: KotlinJsCompilerType.IR
// Necessary to get rid of KotlinJsCompilerType import in build script
@Deprecated("Legacy compiler is deprecated. Migrate your project to the new IR-based compiler", level = DeprecationLevel.HIDDEN)
@Deprecated("Legacy compiler is deprecated. Migrate your project to the new IR-based compiler")
val LEGACY: KotlinJsCompilerType
get() = KotlinJsCompilerType.LEGACY
val IR: KotlinJsCompilerType
get() = KotlinJsCompilerType.IR
@Deprecated("Legacy compiler is deprecated. Migrate your project to the new IR-based compiler", level = DeprecationLevel.HIDDEN)
@Deprecated("Legacy compiler is deprecated. Migrate your project to the new IR-based compiler")
val BOTH: KotlinJsCompilerType
get() = KotlinJsCompilerType.BOTH
}
@@ -40,7 +40,7 @@ public abstract class org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtensi
public fun getAndroidNativeTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getAppleMain (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getAppleTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public synthetic fun getBOTH ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public fun getBOTH ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public fun getCommonMain (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getCommonTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getCompilerTypeFromProperties ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
@@ -52,7 +52,7 @@ public abstract class org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtensi
public fun getJsTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getJvmMain (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getJvmTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public synthetic fun getLEGACY ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public fun getLEGACY ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerType;
public fun getLinuxMain (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getLinuxTest (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
public fun getMacosMain (Lorg/gradle/api/NamedDomainObjectContainer;)Lorg/gradle/api/NamedDomainObjectProvider;
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.AfterFinaliseDsl
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.hierarchy.KotlinHierarchyDslImpl
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import javax.inject.Inject
@@ -32,14 +33,13 @@ abstract class KotlinMultiplatformExtension
final override val targets: NamedDomainObjectCollection<KotlinTarget> = project.container(KotlinTarget::class.java)
@Deprecated("Because only IR compiler is left, no more necessary to know about compiler type in properties")
override val compilerTypeFromProperties: KotlinJsCompilerType? = null
internal suspend fun awaitTargets(): NamedDomainObjectCollection<KotlinTarget> {
AfterFinaliseDsl.await()
return targets
}
override val compilerTypeFromProperties: KotlinJsCompilerType? = project.kotlinPropertiesProvider.jsCompiler
private val presetExtension = project.objects.newInstance(
DefaultTargetsFromPresetExtension::class.java,
{ this },
@@ -16,16 +16,20 @@ 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.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.targets.js.calculateJsCompilerType
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.castIsolatedKotlinPluginClassLoaderAware
import org.jetbrains.kotlin.gradle.utils.configureExperimentalTryK2
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import javax.inject.Inject
@@ -228,10 +232,9 @@ abstract class KotlinJsProjectExtension(project: Project) :
KotlinJsCompilerTypeHolder {
lateinit var irPreset: KotlinJsIrSingleTargetPreset
private val targetSetObservers = mutableListOf<(KotlinJsTargetDsl?) -> Unit>()
lateinit var legacyPreset: KotlinJsSingleTargetPreset
@Deprecated("Because only IR compiler is left, no more necessary to know about compiler type in properties")
override val compilerTypeFromProperties: KotlinJsCompilerType? = null
private val targetSetObservers = mutableListOf<(KotlinJsTargetDsl?) -> Unit>()
// target is public property
// Users can write kotlin.target and it should work
@@ -247,6 +250,41 @@ abstract class KotlinJsProjectExtension(project: Project) :
targetSetObservers.add(observer)
}
companion object {
internal fun warnAboutDeprecatedCompiler(project: Project, compilerType: KotlinJsCompilerType) {
if (PropertiesProvider(project).jsCompilerNoWarn) return
val logger = project.logger
when (compilerType) {
KotlinJsCompilerType.LEGACY -> logger.warn(LEGACY_DEPRECATED)
KotlinJsCompilerType.IR -> {}
KotlinJsCompilerType.BOTH -> logger.warn(BOTH_DEPRECATED)
}
}
private val LEGACY_DEPRECATED =
"""
|
|==========
|This project currently uses the Kotlin/JS Legacy compiler backend, which has been deprecated and will be removed in a future release.
|
|Please migrate the project to the new IR-based compiler (https://kotl.in/jsir).
|==========
|
""".trimMargin()
private val BOTH_DEPRECATED =
"""
|
|==========
|This project currently uses Both mode, which requires the Kotlin/JS Legacy compiler backend.
|This backend has been deprecated and will be removed in a future release.
|
|Please migrate the project to the new IR-based compiler (https://kotl.in/jsir).
|==========
|
""".trimMargin()
}
@Deprecated("Use js() instead", ReplaceWith("js()"))
@Suppress("DEPRECATION")
override val target: KotlinJsTargetDsl
@@ -257,15 +295,49 @@ abstract class KotlinJsProjectExtension(project: Project) :
return _target!!
}
override val compilerTypeFromProperties: KotlinJsCompilerType? = project.kotlinPropertiesProvider.jsCompiler
@Suppress("DEPRECATION")
private fun jsInternal(
compiler: KotlinJsCompilerType? = null,
body: KotlinJsTargetDsl.() -> Unit,
body: KotlinJsTargetDsl.() -> Unit
): KotlinJsTargetDsl {
if (_target != null) {
val previousCompilerType = _target!!.calculateJsCompilerType()
check(compiler == null || previousCompilerType == compiler) {
"You already registered Kotlin/JS target with another compiler: ${previousCompilerType.lowerName}"
}
}
if (_target == null) {
val compilerOrDefault = compiler ?: defaultJsCompilerType
val target: KotlinJsTargetDsl = irPreset
.createTarget("js")
val compilerOrFromProperties = compiler ?: compilerTypeFromProperties
val compilerOrDefault = compilerOrFromProperties ?: defaultJsCompilerType
warnAboutDeprecatedCompiler(project, compilerOrDefault)
val target: KotlinJsTargetDsl = when (compilerOrDefault) {
KotlinJsCompilerType.LEGACY -> legacyPreset
.also {
it.irPreset = null
}
.createTarget("js")
KotlinJsCompilerType.IR -> irPreset
.also {
it.mixedMode = false
}
.createTarget("js")
KotlinJsCompilerType.BOTH -> legacyPreset
.also {
irPreset.mixedMode = true
it.irPreset = irPreset
}
.createTarget(
lowerCamelCaseName(
"js",
LEGACY.lowerName
)
)
}
this._target = target
@@ -279,19 +351,19 @@ abstract class KotlinJsProjectExtension(project: Project) :
fun js(
compiler: KotlinJsCompilerType = defaultJsCompilerType,
body: KotlinJsTargetDsl.() -> Unit = { },
body: KotlinJsTargetDsl.() -> Unit = { }
): KotlinJsTargetDsl = jsInternal(compiler, body)
fun js(
compiler: String,
body: KotlinJsTargetDsl.() -> Unit = { },
body: KotlinJsTargetDsl.() -> Unit = { }
): KotlinJsTargetDsl = js(
KotlinJsCompilerType.byArgument(compiler),
body
)
fun js(
body: KotlinJsTargetDsl.() -> Unit = { },
body: KotlinJsTargetDsl.() -> Unit = { }
) = jsInternal(body = body)
fun js() = js { }
@@ -330,7 +402,7 @@ abstract class KotlinCommonProjectExtension(project: Project) : KotlinSingleJava
internal set
open fun target(
body: KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>.() -> Unit,
body: KotlinWithJavaTarget<KotlinMultiplatformCommonOptions, KotlinMultiplatformCommonCompilerOptions>.() -> Unit
) = target.run(body)
}
@@ -6,8 +6,11 @@
package org.jetbrains.kotlin.gradle.dsl
import org.gradle.api.Action
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension.Companion.warnAboutDeprecatedCompiler
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.js.calculateJsCompilerType
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@KotlinGradlePluginDsl
interface KotlinTargetContainerWithJsPresetFunctions :
@@ -80,12 +83,72 @@ private fun KotlinTargetContainerWithJsPresetFunctions.jsInternal(
compiler: KotlinJsCompilerType? = null,
configure: KotlinJsTargetDsl.() -> Unit
): KotlinJsTargetDsl {
val existingTarget = getExistingTarget(name, compiler)
val kotlinJsCompilerType = (compiler
?: existingTarget?.calculateJsCompilerType())
val compilerOrDefault = kotlinJsCompilerType
?: defaultJsCompilerType
val targetName = getTargetName(name, compilerOrDefault)
if (existingTarget != null) {
val previousCompilerType = existingTarget.calculateJsCompilerType()
check(compiler == null || previousCompilerType == compiler) {
"You already registered Kotlin/JS target '$targetName' with another compiler: ${previousCompilerType.lowerName}"
}
}
@Suppress("UNCHECKED_CAST")
return configureOrCreate(
name,
targetName,
presets.getByName(
"js"
lowerCamelCaseName(
"js",
if (compilerOrDefault == KotlinJsCompilerType.LEGACY) null else compilerOrDefault.lowerName
)
) as KotlinTargetPreset<KotlinJsTargetDsl>,
configure
).also { target ->
warnAboutDeprecatedCompiler(target.project, compilerOrDefault)
}
}
// Try to find existing target with exact name
// and with append suffix Legacy in case when compiler for found target is BOTH,
// and removed suffix Legacy in case when current compiler is BOTH
private fun KotlinTargetContainerWithJsPresetFunctions.getExistingTarget(
name: String,
compiler: KotlinJsCompilerType?
): KotlinJsTargetDsl? {
fun getPreviousTarget(
targetName: String,
currentBoth: Boolean
): KotlinJsTargetDsl? {
val singleTarget = targets.findByName(
targetName
) as KotlinJsTargetDsl?
return singleTarget?.let {
val previousCompiler = it.calculateJsCompilerType()
if (compiler == KotlinJsCompilerType.BOTH && currentBoth || previousCompiler == KotlinJsCompilerType.BOTH && !currentBoth) {
it
} else null
}
}
val targetNameCandidate = getTargetName(name, compiler)
return targets.findByName(targetNameCandidate) as KotlinJsTargetDsl?
?: getPreviousTarget(targetNameCandidate.removeJsCompilerSuffix(KotlinJsCompilerType.LEGACY), true)
?: getPreviousTarget(lowerCamelCaseName(targetNameCandidate, KotlinJsCompilerType.LEGACY.lowerName), false)
}
private fun getTargetName(name: String, compiler: KotlinJsCompilerType?): String {
return lowerCamelCaseName(
name,
if (compiler == KotlinJsCompilerType.BOTH) KotlinJsCompilerType.LEGACY.lowerName else null
)
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.SemVer
@@ -78,6 +79,10 @@ private fun KotlinTarget.addKotlinDomApiDependency(
dependencies: DependencyHandler,
coreLibrariesVersion: Provider<String>
) {
if (this is KotlinJsTarget) {
irTarget?.addKotlinDomApiDependency(configurations, dependencies, coreLibrariesVersion)
}
compilations.configureEach { compilation ->
if (compilation.platformType != KotlinPlatformType.js) return@configureEach
if (compilation !is KotlinJsIrCompilation) return@configureEach
@@ -350,7 +350,6 @@ abstract class AbstractKotlinJsPluginWrapper : KotlinBasePluginWrapper() {
override val projectExtensionClass: KClass<out KotlinJsProjectExtension>
get() = KotlinJsProjectExtension::class
@Suppress("DEPRECATION")
override fun whenBuildEvaluated(project: Project) = project.runProjectConfigurationHealthCheck {
val isJsTargetUninitialized = (project.kotlinExtension as KotlinJsProjectExtension)
._target == null
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.internal.artifactTypeAttribute
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
@@ -431,6 +432,10 @@ fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
val publishJsCompilerAttribute = PropertiesProvider(target.project).publishJsCompilerAttribute
if (publishJsCompilerAttribute && target is KotlinJsTarget) {
attributes.attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.legacy)
}
if (publishJsCompilerAttribute && target is KotlinJsIrTarget) {
if (target.platformType == KotlinPlatformType.js) {
attributes.attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
@@ -241,6 +241,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val wasmStabilityNoWarn: Boolean
get() = booleanProperty("kotlin.wasm.stability.nowarn") ?: false
val jsCompilerNoWarn: Boolean
get() = booleanProperty("$jsCompilerProperty.nowarn") ?: false
val ignoreDisabledNativeTargets: Boolean?
get() = booleanProperty(KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS)
@@ -440,12 +443,27 @@ internal class PropertiesProvider private constructor(private val project: Proje
val errorJsGenerateExternals: Boolean?
get() = booleanProperty("kotlin.js.generate.externals")
/**
* Use Kotlin/JS backend compiler type
*/
val jsCompiler: KotlinJsCompilerType?
get() = this.property(jsCompilerProperty)?.let { KotlinJsCompilerType.byArgumentOrNull(it) }
/**
* Use Kotlin/JS backend compiler publishing attribute
*/
val publishJsCompilerAttribute: Boolean
get() = this.booleanProperty("$jsCompilerProperty.publish.attribute") ?: true
/**
* Use Kotlin/JS backend compiler type
*/
val jsGenerateExecutableDefault: Boolean
get() = (booleanProperty("kotlin.js.generate.executable.default") ?: true).also {
KotlinBuildStatsService.getInstance()
?.report(StringMetrics.JS_GENERATE_EXECUTABLE_DEFAULT, it.toString())
}
val stdlibDefaultDependency: Boolean
get() = booleanProperty(KOTLIN_STDLIB_DEFAULT_DEPENDENCY) ?: true
@@ -130,7 +130,13 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
fun setupDefaultPresets(project: Project) {
with(project.multiplatformExtension.presets) {
add(KotlinJvmTargetPreset(project))
add(KotlinJsIrTargetPreset(project))
add(KotlinJsTargetPreset(project).apply { irPreset = null })
add(KotlinJsIrTargetPreset(project).apply { mixedMode = false })
add(
KotlinJsTargetPreset(project).apply {
irPreset = KotlinJsIrTargetPreset(project).apply { mixedMode = true }
}
)
add(KotlinWasmTargetPreset(project))
add(project.objects.newInstance(KotlinAndroidTargetPreset::class.java, project))
add(KotlinJvmWithJavaTargetPreset(project))
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationC
import org.jetbrains.kotlin.gradle.plugin.mpp.javaSourceSets
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.utils.*
@@ -50,6 +51,20 @@ internal object NativeKotlinCompilationDependencyConfigurationsFactory :
}
}
internal object JsKotlinCompilationDependencyConfigurationsFactory :
KotlinCompilationImplFactory.KotlinCompilationDependencyConfigurationsFactory {
override fun create(target: KotlinTarget, compilationName: String): KotlinCompilationConfigurationsContainer {
val defaultNaming = ConfigurationNaming.Default(target, compilationName)
return KotlinCompilationDependencyConfigurationsContainer(
target, compilationName, withRuntime = true,
naming = ConfigurationNaming.Js(target, compilationName),
compileClasspathConfigurationName = defaultNaming.name(compileClasspath),
runtimeClasspathConfigurationName = defaultNaming.name(runtimeClasspath)
)
}
}
internal class JvmWithJavaCompilationDependencyConfigurationsFactory(private val target: KotlinWithJavaTarget<*, *>) :
KotlinCompilationImplFactory.KotlinCompilationDependencyConfigurationsFactory {
override fun create(target: KotlinTarget, compilationName: String): KotlinCompilationConfigurationsContainer {
@@ -77,6 +92,22 @@ private fun interface ConfigurationNaming {
target.disambiguationClassifier, compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, *parts
)
}
class Js(
private val target: KotlinTarget,
private val compilationName: String
) : ConfigurationNaming {
override fun name(vararg parts: String): String = lowerCamelCaseName(
target.disambiguationClassifierInPlatform, compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }, *parts
)
private val KotlinTarget.disambiguationClassifierInPlatform: String?
get() = when (this) {
is KotlinJsTarget -> disambiguationClassifierInPlatform
is KotlinJsIrTarget -> disambiguationClassifierInPlatform
else -> error("Unexpected target type of $this")
}
}
}
private const val compilation = "compilation"
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetsContainer
import org.jetbrains.kotlin.gradle.plugin.sources.android.kotlinAndroidSourceSetLayout
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
internal class DefaultKotlinCompilationSourceSetsContainerFactory(
@@ -48,4 +50,28 @@ internal class AndroidCompilationSourceSetsContainerFactory(
?: lowerCamelCaseName(target.disambiguationClassifier, compilationName)
return KotlinCompilationSourceSetsContainer(target.project.kotlinExtension.sourceSets.maybeCreate(sourceSetName))
}
}
}
internal object JsCompilationSourceSetsContainerFactory : KotlinCompilationImplFactory.KotlinCompilationSourceSetsContainerFactory {
override fun create(target: KotlinTarget, compilationName: String): KotlinCompilationSourceSetsContainer {
val defaultSourceSetName = lowerCamelCaseName(
if (target is KotlinJsTarget && target.irTarget != null) target.disambiguationClassifierInPlatform
else target.disambiguationClassifier,
compilationName
)
return KotlinCompilationSourceSetsContainer(target.project.kotlinExtension.sourceSets.maybeCreate(defaultSourceSetName))
}
}
internal object JsIrCompilationSourceSetsContainerFactory : KotlinCompilationImplFactory.KotlinCompilationSourceSetsContainerFactory {
override fun create(target: KotlinTarget, compilationName: String): KotlinCompilationSourceSetsContainer {
val defaultSourceSetName = lowerCamelCaseName(
if (target is KotlinJsIrTarget && target.mixedMode) target.disambiguationClassifierInPlatform
else target.disambiguationClassifier,
compilationName
)
return KotlinCompilationSourceSetsContainer(target.project.kotlinExtension.sourceSets.maybeCreate(defaultSourceSetName))
}
}
@@ -7,10 +7,17 @@ package org.jetbrains.kotlin.gradle.plugin.sources
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.targets
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.utils.getOrCreate
import java.io.File
@@ -101,10 +108,67 @@ internal class DefaultKotlinSourceSetFactory(
if (project.isKotlinGranularMetadataEnabled) {
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.usageByName(KotlinUsages.KOTLIN_METADATA))
}
project.afterEvaluate {
setJsCompilerIfNecessary(sourceSet, this)
}
}
}
}
// KT-47163
// It is necessary to set jsCompilerAttribute to configurations which associated with ONLY js source sets
// Otherwise configuration cannot be resolved because ambiguity between IR and Legacy variants inside one module
private val notOnlyJsSourceSets = mutableSetOf<KotlinSourceSet>()
private val jsOnlySourceSetsAttributes = mutableMapOf<KotlinSourceSet, KotlinJsCompilerAttribute>()
private fun setJsCompilerIfNecessary(sourceSet: KotlinSourceSet, configuration: Configuration) {
if (sourceSet in notOnlyJsSourceSets) return
if (sourceSet in jsOnlySourceSetsAttributes) {
configuration.attributes.attribute(
KotlinJsCompilerAttribute.jsCompilerAttribute,
jsOnlySourceSetsAttributes.getValue(sourceSet)
)
return
}
project.kotlinExtension.targets
.filter { it !is KotlinJsIrTarget && it !is KotlinJsTarget }
.forEach { target ->
target.compilations.forEach { compilation ->
notOnlyJsSourceSets.addAll(compilation.allKotlinSourceSets)
}
}
if (sourceSet in notOnlyJsSourceSets) return
fun chooseCompilerAttribute(target: KotlinTarget): KotlinJsCompilerAttribute {
if (target is KotlinJsIrTarget) {
return KotlinJsCompilerAttribute.ir
}
target as KotlinJsTarget
return if (target.irTarget != null) KotlinJsCompilerAttribute.ir else KotlinJsCompilerAttribute.legacy
}
project.kotlinExtension.targets
.filter { it is KotlinJsTarget || (it is KotlinJsIrTarget && it.platformType == KotlinPlatformType.js) }
.forEach { target ->
target.compilations
.filterIsInstance<KotlinJsCompilation>()
.forEach { compilation ->
if (sourceSet in compilation.allKotlinSourceSets) {
val compilerAttribute = chooseCompilerAttribute(target)
jsOnlySourceSetsAttributes[sourceSet] = compilerAttribute
configuration.attributes.attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, compilerAttribute)
return
}
}
}
}
override fun doCreateSourceSet(name: String): DefaultKotlinSourceSet =
project.objects.newInstance(DefaultKotlinSourceSet::class.java, project, name)
}
@@ -6,7 +6,24 @@
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsCompilationSourceSetsContainerFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsKotlinCompilationDependencyConfigurationsFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinJsCompilerOptionsFactory
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued")
typealias KotlinJsCompilationFactory = KotlinJsIrCompilationFactory
class KotlinJsCompilationFactory internal constructor(
override val target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinCompilationFactory<KotlinJsCompilation> {
override val itemClass: Class<KotlinJsCompilation>
get() = KotlinJsCompilation::class.java
private val compilationImplFactory: KotlinCompilationImplFactory = KotlinCompilationImplFactory(
compilerOptionsFactory = KotlinJsCompilerOptionsFactory,
compilationSourceSetsContainerFactory = JsCompilationSourceSetsContainerFactory,
compilationDependencyConfigurationsFactory = JsKotlinCompilationDependencyConfigurationsFactory
)
override fun create(name: String): KotlinJsCompilation = target.project.objects.newInstance(
itemClass, compilationImplFactory.create(target, name)
)
}
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.setupGeneralKotlinExtensionParameters
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrSingleTargetPreset
import org.jetbrains.kotlin.gradle.utils.*
@@ -36,6 +38,7 @@ open class KotlinJsPlugin: Plugin<Project> {
kotlinExtension.apply {
irPreset = KotlinJsIrSingleTargetPreset(project)
legacyPreset = KotlinJsSingleTargetPreset(project)
}
project.runProjectConfigurationHealthCheckWhenEvaluated {
@@ -5,11 +5,248 @@
package org.jetbrains.kotlin.gradle.targets.js
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinTarget
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Companion.runTaskNameSuffix
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.LEGACY
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.PRIMARY_SINGLE_COMPONENT_NAME
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsBinaryContainer
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinNodeJs
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport
import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.setProperty
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.addIfNotNull
import javax.inject.Inject
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.WARNING)
abstract class KotlinJsTarget : KotlinTarget, InternalKotlinTarget {
@Deprecated("Only for compatibility")
val irTarget = null
abstract class KotlinJsTarget
@Inject
constructor(
project: Project,
platformType: KotlinPlatformType
) :
KotlinTargetWithBinaries<KotlinJsCompilation, KotlinJsBinaryContainer>(project, platformType),
KotlinTargetWithTests<JsAggregatingExecutionSource, KotlinJsReportAggregatingTestRun>,
KotlinJsTargetDsl,
KotlinJsSubTargetContainerDsl {
override lateinit var testRuns: NamedDomainObjectContainer<KotlinJsReportAggregatingTestRun>
internal set
override var moduleName: String? = null
set(value) {
check(!isBrowserConfigured && !isNodejsConfigured) {
"Please set moduleName before initialize browser() or nodejs()"
}
field = value
}
internal val commonFakeApiElementsConfigurationName: String
get() = lowerCamelCaseName(
irTarget?.let {
this.disambiguationClassifierInPlatform
} ?: disambiguationClassifier,
"commonFakeApiElements"
)
val disambiguationClassifierInPlatform: String?
get() = if (irTarget != null) {
disambiguationClassifier?.removeJsCompilerSuffix(LEGACY)
} else {
disambiguationClassifier
}
override val kotlinComponents: Set<KotlinTargetComponent> by lazy {
if (irTarget == null)
super.kotlinComponents
else {
val mainCompilation = compilations.getByName(MAIN_COMPILATION_NAME)
val usageContexts = createUsageContexts(mainCompilation).toMutableSet()
usageContexts += irTarget!!.createUsageContexts(irTarget!!.compilations.getByName(MAIN_COMPILATION_NAME))
val componentName =
if (project.kotlinExtension is KotlinMultiplatformExtension)
irTarget?.let { targetName.removeJsCompilerSuffix(LEGACY) } ?: targetName
else PRIMARY_SINGLE_COMPONENT_NAME
usageContexts.addIfNotNull(
createSourcesJarAndUsageContextIfPublishable(
mainCompilation,
componentName,
dashSeparatedName(targetName.toLowerCaseAsciiOnly()),
mavenScope = KotlinUsageContext.MavenScope.RUNTIME
)
)
val result = createKotlinVariant(componentName, mainCompilation, usageContexts)
setOf(result)
}
}
override fun createUsageContexts(producingCompilation: KotlinCompilation<*>): Set<DefaultKotlinUsageContext> {
val usageContexts = super.createUsageContexts(producingCompilation)
if (isMpp!!) return usageContexts
return usageContexts +
DefaultKotlinUsageContext(
compilation = compilations.getByName(MAIN_COMPILATION_NAME),
mavenScope = KotlinUsageContext.MavenScope.COMPILE,
dependencyConfigurationName = commonFakeApiElementsConfigurationName,
overrideConfigurationArtifacts = project.setProperty { emptyList() }
)
}
override fun createKotlinVariant(
componentName: String,
compilation: KotlinCompilation<*>,
usageContexts: Set<DefaultKotlinUsageContext>
): KotlinVariant {
return super.createKotlinVariant(componentName, compilation, usageContexts).apply {
irTarget?.let {
artifactTargetName = targetName.removeJsCompilerSuffix(LEGACY)
}
}
}
override val binaries: KotlinJsBinaryContainer
get() = compilations.withType(KotlinJsCompilation::class.java)
.named(MAIN_COMPILATION_NAME)
.map { it.binaries }
.get()
var irTarget: KotlinJsIrTarget? = null
internal set
open var isMpp: Boolean? = null
internal set
val testTaskName get() = testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).testTaskName
val testTask: TaskProvider<KotlinTestReport>
get() = checkNotNull(project.locateTask(testTaskName))
val runTaskName get() = lowerCamelCaseName(disambiguationClassifier, runTaskNameSuffix)
val runTask: TaskProvider<Task>
get() = project.locateOrRegisterTask(runTaskName) {
it.description = "Run js on all configured platforms"
}
private val propertiesProvider = PropertiesProvider(project)
private val commonLazy by lazy {
NpmResolverPlugin.apply(project)
}
//Browser
private val browserLazyDelegate = lazy {
commonLazy
project.objects.newInstance(KotlinBrowserJs::class.java, this).also {
it.configure()
if (propertiesProvider.jsGenerateExecutableDefault && irTarget == null) {
binaries.executable()
}
browserConfiguredHandlers.forEach { handler ->
handler(it)
}
browserConfiguredHandlers.clear()
}
}
private val browserConfiguredHandlers = mutableListOf<KotlinJsBrowserDsl.() -> Unit>()
override val browser by browserLazyDelegate
override val isBrowserConfigured: Boolean
get() = browserLazyDelegate.isInitialized()
override fun browser(body: KotlinJsBrowserDsl.() -> Unit) {
body(browser)
irTarget?.browser(body)
}
//node.js
private val nodejsLazyDelegate = lazy {
commonLazy
project.objects.newInstance(KotlinNodeJs::class.java, this).also {
it.configure()
if (propertiesProvider.jsGenerateExecutableDefault && irTarget == null) {
binaries.executable()
}
nodejsConfiguredHandlers.forEach { handler ->
handler(it)
}
nodejsConfiguredHandlers.clear()
}
}
private val nodejsConfiguredHandlers = mutableListOf<KotlinJsNodeDsl.() -> Unit>()
override val nodejs by nodejsLazyDelegate
override val isNodejsConfigured: Boolean
get() = nodejsLazyDelegate.isInitialized()
override fun nodejs(body: KotlinJsNodeDsl.() -> Unit) {
body(nodejs)
irTarget?.nodejs(body)
}
override fun whenBrowserConfigured(body: KotlinJsBrowserDsl.() -> Unit) {
if (browserLazyDelegate.isInitialized()) {
browser(body)
} else {
browserConfiguredHandlers += body
}
}
override fun whenNodejsConfigured(body: KotlinJsNodeDsl.() -> Unit) {
if (nodejsLazyDelegate.isInitialized()) {
nodejs(body)
} else {
nodejsConfiguredHandlers += body
}
}
override fun useCommonJs() {
compilations.all {
it.kotlinOptions {
moduleKind = "commonjs"
sourceMap = true
sourceMapEmbedSources = null
}
}
irTarget?.useCommonJs()
}
override fun useEsModules() {
error("ES modules are not supported in legacy JS compiler. Please, use IR one instead.")
}
override fun generateTypeScriptDefinitions() {
project.logger.warn("Legacy compiler does not support generation of TypeScript Definitions")
}
}
@@ -5,7 +5,123 @@
package org.jetbrains.kotlin.gradle.targets.js
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetConfigurator
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.attributes.Usage
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.mpp.addSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.testing.testTaskName
import java.util.concurrent.Callable
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued")
typealias KotlinJsTargetConfigurator = KotlinJsIrTargetConfigurator
open class KotlinJsTargetConfigurator :
KotlinOnlyTargetConfigurator<KotlinJsCompilation, KotlinJsTarget>(true),
KotlinTargetWithTestsConfigurator<KotlinJsReportAggregatingTestRun, KotlinJsTarget> {
override val testRunClass: Class<KotlinJsReportAggregatingTestRun> get() = KotlinJsReportAggregatingTestRun::class.java
override fun createTestRun(
name: String,
target: KotlinJsTarget
): KotlinJsReportAggregatingTestRun {
val result = target.project.objects.newInstance(
KotlinJsReportAggregatingTestRun::class.java,
name,
target
)
val testTask = target.project.kotlinTestRegistry.getOrCreateAggregatedTestTask(
name = result.testTaskName,
description = "Run JS tests for all platforms"
)
// workaround to avoid the infinite recursion in item factories of the target and the subtargets:
target.testRuns.matching { it.name == name }.whenObjectAdded {
it.configureAllExecutions {
// do not do anything with the aggregated test run, but ensure that they are created
}
}
result.executionTask = testTask
return result
}
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider()
return Kotlin2JsSourceSetProcessor(tasksProvider, KotlinCompilationInfo(compilation))
}
override fun configureCompilationDefaults(target: KotlinJsTarget) {
val project = target.project
target.compilations.all { compilation ->
@Suppress("DEPRECATION")
compilation.addSourceSet(compilation.defaultSourceSet)
configureResourceProcessing(
compilation,
compilation.processResourcesTaskName,
project.files(Callable { compilation.allKotlinSourceSets.map { it.resources } })
)
createLifecycleTaskInternal(compilation)
}
}
private fun createLifecycleTaskInternal(compilation: KotlinJsCompilation) {
val project = compilation.target.project
compilation.output.classesDirs.from(project.files().builtBy(compilation.compileAllTaskName))
val compileAllTask = project.locateTask<Task>(compilation.compileAllTaskName)
if (compileAllTask != null) {
compileAllTask.configure {
it.dependsOn(compilation.compileKotlinTaskName)
it.dependsOn(compilation.processResourcesTaskName)
}
} else {
project.registerTask<DefaultTask>(compilation.compileAllTaskName) {
it.group = LifecycleBasePlugin.BUILD_GROUP
it.description = "Assembles outputs for compilation '${compilation.name}' of target '${compilation.target.name}'"
it.dependsOn(compilation.compileKotlinTaskName)
it.dependsOn(compilation.processResourcesTaskName)
}
}
}
override fun configureCompilations(target: KotlinJsTarget) {
super.configureCompilations(target)
target.compilations.all {
it.kotlinOptions {
moduleKind = "umd"
sourceMap = true
sourceMapEmbedSources = null
}
}
}
override fun defineConfigurationsForTarget(target: KotlinJsTarget) {
super.defineConfigurationsForTarget(target)
if (target.isMpp!!) return
target.project.configurations.maybeCreate(
target.commonFakeApiElementsConfigurationName
).apply {
description = "Common Fake API elements for main."
isVisible = false
isCanBeResolved = false
isCanBeConsumed = true
attributes.attribute<Usage>(Usage.USAGE_ATTRIBUTE, KotlinUsages.producerApiUsage(target))
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
}
}
}
@@ -9,18 +9,102 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.WARNING)
abstract class KotlinJsTargetPreset(
open class KotlinJsTargetPreset(
project: Project
) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinJsCompilation>, KotlinJsCompilation>(
) : KotlinOnlyTargetPreset<KotlinJsTarget, KotlinJsCompilation>(
project
)
) {
var irPreset: KotlinJsIrTargetPreset? = null
internal set
@Suppress("DEPRECATION")
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.HIDDEN)
abstract class KotlinJsSingleTargetPreset(
open val isMpp: Boolean
get() = true
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun useDisambiguationClassifierAsSourceSetNamePrefix() = irPreset == null
override fun overrideDisambiguationClassifierOnIdeImport(name: String): String? =
irPreset?.let {
name.removeJsCompilerSuffix(KotlinJsCompilerType.LEGACY)
}
override fun instantiateTarget(name: String): KotlinJsTarget {
return project.objects.newInstance(
KotlinJsTarget::class.java,
project,
platformType
).apply {
this.irTarget = irPreset?.createTarget(
lowerCamelCaseName(
name.removeJsCompilerSuffix(KotlinJsCompilerType.LEGACY),
KotlinJsCompilerType.IR.lowerName
)
)?.also {
it.legacyTarget = this
}
this.isMpp = this@KotlinJsTargetPreset.isMpp
project.runProjectConfigurationHealthCheckWhenEvaluated {
val buildStatsService = KotlinBuildStatsService.getInstance()
when {
isBrowserConfigured && isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "both")
isBrowserConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "browser")
isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "nodejs")
!isBrowserConfigured && !isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "none")
}
Unit
}
}
}
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator()
override fun getName(): String {
return lowerCamelCaseName(
PRESET_NAME,
irPreset?.let { KotlinJsCompilerType.BOTH.lowerName }
)
}
override fun createCompilationFactory(forTarget: KotlinJsTarget): KotlinJsCompilationFactory {
return KotlinJsCompilationFactory(forTarget)
}
companion object {
const val PRESET_NAME = "js"
}
}
class KotlinJsSingleTargetPreset(
project: Project
) : KotlinJsTargetPreset(
project
)
) {
override val isMpp: Boolean
get() = false
override fun overrideDisambiguationClassifierOnIdeImport(name: String): String? =
null
// In a Kotlin/JS single-platform project, we don't need any disambiguation suffixes or prefixes in the names:
override fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<KotlinJsCompilation>): String? =
irPreset?.let {
super.provideTargetDisambiguationClassifier(target)
?.removePrefix(target.name.removeJsCompilerSuffix(KotlinJsCompilerType.LEGACY))
?.decapitalizeAsciiOnly()
}
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator()
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2020 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.
*/
package org.jetbrains.kotlin.gradle.targets.js
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
fun KotlinJsTargetDsl.calculateJsCompilerType(): KotlinJsCompilerType {
return when {
this is KotlinJsTarget && this.irTarget == null -> KotlinJsCompilerType.LEGACY
this is KotlinJsIrTarget && !this.mixedMode -> KotlinJsCompilerType.IR
this is KotlinJsTarget && this.irTarget != null -> KotlinJsCompilerType.BOTH
else -> throw IllegalStateException("Unable to find previous Kotlin/JS compiler type for $this")
}
}
@@ -14,11 +14,14 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinTargetWithBinaries
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.DEVELOPMENT
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.PRODUCTION
import org.jetbrains.kotlin.gradle.targets.js.subtargets.DefaultDistribution
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinJsSubTarget
import org.jetbrains.kotlin.gradle.targets.js.subtargets.createDefaultDistribution
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
@@ -79,7 +82,22 @@ constructor(
return compilation.binaries.executableIrInternal(compilation)
}
throw GradleException("Target should be KotlinJsIrTarget, but found $target")
if (target is KotlinJsTarget) {
target.irTarget
?.let { throw IllegalStateException("Can't use `executable()` with 'both' compiler type") }
target.whenBrowserConfigured {
(this as KotlinJsSubTarget).produceExecutable()
}
target.whenNodejsConfigured {
(this as KotlinJsSubTarget).produceExecutable()
}
return compilation.binaries.executableLegacyInternal(compilation)
}
throw GradleException("Target should be either KotlinJsTarget or KotlinJsIrTarget, but found $target")
}
internal fun executableIrInternal(compilation: KotlinJsCompilation): List<JsBinary> = createBinaries(
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.gradle.targets.js.ir
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationFriendPathsResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsIrCompilationSourceSetsContainerFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.JsKotlinCompilationDependencyConfigurationsFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinJsCompilerOptionsFactory
@@ -20,10 +22,12 @@ class KotlinJsIrCompilationFactory internal constructor(
private val compilationImplFactory: KotlinCompilationImplFactory = KotlinCompilationImplFactory(
compilerOptionsFactory = KotlinJsCompilerOptionsFactory,
compilationFriendPathsResolver = DefaultKotlinCompilationFriendPathsResolver(
friendArtifactResolver = { _ ->
friendArtifactResolver = DefaultKotlinCompilationFriendPathsResolver.FriendArtifactResolver { _ ->
target.project.files()
}
),
compilationSourceSetsContainerFactory = JsIrCompilationSourceSetsContainerFactory,
compilationDependencyConfigurationsFactory = JsKotlinCompilationDependencyConfigurationsFactory
)
override fun create(name: String): KotlinJsIrCompilation = target.project.objects.newInstance(
@@ -10,17 +10,15 @@ import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Companion.runTaskNameSuffix
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinUsageContext
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinTargetWithBinaries
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsageContext
import org.jetbrains.kotlin.gradle.targets.js.JsAggregatingExecutionSource
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin
@@ -37,6 +35,7 @@ abstract class KotlinJsIrTarget
constructor(
project: Project,
platformType: KotlinPlatformType,
internal val mixedMode: Boolean
) :
KotlinTargetWithBinaries<KotlinJsIrCompilation, KotlinJsBinaryContainer>(project, platformType),
KotlinTargetWithTests<JsAggregatingExecutionSource, KotlinJsReportAggregatingTestRun>,
@@ -51,6 +50,9 @@ constructor(
open var isMpp: Boolean? = null
internal set
var legacyTarget: KotlinJsTarget? = null
internal set
override var moduleName: String? = null
set(value) {
check(!isBrowserConfigured && !isNodejsConfigured) {
@@ -62,7 +64,7 @@ constructor(
override fun createUsageContexts(producingCompilation: KotlinCompilation<*>): Set<DefaultKotlinUsageContext> {
val usageContexts = super.createUsageContexts(producingCompilation)
if (isMpp!!) return usageContexts
if (isMpp!! || mixedMode) return usageContexts
return usageContexts +
DefaultKotlinUsageContext(
@@ -75,10 +77,20 @@ constructor(
internal val commonFakeApiElementsConfigurationName: String
get() = lowerCamelCaseName(
disambiguationClassifier,
if (mixedMode)
disambiguationClassifierInPlatform
else
disambiguationClassifier,
"commonFakeApiElements"
)
val disambiguationClassifierInPlatform: String?
get() = if (mixedMode) {
disambiguationClassifier?.removeJsCompilerSuffix(KotlinJsCompilerType.IR)
} else {
disambiguationClassifier
}
override val binaries: KotlinJsBinaryContainer
get() = compilations.withType(KotlinJsIrCompilation::class.java)
.named(MAIN_COMPILATION_NAME)
@@ -6,39 +6,42 @@
package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinOnlyTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTargetPreset
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
open class KotlinJsIrTargetPreset(
project: Project,
project: Project
) : KotlinOnlyTargetPreset<KotlinJsIrTarget, KotlinJsIrCompilation>(
project
) {
internal var mixedMode: Boolean? = null
open val isMpp: Boolean
get() = true
override val platformType: KotlinPlatformType = KotlinPlatformType.js
override fun instantiateTarget(name: String): KotlinJsIrTarget {
return project.objects.newInstance(KotlinJsIrTarget::class.java, project, platformType).apply {
return project.objects.newInstance(KotlinJsIrTarget::class.java, project, platformType, mixedMode).apply {
this.isMpp = this@KotlinJsIrTargetPreset.isMpp
project.runProjectConfigurationHealthCheckWhenEvaluated {
val buildStatsService = KotlinBuildStatsService.getInstance()
when {
isBrowserConfigured && isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "both")
isBrowserConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "browser")
isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "nodejs")
!isBrowserConfigured && !isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "none")
if (!mixedMode) {
project.runProjectConfigurationHealthCheckWhenEvaluated {
val buildStatsService = KotlinBuildStatsService.getInstance()
when {
isBrowserConfigured && isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "both")
isBrowserConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "browser")
isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "nodejs")
!isBrowserConfigured && !isNodejsConfigured -> buildStatsService?.report(StringMetrics.JS_TARGET_MODE, "none")
}
Unit
}
Unit
}
}
}
@@ -50,17 +53,20 @@ open class KotlinJsIrTargetPreset(
//TODO[Ilya Goncharov] remove public morozov
public override fun createCompilationFactory(
forTarget: KotlinJsIrTarget,
forTarget: KotlinJsIrTarget
): KotlinCompilationFactory<KotlinJsIrCompilation> =
KotlinJsIrCompilationFactory(forTarget)
companion object {
val JS_PRESET_NAME = "js"
val JS_PRESET_NAME = lowerCamelCaseName(
"js",
KotlinJsCompilerType.IR.lowerName
)
}
}
class KotlinJsIrSingleTargetPreset(
project: Project,
project: Project
) : KotlinJsIrTargetPreset(
project
) {
@@ -69,7 +75,13 @@ class KotlinJsIrSingleTargetPreset(
// In a Kotlin/JS single-platform project, we don't need any disambiguation suffixes or prefixes in the names:
override fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<KotlinJsIrCompilation>): String? {
return null
return if (mixedMode!!) {
super.provideTargetDisambiguationClassifier(target)
?.removePrefix(target.name.removeJsCompilerSuffix(KotlinJsCompilerType.IR))
?.decapitalizeAsciiOnly()
} else {
null
}
}
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinJsIrCompilation, KotlinJsIrTarget> =
@@ -24,7 +24,7 @@ class KotlinWasmTargetPreset(
project.logger.warn("New 'wasm' target is Work-in-Progress and is subject to change without notice.")
}
val irTarget = project.objects.newInstance(KotlinJsIrTarget::class.java, project, KotlinPlatformType.wasm)
val irTarget = project.objects.newInstance(KotlinJsIrTarget::class.java, project, KotlinPlatformType.wasm, false)
irTarget.isMpp = true
return irTarget
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution
import java.io.Serializable
@@ -72,6 +73,15 @@ class KotlinProjectNpmResolver(
addCompilation(compilation)
}
}
// Hack for mixed mode, when target is JS and contain JS-IR
if (target is KotlinJsTarget) {
target.irTarget?.compilations?.all { compilation ->
if (compilation is KotlinJsCompilation) {
addCompilation(compilation)
}
}
}
}
}
@@ -5,10 +5,386 @@
package org.jetbrains.kotlin.gradle.targets.js.subtargets
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce
import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.ir.executeTaskBaseName
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.testing.karma.KotlinKarma
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode
import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackDevtool
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.doNotTrackStateCompat
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import java.io.File
import javax.inject.Inject
import org.jetbrains.kotlin.gradle.tasks.KotlinJsDce as KotlinJsDceTask
@Suppress("DEPRECATION")
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.HIDDEN)
abstract class KotlinBrowserJs :
KotlinJsSubTarget(),
KotlinJsBrowserDsl
abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
KotlinJsSubTarget(target, "browser"),
KotlinJsBrowserDsl {
private val webpackTaskConfigurations: MutableList<Action<KotlinWebpack>> = mutableListOf()
private val runTaskConfigurations: MutableList<Action<KotlinWebpack>> = mutableListOf()
private val dceConfigurations: MutableList<Action<KotlinJsDce>> = mutableListOf()
private val distribution: Distribution = createDefaultDistribution(project, target.targetName)
override val testTaskDescription: String
get() = "Run all ${target.name} tests inside browser using karma and webpack"
override fun configureDefaultTestFramework(testTask: KotlinJsTest) {
testTask.useKarma {
useChromeHeadless()
}
}
override fun commonWebpackConfig(body: Action<KotlinWebpackConfig>) {
webpackTaskConfigurations.add {
it.webpackConfigApplier(body)
}
runTaskConfigurations.add {
it.webpackConfigApplier(body)
}
testTask(Action {
it.onTestFrameworkSet {
if (it is KotlinKarma) {
body.execute(it.webpackConfig)
}
}
})
}
override fun runTask(body: Action<KotlinWebpack>) {
runTaskConfigurations.add(body)
}
@ExperimentalDistributionDsl
override fun distribution(body: Action<Distribution>) {
body.execute(distribution)
}
override fun webpackTask(body: Action<KotlinWebpack>) {
webpackTaskConfigurations.add(body)
}
@ExperimentalDceDsl
override fun dceTask(body: Action<KotlinJsDce>) {
dceConfigurations.add(body)
}
override fun configureMain(compilation: KotlinJsCompilation) {
val (dceTaskProvider, devDceTaskProvider) = compilation.configureDceTasks()
// Adding dce tasks to additional JS compilations
target.compilations.configureEach {
if (!it.isMain() && !it.isTest()) it.configureDceTasks()
}
configureRun(
compilation = compilation,
dceTaskProvider = dceTaskProvider,
devDceTaskProvider = devDceTaskProvider
)
configureBuild(
compilation = compilation,
dceTaskProvider = dceTaskProvider,
devDceTaskProvider = devDceTaskProvider
)
}
private fun KotlinJsCompilation.configureDceTasks(): Pair<TaskProvider<KotlinJsDceTask>, TaskProvider<KotlinJsDceTask>> {
val dceTaskProvider = configureDce(
compilation = this,
dev = false
)
val devDceTaskProvider = configureDce(
compilation = this,
dev = true
)
return dceTaskProvider to devDceTaskProvider
}
private fun configureRun(
compilation: KotlinJsCompilation,
dceTaskProvider: TaskProvider<KotlinJsDceTask>,
devDceTaskProvider: TaskProvider<KotlinJsDceTask>
) {
val project = compilation.target.project
val nodeJs = project.rootProject.kotlinNodeJsExtension
val commonRunTask = registerSubTargetTask<Task>(disambiguateCamelCased(RUN_TASK_NAME)) {}
compilation.binaries
.all { binary ->
val type = binary.mode
val distsDirectory = project.distsDirectory
val archivesName = project.archivesName
val runTask = registerSubTargetTask<KotlinWebpack>(
disambiguateCamelCased(
binary.executeTaskBaseName,
RUN_TASK_NAME
),
listOf(compilation)
) { task ->
task.outputDirectory.convention(distsDirectory).finalizeValueOnRead()
task.args.add(0, "serve")
task.description = "start ${type.name.toLowerCaseAsciiOnly()} webpack dev server"
task.devServer = KotlinWebpackConfig.DevServer(
open = true,
static = mutableListOf(compilation.output.resourcesDir.canonicalPath),
client = KotlinWebpackConfig.DevServer.Client(
KotlinWebpackConfig.DevServer.Client.Overlay(
errors = true,
warnings = false
)
)
)
task.doNotTrackStateCompat("Tracked by external webpack tool")
task.commonConfigure(
compilation = compilation,
dceTaskProvider = dceTaskProvider,
devDceTaskProvider = devDceTaskProvider,
mode = type,
configurationActions = runTaskConfigurations,
nodeJs = nodeJs,
defaultArchivesName = archivesName,
)
}
if (type == KotlinJsBinaryMode.DEVELOPMENT) {
target.runTask.dependsOn(runTask)
commonRunTask.configure {
it.dependsOn(runTask)
}
}
}
}
private fun configureBuild(
compilation: KotlinJsCompilation,
dceTaskProvider: TaskProvider<KotlinJsDceTask>,
devDceTaskProvider: TaskProvider<KotlinJsDceTask>
) {
val project = compilation.target.project
val nodeJs = project.rootProject.kotlinNodeJsExtension
val processResourcesTask = target.project.tasks.named(compilation.processResourcesTaskName)
val distributeResourcesTask = registerSubTargetTask<Copy>(
disambiguateCamelCased(
DISTRIBUTE_RESOURCES_TASK_NAME
)
) {
it.from(processResourcesTask)
it.into(distribution.directory)
}
val assembleTaskProvider = project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)
assembleTaskProvider.dependsOn(distributeResourcesTask)
compilation.binaries
.all { binary ->
val type = binary.mode
val archivesName = project.archivesName
val webpackTask = registerSubTargetTask<KotlinWebpack>(
disambiguateCamelCased(
binary.executeTaskBaseName,
WEBPACK_TASK_NAME
),
listOf(compilation)
) { task ->
task.dependsOn(
distributeResourcesTask
)
task.description = "build webpack ${type.name.toLowerCaseAsciiOnly()} bundle"
task.outputDirectory.fileValue(distribution.directory).finalizeValueOnRead()
BuildMetricsService.registerIfAbsent(project)?.let {
task.buildMetricsService.value(it)
}
task.commonConfigure(
compilation = compilation,
dceTaskProvider = dceTaskProvider,
devDceTaskProvider = devDceTaskProvider,
mode = type,
configurationActions = webpackTaskConfigurations,
nodeJs = nodeJs,
defaultArchivesName = archivesName,
)
}
if (type == KotlinJsBinaryMode.PRODUCTION) {
assembleTaskProvider.dependsOn(webpackTask)
val webpackCommonTask = registerSubTargetTask<Task>(
disambiguateCamelCased(WEBPACK_TASK_NAME)
) {
it.dependsOn(webpackTask)
}
registerSubTargetTask<Task>(disambiguateCamelCased(DISTRIBUTION_TASK_NAME)) {
it.dependsOn(webpackCommonTask)
it.dependsOn(distributeResourcesTask)
it.outputs.dir(distribution.directory)
}
}
}
}
private fun KotlinWebpack.commonConfigure(
compilation: KotlinJsCompilation,
dceTaskProvider: TaskProvider<KotlinJsDceTask>,
devDceTaskProvider: TaskProvider<KotlinJsDceTask>,
mode: KotlinJsBinaryMode,
configurationActions: List<Action<KotlinWebpack>>,
nodeJs: NodeJsRootExtension,
defaultArchivesName: Property<String>,
) {
dependsOn(
nodeJs.npmInstallTaskProvider,
nodeJs.storeYarnLockTaskProvider,
target.project.tasks.named(compilation.processResourcesTaskName)
)
configureOptimization(mode)
val actualDceTaskProvider = when (mode) {
KotlinJsBinaryMode.PRODUCTION -> dceTaskProvider
KotlinJsBinaryMode.DEVELOPMENT -> devDceTaskProvider
}
dependsOn(actualDceTaskProvider)
inputFilesDirectory.set(
actualDceTaskProvider.flatMap { dceTask ->
compilation.compileTaskProvider.flatMap { compileTask ->
dceTask.destinationDirectory
}
}
)
entryModuleName.set(
compilation.compileTaskProvider.flatMap { compileTask ->
compileTask.outputFileProperty.map { it.nameWithoutExtension }
}
)
this.esModules.convention(false).finalizeValueOnRead()
resolveFromModulesFirst = true
mainOutputFileName.convention(defaultArchivesName.orElse("main").map { "$it.js" }).finalizeValueOnRead()
configurationActions.forEach { configure ->
configure.execute(this)
}
}
private fun configureDce(
compilation: KotlinJsCompilation,
dev: Boolean
): TaskProvider<KotlinJsDceTask> {
val project = compilation.target.project
val dceTaskName = lowerCamelCaseName(
DCE_TASK_PREFIX,
if (dev) DCE_DEV_PART else null,
compilation.target.disambiguationClassifier,
compilation.name.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME },
DCE_TASK_SUFFIX
)
val kotlinTask = compilation.compileKotlinTaskProvider
return project.registerTask(dceTaskName) {
if (dev) {
it.dceOptions.devMode = true
} else {
dceConfigurations.forEach { configure ->
configure.execute(it)
}
}
it.kotlinFilesOnly = true
it.libraries.from(project.configurations.getByName(compilation.runtimeDependencyConfigurationName))
it.destinationDirectory.set(
it.dceOptions.outputDirectory?.let { File(it) }
?: compilation.npmProject.dir.resolve(if (dev) DCE_DEV_DIR else DCE_DIR)
)
it.defaultCompilerClasspath.setFrom(project.configurations.named(COMPILER_CLASSPATH_CONFIGURATION_NAME))
it.runViaBuildToolsApi.value(false).disallowChanges() // The legacy backend task is not going to be supported
it.setSource(kotlinTask.map { it.outputFileProperty })
}
}
private fun KotlinWebpack.configureOptimization(kind: KotlinJsBinaryMode) {
mode = getByKind(
kind = kind,
releaseValue = Mode.PRODUCTION,
debugValue = Mode.DEVELOPMENT
)
devtool = getByKind(
kind = kind,
releaseValue = WebpackDevtool.SOURCE_MAP,
debugValue = WebpackDevtool.EVAL_SOURCE_MAP
)
}
private fun <T> getByKind(
kind: KotlinJsBinaryMode,
releaseValue: T,
debugValue: T
): T = when (kind) {
KotlinJsBinaryMode.PRODUCTION -> releaseValue
KotlinJsBinaryMode.DEVELOPMENT -> debugValue
}
companion object {
const val DCE_TASK_PREFIX = "processDce"
private const val DCE_DEV_PART = "dev"
const val DCE_TASK_SUFFIX = "kotlinJs"
const val DCE_DIR = "kotlin-dce"
const val DCE_DEV_DIR = "kotlin-dce-dev"
const val PRODUCTION = "production"
const val DEVELOPMENT = "development"
private const val WEBPACK_TASK_NAME = "webpack"
private const val DISTRIBUTE_RESOURCES_TASK_NAME = "distributeResources"
private const val DISTRIBUTION_TASK_NAME = "distribution"
}
}
@@ -5,7 +5,169 @@
package org.jetbrains.kotlin.gradle.targets.js.subtargets
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Task
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.WARNING)
abstract class KotlinJsSubTarget : KotlinJsSubTargetDsl
abstract class KotlinJsSubTarget(
val target: KotlinJsTarget,
private val disambiguationClassifier: String
) : KotlinJsSubTargetDsl {
val project get() = target.project
private val nodeJs = project.rootProject.kotlinNodeJsExtension
private val nodeJsTaskProviders = project.rootProject.kotlinNodeJsExtension
abstract val testTaskDescription: String
final override lateinit var testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun>
private set
protected val taskGroupName = "Kotlin $disambiguationClassifier"
private val produceExecutable: Unit by lazy {
configureMain()
}
internal fun produceExecutable() {
produceExecutable
}
internal fun configure() {
configureTests()
target.compilations.all {
val npmProject = it.npmProject
it.kotlinOptions {
outputFile = npmProject.dir.resolve(npmProject.main).canonicalPath
}
}
}
override fun testTask(body: Action<KotlinJsTest>) {
testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).executionTask.configure(body)
}
protected fun disambiguateCamelCased(vararg names: String): String =
lowerCamelCaseName(target.disambiguationClassifier, disambiguationClassifier, *names)
private fun configureTests() {
testRuns = project.container(KotlinJsPlatformTestRun::class.java) { name -> KotlinJsPlatformTestRun(name, target) }.also {
(this as ExtensionAware).extensions.add(this::testRuns.name, it)
}
testRuns.all { configureTestRunDefaults(it) }
testRuns.create(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME)
}
protected open fun configureTestRunDefaults(testRun: KotlinJsPlatformTestRun) {
target.compilations.matching { it.name == KotlinCompilation.TEST_COMPILATION_NAME }.all { compilation ->
configureTestsRun(testRun, compilation)
}
}
private fun configureTestsRun(testRun: KotlinJsPlatformTestRun, compilation: KotlinJsCompilation) {
fun KotlinJsPlatformTestRun.subtargetTestTaskName(): String = disambiguateCamelCased(
lowerCamelCaseName(
name.takeIf { it != KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME },
AbstractKotlinTargetConfigurator.testTaskNameSuffix
)
)
val testJs = project.registerTask<KotlinJsTest>(
testRun.subtargetTestTaskName(),
listOf(compilation)
) { testJs ->
val compileTask = compilation.compileTaskProvider
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
testJs.description = testTaskDescription
val compileOutputFile = compileTask.flatMap { it.outputFileProperty }
testJs.inputFileProperty.fileProvider(compileOutputFile)
testJs.dependsOn(
nodeJsTaskProviders.npmInstallTaskProvider,
nodeJsTaskProviders.storeYarnLockTaskProvider,
compileTask,
nodeJsTaskProviders.nodeJsSetupTaskProvider
)
testJs.onlyIf {
compileOutputFile.get().exists()
}
testJs.targetName = listOfNotNull(target.disambiguationClassifier, disambiguationClassifier)
.takeIf { it.isNotEmpty() }
?.joinToString()
testJs.configureConventions()
}
testRun.executionTask = testJs
target.testRuns.matching { it.name == testRun.name }.all { parentTestRun ->
target.project.kotlinTestRegistry.registerTestTask(
testJs,
parentTestRun.executionTask
)
}
project.whenEvaluated {
testJs.configure {
if (it.testFramework == null) {
configureDefaultTestFramework(it)
}
if (it.enabled) {
nodeJs.taskRequirements.addTaskRequirements(it)
}
}
}
}
protected abstract fun configureDefaultTestFramework(testTask: KotlinJsTest)
private fun configureMain() {
target.compilations.all { compilation ->
if (compilation.isMain()) {
configureMain(compilation)
}
}
}
protected abstract fun configureMain(compilation: KotlinJsCompilation)
internal inline fun <reified T : Task> registerSubTargetTask(
name: String,
args: List<Any> = emptyList(),
noinline body: (T) -> (Unit)
): TaskProvider<T> =
project.registerTask(name, args) {
it.group = taskGroupName
body(it)
}
companion object {
const val RUN_TASK_NAME = "run"
}
}
@@ -5,10 +5,54 @@
package org.jetbrains.kotlin.gradle.targets.js.subtargets
import org.gradle.api.Action
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.withType
import javax.inject.Inject
@Suppress("DEPRECATION")
@Deprecated("The Kotlin/JS legacy target is deprecated and its support completely discontinued", level = DeprecationLevel.HIDDEN)
abstract class KotlinNodeJs :
KotlinJsSubTarget(),
KotlinJsNodeDsl
abstract class KotlinNodeJs @Inject constructor(target: KotlinJsTarget) :
KotlinJsSubTarget(target, "node"),
KotlinJsNodeDsl {
override val testTaskDescription: String
get() = "Run all ${target.name} tests inside nodejs using the builtin test framework"
private val runTaskName = disambiguateCamelCased("run")
override fun runTask(body: Action<NodeJsExec>) {
project.tasks.withType<NodeJsExec>().named(runTaskName).configure(body)
}
@ExperimentalDistributionDsl
override fun distribution(body: Action<Distribution>) {
TODO("Not yet implemented")
}
override fun testTask(body: Action<KotlinJsTest>) {
super<KotlinJsSubTarget>.testTask(body)
}
override fun configureDefaultTestFramework(testTask: KotlinJsTest) {
testTask.useMocha { }
}
override fun configureMain(compilation: KotlinJsCompilation) {
configureRun(compilation)
}
private fun configureRun(
compilation: KotlinJsCompilation
) {
val runTaskHolder = NodeJsExec.create(compilation, disambiguateCamelCased(RUN_TASK_NAME)) {
group = taskGroupName
inputFileProperty.fileProvider(compilation.compileKotlinTaskProvider.flatMap { it.outputFileProperty })
}
target.runTask.dependsOn(runTaskHolder)
}
}
@@ -43,7 +43,6 @@ internal open class BaseKotlin2JsCompileConfig<TASK : Kotlin2JsCompile>(
}
)
@Suppress("DEPRECATION")
task.destinationDirectory
.convention(
project.objects.directoryProperty().fileProvider(
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.gradle.unitTests
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmWithJavaTargetPreset
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinWasmTargetPreset
@@ -26,6 +27,7 @@ class KotlinHierarchyBuilderTest {
buildProjectWithMPP().multiplatformExtension.presets
// JS targets are special and therefore are only handled manually using `withJs()`
.filter { it !is KotlinJsTargetPreset }
.filter { it !is KotlinJsIrTargetPreset }
.filter { it !is KotlinWasmTargetPreset }