Skip to content
This repository was archived by the owner on Jun 27, 2020. It is now read-only.

Commit 188920a

Browse files
committed
Support 1.2.70+ multiplatform model
1 parent 793048a commit 188920a

File tree

7 files changed

+225
-47
lines changed

7 files changed

+225
-47
lines changed

kotlin-frontend/src/main/kotlin/org/jetbrains/kotlin/gradle/frontend/FrontendPlugin.kt

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ class FrontendPlugin : Plugin<Project> {
3737
}
3838
}
3939

40-
project.pluginManager.withPlugin("kotlin2js", ::tryCallBlock)
41-
project.pluginManager.withPlugin("kotlin-platform-js", ::tryCallBlock)
40+
try {
41+
Class.forName("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension")
42+
// This line executed only on Kotlin 1.2.70+
43+
KotlinNewMpp.forEachJsTargetCompilationTasks(project, block)
44+
} catch (e: ClassNotFoundException) {
45+
// Fallback for Kotlin before 1.2.70
46+
project.pluginManager.withPlugin("kotlin2js", ::tryCallBlock)
47+
project.pluginManager.withPlugin("kotlin-platform-js", ::tryCallBlock)
48+
}
4249
}
4350

4451
override fun apply(project: Project) {
@@ -75,6 +82,8 @@ class FrontendPlugin : Plugin<Project> {
7582
description = "Stops dev-server running in background if running"
7683
}
7784

85+
var configured = false
86+
7887
withKotlinPlugin(project) { kotlin2js, _ ->
7988
project.afterEvaluate { project ->
8089
// TODO this need to be done in kotlin plugin itself
@@ -86,41 +95,45 @@ class FrontendPlugin : Plugin<Project> {
8695
?.dir(File(output).parentFile)
8796
}
8897

89-
if (frontend.sourceMaps) {
90-
val kotlinVersion = project.plugins.findPlugin(Kotlin2JsPluginWrapper::class.java)?.kotlinPluginVersion
98+
if (!configured) {
99+
configured = true
100+
if (frontend.sourceMaps) {
101+
val kotlinVersion = project.plugins.findPlugin(Kotlin2JsPluginWrapper::class.java)?.kotlinPluginVersion
91102

92-
if (kotlinVersion != null && compareVersions(kotlinVersion, "1.1.4") < 0) {
93-
project.tasks.withType(KotlinJsCompile::class.java).toList().mapNotNull { compileTask ->
94-
val task = project.tasks.create(compileTask.name + "RelativizeSMAP", RelativizeSourceMapTask::class.java) { task ->
95-
task.compileTask = compileTask
96-
}
103+
if (kotlinVersion != null && compareVersions(kotlinVersion, "1.1.4") < 0) {
104+
project.tasks.withType(KotlinJsCompile::class.java).toList().mapNotNull { compileTask ->
105+
val task = project.tasks.create(compileTask.name + "RelativizeSMAP", RelativizeSourceMapTask::class.java) { task ->
106+
task.compileTask = compileTask
107+
}
97108

98-
task.dependsOn(compileTask)
99-
}
100-
} else {
101-
project.tasks.withType(KotlinJsCompile::class.java).forEach { task ->
102-
if (!task.kotlinOptions.sourceMap) {
103-
project.logger.warn("Source map generation is not enabled for kotlin task ${task.name}")
109+
task.dependsOn(compileTask)
110+
}
111+
} else {
112+
project.tasks.withType(KotlinJsCompile::class.java).forEach { task ->
113+
if (!task.kotlinOptions.sourceMap) {
114+
project.logger.warn("Source map generation is not enabled for kotlin task ${task.name}")
115+
}
104116
}
105117
}
106118
}
107-
}
108119

109-
for ((id, bundles) in frontend.bundles().groupBy { it.bundlerId }) {
110-
val bundler = frontend.bundlers[id] ?: throw GradleException("Bundler $id is not supported (or not plugged-in), required for bundles: ${bundles.map { it.bundleName }}")
120+
for ((id, bundles) in frontend.bundles().groupBy { it.bundlerId }) {
121+
val bundler = frontend.bundlers[id]
122+
?: throw GradleException("Bundler $id is not supported (or not plugged-in), required for bundles: ${bundles.map { it.bundleName }}")
111123

112-
bundler.apply(project, packageManager, packages, bundle, run, stop)
113-
}
124+
bundler.apply(project, packageManager, packages, bundle, run, stop)
125+
}
114126

115-
if (frontend.downloadNodeJsVersion.isNotBlank()) {
116-
val downloadTask = project.tasks.create("nodejs-download", NodeJsDownloadTask::class.java) { task ->
117-
task.version = frontend.downloadNodeJsVersion
118-
if (frontend.nodeJsMirror.isNotBlank()) {
119-
task.mirror = frontend.nodeJsMirror
127+
if (frontend.downloadNodeJsVersion.isNotBlank()) {
128+
val downloadTask = project.tasks.create("nodejs-download", NodeJsDownloadTask::class.java) { task ->
129+
task.version = frontend.downloadNodeJsVersion
130+
if (frontend.nodeJsMirror.isNotBlank()) {
131+
task.mirror = frontend.nodeJsMirror
132+
}
120133
}
121-
}
122134

123-
packages.dependsOn(downloadTask)
135+
packages.dependsOn(downloadTask)
136+
}
124137
}
125138
}
126139
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package org.jetbrains.kotlin.gradle.frontend
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.Task
5+
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
6+
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
7+
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
8+
import org.jetbrains.kotlin.gradle.frontend.npm.UnpackGradleDependenciesTask
9+
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsPluginWrapper
10+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
11+
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
12+
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
13+
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
14+
import java.io.File
15+
import java.util.logging.Logger
16+
import kotlin.reflect.full.declaredMemberProperties
17+
18+
internal const val pluginName = "kotlin-frontend-plugin"
19+
internal val logger = Logger.getLogger(pluginName)
20+
21+
/**
22+
* Extracted class for support new MPP
23+
* (Should not be loaded on Kotlin < 1.2.70)
24+
*/
25+
object KotlinNewMpp {
26+
private val Project.kotlinExtension: KotlinProjectExtension
27+
get() = extensions.getByName("kotlin") as KotlinProjectExtension
28+
29+
private val Project.multiplatformExtension: KotlinMultiplatformExtension?
30+
get() = kotlinExtension as? KotlinMultiplatformExtension
31+
32+
private val KotlinSingleJavaTargetExtension.internalTarget: KotlinTarget
33+
get() = KotlinSingleJavaTargetExtension::class.declaredMemberProperties.find { it.name == "target" }!!.get(this) as KotlinTarget
34+
35+
fun forEachJsTargetCompilationTasks(project: Project, action: (kotlin2js: Task, testKotlin2js: Task) -> Unit) {
36+
forEachJsTarget(project) { mainCompilation, testCompilation ->
37+
val main = requireKotlin2JsCompile(project, mainCompilation.compileKotlinTaskName)
38+
val test = requireKotlin2JsCompile(project, testCompilation.compileKotlinTaskName)
39+
40+
if (main != null && test != null) {
41+
action(main, test)
42+
}
43+
}
44+
}
45+
46+
private fun forEachJsTarget(project: Project, action: (kotlin2js: KotlinCompilation, testKotlin2js: KotlinCompilation) -> Unit) {
47+
val kotlinExtension = project.multiplatformExtension ?: run {
48+
project.pluginManager.apply(Kotlin2JsPluginWrapper::class.java)
49+
project.kotlinExtension as KotlinSingleJavaTargetExtension
50+
}
51+
52+
var hasTarget = false
53+
54+
fun processTarget(target: KotlinTarget, name: String?) {
55+
if (hasTarget) {
56+
logger.warning("$pluginName supports only one js target. Target \"$name\" is ignored")
57+
return
58+
}
59+
60+
hasTarget = true
61+
62+
var mainCompilation: KotlinCompilation? = null
63+
var testCompilation: KotlinCompilation? = null
64+
65+
target.compilations.all { compilation ->
66+
when (compilation.name) {
67+
KotlinCompilation.MAIN_COMPILATION_NAME -> mainCompilation = compilation
68+
KotlinCompilation.TEST_COMPILATION_NAME -> testCompilation = compilation
69+
else -> logger.warning("Unsupported compilation ${name?.let { "$it." }}${compilation.name}. " +
70+
"Only \"main\" and \"test\" compilations are supported by $pluginName")
71+
}
72+
}
73+
74+
if (mainCompilation == null || testCompilation == null) {
75+
logger.severe("$pluginName requires both \"main\" and \"tests\" compilations")
76+
return
77+
}
78+
79+
action(mainCompilation!!, testCompilation!!)
80+
}
81+
82+
when (kotlinExtension) {
83+
is KotlinSingleJavaTargetExtension -> processTarget(kotlinExtension.internalTarget, null)
84+
is KotlinMultiplatformExtension ->
85+
kotlinExtension.targets
86+
.matching { it.platformType == KotlinPlatformType.js }
87+
.all { processTarget(it, it.targetName) }
88+
}
89+
}
90+
91+
private fun requireKotlin2JsCompile(project: Project, name: String?): Kotlin2JsCompile? {
92+
if (name == null) return null
93+
val task = project.tasks.getByName(name) as? Kotlin2JsCompile
94+
if (task == null) {
95+
logger.severe("$pluginName requires task '$name' with type Kotlin2JsCompile")
96+
return null
97+
}
98+
return task
99+
}
100+
101+
fun configureNpmCompileConfigurations(task: UnpackGradleDependenciesTask) {
102+
forEachJsTarget(task.project) { mainCompilation, testCompilation ->
103+
task.customCompileConfiguration = task.project.configurations.getByName(mainCompilation.compileDependencyConfigurationName)
104+
task.customTestCompileConfiguration = task.project.configurations.getByName(testCompilation.compileDependencyConfigurationName)
105+
}
106+
}
107+
108+
fun ktorClassPath(project: Project): Collection<File>? {
109+
val mpp = project.multiplatformExtension ?: return null
110+
val jvmTargets = mpp.targets.filter { it.platformType == KotlinPlatformType.jvm }
111+
if (jvmTargets.isEmpty()) return null
112+
113+
114+
val jvmTarget = jvmTargets.first()
115+
if (jvmTargets.size > 1) logger.warning("Ktor: using target \"${jvmTarget.name}\", other targets ignored")
116+
117+
val main = jvmTarget.compilations.getByName("main")
118+
val runtimeConfiguration = project.configurations.getByName("${jvmTarget.name}RuntimeClasspath")
119+
return main.output.allOutputs.files + runtimeConfiguration
120+
}
121+
}

kotlin-frontend/src/main/kotlin/org/jetbrains/kotlin/gradle/frontend/ktor/KtorExtension.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ open class KtorExtension {
77
var port: Int? = null
88
var jvmOptions: Array<String> = emptyArray()
99
var workDir: Any? = null
10+
var mainClass: String? = null
1011
}

kotlin-frontend/src/main/kotlin/org/jetbrains/kotlin/gradle/frontend/ktor/KtorStartStopTask.kt

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
package org.jetbrains.kotlin.gradle.frontend.ktor
22

3-
import org.gradle.api.plugins.*
4-
import org.gradle.api.tasks.*
5-
import org.jetbrains.kotlin.gradle.frontend.servers.*
6-
import org.jetbrains.kotlin.gradle.frontend.util.*
7-
import java.io.*
8-
import java.net.*
3+
import org.gradle.api.Project
4+
import org.gradle.api.plugins.JavaPluginConvention
5+
import org.gradle.api.tasks.Input
6+
import org.gradle.api.tasks.Internal
7+
import org.gradle.api.tasks.TaskAction
8+
import org.jetbrains.kotlin.gradle.frontend.KotlinNewMpp
9+
import org.jetbrains.kotlin.gradle.frontend.servers.AbstractStartStopTask
10+
import org.jetbrains.kotlin.gradle.frontend.util.LogTail
11+
import org.jetbrains.kotlin.gradle.frontend.util.whereIs
12+
import java.io.File
13+
import java.io.IOException
14+
import java.net.Socket
15+
import java.net.URL
916

1017
/**
1118
* @author Sergey Mashkov
@@ -34,8 +41,9 @@ open class KtorStartStopTask : AbstractStartStopTask<Int>() {
3441
@Input
3542
var shutdownPath = "/ktor/application/shutdown"
3643

37-
@Input
38-
var mainClass = "org.jetbrains.ktor.jetty.DevelopmentHost"
44+
@get: Input
45+
val mainClass: String
46+
get() = ext.mainClass ?: "org.jetbrains.ktor.jetty.DevelopmentHost"
3947

4048
init {
4149
logTailer.rememberLogStartPosition()
@@ -60,20 +68,44 @@ open class KtorStartStopTask : AbstractStartStopTask<Int>() {
6068
val gradleJavaHome = project.findProperty("org.gradle.java.home")?.let { listOf(it.toString() + File.separator + "bin") } ?: emptyList()
6169

6270
return ProcessBuilder(
63-
listOf(whereIs("java", gradleJavaHome).first().absolutePath, "-cp")
64-
+ (project.configurations.flatMap { it.files.filter { it.canRead() && it.extension == "jar" } }
65-
+ project.convention.findPlugin(JavaPluginConvention::class.java)?.sourceSets?.getByName("main")?.output?.toList().orEmpty()
66-
)
67-
.distinct().joinToString(File.pathSeparator) { it.absolutePath }
68-
+ listOf(
69-
"-Dktor.deployment.port=$port",
70-
"-Dktor.deployment.autoreload=true",
71-
"-Dktor.deployment.shutdown.url=$shutdownPath")
71+
listOf<String>()
72+
+ whereIs("java", gradleJavaHome).first().absolutePath
73+
+ "-cp"
74+
+ classPath()
75+
+ ktorOptions()
7276
+ jvmOptions
7377
+ mainClass
7478
).directory(workDir)
7579
}
7680

81+
private fun ktorOptions(): List<String> {
82+
return listOf(
83+
"-Dktor.deployment.port=$port",
84+
"-Dktor.deployment.autoreload=true",
85+
"-Dktor.deployment.shutdown.url=$shutdownPath")
86+
}
87+
88+
private fun classPath() = classPathFiles(project).distinct().joinToString(File.pathSeparator) { it.absolutePath }
89+
90+
private fun classPathFiles(project: Project) =
91+
try {
92+
Class.forName("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension")
93+
// This line executed only on Kotlin 1.2.70+
94+
KotlinNewMpp.ktorClassPath(project)
95+
} catch (e: ClassNotFoundException) {
96+
null
97+
} ?: (jars() + mainOutput())
98+
99+
private fun mainOutput() =
100+
project.convention.findPlugin(JavaPluginConvention::class.java)
101+
?.sourceSets?.getByName("main")
102+
?.output?.toList().orEmpty()
103+
104+
private fun jars() =
105+
project.configurations
106+
.filter { it.isCanBeResolved }
107+
.flatMap { it.files.filter { it.canRead() && it.extension == "jar" } }
108+
77109
override fun stop(state: Int?) {
78110
if (state != null) {
79111
try {

kotlin-frontend/src/main/kotlin/org/jetbrains/kotlin/gradle/frontend/npm/NpmPackageManager.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ class NpmPackageManager(val project: Project) : PackageManager {
7474
if (npm.dependencies.isNotEmpty() || npm.developmentDependencies.isNotEmpty() || project.projectDir.resolve("package.json.d").exists() || requiredDependencies.isNotEmpty()) {
7575

7676
val unpack = project.tasks.create("npm-preunpack", UnpackGradleDependenciesTask::class.java) { task ->
77+
78+
try {
79+
Class.forName("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension")
80+
// This line executed only on Kotlin 1.2.70+
81+
KotlinNewMpp.configureNpmCompileConfigurations(task)
82+
} catch (e: ClassNotFoundException) {}
83+
7784
task.dependenciesProvider = { requiredDependencies }
7885
}
7986
val configure = project.tasks.create("npm-configure", GeneratePackagesJsonTask::class.java) { task ->

kotlin-frontend/src/main/kotlin/org/jetbrains/kotlin/gradle/frontend/npm/UnpackGradleDependenciesTask.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ open class UnpackGradleDependenciesTask : DefaultTask() {
1717
@Internal
1818
lateinit var dependenciesProvider: () -> List<Dependency>
1919

20+
var customCompileConfiguration: Configuration? = null
21+
22+
var customTestCompileConfiguration: Configuration? = null
23+
2024
@get:Input
2125
val compileConfiguration: Configuration
22-
get() = project.configurations.getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME)
26+
get() = customCompileConfiguration ?: project.configurations.getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME)
2327

2428
@get:Input
2529
val testCompileConfiguration: Configuration
26-
get() = project.configurations.getByName(JavaPlugin.TEST_COMPILE_CONFIGURATION_NAME)
30+
get() = customTestCompileConfiguration ?: project.configurations.getByName(JavaPlugin.TEST_COMPILE_CONFIGURATION_NAME)
2731

2832
@OutputFile
2933
val resultFile = unpackFile(project)

kotlin-frontend/src/test/kotlin/org/jetbrains/kotlin/gradle/frontend/BuildScriptBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class BuildScriptBuilder {
2727
fun addJsDependency() {
2828
if (kotlinVersion.startsWith("1.0."))
2929
compileDependencies += "org.jetbrains.kotlin:kotlin-js-library:$kotlinVersion"
30-
else if (kotlinVersion.startsWith("1.1.") || kotlinVersion.startsWith("1.2."))
30+
else if (kotlinVersion.startsWith("1.1.") || kotlinVersion.startsWith("1.2.") || kotlinVersion.startsWith("1.3."))
3131
compileDependencies += "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlinVersion"
3232
else
3333
throw IllegalArgumentException("Only 1.0, 1.1 and 1.2 kotlin supported")

0 commit comments

Comments
 (0)