diff --git a/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api b/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api index 7c7af1cd099..5ec3ab0a764 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api +++ b/libraries/tools/kotlin-gradle-plugin-api/api/kotlin-gradle-plugin-api.api @@ -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 { diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder.kt index 7a45da3798e..c7e6ef85446 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinJsCompilerTypeHolder.kt @@ -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 } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api index 890e7582b9e..fab62da5c0d 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -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; diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt index 85a220a63c5..6b0ddd97dfb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt @@ -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 = 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 { AfterFinaliseDsl.await() return targets } + override val compilerTypeFromProperties: KotlinJsCompilerType? = project.kotlinPropertiesProvider.jsCompiler + private val presetExtension = project.objects.newInstance( DefaultTargetsFromPresetExtension::class.java, { this }, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index 82308b2750c..ba4a085f02e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -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.() -> Unit, + body: KotlinWithJavaTarget.() -> Unit ) = target.run(body) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithJsPresetFunctions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithJsPresetFunctions.kt index 65cca3d79b3..85b6506abe5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithJsPresetFunctions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithJsPresetFunctions.kt @@ -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, 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 ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinDomApiDependencyManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinDomApiDependencyManagement.kt index 0c071a10c90..f1e5881c3e2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinDomApiDependencyManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/kotlinDomApiDependencyManagement.kt @@ -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 ) { + 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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 9bd0ffd7e2c..1a4e5451a61 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -350,7 +350,6 @@ abstract class AbstractKotlinJsPluginWrapper : KotlinBasePluginWrapper() { override val projectExtensionClass: KClass get() = KotlinJsProjectExtension::class - @Suppress("DEPRECATION") override fun whenBuildEvaluated(project: Project) = project.runProjectConfigurationHealthCheck { val isJsTargetUninitialized = (project.kotlinExtension as KotlinJsProjectExtension) ._target == null diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 64941b0f833..08c2c8a03be 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index e9929617b6d..dec9909dbc5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index 7c1f5eec614..b74d8f4d72e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -130,7 +130,13 @@ class KotlinMultiplatformPlugin : Plugin { 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)) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationDependencyConfigurationsFactories.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationDependencyConfigurationsFactories.kt index 8ad88bda6b5..526a76816e4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationDependencyConfigurationsFactories.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationDependencyConfigurationsFactories.kt @@ -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" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationSourceSetsContainerFactories.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationSourceSetsContainerFactories.kt index c39e3b49699..50be528080f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationSourceSetsContainerFactories.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationSourceSetsContainerFactories.kt @@ -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)) } -} \ No newline at end of file +} + +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)) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt index a3cbf2d273b..7e1a202c512 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt @@ -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() + + private val jsOnlySourceSetsAttributes = mutableMapOf() + + 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() + .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) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationFactory.kt index 8974cb1ca11..61aac6fff82 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationFactory.kt @@ -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 \ No newline at end of file +class KotlinJsCompilationFactory internal constructor( + override val target: KotlinOnlyTarget +) : KotlinCompilationFactory { + override val itemClass: Class + 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) + ) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlugin.kt index b65b53ec02b..85892b9ee38 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlugin.kt @@ -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 { kotlinExtension.apply { irPreset = KotlinJsIrSingleTargetPreset(project) + legacyPreset = KotlinJsSingleTargetPreset(project) } project.runProjectConfigurationHealthCheckWhenEvaluated { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt index 2c77a6c1b36..8b2738914ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt @@ -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(project, platformType), + KotlinTargetWithTests, + KotlinJsTargetDsl, + KotlinJsSubTargetContainerDsl { + override lateinit var testRuns: NamedDomainObjectContainer + 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 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 { + 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 + ): 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 + get() = checkNotNull(project.locateTask(testTaskName)) + + val runTaskName get() = lowerCamelCaseName(disambiguationClassifier, runTaskNameSuffix) + val runTask: TaskProvider + 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 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 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") + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt index 941eb52b8bd..cc7e64bbde4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt @@ -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 \ No newline at end of file +open class KotlinJsTargetConfigurator : + KotlinOnlyTargetConfigurator(true), + KotlinTargetWithTestsConfigurator { + + override val testRunClass: Class 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(compilation.compileAllTaskName) + if (compileAllTask != null) { + compileAllTask.configure { + it.dependsOn(compilation.compileKotlinTaskName) + it.dependsOn(compilation.processResourcesTaskName) + } + } else { + project.registerTask(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_ATTRIBUTE, KotlinUsages.producerApiUsage(target)) + attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt index 69bbb62d861..3e2a7676b32 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetPreset.kt @@ -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, KotlinJsCompilation>( +) : KotlinOnlyTargetPreset( 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): String? = + irPreset?.let { + super.provideTargetDisambiguationClassifier(target) + ?.removePrefix(target.name.removeJsCompilerSuffix(KotlinJsCompilerType.LEGACY)) + ?.decapitalizeAsciiOnly() + } + + override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator() +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargets.kt new file mode 100644 index 00000000000..d984bd7b9e5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargets.kt @@ -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") + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsBinaryContainer.kt index 3267e58ea04..a8697e0318e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsBinaryContainer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsBinaryContainer.kt @@ -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 = createBinaries( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrCompilationFactory.kt index a686a061a69..a6b4e34ab85 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrCompilationFactory.kt @@ -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( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt index 3dc86098fe1..3d90deea5c0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt @@ -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(project, platformType), KotlinTargetWithTests, @@ -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 { 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) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTargetPreset.kt index 6471cac7682..e69fb249ffa 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTargetPreset.kt @@ -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( 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 = 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): String? { - return null + return if (mixedMode!!) { + super.provideTargetDisambiguationClassifier(target) + ?.removePrefix(target.name.removeJsCompilerSuffix(KotlinJsCompilerType.IR)) + ?.decapitalizeAsciiOnly() + } else { + null + } } override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator = diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinWasmTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinWasmTargetPreset.kt index 840e95ae095..8582709cf71 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinWasmTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinWasmTargetPreset.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt index 4af636bd6e6..c3d980e76fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt @@ -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) + } + } + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt index 3f3adb6a936..e8042d3e7a8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt @@ -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 \ No newline at end of file +abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : + KotlinJsSubTarget(target, "browser"), + KotlinJsBrowserDsl { + + private val webpackTaskConfigurations: MutableList> = mutableListOf() + private val runTaskConfigurations: MutableList> = mutableListOf() + private val dceConfigurations: MutableList> = 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) { + 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) { + runTaskConfigurations.add(body) + } + + @ExperimentalDistributionDsl + override fun distribution(body: Action) { + body.execute(distribution) + } + + override fun webpackTask(body: Action) { + webpackTaskConfigurations.add(body) + } + + @ExperimentalDceDsl + override fun dceTask(body: Action) { + 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> { + 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, + devDceTaskProvider: TaskProvider + ) { + val project = compilation.target.project + val nodeJs = project.rootProject.kotlinNodeJsExtension + + val commonRunTask = registerSubTargetTask(disambiguateCamelCased(RUN_TASK_NAME)) {} + + compilation.binaries + .all { binary -> + val type = binary.mode + val distsDirectory = project.distsDirectory + val archivesName = project.archivesName + + val runTask = registerSubTargetTask( + 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, + devDceTaskProvider: TaskProvider + ) { + val project = compilation.target.project + val nodeJs = project.rootProject.kotlinNodeJsExtension + + val processResourcesTask = target.project.tasks.named(compilation.processResourcesTaskName) + + val distributeResourcesTask = registerSubTargetTask( + 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( + 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( + disambiguateCamelCased(WEBPACK_TASK_NAME) + ) { + it.dependsOn(webpackTask) + } + registerSubTargetTask(disambiguateCamelCased(DISTRIBUTION_TASK_NAME)) { + it.dependsOn(webpackCommonTask) + it.dependsOn(distributeResourcesTask) + + it.outputs.dir(distribution.directory) + } + } + } + } + + private fun KotlinWebpack.commonConfigure( + compilation: KotlinJsCompilation, + dceTaskProvider: TaskProvider, + devDceTaskProvider: TaskProvider, + mode: KotlinJsBinaryMode, + configurationActions: List>, + nodeJs: NodeJsRootExtension, + defaultArchivesName: Property, + ) { + 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 { + 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 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" + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt index 5caf1b372de..dd0e2441e4b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt @@ -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 \ No newline at end of file +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 + 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) { + 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( + 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 registerSubTargetTask( + name: String, + args: List = emptyList(), + noinline body: (T) -> (Unit) + ): TaskProvider = + project.registerTask(name, args) { + it.group = taskGroupName + body(it) + } + + companion object { + const val RUN_TASK_NAME = "run" + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt index 983209a3e49..b59d8621d2a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt @@ -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 \ No newline at end of file +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) { + project.tasks.withType().named(runTaskName).configure(body) + } + + @ExperimentalDistributionDsl + override fun distribution(body: Action) { + TODO("Not yet implemented") + } + + override fun testTask(body: Action) { + super.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) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/Kotlin2JsCompileConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/Kotlin2JsCompileConfig.kt index c1118afecb8..5056c0ca1c7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/Kotlin2JsCompileConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/configuration/Kotlin2JsCompileConfig.kt @@ -43,7 +43,6 @@ internal open class BaseKotlin2JsCompileConfig( } ) - @Suppress("DEPRECATION") task.destinationDirectory .convention( project.objects.directoryProperty().fileProvider( diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinHierarchyBuilderTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinHierarchyBuilderTest.kt index e9e81a919c9..f4bb94340fe 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinHierarchyBuilderTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinHierarchyBuilderTest.kt @@ -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 }