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 } + ) + } +}