diff --git a/README.md b/README.md
index 5d9e04b..246e7f9 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,17 @@ To build and run the application:
`./gradlew wasmWasiWasmEdgeTest`
+* **Run the program with Wasmtime:**
+
+ `./gradlew wasmWasiWasmtimeRun`
+
+ > **Note:**
+ > The Exception Handling proposal [is not yet implemented](https://github.com/bytecodealliance/wasmtime/issues/3427)
+
+* **Run tests with Wasmtime:**
+
+ `./gradlew wasmWasiWasmtimeTest`
+
* **Run the program with NodeJs:**
`./gradlew wasmWasiNodeRun`
diff --git a/build.gradle.kts b/build.gradle.kts
index 125a626..21fad03 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,10 +2,13 @@
import org.gradle.internal.os.OperatingSystem
import de.undercouch.gradle.tasks.download.Download
+import org.gradle.api.internal.file.archive.compression.*
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport
+import java.io.*
+import java.net.*
import java.nio.file.Files
import java.util.Locale
@@ -14,6 +17,13 @@ plugins {
alias(libs.plugins.undercouchDownload) apply false
}
+buildscript {
+ dependencies {
+ // to extract `tar.xz`
+ classpath("org.tukaani:xz:1.9")
+ }
+}
+
repositories {
mavenCentral()
}
@@ -352,3 +362,134 @@ tasks.withType().all {
)
}
}
+
+// Wasmtime tasks
+val wasmtimeVersion = "dev" // only `dev` supports GC
+
+val wasmtimeSuffix = when (currentOsType) {
+ OsType(OsName.LINUX, OsArch.X86_64) -> "x86_64-linux"
+ OsType(OsName.LINUX, OsArch.ARM64) -> "aarch64-linux"
+ OsType(OsName.MAC, OsArch.X86_64) -> "x86_64-macos"
+ OsType(OsName.MAC, OsArch.ARM64) -> "aarch64-macos"
+ OsType(OsName.WINDOWS, OsArch.X86_32),
+ OsType(OsName.WINDOWS, OsArch.X86_64) -> "x86_64-windows"
+
+ else -> error("unsupported os type $currentOsType")
+}
+
+val wasmtimeArtifactName = "wasmtime-$wasmtimeVersion-$wasmtimeSuffix"
+
+val unzipWasmtime = run {
+ val wasmtimeDirectory = "https://github.com/bytecodealliance/wasmtime/releases/download/$wasmtimeVersion"
+ val archiveType = if (currentOsType.name == OsName.WINDOWS) "zip" else "tar.xz"
+ val wasmtimeArchiveName = "$wasmtimeArtifactName.$archiveType"
+ val wasmtimeLocation = "$wasmtimeDirectory/$wasmtimeArchiveName"
+
+ val downloadedTools = File(layout.buildDirectory.asFile.get(), "tools")
+
+ val downloadWasmtime = tasks.register("wasmtimeDownload", Download::class) {
+ src(wasmtimeLocation)
+ dest(File(downloadedTools, wasmtimeArchiveName))
+ overwrite(false)
+ }
+
+ tasks.register("wasmtimeUnzip", Copy::class) {
+ dependsOn(downloadWasmtime)
+
+ val archive = downloadWasmtime.get().dest
+
+ from(if (archive.extension == "zip") zipTree(archive) else tarTree(XzArchiver(archive)))
+
+ into(downloadedTools)
+ }
+}
+
+private class XzArchiver(private val file: File) : CompressedReadableResource {
+ override fun read(): InputStream = org.tukaani.xz.XZInputStream(file.inputStream().buffered())
+ override fun getURI(): URI = URIBuilder(file.toURI()).schemePrefix("xz:").build()
+ override fun getBackingFile(): File = file
+ override fun getBaseName(): String = file.name
+ override fun getDisplayName(): String = file.path
+}
+
+fun Project.createWasmtimeExec(
+ nodeMjsFile: RegularFileProperty,
+ taskName: String,
+ taskGroup: String?,
+ startFunction: String
+): TaskProvider {
+ val outputDirectory = nodeMjsFile.map { it.asFile.parentFile }
+ val wasmFileName = nodeMjsFile.map { "${it.asFile.nameWithoutExtension}.wasm" }
+
+ return tasks.register(taskName, Exec::class) {
+ dependsOn(unzipWasmtime)
+ inputs.property("wasmFileName", wasmFileName)
+
+ taskGroup?.let { group = it }
+
+ description = "Executes tests with Wasmtime"
+
+ val wasmtimeDirectory = unzipWasmtime.get().destinationDir.resolve(wasmtimeArtifactName)
+
+ val executableName = when (currentOsType.name) {
+ OsName.WINDOWS -> "wasmtime.exe"
+ else -> "wasmtime"
+ }
+ executable = wasmtimeDirectory.resolve(executableName).absolutePath
+
+ doFirst {
+ val newArgs = mutableListOf()
+
+ newArgs.add("-W")
+ newArgs.add("function-references,gc")
+
+ newArgs.add("-D")
+ newArgs.add("logging=y")
+
+ newArgs.add("--invoke")
+ newArgs.add(startFunction)
+
+ newArgs.add(wasmFileName.get())
+
+ args(newArgs)
+ workingDir(outputDirectory)
+
+ // to show stacktraces
+ environment("RUST_BACKTRACE", "full")
+ }
+ }
+}
+
+tasks.withType().all {
+ val wasmtimeRunTask = createWasmtimeExec(
+ inputFileProperty,
+ name.replace("Node", "Wasmtime"),
+ group,
+ "startUnitTests"
+ )
+
+ wasmtimeRunTask.configure {
+ dependsOn(
+ project.provider { this@all.taskDependencies }
+ )
+ }
+
+ tasks.withType {
+ dependsOn(wasmtimeRunTask)
+ }
+}
+
+tasks.withType().all {
+ val wasmtimeRunTask = createWasmtimeExec(
+ inputFileProperty,
+ name.replace("Node", "Wasmtime"),
+ group,
+ "dummy"
+ )
+
+ wasmtimeRunTask.configure {
+ dependsOn(
+ project.provider { this@all.taskDependencies }
+ )
+ }
+}