@@ -8,16 +8,107 @@ plugins {
88 `kotlin- conventions`
99 `library- publishing- conventions`
1010 alias(libs.plugins.jsonschema2pojo)
11- alias(libs.plugins.protobuf)
12- alias(libs.plugins.shadow)
1311 alias(libs.plugins.ksp)
12+ // Chicory AOT: compile .wasm to JVM bytecode at build time
13+ id(" at.released.wasm2class.plugin" ) version " 0.5.0"
1414
1515 // https://github.com/gradle/gradle/issues/20084#issuecomment-1060822638
1616 id(libs.plugins.spotless.get().pluginId) apply false
1717}
1818
1919description = " Restate SDK Core"
2020
21+ // ---------------------------------------------------------------------------
22+ // Rust WASM build pipeline (merged from sdk-shared-core)
23+ // ---------------------------------------------------------------------------
24+
25+ val rustSrcDir = file(" src/main/rust" )
26+ val wasmReleaseDir = file(" $rustSrcDir /target/wasm32-unknown-unknown/release" )
27+ val wasmFile = file(" $wasmReleaseDir /restate_sdk_shared_core_wasm.wasm" )
28+ val wasmResourceDir = layout.buildDirectory.dir(" wasm-resource" )
29+ val wasmResourceFile = wasmResourceDir.map { it.file(" restate_sdk_shared_core_wasm.wasm" ) }
30+
31+ val compileRustToWasm by
32+ tasks.registering(Exec ::class ) {
33+ group = " build"
34+ description = " Compile the Rust WASM wrapper crate"
35+ workingDir = rustSrcDir
36+ commandLine(" cargo" , " build" , " --target" , " wasm32-unknown-unknown" , " --release" )
37+ inputs.dir(" $rustSrcDir /src" )
38+ inputs.file(" $rustSrcDir /Cargo.toml" )
39+ outputs.file(wasmFile)
40+ }
41+
42+ val copyWasm by
43+ tasks.registering(Copy ::class ) {
44+ group = " build"
45+ dependsOn(compileRustToWasm)
46+ from(wasmFile)
47+ into(wasmResourceDir)
48+ }
49+
50+ // Chicory AOT: compile .wasm → JVM bytecode
51+ wasm2class {
52+ modules {
53+ targetPackage = " dev.restate.sdk.core.sharedcore.generated"
54+ create(" SharedCoreWasm" ) { wasm = wasmResourceFile }
55+ }
56+ }
57+
58+ tasks.named(" precompileWasm2Class" ) { dependsOn(copyWasm) }
59+
60+ tasks.named(" processResources" ) { dependsOn(copyWasm) }
61+
62+ tasks.withType<KotlinCompile >().configureEach {
63+ mustRunAfter(generateWasmMarker, " precompileWasm2Class" )
64+ }
65+
66+ // Generate the @WasmModuleInterface marker class with the absolute wasm file URI so the
67+ // Chicory annotation processor can find it at compile time (it uses StandardLocation.CLASS_OUTPUT
68+ // which is not reliable cross-tool, so a file: URI works around that).
69+ val generatedWasmMarkerDir = layout.buildDirectory.dir(" generated-wasm-marker" )
70+
71+ val generateWasmMarker by
72+ tasks.registering {
73+ group = " build"
74+ dependsOn(copyWasm)
75+ inputs.file(wasmResourceFile)
76+ outputs.dir(generatedWasmMarkerDir)
77+ doLast {
78+ val wasmUri = wasmResourceFile.get().asFile.toURI().toString()
79+ val content =
80+ """
81+ // AUTO-GENERATED — do not edit. Regenerated by generateWasmMarker Gradle task.
82+ // Copyright (c) 2023 - Restate Software, Inc., Restate GmbH
83+ //
84+ // This file is part of the Restate Java SDK,
85+ // which is released under the MIT license.
86+ package dev.restate.sdk.core.sharedcore;
87+
88+ import com.dylibso.chicory.annotations.WasmModuleInterface;
89+
90+ /**
91+ * Marker class processed by Chicory's annotation processor. Generates
92+ * {@code SharedCoreWasm_ModuleExports} (typed wrapper for all WASM exports).
93+ */
94+ @WasmModuleInterface("$wasmUri ")
95+ public final class SharedCoreWasm {}
96+ """
97+ .trimIndent() + " \n "
98+ val out =
99+ generatedWasmMarkerDir
100+ .get()
101+ .file(" dev/restate/sdk/core/sharedcore/SharedCoreWasm.java" )
102+ .asFile
103+ out .parentFile.mkdirs()
104+ out .writeText(content)
105+ }
106+ }
107+
108+ // ---------------------------------------------------------------------------
109+ // Dependency configurations
110+ // ---------------------------------------------------------------------------
111+
21112val shade by configurations.creating
22113val implementation by configurations.getting
23114
@@ -30,17 +121,21 @@ api.extendsFrom(shade)
30121dependencies {
31122 compileOnly(libs.jspecify)
32123
33- shadow(project(" :sdk-common" ))
124+ // Chicory annotation processor (@WasmModuleInterface, @HostModule)
125+ compileOnly(libs.chicory.annotations)
126+ annotationProcessor(libs.chicory.annotations.processor)
34127
35- shadow(libs.log4j.api)
36- shadow(libs.opentelemetry.api)
128+ implementation(project(" :sdk-common" ))
129+ implementation(libs.log4j.api)
130+ implementation(libs.opentelemetry.api)
37131
38132 // We need this for the manifest
39- shadow (libs.jackson.annotations)
40- shadow (libs.jackson.databind)
133+ implementation (libs.jackson.annotations)
134+ implementation (libs.jackson.databind)
41135
42- // We shade protobuf java
43- shade(libs.protobuf.java)
136+ // Chicory runtime + Jackson CBOR for the WASM bridge — shaded into the jar
137+ implementation(libs.chicory.runtime)
138+ implementation(libs.jackson.cbor)
44139
45140 // We don't want a hard-dependency on it
46141 compileOnly(libs.log4j.core)
@@ -55,10 +150,11 @@ dependencies {
55150 testImplementation(project(" :sdk-api" ))
56151 testImplementation(project(" :sdk-api-kotlin" ))
57152 testImplementation(project(" :sdk-http-vertx" ))
58- testImplementation(project(" :sdk-lambda" ))
59153 testImplementation(libs.jackson.annotations)
60154 testImplementation(libs.jackson.databind)
155+ testImplementation(libs.jackson.cbor)
61156 testImplementation(libs.opentelemetry.api)
157+ testImplementation(libs.chicory.runtime)
62158 testImplementation(libs.protobuf.java)
63159 testImplementation(libs.mutiny)
64160 testImplementation(libs.junit.jupiter)
@@ -71,39 +167,42 @@ dependencies {
71167 testRuntimeOnly(libs.junit.platform.launcher)
72168}
73169
74- // Configure source sets for protobuf plugin and jsonschema2pojo
170+ // ---------------------------------------------------------------------------
171+ // Source sets
172+ // ---------------------------------------------------------------------------
173+
75174val generatedJ2SPDir = layout.buildDirectory.dir(" generated/j2sp" )
76175
77176sourceSets {
78177 main {
79178 java.srcDir(generatedJ2SPDir)
80- proto { srcDirs(" src/main/service-protocol" ) }
179+ java.srcDir(generatedWasmMarkerDir)
180+ proto { srcDirs(" src/main/service-protocol" ) } // TODO(Phase3): remove
181+ resources.srcDir(wasmResourceDir)
81182 }
82183}
83184
84- // Configure jsonSchema2Pojo
185+ // ---------------------------------------------------------------------------
186+ // jsonSchema2Pojo
187+ // ---------------------------------------------------------------------------
188+
85189jsonSchema2Pojo {
86190 setSource(files(" $projectDir /src/main/service-protocol/endpoint_manifest_schema.json" ))
87191 targetPackage = " dev.restate.sdk.core.generated.manifest"
88192 targetDirectory = generatedJ2SPDir.get().asFile
89-
90193 useLongIntegers = true
91194 includeSetters = true
92195 includeGetters = true
93196 generateBuilders = true
94197}
95198
96- // Configure protobuf
97-
98- val protobufVersion = libs.versions.protobuf.get()
99-
100- protobuf { protoc { artifact = " com.google.protobuf:protoc:$protobufVersion " } }
101-
102- // Make sure task dependencies are correct
199+ // ---------------------------------------------------------------------------
200+ // Task wiring
201+ // ---------------------------------------------------------------------------
103202
104203tasks {
105204 withType<JavaCompile > {
106- dependsOn(generateJsonSchema2Pojo, generateProto)
205+ dependsOn(generateJsonSchema2Pojo, generateProto, generateWasmMarker, " precompileWasm2Class " )
107206
108207 val disabledClassesCodegen =
109208 listOf (
@@ -129,25 +228,9 @@ tasks {
129228 }
130229 withType<KotlinCompile >().configureEach { dependsOn(generateJsonSchema2Pojo, generateProto) }
131230 withType< org.gradle.jvm.tasks.Jar > ().configureEach {
132- dependsOn(generateJsonSchema2Pojo, generateProto)
231+ dependsOn(generateJsonSchema2Pojo, generateProto, generateWasmMarker )
133232 }
134233 withType<AbstractDokkaTask >().configureEach { dependsOn(generateJsonSchema2Pojo, generateProto) }
135-
136- getByName(" jar" ) {
137- enabled = false
138- dependsOn(shadowJar)
139- }
140-
141- shadowJar {
142- configurations = listOf (shade)
143- enableRelocation = true
144- archiveClassifier = null
145- relocate(" com.google.protobuf" , " dev.restate.shaded.com.google.protobuf" )
146- dependencies {
147- project.configurations[" shadow" ].allDependencies.forEach { exclude(dependency(it)) }
148- exclude(" **/google/protobuf/*.proto" )
149- }
150- }
151234}
152235
153236ksp {
@@ -169,20 +252,3 @@ ksp {
169252 )
170253 arg(" dev.restate.codegen.disabledClasses" , disabledClassesCodegen.joinToString(" ," ))
171254}
172-
173- // spotless configuration for protobuf
174-
175- configure< com.diffplug.gradle.spotless.SpotlessExtension > {
176- format(" proto" ) {
177- target(" **/*.proto" )
178-
179- // Exclude proto and service-protocol directories because those get the license header from
180- // their repos.
181- targetExclude(
182- fileTree(" $rootDir /sdk-common/src/main/proto" ) { include(" **/*.*" ) },
183- fileTree(" $rootDir /sdk-core/src/main/service-protocol" ) { include(" **/*.*" ) },
184- )
185-
186- licenseHeaderFile(" $rootDir /config/license-header" , " syntax" )
187- }
188- }
0 commit comments