From bc42f8a2d8d72ddc51713f0bc6600d9f8357517f Mon Sep 17 00:00:00 2001 From: Martin Jakobsson Date: Thu, 26 Dec 2024 01:53:16 +0100 Subject: [PATCH] Added ability to call Rust code from Scala --- build.sbt | 13 +- .../src/main/scala/hexacraft/rs/RustLib.scala | 7 + native/src/native/Cargo.lock | 294 ++++++++++++++++++ native/src/native/Cargo.toml | 10 + native/src/native/README.md | 12 + .../native/jni/hexacraft_rs_RustLib_00024.h | 21 ++ native/src/native/src/lib.rs | 12 + .../test/scala/hexacraft/rs/RustLibTest.scala | 9 + project/plugins.sbt | 1 + 9 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 native/src/main/scala/hexacraft/rs/RustLib.scala create mode 100644 native/src/native/Cargo.lock create mode 100644 native/src/native/Cargo.toml create mode 100644 native/src/native/README.md create mode 100644 native/src/native/jni/hexacraft_rs_RustLib_00024.h create mode 100644 native/src/native/src/lib.rs create mode 100644 native/src/test/scala/hexacraft/rs/RustLibTest.scala diff --git a/build.sbt b/build.sbt index 19641ea3..92dab5ad 100644 --- a/build.sbt +++ b/build.sbt @@ -20,10 +20,21 @@ val commonSettings: scala.Seq[Def.Setting[?]] = Defaults.coreDefaultSettings ++ lazy val hexacraft = project .in(file(".")) - .aggregate(common, nbt, window, audio, fs, gpu, system, game, client, server, main) + .aggregate(native, common, nbt, window, audio, fs, gpu, system, game, client, server, main) + +lazy val native = project + .in(file("native")) + .settings(commonSettings*) + .settings(libraryDependencies += MUnit) + .settings( + sbtJniCoreScope := Compile, + javah / target := (nativeCompile / sourceDirectory).value / "jni" // just for reference + ) + .enablePlugins(JniNative) lazy val common = project .in(file("common")) + .dependsOn(native % Runtime) .settings(commonSettings*) .settings( libraryDependencies ++= Seq(Joml) :+ MUnit diff --git a/native/src/main/scala/hexacraft/rs/RustLib.scala b/native/src/main/scala/hexacraft/rs/RustLib.scala new file mode 100644 index 00000000..6f31a28e --- /dev/null +++ b/native/src/main/scala/hexacraft/rs/RustLib.scala @@ -0,0 +1,7 @@ +package hexacraft.rs + +import com.github.sbt.jni.syntax.NativeLoader + +object RustLib extends NativeLoader("hexacraft_rs") { + @native def hello(): String +} diff --git a/native/src/native/Cargo.lock b/native/src/native/Cargo.lock new file mode 100644 index 00000000..63f1a59f --- /dev/null +++ b/native/src/native/Cargo.lock @@ -0,0 +1,294 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "hexacraft-rs" +version = "0.1.0" +dependencies = [ + "jni", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/native/src/native/Cargo.toml b/native/src/native/Cargo.toml new file mode 100644 index 00000000..5670225f --- /dev/null +++ b/native/src/native/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hexacraft-rs" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +jni = "0.21.1" diff --git a/native/src/native/README.md b/native/src/native/README.md new file mode 100644 index 00000000..7b8aeba4 --- /dev/null +++ b/native/src/native/README.md @@ -0,0 +1,12 @@ +## JNI wrapper (calling Rust from Scala) + +This folder contains the glue code needed for calling Rust code from Scala. The code is compiled into a dynamic library which is then shipped as a single file inside the JAR file of this sbt project. + +### Header files + +The header files in the `jni` folder can be generated using the `sbt` command `javah`, and are only there to show what the API should look like. Since the header files are checked in to git it should be easy to see if something has changed since the last commit. + +### Useful sbt commands + +- `native / test` +- `javah` diff --git a/native/src/native/jni/hexacraft_rs_RustLib_00024.h b/native/src/native/jni/hexacraft_rs_RustLib_00024.h new file mode 100644 index 00000000..bc32d572 --- /dev/null +++ b/native/src/native/jni/hexacraft_rs_RustLib_00024.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class hexacraft_rs_RustLib_00024 */ + +#ifndef _Included_hexacraft_rs_RustLib_00024 +#define _Included_hexacraft_rs_RustLib_00024 +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: hexacraft_rs_RustLib_00024 + * Method: hello + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_hexacraft_rs_RustLib_00024_hello + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/src/native/src/lib.rs b/native/src/native/src/lib.rs new file mode 100644 index 00000000..b922f671 --- /dev/null +++ b/native/src/native/src/lib.rs @@ -0,0 +1,12 @@ +use jni::objects::JObject; +use jni::sys::jstring; +use jni::JNIEnv; + +#[no_mangle] +pub extern "system" fn Java_hexacraft_rs_RustLib_00024_hello<'local>( + env: JNIEnv<'local>, + _object: JObject<'local>, +) -> jstring { + let s = env.new_string("Hello from Rust!").unwrap(); + s.into_raw() +} diff --git a/native/src/test/scala/hexacraft/rs/RustLibTest.scala b/native/src/test/scala/hexacraft/rs/RustLibTest.scala new file mode 100644 index 00000000..eeba1c66 --- /dev/null +++ b/native/src/test/scala/hexacraft/rs/RustLibTest.scala @@ -0,0 +1,9 @@ +package hexacraft.rs + +import munit.FunSuite + +class RustLibTest extends FunSuite { + test("hello") { + assertEquals(RustLib.hello(), "Hello from Rust!") + } +} diff --git a/project/plugins.sbt b/project/plugins.sbt index 8eafe0ba..e8e615d0 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,3 +2,4 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.1.0") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.11") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.20") +addSbtPlugin("com.github.sbt" % "sbt-jni" % "1.7.1")