[Wasm] Support wasm target in multiplatform plugin

This commit is contained in:
Svyatoslav Kuzmich
2021-09-25 12:46:21 +03:00
parent 070c64f9b5
commit 841f5a583e
32 changed files with 183 additions and 20 deletions
@@ -319,7 +319,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
outputWatFile.writeText(res.wat)
val runner = """
const ${sanitizeName(moduleName)} = WebAssembly.instantiateStreaming(fetch('${outputWasmFile.name}'), { runtime, js_code }).then((it) => {
export default WebAssembly.instantiateStreaming(fetch('${outputWasmFile.name}'), { runtime, js_code }).then((it) => {
wasmInstance = it.instance;
wasmInstance.exports.main?.();
return it.instance.exports;
@@ -11,7 +11,7 @@ import org.gradle.util.GradleVersion
import java.io.Serializable
enum class KotlinPlatformType: Named, Serializable {
common, jvm, js, androidJvm, native;
common, jvm, js, androidJvm, native, wasm;
override fun toString(): String = name
override fun getName(): String = name
@@ -1144,7 +1144,7 @@ class NewMultiplatformIT : BaseGradleIT() {
}
assertEquals(
setOf("commonMain", "jvm6Main", "linux64Main", "linuxMipsel32Main", "macos64Main", "mingw64Main", "mingw86Main", "nodeJsMain"),
setOf("commonMain", "jvm6Main", "linux64Main", "linuxMipsel32Main", "macos64Main", "mingw64Main", "mingw86Main", "nodeJsMain", "wasmMain"),
sourceJarSourceRoots[null]
)
assertEquals(setOf("commonMain", "jvm6Main"), sourceJarSourceRoots["jvm6"])
@@ -1472,7 +1472,7 @@ class NewMultiplatformIT : BaseGradleIT() {
}
val expectedDefaultSourceSets = listOf(
"jvm6", "nodeJs", "mingw64", "mingw86", "linux64", "macos64", "linuxMipsel32"
"jvm6", "nodeJs", "mingw64", "mingw86", "linux64", "macos64", "linuxMipsel32", "wasm"
).flatMapTo(mutableSetOf()) { target ->
listOf("main", "test").map { compilation ->
Triple(target, compilation, "$target${compilation.capitalize()}")
@@ -1872,6 +1872,26 @@ class NewMultiplatformIT : BaseGradleIT() {
}
}
@Test
fun testWasmJs() = with(Project("new-mpp-wasm-js", gradleVersion)) {
setupWorkingDir()
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
build("build") {
assertSuccessful()
assertTasksExecuted(":compileKotlinJs")
assertTasksExecuted(":compileKotlinWasm")
val outputPrefix = "build/js/packages/"
val jsOutput = outputPrefix + "redefined-js-module-name/kotlin/"
assertFileExists(jsOutput + "redefined-js-module-name.js")
val wasmOutput = outputPrefix + "redefined-wasm-module-name/kotlin/"
assertFileExists(wasmOutput + "redefined-wasm-module-name.js")
assertFileExists(wasmOutput + "redefined-wasm-module-name.wasm")
}
}
private fun detectNativeEnabledCompilation(): String = when {
HostManager.hostIsLinux -> "linuxX64"
HostManager.hostIsMingw -> "mingwX64"
@@ -22,6 +22,9 @@ kotlin {
val nodeJs = js("nodeJs")
val linux64 = linuxX64("linux64")
wasm {
}
configure(listOf(linux64)) {
binaries.executable("main", listOf(DEBUG)) {
entryPoint = "com.example.app.native.main"
@@ -0,0 +1,5 @@
import com.example.lib.*
fun wasmMain(args: Array<String>) {
println("${id(123)} ${idUsage()} ${expectedFun()}")
}
@@ -48,6 +48,8 @@ kotlin {
js('nodeJs')
wasm()
targets {
fromPreset(presets.jvm, 'jvm6') {
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 6)
@@ -0,0 +1,5 @@
import com.example.lib.*
fun wasmMain(args: Array<String>) {
println("${id(123)} ${idUsage()} ${expectedFun()}")
}
@@ -22,6 +22,7 @@ kotlin {
// Check the new preset functions:
jvm('jvm6') { }
js('nodeJs')
wasm()
targetFromPreset(presets.jvm, 'jvm6') { println targetName } // access existing target
targets {
@@ -0,0 +1,9 @@
package com.example.externalLib
fun idUsage() = id("123")
actual fun expectedFun() = Unit
fun main(args: Array<String>) {
expectedFun()
}
@@ -15,6 +15,7 @@ kotlin {
val jvm = jvm("jvm6")
val js = js("nodeJs")
linuxX64("linux64")
wasm()
targets.all {
mavenPublication(Action<MavenPublication> {
@@ -0,0 +1,10 @@
package com.example.lib
fun idUsage() = id("123")
actual fun expectedFun() {
}
fun main(args: Array<String>) {
expectedFun()
}
@@ -22,6 +22,7 @@ kotlin {
// Check the new preset functions:
jvm('jvm6') { }
js('nodeJs')
wasm()
targetFromPreset(presets.jvm, 'jvm6') { println targetName } // access existing target
targets {
@@ -0,0 +1,10 @@
package com.example.lib
fun idUsage() = id("123")
actual fun expectedFun() {
}
fun main(args: Array<String>) {
expectedFun()
}
@@ -0,0 +1,23 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
wasm {
moduleName = "redefined-wasm-module-name"
browser {
}
binaries.executable()
}
js {
moduleName = "redefined-js-module-name"
browser {
}
binaries.executable()
}
}
@@ -0,0 +1,8 @@
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
}
}
rootProject.name = "new-mpp-wasm-js"
@@ -17,6 +17,7 @@ open class KotlinMultiplatformExtension(project: Project) :
KotlinProjectExtension(project),
KotlinTargetContainerWithPresetFunctions,
KotlinTargetContainerWithJsPresetFunctions,
KotlinTargetContainerWithWasmPresetFunctions,
KotlinTargetContainerWithNativeShortcuts {
override lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>>
internal set
@@ -0,0 +1,28 @@
/*
* 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.dsl
import groovy.lang.Closure
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
interface KotlinTargetContainerWithWasmPresetFunctions : KotlinTargetContainerWithPresetFunctions {
fun wasm(
name: String = "wasm",
configure: KotlinJsTargetDsl.() -> Unit = { }
): KotlinJsTargetDsl =
configureOrCreate(
name,
presets.getByName("wasm") as KotlinJsIrTargetPreset,
configure
)
fun wasm() = wasm("wasm") { }
fun wasm(name: String) = wasm(name) { }
fun wasm(name: String, configure: Closure<*>) = wasm(name) { ConfigureUtil.configure(configure, this) }
fun wasm(configure: Closure<*>) = wasm { ConfigureUtil.configure(configure, this) }
}
@@ -138,6 +138,7 @@ private fun addStdlibToPm20Project(
KotlinPlatformType.common -> error("variants are not expected to be common")
KotlinPlatformType.jvm -> "kotlin-stdlib" // TODO get JDK from JVM variants
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.androidJvm -> null // TODO: expect support on the AGP side?
KotlinPlatformType.native -> null
}
@@ -189,6 +190,7 @@ private fun chooseAndAddStdlibDependency(
KotlinPlatformType.androidJvm ->
if (kotlinSourceSet.name == androidMainSourceSetName(project)) stdlibModuleForJvmCompilations(compilations) else null
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.native -> null
KotlinPlatformType.common -> // there's no platform compilation that the source set is default for
"kotlin-stdlib-common"
@@ -159,7 +159,10 @@ internal fun PropertiesProvider.mapKotlinDaemonProperties(task: CompileUsingKotl
val mppStabilityNoWarn: Boolean?
get() = booleanProperty(KotlinMultiplatformPlugin.STABILITY_NOWARN_FLAG)
val ignoreDisabledNativeTargets: Boolean?
val wasmStabilityNoWarn: Boolean
get() = booleanProperty("kotlin.wasm.stability.nowarn") ?: false
val ignoreDisabledNativeTargets: Boolean?
get() = booleanProperty(DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME)
val ignoreIncorrectNativeDependencies: Boolean?
@@ -174,13 +174,14 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
with(project.multiplatformExtension.presets) {
add(KotlinJvmTargetPreset(project))
add(KotlinJsTargetPreset(project).apply { irPreset = null })
add(KotlinJsIrTargetPreset(project).apply { mixedMode = false })
add(KotlinJsIrTargetPreset(project, isWasm = false).apply { mixedMode = false })
add(
KotlinJsTargetPreset(project).apply {
irPreset = KotlinJsIrTargetPreset(project)
irPreset = KotlinJsIrTargetPreset(project, isWasm = false)
.apply { mixedMode = true }
}
)
add(KotlinJsIrTargetPreset(project, isWasm = true).apply { mixedMode = false })
add(KotlinAndroidTargetPreset(project))
add(KotlinJvmWithJavaTargetPreset(project))
@@ -26,5 +26,7 @@ internal const val MODULE_NAME = "-Xir-module-name"
internal const val PER_MODULE_OUTPUT_NAME = "-Xir-per-module-output-name"
internal const val WASM_BACKEND = "-Xwasm"
fun KotlinJsOptions.isProduceUnzippedKlib() = PRODUCE_UNZIPPED_KLIB in freeCompilerArgs
fun KotlinJsOptions.isProduceZippedKlib() = PRODUCE_ZIPPED_KLIB in freeCompilerArgs
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.copyFreeCompilerArgsToArgs
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
@@ -184,6 +185,10 @@ abstract class KotlinJsIrLink @Inject constructor(
freeCompilerArgs += additionalCompilerArgs.toList() +
PRODUCE_JS +
"$ENTRY_IR_MODULE=${entryModule.get().asFile.canonicalPath}"
if (compilation.platformType == KotlinPlatformType.wasm) {
freeCompilerArgs += WASM_BACKEND
}
}
}
@@ -71,6 +71,10 @@ open class KotlinJsIrTargetConfigurator() :
target.compilations.all { compilation ->
compilation.kotlinOptions {
configureOptions()
if (target.platformType == KotlinPlatformType.wasm) {
freeCompilerArgs = freeCompilerArgs + WASM_BACKEND
}
var produceUnzippedKlib = isProduceUnzippedKlib()
val produceZippedKlib = isProduceZippedKlib()
@@ -16,7 +16,8 @@ import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenE
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
open class KotlinJsIrTargetPreset(
project: Project
project: Project,
isWasm: Boolean,
) : KotlinOnlyTargetPreset<KotlinJsIrTarget, KotlinJsIrCompilation>(
project
) {
@@ -25,10 +26,21 @@ open class KotlinJsIrTargetPreset(
open val isMpp: Boolean
get() = true
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override val platformType: KotlinPlatformType =
if (isWasm)
KotlinPlatformType.wasm
else
KotlinPlatformType.js
override fun instantiateTarget(name: String): KotlinJsIrTarget {
if (platformType == KotlinPlatformType.wasm && !PropertiesProvider(project).wasmStabilityNoWarn) {
project.logger.warn(
"""
New 'wasm' target is Work-in-Progress and is subject to change without notice.
""".trimIndent()
)
}
return project.objects.newInstance(KotlinJsIrTarget::class.java, project, platformType, mixedMode).apply {
this.isMpp = this@KotlinJsIrTargetPreset.isMpp
if (!mixedMode) {
@@ -64,7 +76,11 @@ open class KotlinJsIrTargetPreset(
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinJsIrCompilation, KotlinJsIrTarget> =
KotlinJsIrTargetConfigurator()
override fun getName(): String = PRESET_NAME
override fun getName(): String = when (platformType) {
KotlinPlatformType.wasm -> WASM_PRESET_NAME
KotlinPlatformType.js -> JS_PRESET_NAME
else -> error("Unsupported platform type")
}
//TODO[Ilya Goncharov] remove public morozov
public override fun createCompilationFactory(
@@ -73,17 +89,19 @@ open class KotlinJsIrTargetPreset(
KotlinJsIrCompilationFactory(project, forTarget)
companion object {
val PRESET_NAME = lowerCamelCaseName(
val JS_PRESET_NAME = lowerCamelCaseName(
"js",
KotlinJsCompilerType.IR.lowerName
)
private const val WASM_PRESET_NAME = "wasm"
}
}
class KotlinJsIrSingleTargetPreset(
project: Project
) : KotlinJsIrTargetPreset(
project
project,
isWasm = false,
) {
override val isMpp: Boolean
get() = false
@@ -89,7 +89,8 @@ internal class KotlinProjectNpmResolver(
private fun addTargetListeners(target: KotlinTarget) {
check(!closed) { resolver.alreadyResolvedMessage("add target $target") }
if (target.platformType == KotlinPlatformType.js) {
if (target.platformType == KotlinPlatformType.js ||
target.platformType == KotlinPlatformType.wasm) {
target.compilations.all { compilation ->
if (compilation is KotlinJsCompilation) {
// compilation may be KotlinWithJavaTarget for old Kotlin2JsPlugin
@@ -12,12 +12,6 @@ publish()
val default = configurations.getByName(Dependency.DEFAULT_CONFIGURATION)
default.extendsFrom(configurations.publishedRuntime.get())
dependencies {
if (!kotlinBuildProperties.isInJpsBuildIdeaSync) {
publishedRuntime(project(":kotlin-test:kotlin-test-js"))
}
}
node {
version.set("16.2.0")
download.set(true)