From 268f2f15d92a6de81e9397e05e59f5fc9dd72abf Mon Sep 17 00:00:00 2001 From: Cayden Tisdale Date: Sat, 5 Sep 2026 19:00:45 -0400 Subject: [PATCH] build: move Kotlin plugin build to Gradle --- Makefile | 54 ------------- README.md | 20 ++--- build.gradle.kts | 186 ++++++++++++++++++++++++++++++++++++++++++++ settings.gradle.kts | 1 - 4 files changed, 196 insertions(+), 65 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 8091c66..0000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -BUILD_DEV_OUT_DIR=build/compileSync/wasmWasi/main/developmentExecutable/kotlin -BUILD_PROD_OUT_DIR=build/compileSync/wasmWasi/main/productionExecutable/optimized -BUILD_ROOT_DIR=build/ -BINDINGS_OUT_DIR=src/wasmWasiMain/kotlin/bindings/ -TOOLS_DIR=tools -WIT_BINDGEN_BRANCH=kotlin -WIT_BINDGEN_PATH=$(TOOLS_DIR)/bin/wit-bindgen -WIT_PATH=wit/v0.1 - -# Make sure this matches the value in `settings.gradle.kts` -PROJECT_NAME=pumpkin-plugin-template-kt - -.PHONY: componentify componentify-dev componentify-prod compile compile-dev compile-prod setup setup-and-componentify clean install-wit-bindgen run-wit-bindgen - -# default target for when you don't want to think about it -setup-and-componentify: # no dependencies, as setup and run "look" independent to the Makefile, this guarantees the order: - $(MAKE) setup - $(MAKE) componentify - -setup: install-wit-bindgen - -compile: compile-prod - -compile-dev: run-wit-bindgen - ./gradlew compileDevelopmentExecutableKotlinWasmWasi - -compile-prod: run-wit-bindgen - ./gradlew compileProductionExecutableKotlinWasmWasiOptimize - -componentify: componentify-prod - -componentify-dev: compile-dev - wasm-tools component embed $(WIT_PATH) $(BUILD_DEV_OUT_DIR)/$(PROJECT_NAME).wasm -o $(BUILD_DEV_OUT_DIR)/$(PROJECT_NAME)-embedded.wasm - wasm-tools component new $(BUILD_DEV_OUT_DIR)/$(PROJECT_NAME)-embedded.wasm --adapt wasi_snapshot_preview1=wasi_snapshot_preview1.reactor.wasm -o $(BUILD_DEV_OUT_DIR)/$(PROJECT_NAME)-component.wasm - cp $(BUILD_DEV_OUT_DIR)/$(PROJECT_NAME)-component.wasm $(BUILD_ROOT_DIR)/$(PROJECT_NAME).wasm - -componentify-prod: compile-prod - wasm-tools component embed $(WIT_PATH) $(BUILD_PROD_OUT_DIR)/$(PROJECT_NAME).wasm -o $(BUILD_PROD_OUT_DIR)/$(PROJECT_NAME)-embedded.wasm - wasm-tools component new $(BUILD_PROD_OUT_DIR)/$(PROJECT_NAME)-embedded.wasm --adapt wasi_snapshot_preview1=wasi_snapshot_preview1.reactor.wasm -o $(BUILD_PROD_OUT_DIR)/$(PROJECT_NAME)-component.wasm - cp $(BUILD_PROD_OUT_DIR)/$(PROJECT_NAME)-component.wasm $(BUILD_ROOT_DIR)/$(PROJECT_NAME).wasm - -clean: - ./gradlew clean - rm -rf build - rm -rf $(TOOLS_DIR) - rm -f $(BINDINGS_OUT_DIR)/* - -# doesn't depend on install-wit-bindgen* so that we can run the target -# multiple times with the same wit-bindgen installation -run-wit-bindgen: - $(WIT_BINDGEN_PATH) kotlin --kotlin-imports 'plugin.*' --kotlin-package-name pumpkin $(WIT_PATH) --out-dir=$(BINDINGS_OUT_DIR) - -install-wit-bindgen: - cargo install wit-bindgen-cli --git https://github.com/Kotlin/wit-bindgen --branch $(WIT_BINDGEN_BRANCH) --root $(TOOLS_DIR) diff --git a/README.md b/README.md index eb872cc..7e674c2 100644 --- a/README.md +++ b/README.md @@ -13,24 +13,24 @@ Kotlin/Wasm + components it's still in it's early stages, and not as straightfor - Rust - This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork. - You only need a default Rust install for your host platform. NOT for any WebAssembly targets -- Make - - To run the convenience `Makefile`. You may opt to do without and perform the necessary steps manually. -- `wasm-tools` - - To bundle the Wasm Kotlin produces into a component. ## Initial setup -To being working on your project, you'll want to adjust the project name in `settings.gradle.kts` and in the Makefile. Whatever your name you'll project, is what the generated Wasm will be named. +Initialize the WIT submodule after cloning: -After that, you'll want to run `make` for the first time. This will setup the toolchain, generate the `pumpkin` package bindings into `src/wasmWasiMain/kotlin/bindings`, and build your plugin, leaving it in `build/.wasm`, ready to be installed into Pumpkin. +```sh +git submodule update --init --recursive +``` -Then you can tweaking the plugin src in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. +Then adjust the project name in `settings.gradle.kts`. The generated component is named after that project. + +Run `./gradlew build` to install the pinned build tools, generate the `pumpkin` package bindings, compile the Kotlin/Wasm module, and produce a validated component at `build/.wasm`, ready to be installed into Pumpkin. Generated bindings live under `build/generated/`. + +Then you can tweak the plugin source in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`. ## Rebuilding -Rebuilding the plugin is as simple as running `make` again. You can also be more specific by running `make componentify` to avoid the overhead of checking the `wit-bindgen` installation and regenerating the bindings. - -If you need a debug build instead of a release build, run `make componentify-dev` (`componentify` is an alias of `componentify-prod`). Note that this will make loading the plugin take SIGNFICANTLY longer (making optimized builds actually better for the development loop). +Rebuild with `./gradlew build`. Gradle skips tool installation, binding generation, and component assembly when their declared inputs are unchanged. Use `./gradlew clean` to remove generated build output; it intentionally retains the installed tool cache under `tools/`. ## Updating the WIT diff --git a/build.gradle.kts b/build.gradle.kts index fc80218..74aa151 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,8 @@ +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl + +import org.gradle.process.CommandLineArgumentProvider +import org.jetbrains.kotlin.gradle.targets.wasm.binaryen.BinaryenExec + plugins { alias(libs.plugins.kotlinMultiplatform) } @@ -7,8 +12,189 @@ repositories { } kotlin { + @OptIn(ExperimentalWasmDsl::class) wasmWasi { nodejs() binaries.executable() } } + +val cargoHome = providers.environmentVariable("CARGO_HOME") + .orElse(providers.systemProperty("user.home").map { "$it/.cargo" }) +val executableSuffix = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) ".exe" else "" +val cargoExecutable = cargoHome.map { "$it/bin/cargo$executableSuffix" } + +val witDirectory = layout.projectDirectory.dir("wit/v0.1") +val reactorAdapter = layout.projectDirectory.file("wasi_snapshot_preview1.reactor.wasm") + +val witBindgenRevision = "700f2db5e1d01f7bee8d756750c6f631171f520e" +val witBindgenDirectory = layout.projectDirectory.dir("tools/wit-bindgen/$witBindgenRevision") +val witBindgen = witBindgenDirectory.file("bin/wit-bindgen") + +val wasmToolsVersion = "1.258.0" +val wasmToolsDirectory = layout.projectDirectory.dir("tools/wasm-tools/$wasmToolsVersion") +val wasmTools = wasmToolsDirectory.file("bin/wasm-tools$executableSuffix") + +val generatedBindings = layout.buildDirectory.dir("generated/wit/wasmWasiMain/kotlin") +val projectWasmName = rootProject.name +val embeddedReleaseComponent = layout.buildDirectory.file( + "intermediates/pumpkin-components/release/$projectWasmName-embedded.wasm" +) +val releaseComponent = layout.buildDirectory.file("$projectWasmName.wasm") + +val installWitBindgen by tasks.registering(Exec::class) { + group = "build setup" + description = "Installs Pumpkin's pinned Kotlin binding generator." + + inputs.property("revision", witBindgenRevision) + outputs.dir(witBindgenDirectory) + + commandLine( + cargoExecutable.get(), + "install", + "wit-bindgen-cli", + "--git", "https://github.com/Kotlin/wit-bindgen", + "--rev", witBindgenRevision, + "--locked", + "--root", witBindgenDirectory.asFile.absolutePath, + ) +} + +val installWasmTools by tasks.registering(Exec::class) { + group = "build setup" + description = "Installs the pinned wasm-tools executable." + + inputs.property("version", wasmToolsVersion) + outputs.dir(wasmToolsDirectory) + + commandLine( + cargoExecutable.get(), + "install", + "wasm-tools", + "--version", wasmToolsVersion, + "--locked", + "--root", wasmToolsDirectory.asFile.absolutePath, + ) +} + +val generateWitBindings by tasks.registering(Exec::class) { + group = "build" + description = "Generates Kotlin bindings from Pumpkin's WIT definitions." + + dependsOn(installWitBindgen) + inputs.dir(witDirectory) + inputs.file(witBindgen) + inputs.property("kotlinPackage", "pumpkin") + inputs.property("kotlinImports", "plugin.*") + outputs.dir(generatedBindings) + + doFirst { + val outputDirectory = generatedBindings.get().asFile + check(outputDirectory.deleteRecursively() || !outputDirectory.exists()) { + "Could not remove stale bindings from $outputDirectory" + } + check(outputDirectory.mkdirs() || outputDirectory.isDirectory) { + "Could not create bindings directory $outputDirectory" + } + } + + commandLine( + witBindgen.asFile.absolutePath, + "kotlin", + "--kotlin-imports", "plugin.*", + "--kotlin-package-name", "pumpkin", + witDirectory.asFile.absolutePath, + "--out-dir", generatedBindings.get().asFile.absolutePath, + ) +} + +kotlin { + sourceSets.named("wasmWasiMain") { + kotlin.srcDir(generateWitBindings) + } +} + +val compileReleaseWasm = tasks.named("compileProductionExecutableKotlinWasmWasiOptimize") +val releaseCoreWasm = compileReleaseWasm.flatMap { + it.outputDirectory.file("$projectWasmName.wasm") +} + +val embedComponentWitRelease by tasks.registering(Exec::class) { + group = "build" + description = "Embeds Pumpkin's WIT into the release Kotlin/Wasm module." + + dependsOn(compileReleaseWasm, installWasmTools) + inputs.dir(witDirectory) + inputs.file(releaseCoreWasm) + inputs.file(wasmTools) + outputs.file(embeddedReleaseComponent) + + executable = wasmTools.asFile.absolutePath + argumentProviders.add( + object : CommandLineArgumentProvider { + override fun asArguments() = listOf( + "component", + "embed", + witDirectory.asFile.absolutePath, + releaseCoreWasm.get().asFile.absolutePath, + "-o", embeddedReleaseComponent.get().asFile.absolutePath, + ) + } + ) + + doFirst { + val outputDirectory = embeddedReleaseComponent.get().asFile.parentFile + check(outputDirectory.mkdirs() || outputDirectory.isDirectory) { + "Could not create component directory $outputDirectory" + } + } +} + +val assemblePluginRelease by tasks.registering(Exec::class) { + group = "build" + description = "Creates the release WebAssembly component that Pumpkin can load." + + dependsOn(embedComponentWitRelease) + inputs.file(embeddedReleaseComponent) + inputs.file(reactorAdapter) + inputs.file(wasmTools) + outputs.file(releaseComponent) + + commandLine( + wasmTools.asFile.absolutePath, + "component", + "new", + embeddedReleaseComponent.get().asFile.absolutePath, + "--adapt", "wasi_snapshot_preview1=${reactorAdapter.asFile.absolutePath}", + "-o", releaseComponent.get().asFile.absolutePath, + ) +} + +val validatePluginRelease by tasks.registering(Exec::class) { + group = "verification" + description = "Validates the release WebAssembly component." + + dependsOn(assemblePluginRelease) + inputs.file(releaseComponent) + inputs.file(wasmTools) + + commandLine( + wasmTools.asFile.absolutePath, + "validate", + releaseComponent.get().asFile.absolutePath, + ) +} + +tasks.named("assemble") { + dependsOn(assemblePluginRelease) +} + +tasks.named("check") { + dependsOn(validatePluginRelease) +} + +tasks.register("cleanProject") { + group = "build" + description = "Removes generated build output." + dependsOn(tasks.named("clean")) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 87eac3d..626cba4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,3 @@ -// When changing this, make sure to modify the Makefile accordingly rootProject.name = "pumpkin-plugin-template-kt" pluginManagement {