Introduce new Gradle plugin org.jetbrains.kotlin.js

This is the Gradle plugin that does not rely on the
Java model and uses the same Kotlin model
(target – compilation – source set) as MPP does.

Also add JS DCE support for this plugin.

Issue #KT-5756 Fixed
Issue #KT-13256 Fixed
Issue #KT-16355 Fixed
Issue #KT-20156 Fixed
Issue #KT-29284 Fixed
This commit is contained in:
Sergey Igushkin
2019-03-12 18:24:23 +03:00
parent 03b74343c8
commit 91ce7ef5ca
18 changed files with 230 additions and 57 deletions
@@ -349,4 +349,36 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
assertNotContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
}
}
@Test
fun testNewKotlinJsPlugin() = with(Project("kotlin-js-plugin-project", GradleVersionRequired.AtLeast("4.10.2"))) {
setupWorkingDir()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl)
build("publish", "runDceKotlin", "test", "runDceBenchmarkKotlin") {
assertSuccessful()
assertTasksExecuted(
":compileKotlin", ":compileTestKotlin", ":compileBenchmarkKotlin",
":runDceKotlin", ":runDceBenchmarkKotlin"
)
val moduleDir = "build/repo/com/example/kotlin-js-plugin/1.0/"
val publishedJar = fileInWorkingDir(moduleDir + "kotlin-js-plugin-1.0.jar")
ZipFile(publishedJar).use { zip ->
val entries = zip.entries().asSequence().map { it.name }
assertTrue { "kotlin-js-plugin.js" in entries }
}
val publishedPom = fileInWorkingDir(moduleDir + "kotlin-js-plugin-1.0.pom")
val kotlinVersion = defaultBuildOptions().kotlinVersion
val pomText = publishedPom.readText().replace(Regex("\\s+"), "")
assertTrue { "kotlinx-html-js</artifactId><version>0.6.10</version><scope>compile</scope>" in pomText }
assertTrue { "kotlin-stdlib-js</artifactId><version>$kotlinVersion</version><scope>runtime</scope>" in pomText }
assertFileExists(moduleDir + "kotlin-js-plugin-1.0-sources.jar")
}
}
}
@@ -0,0 +1,47 @@
plugins {
kotlin("js").version("<pluginMarkerVersion>")
id("kotlin-dce-js")
`maven-publish`
}
group = "com.example"
version = "1.0"
repositories {
mavenLocal()
jcenter()
}
kotlin.sourceSets {
main {
dependencies {
api("org.jetbrains.kotlinx:kotlinx-html-js:0.6.10")
implementation(kotlin("stdlib-js"))
}
}
test {
dependencies {
implementation(kotlin("test-js"))
}
}
}
kotlin.target.compilations.create("benchmark") {
defaultSourceSet.dependencies {
val main by kotlin.target.compilations
implementation(main.compileDependencyFiles + main.output.classesDirs)
runtimeOnly(main.runtimeDependencyFiles)
}
}
publishing {
publications {
create("default", MavenPublication::class.java) {
from(kotlin.target.components.single())
artifact(tasks.getByName("kotlinSourcesJar"))
}
}
repositories {
maven("$buildDir/repo")
}
}
@@ -0,0 +1,15 @@
pluginManagement {
resolutionStrategy {
eachPlugin{
when (requested.id.id) {
"kotlin-dce-js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:<pluginMarkerVersion>")
}
}
}
repositories {
mavenLocal()
gradlePluginPortal()
}
}
rootProject.name = "kotlin-js-plugin"
@@ -0,0 +1,9 @@
package com.example
class MainBenchmark {
fun runBenchmark() {
main()
hello()
console.log("ran at the speed of light")
}
}
@@ -0,0 +1,15 @@
package com.example
import kotlinx.html.dom.create
import kotlinx.html.js.div
import kotlinx.html.p
import kotlin.browser.document
fun hello() = "hello"
fun main() {
document.create.div {
p { hello() }
}
console.log(hello())
}
@@ -0,0 +1,11 @@
package com.example
import kotlin.test.Test
import kotlin.test.assertEquals
class MainTest {
@Test
fun testHello() {
assertEquals("hello", hello())
}
}
@@ -128,6 +128,11 @@ pluginBundle {
id = "org.jetbrains.kotlin.jvm",
display = "Kotlin JVM plugin"
)
create(
name = "kotlinJsPlugin",
id = "org.jetbrains.kotlin.js",
display = "Kotlin JS plugin"
)
create(
name = "kotlinMultiplatformPlugin",
id = "org.jetbrains.kotlin.multiplatform",
@@ -22,6 +22,8 @@ import org.gradle.api.internal.plugins.DslObject
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import kotlin.reflect.KClass
@@ -72,6 +74,17 @@ open class Kotlin2JsProjectExtension : KotlinSingleJavaTargetExtension() {
internal set
}
open class KotlinJsProjectExtension : KotlinSingleTargetExtension() {
override lateinit var target: KotlinOnlyTarget<KotlinJsCompilation>
@Deprecated(
"Needed for IDE import using the MPP import mechanism",
level = DeprecationLevel.HIDDEN
)
fun getTargets() =
target.project.container(KotlinTarget::class.java).apply { add(target) }
}
open class KotlinCommonProjectExtension : KotlinSingleJavaTargetExtension() {
override lateinit var target: KotlinWithJavaTarget<KotlinMultiplatformCommonOptions>
internal set
@@ -19,10 +19,10 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinJsDce
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -30,14 +30,17 @@ import java.io.File
class KotlinJsDcePlugin : Plugin<Project> {
override fun apply(project: Project) {
val kotlinExtension = project.multiplatformExtensionOrNull ?: run {
project.pluginManager.apply(Kotlin2JsPluginWrapper::class.java)
project.kotlinExtension as KotlinSingleJavaTargetExtension
}
val kotlinExtension =
project.multiplatformExtensionOrNull
?: project.extensions.getByName("kotlin") as? KotlinSingleTargetExtension
?: run {
project.pluginManager.apply(Kotlin2JsPluginWrapper::class.java)
project.kotlinExtension as KotlinSingleTargetExtension
}
fun forEachJsTarget(action: (KotlinTarget) -> Unit) {
when (kotlinExtension) {
is KotlinSingleJavaTargetExtension -> action(kotlinExtension.target)
is KotlinSingleTargetExtension -> action(kotlinExtension.target)
is KotlinMultiplatformExtension ->
kotlinExtension.targets
.matching { it.platformType == KotlinPlatformType.js }
@@ -23,13 +23,13 @@ import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.internal.reflect.Instantiator
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
@@ -139,15 +139,24 @@ open class Kotlin2JsPluginWrapper @Inject constructor(
get() = Kotlin2JsProjectExtension::class
}
open class KotlinJsPluginWrapper @Inject constructor(
fileResolver: FileResolver
) : KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinJsPlugin(kotlinPluginVersion)
override val projectExtensionClass: KClass<out KotlinJsProjectExtension>
get() = KotlinJsProjectExtension::class
}
open class KotlinMultiplatformPluginWrapper @Inject constructor(
fileResolver: FileResolver,
private val instantiator: Instantiator,
private val featurePreviews: FeaturePreviews
) : KotlinBasePluginWrapper(fileResolver) {
override fun getPlugin(project: Project, kotlinGradleBuildServices: KotlinGradleBuildServices): Plugin<Project> =
KotlinMultiplatformPlugin(
fileResolver,
instantiator, kotlinPluginVersion, featurePreviews
kotlinPluginVersion,
featurePreviews
)
override val projectExtensionClass: KClass<out KotlinMultiplatformExtension>
@@ -7,37 +7,25 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.testing.Test
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
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.mpp.*
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.*
import java.util.concurrent.Callable
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>(
@@ -11,7 +11,6 @@ import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.publish.PublicationContainer
@@ -20,7 +19,6 @@ import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.internal.reflect.Instantiator
import org.gradle.jvm.tasks.Jar
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@@ -36,8 +34,6 @@ import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.presetName
class KotlinMultiplatformPlugin(
private val fileResolver: FileResolver,
private val instantiator: Instantiator,
private val kotlinPluginVersion: String,
private val featurePreviews: FeaturePreviews // TODO get rid of this internal API usage once we don't need it
) : Plugin<Project> {
@@ -83,7 +79,7 @@ class KotlinMultiplatformPlugin(
// set up metadata publishing
targetsFromPreset.fromPreset(
KotlinMetadataTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion),
KotlinMetadataTargetPreset(project, kotlinPluginVersion),
METADATA_TARGET_NAME
)
configurePublishingWithMavenPublish(project)
@@ -135,8 +131,8 @@ class KotlinMultiplatformPlugin(
fun setupDefaultPresets(project: Project) {
with(project.multiplatformExtension.presets) {
add(KotlinJvmTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion))
add(KotlinJsTargetPreset(project, instantiator, fileResolver, kotlinPluginVersion))
add(KotlinJvmTargetPreset(project, kotlinPluginVersion))
add(KotlinJsTargetPreset(project, kotlinPluginVersion))
add(KotlinAndroidTargetPreset(project, kotlinPluginVersion))
add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion))
HostManager().targets.forEach { _, target ->
@@ -6,24 +6,23 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.plugin.*
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
protected val project: Project,
private val instantiator: Instantiator,
private val fileResolver: FileResolver,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
protected open fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> =
KotlinTargetConfigurator(createDefaultSourceSets = true, createTestCompilation = true)
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
target.targetName
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = KotlinOnlyTarget<T>(project, platformType).apply {
targetName = name
disambiguationClassifier = name
disambiguationClassifier = provideTargetDisambiguationClassifier(this@apply)
preset = this@KotlinOnlyTargetPreset
val compilationFactory = createCompilationFactory(this)
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaBasePlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.configureDefaultVersionsResolutionStrategy
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
open class KotlinJsPlugin(
private val kotlinPluginVersion: String
) : Plugin<Project> {
override fun apply(project: Project) {
// TODO get rid of this plugin, too? Use the 'base' plugin instead?
// in fact, the attributes schema of the Java base plugin may be required to consume non-MPP Kotlin/JS libs,
// so investigation is needed
project.plugins.apply(JavaBasePlugin::class.java)
checkGradleCompatibility()
val kotlinExtension = project.kotlinExtension as KotlinJsProjectExtension
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
val target = KotlinJsSingleTargetPreset(project, kotlinPluginVersion).createTarget("kotlin")
kotlinExtension.target = target
project.components.addAll(target.components)
}
}
@@ -7,22 +7,16 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
class KotlinJsTargetPreset(
open class KotlinJsTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
@@ -42,3 +36,13 @@ class KotlinJsTargetPreset(
const val PRESET_NAME = "js"
}
}
class KotlinJsSingleTargetPreset(
project: Project,
kotlinPluginVersion: String
) :
KotlinJsTargetPreset(project, kotlinPluginVersion) {
// In a Kotlin/JS single-platform project, we don't need any disambiguation suffixes or prefixes in the names:
override fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<KotlinJsCompilation>): String? = null
}
@@ -7,8 +7,6 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor
@@ -16,13 +14,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
class KotlinJvmTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJvmCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
@@ -7,8 +7,6 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.internal.file.FileResolver
import org.gradle.internal.reflect.Instantiator
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
@@ -17,13 +15,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
class KotlinMetadataTargetPreset(
project: Project,
instantiator: Instantiator,
fileResolver: FileResolver,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinCommonCompilation>(
project,
instantiator,
fileResolver,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.KotlinJsPluginWrapper