22
33import org.gradle.internal.os.OperatingSystem
44import de.undercouch.gradle.tasks.download.Download
5+ import org.gradle.api.internal.file.archive.compression.*
56import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
67import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
78import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
89import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport
10+ import java.io.*
11+ import java.net.*
912import java.nio.file.Files
1013import java.util.Locale
1114
@@ -14,6 +17,13 @@ plugins {
1417 alias(libs.plugins.undercouchDownload) apply false
1518}
1619
20+ buildscript {
21+ dependencies {
22+ // to extract `tar.xz`
23+ classpath(" org.tukaani:xz:1.9" )
24+ }
25+ }
26+
1727repositories {
1828 mavenCentral()
1929}
@@ -352,3 +362,134 @@ tasks.withType<NodeJsExec>().all {
352362 )
353363 }
354364}
365+
366+ // Wasmtime tasks
367+ val wasmtimeVersion = " dev" // only `dev` supports GC
368+
369+ val wasmtimeSuffix = when (currentOsType) {
370+ OsType (OsName .LINUX , OsArch .X86_64 ) -> " x86_64-linux"
371+ OsType (OsName .LINUX , OsArch .ARM64 ) -> " aarch64-linux"
372+ OsType (OsName .MAC , OsArch .X86_64 ) -> " x86_64-macos"
373+ OsType (OsName .MAC , OsArch .ARM64 ) -> " aarch64-macos"
374+ OsType (OsName .WINDOWS , OsArch .X86_32 ),
375+ OsType (OsName .WINDOWS , OsArch .X86_64 ) -> " x86_64-windows"
376+
377+ else -> error(" unsupported os type $currentOsType " )
378+ }
379+
380+ val wasmtimeArtifactName = " wasmtime-$wasmtimeVersion -$wasmtimeSuffix "
381+
382+ val unzipWasmtime = run {
383+ val wasmtimeDirectory = " https://github.com/bytecodealliance/wasmtime/releases/download/$wasmtimeVersion "
384+ val archiveType = if (currentOsType.name == OsName .WINDOWS ) " zip" else " tar.xz"
385+ val wasmtimeArchiveName = " $wasmtimeArtifactName .$archiveType "
386+ val wasmtimeLocation = " $wasmtimeDirectory /$wasmtimeArchiveName "
387+
388+ val downloadedTools = File (layout.buildDirectory.asFile.get(), " tools" )
389+
390+ val downloadWasmtime = tasks.register(" wasmtimeDownload" , Download ::class ) {
391+ src(wasmtimeLocation)
392+ dest(File (downloadedTools, wasmtimeArchiveName))
393+ overwrite(false )
394+ }
395+
396+ tasks.register(" wasmtimeUnzip" , Copy ::class ) {
397+ dependsOn(downloadWasmtime)
398+
399+ val archive = downloadWasmtime.get().dest
400+
401+ from(if (archive.extension == " zip" ) zipTree(archive) else tarTree(XzArchiver (archive)))
402+
403+ into(downloadedTools)
404+ }
405+ }
406+
407+ private class XzArchiver (private val file : File ) : CompressedReadableResource {
408+ override fun read (): InputStream = org.tukaani.xz.XZInputStream (file.inputStream().buffered())
409+ override fun getURI (): URI = URIBuilder (file.toURI()).schemePrefix(" xz:" ).build()
410+ override fun getBackingFile (): File = file
411+ override fun getBaseName (): String = file.name
412+ override fun getDisplayName (): String = file.path
413+ }
414+
415+ fun Project.createWasmtimeExec (
416+ nodeMjsFile : RegularFileProperty ,
417+ taskName : String ,
418+ taskGroup : String? ,
419+ startFunction : String
420+ ): TaskProvider <Exec > {
421+ val outputDirectory = nodeMjsFile.map { it.asFile.parentFile }
422+ val wasmFileName = nodeMjsFile.map { " ${it.asFile.nameWithoutExtension} .wasm" }
423+
424+ return tasks.register(taskName, Exec ::class ) {
425+ dependsOn(unzipWasmtime)
426+ inputs.property(" wasmFileName" , wasmFileName)
427+
428+ taskGroup?.let { group = it }
429+
430+ description = " Executes tests with Wasmtime"
431+
432+ val wasmtimeDirectory = unzipWasmtime.get().destinationDir.resolve(wasmtimeArtifactName)
433+
434+ val executableName = when (currentOsType.name) {
435+ OsName .WINDOWS -> " wasmtime.exe"
436+ else -> " wasmtime"
437+ }
438+ executable = wasmtimeDirectory.resolve(executableName).absolutePath
439+
440+ doFirst {
441+ val newArgs = mutableListOf<String >()
442+
443+ newArgs.add(" -W" )
444+ newArgs.add(" function-references,gc" )
445+
446+ newArgs.add(" -D" )
447+ newArgs.add(" logging=y" )
448+
449+ newArgs.add(" --invoke" )
450+ newArgs.add(startFunction)
451+
452+ newArgs.add(wasmFileName.get())
453+
454+ args(newArgs)
455+ workingDir(outputDirectory)
456+
457+ // to show stacktraces
458+ environment(" RUST_BACKTRACE" , " full" )
459+ }
460+ }
461+ }
462+
463+ tasks.withType<KotlinJsTest >().all {
464+ val wasmtimeRunTask = createWasmtimeExec(
465+ inputFileProperty,
466+ name.replace(" Node" , " Wasmtime" ),
467+ group,
468+ " startUnitTests"
469+ )
470+
471+ wasmtimeRunTask.configure {
472+ dependsOn(
473+ project.provider { this @all.taskDependencies }
474+ )
475+ }
476+
477+ tasks.withType<KotlinTestReport > {
478+ dependsOn(wasmtimeRunTask)
479+ }
480+ }
481+
482+ tasks.withType<NodeJsExec >().all {
483+ val wasmtimeRunTask = createWasmtimeExec(
484+ inputFileProperty,
485+ name.replace(" Node" , " Wasmtime" ),
486+ group,
487+ " dummy"
488+ )
489+
490+ wasmtimeRunTask.configure {
491+ dependsOn(
492+ project.provider { this @all.taskDependencies }
493+ )
494+ }
495+ }
0 commit comments