[WASM] Implement gradle plugin binaryen support
This commit is contained in:
+17
-3
@@ -1956,16 +1956,24 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun testWasmTest(engine: String, name: String) = with(
|
||||
private fun testWasmTest(engine: String, name: String, useBinaryen: Boolean) = with(
|
||||
Project("new-mpp-wasm-test", gradleVersionRequirement = GradleVersionRequired.AtLeast(TestVersions.Gradle.G_7_0))
|
||||
) {
|
||||
setupWorkingDir()
|
||||
gradleBuildScript().modify {
|
||||
transformBuildScriptWithPluginsDsl(it)
|
||||
.replace("<JsEngine>", engine)
|
||||
.replace("<ApplyBinaryen>", if (useBinaryen) "applyBinaryen()" else "")
|
||||
}
|
||||
build(":wasm${name}Test") {
|
||||
assertTasksExecuted(":compileKotlinWasm")
|
||||
if (useBinaryen) {
|
||||
assertTasksExecuted(":compileTestProductionExecutableKotlinWasmOptimize")
|
||||
assertTasksExecuted(":compileTestDevelopmentExecutableKotlinWasmOptimize")
|
||||
} else {
|
||||
assertTasksNotExecuted(":compileTestProductionExecutableKotlinWasmOptimize")
|
||||
assertTasksNotExecuted(":compileTestDevelopmentExecutableKotlinWasmOptimize")
|
||||
}
|
||||
assertTasksFailed(":wasm${name}Test")
|
||||
assertTestResults(
|
||||
"testProject/new-mpp-wasm-test/TEST-${engine}.xml",
|
||||
@@ -1975,10 +1983,16 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWasmNodeTest() = testWasmTest("nodejs", "Node")
|
||||
fun testWasmNodeTest() = testWasmTest("nodejs", "Node", useBinaryen = false)
|
||||
|
||||
@Test
|
||||
fun testWasmD8Test() = testWasmTest("d8", "D8")
|
||||
fun testWasmWithBinaryenNodeTest() = testWasmTest("nodejs", "Node", useBinaryen = true)
|
||||
|
||||
@Test
|
||||
fun testWasmD8Test() = testWasmTest("d8", "D8", useBinaryen = false)
|
||||
|
||||
@Test
|
||||
fun testWasmWithBinaryenD8Test() = testWasmTest("d8", "D8", useBinaryen = true)
|
||||
|
||||
@Test
|
||||
fun testResolveMetadataCompileClasspathKt50925() {
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ tasks.named<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask>("kotli
|
||||
kotlin {
|
||||
wasm {
|
||||
<JsEngine>()
|
||||
<ApplyBinaryen>
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.binaryen
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
data class BinaryenEnv(
|
||||
val cleanableStore: CleanableStore,
|
||||
val zipPath: File,
|
||||
val targetPath: File,
|
||||
val executablePath: File,
|
||||
val isWindows: Boolean,
|
||||
val downloadUrl: URL
|
||||
)
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.binaryen
|
||||
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.utils.newFileProperty
|
||||
import javax.inject.Inject
|
||||
|
||||
open class BinaryenExec
|
||||
@Inject
|
||||
constructor() : AbstractExecTask<BinaryenExec>(BinaryenExec::class.java) {
|
||||
@Transient
|
||||
@get:Internal
|
||||
lateinit var binaryen: BinaryenRootExtension
|
||||
|
||||
init {
|
||||
onlyIf {
|
||||
!inputFileProperty.isPresent || inputFileProperty.asFile.map { it.exists() }.get()
|
||||
}
|
||||
}
|
||||
|
||||
@Input
|
||||
var binaryenArgs: MutableList<String> = mutableListOf(
|
||||
"--enable-nontrapping-float-to-int",
|
||||
"--enable-typed-function-references",
|
||||
"--enable-gc",
|
||||
"--enable-reference-types",
|
||||
"--enable-exception-handling",
|
||||
"--hybrid",
|
||||
"-O3",
|
||||
"--inline-functions-with-loops",
|
||||
"--traps-never-happen",
|
||||
"--fast-math",
|
||||
)
|
||||
|
||||
@InputFile
|
||||
val inputFileProperty: RegularFileProperty = project.newFileProperty()
|
||||
|
||||
@OutputFile
|
||||
val outputFileProperty: RegularFileProperty = project.newFileProperty()
|
||||
|
||||
override fun exec() {
|
||||
val inputFile = inputFileProperty.asFile.get()
|
||||
val newArgs = mutableListOf<String>()
|
||||
newArgs.addAll(binaryenArgs)
|
||||
newArgs.add(inputFile.canonicalPath)
|
||||
newArgs.add("-o")
|
||||
newArgs.add(outputFileProperty.asFile.get().canonicalPath)
|
||||
workingDir = inputFile.parentFile
|
||||
this.args = newArgs
|
||||
super.exec()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
compilation: KotlinJsCompilation,
|
||||
name: String,
|
||||
configuration: BinaryenExec.() -> Unit = {}
|
||||
): TaskProvider<BinaryenExec> {
|
||||
val target = compilation.target
|
||||
val project = target.project
|
||||
val binaryen = BinaryenRootPlugin.apply(project.rootProject)
|
||||
return project.registerTask(
|
||||
name,
|
||||
) {
|
||||
it.binaryen = binaryen
|
||||
it.executable = binaryen.requireConfigured().executablePath.absolutePath
|
||||
it.dependsOn(binaryen.setupTaskProvider)
|
||||
it.dependsOn(compilation.compileKotlinTaskProvider)
|
||||
it.configuration()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.binaryen
|
||||
|
||||
/**
|
||||
* Provides platform and architecture names that is used to download Binaryen.
|
||||
*/
|
||||
internal object BinaryenPlatform {
|
||||
private val props = System.getProperties()
|
||||
private fun property(name: String) = props.getProperty(name) ?: System.getProperty(name)
|
||||
|
||||
const val WIN = "windows"
|
||||
const val LINUX = "linux"
|
||||
const val DARWIN = "macos"
|
||||
|
||||
val name: String = run {
|
||||
val name = property("os.name").toLowerCase()
|
||||
when {
|
||||
name.contains("windows") -> WIN
|
||||
name.contains("mac") -> DARWIN
|
||||
name.contains("linux") -> LINUX
|
||||
name.contains("freebsd") -> LINUX
|
||||
else -> throw IllegalArgumentException("Unsupported OS: $name")
|
||||
}
|
||||
}
|
||||
|
||||
const val X64 = "64"
|
||||
const val X86 = "86"
|
||||
|
||||
val architecture: String
|
||||
get() = if (property("os.arch").contains("64")) X64 else X86
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.binaryen
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.net.URL
|
||||
|
||||
open class BinaryenRootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<BinaryenEnv>(), Serializable {
|
||||
init {
|
||||
check(rootProject.rootProject == rootProject)
|
||||
}
|
||||
|
||||
private val gradleHome = rootProject.gradle.gradleUserHomeDir.also {
|
||||
rootProject.logger.kotlinInfo("Storing cached files in $it")
|
||||
}
|
||||
|
||||
var installationPath by Property(gradleHome.resolve("binaryen"))
|
||||
var downloadBaseUrl by Property("https://github.com/WebAssembly/binaryen/releases/download/")
|
||||
var version by Property("109")
|
||||
|
||||
val setupTaskProvider: TaskProvider<out Copy>
|
||||
get() = rootProject.tasks.withType(Copy::class.java).named(BinaryenRootPlugin.INSTALL_TASK_NAME)
|
||||
|
||||
override fun finalizeConfiguration(): BinaryenEnv {
|
||||
val platform = BinaryenPlatform.name
|
||||
val architecture = BinaryenPlatform.architecture
|
||||
val requiredVersionName = "binaryen-version_$version-x86_$architecture-$platform"
|
||||
val requiredZipName = "$requiredVersionName.tar.gz"
|
||||
val cleanableStore = CleanableStore[installationPath.absolutePath]
|
||||
val targetPath = cleanableStore[requiredVersionName].use()
|
||||
val isWindows = BinaryenPlatform.name == BinaryenPlatform.WIN
|
||||
|
||||
return BinaryenEnv(
|
||||
cleanableStore = cleanableStore,
|
||||
zipPath = cleanableStore[requiredZipName].use(),
|
||||
targetPath = targetPath,
|
||||
executablePath = targetPath
|
||||
.resolve("binaryen-version_$version")
|
||||
.resolve("bin")
|
||||
.resolve(if (isWindows) "wasm-opt.exe" else "wasm-opt"),
|
||||
isWindows = isWindows,
|
||||
downloadUrl = URL("${downloadBaseUrl.trimEnd('/')}/version_$version/$requiredZipName"),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTENSION_NAME: String = "kotlinBinaryen"
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.binaryen
|
||||
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
|
||||
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootExtension.Companion.EXTENSION_NAME
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
|
||||
open class BinaryenRootPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
MultiplePluginDeclarationDetector.detect(project)
|
||||
|
||||
project.plugins.apply(BasePlugin::class.java)
|
||||
|
||||
check(project == project.rootProject) {
|
||||
"BinaryenRootPlugin can be applied only to root project"
|
||||
}
|
||||
|
||||
val settings = project.extensions.create(EXTENSION_NAME, BinaryenRootExtension::class.java, project)
|
||||
|
||||
val downloadTask = project.registerTask<Download>("${TASKS_GROUP_NAME}Download") {
|
||||
val env = settings.requireConfigured()
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.src(env.downloadUrl)
|
||||
it.dest(env.zipPath)
|
||||
it.overwrite(false)
|
||||
it.description = "Download local binaryen version"
|
||||
}
|
||||
|
||||
project.registerTask<Copy>(INSTALL_TASK_NAME) {
|
||||
val env = settings.requireConfigured()
|
||||
it.onlyIf { env.zipPath.exists() && !env.executablePath.exists() }
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.from(project.tarTree(env.zipPath))
|
||||
it.into(env.targetPath)
|
||||
it.dependsOn(downloadTask)
|
||||
it.description = "Install local binaryen version"
|
||||
}
|
||||
|
||||
project.registerTask<CleanDataTask>("binaryen" + CleanDataTask.NAME_SUFFIX) {
|
||||
it.cleanableStoreProvider = project.provider { settings.requireConfigured().cleanableStore }
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.description = "Clean unused local binaryen version"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TASKS_GROUP_NAME: String = "binaryen"
|
||||
const val INSTALL_TASK_NAME: String = "${TASKS_GROUP_NAME}Install"
|
||||
|
||||
fun apply(rootProject: Project): BinaryenRootExtension {
|
||||
check(rootProject == rootProject.rootProject)
|
||||
rootProject.plugins.apply(BinaryenRootPlugin::class.java)
|
||||
return rootProject.extensions.getByName(EXTENSION_NAME) as BinaryenRootExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.dsl
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
|
||||
import org.jetbrains.kotlin.gradle.targets.js.d8.D8Exec
|
||||
|
||||
interface KotlinWasmSubTargetContainerDsl : KotlinTarget {
|
||||
@@ -16,6 +17,8 @@ interface KotlinWasmSubTargetContainerDsl : KotlinTarget {
|
||||
val isD8Configured: Boolean
|
||||
|
||||
fun whenD8Configured(body: KotlinWasmD8Dsl.() -> Unit)
|
||||
|
||||
fun whenBinaryenApplied(body: () -> Unit)
|
||||
}
|
||||
|
||||
interface KotlinWasmTargetDsl : KotlinJsTargetDsl {
|
||||
@@ -26,6 +29,8 @@ interface KotlinWasmTargetDsl : KotlinJsTargetDsl {
|
||||
ConfigureUtil.configure(fn, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun applyBinaryen()
|
||||
}
|
||||
|
||||
interface KotlinWasmD8Dsl : KotlinJsSubTargetDsl {
|
||||
|
||||
+33
-1
@@ -10,10 +10,12 @@ import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
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
|
||||
@@ -21,9 +23,9 @@ 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.utils.lowerCamelCaseName
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
open class KotlinJsBinaryContainer
|
||||
@Inject
|
||||
constructor(
|
||||
@@ -38,6 +40,30 @@ constructor(
|
||||
private val defaultCompilation: KotlinJsCompilation
|
||||
get() = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
|
||||
|
||||
private fun configureBinaryen(binary: JsIrBinary) {
|
||||
val linkTask = binary.linkTask
|
||||
|
||||
val compiledMjsFile = linkTask.map { link ->
|
||||
link.kotlinOptions.outputFile?.let(::File)
|
||||
?: link.destinationDirectory.locationOnly.get().asFile.resolve("${link.compilation.ownModuleName}.mjs")
|
||||
}
|
||||
|
||||
val compiledWasmFile = compiledMjsFile.map {
|
||||
it.parentFile.resolve("${it.nameWithoutExtension}.wasm")
|
||||
}
|
||||
|
||||
//TODO This is temporary solution that overrides compiled files that triggers recompile and reoptimize wasm every time (when binaryen is enabled)
|
||||
val binaryenTask = BinaryenExec.create(binary.compilation, "${linkTask.name}Optimize") {
|
||||
inputFileProperty.fileProvider(compiledWasmFile)
|
||||
outputFileProperty.fileProvider(compiledWasmFile)
|
||||
}
|
||||
|
||||
binary.compilation.compileKotlinTask.finalizedBy(binaryenTask)
|
||||
binary.linkSyncTask.configure {
|
||||
it.dependsOn(binaryenTask)
|
||||
}
|
||||
}
|
||||
|
||||
// For Groovy DSL
|
||||
@JvmOverloads
|
||||
fun executable(
|
||||
@@ -175,6 +201,12 @@ constructor(
|
||||
extensions.add(binary.name, binary)
|
||||
}
|
||||
|
||||
if (compilation.platformType == KotlinPlatformType.wasm && target is KotlinJsIrTarget && binary is JsIrBinary) {
|
||||
target.whenBinaryenApplied {
|
||||
configureBinaryen(binary)
|
||||
}
|
||||
}
|
||||
|
||||
return binary
|
||||
}
|
||||
|
||||
|
||||
+22
-3
@@ -144,12 +144,31 @@ constructor(
|
||||
|
||||
task.from(project.tasks.named(compilation.processResourcesTaskName))
|
||||
|
||||
task.into(
|
||||
npmProject.dist
|
||||
)
|
||||
task.into(npmProject.dist)
|
||||
}
|
||||
}
|
||||
|
||||
//Binaryen
|
||||
private val applyBinaryenHandlers = mutableListOf<() -> Unit>()
|
||||
|
||||
private var binaryenApplied: Boolean = false
|
||||
|
||||
override fun whenBinaryenApplied(body: () -> Unit) {
|
||||
if (binaryenApplied) {
|
||||
body()
|
||||
} else {
|
||||
applyBinaryenHandlers += body
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyBinaryen() {
|
||||
binaryenApplied = true
|
||||
applyBinaryenHandlers.forEach { handler ->
|
||||
handler()
|
||||
}
|
||||
browserConfiguredHandlers.clear()
|
||||
}
|
||||
|
||||
//Browser
|
||||
private val browserLazyDelegate = lazy {
|
||||
commonLazy
|
||||
|
||||
Reference in New Issue
Block a user