Skip to content

Commit

Permalink
Add example of using JNI to call into Rust (#2690)
Browse files Browse the repository at this point in the history
  • Loading branch information
illicitonion committed Jun 14, 2024
1 parent d0b0553 commit bb73b64
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 2 deletions.
5 changes: 5 additions & 0 deletions examples/.bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ common --noenable_bzlmod
# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips.
build --incompatible_disallow_empty_glob

build --java_runtime_version=remotejdk_21
build --java_language_version=21
build --tool_java_runtime_version=remotejdk_21
build --tool_java_language_version=21

# This import should always be last to allow users to override
# settings for local development.
try-import %workspace%/user.bazelrc
46 changes: 44 additions & 2 deletions examples/WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ load("@rules_rust//bindgen:transitive_repositories.bzl", "rust_bindgen_transitiv

rust_bindgen_transitive_dependencies()

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

# We need to load rules_java before anything proto-related happens because otherwise it will pull in its own rules_java which isn't compatible with rules_jvm_external.
http_archive(
name = "rules_java",
sha256 = "f8ae9ed3887df02f40de9f4f7ac3873e6dd7a471f9cddf63952538b94b59aeb3",
urls = [
"https://github.com/bazelbuild/rules_java/releases/download/7.6.1/rules_java-7.6.1.tar.gz",
],
)

load("@rules_rust//proto/protobuf:repositories.bzl", "rust_proto_protobuf_dependencies", "rust_proto_protobuf_register_toolchains")

rust_proto_protobuf_dependencies()
Expand All @@ -58,8 +69,6 @@ load("@rules_rust//wasm_bindgen/rules_js:repositories.bzl", "js_rust_wasm_bindge

js_rust_wasm_bindgen_dependencies()

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

###############################################################################
# Workspace examples
###############################################################################
Expand Down Expand Up @@ -161,6 +170,39 @@ http_archive(
],
)

http_archive(
name = "rules_jvm_external",
sha256 = "08ea921df02ffe9924123b0686dc04fd0ff875710bfadb7ad42badb931b0fd50",
strip_prefix = "rules_jvm_external-6.1",
url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/6.1/rules_jvm_external-6.1.tar.gz",
)

load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")

rules_jvm_external_deps()

load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")

rules_jvm_external_setup()

load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
name = "maven",
artifacts = [
"net.java.dev.jna:jna:5.14.0",
"org.hamcrest:hamcrest:2.2",
],
maven_install_json = "@//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
],
)

load("@maven//:defs.bzl", "pinned_maven_install")

pinned_maven_install()

###############################################################################

http_archive(
Expand Down
21 changes: 21 additions & 0 deletions examples/ffi/java_calling_rust/com/example/rustjni/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@rules_java//java:defs.bzl", "java_library", "java_test")

java_library(
name = "rustjni",
srcs = ["RustStringLength.java"],
data = ["//ffi/java_calling_rust/rust-crate:rstrlen"],
deps = [
"@bazel_tools//tools/java/runfiles",
"@maven//:net_java_dev_jna_jna",
],
)

java_test(
name = "rustjni_test",
srcs = ["RustJniTest.java"],
test_class = "com.example.rustjni.RustJniTest",
deps = [
":rustjni",
"@maven//:org_hamcrest_hamcrest",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.rustjni;

import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class RustJniTest {
@Test
public void testCallsJniToRust() throws Exception {
final String s = "hello";
long result = RustStringLength.loadNativeLibrary().calculate_string_length_from_rust(s);
assertThat(result, equalTo(5L));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.example.rustjni;

import com.google.devtools.build.runfiles.AutoBazelRepository;
import com.google.devtools.build.runfiles.Runfiles;

import com.sun.jna.Library;
import com.sun.jna.Native;

import java.io.IOException;

@AutoBazelRepository
public interface RustStringLength extends Library {
long calculate_string_length_from_rust(String s);

static RustStringLength loadNativeLibrary() throws IOException {
String prefix = "lib";
String extension = "so";
if ("Mac OS X".equals(System.getProperty("os.name"))) {
extension = "dylib";
} else if (System.getProperty("os.name").contains("Windows")) {
prefix = "";
extension = "dll";
}
Runfiles.Preloaded runfiles = Runfiles.preload();
String dylibPath = runfiles.withSourceRepository(AutoBazelRepository_RustStringLength.NAME).rlocation("examples/ffi/java_calling_rust/rust-crate/" + prefix + "rstrlen." + extension);

return Native.load(dylibPath, RustStringLength.class);
}
}
8 changes: 8 additions & 0 deletions examples/ffi/java_calling_rust/rust-crate/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@rules_rust//rust:defs.bzl", "rust_shared_library")

rust_shared_library(
name = "rstrlen",
srcs = ["rust_string_length.rs"],
edition = "2021",
visibility = ["//visibility:public"],
)
16 changes: 16 additions & 0 deletions examples/ffi/java_calling_rust/rust-crate/rust_string_length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::ffi::CStr;
use std::os::raw::c_char;

/// Calculates the length of a string.
///
/// # Safety
///
/// The argument must null-terminated.
#[no_mangle]
pub unsafe extern "C" fn calculate_string_length_from_rust(s: *const c_char) -> i64 {
let slice = unsafe { CStr::from_ptr(s).to_bytes() };
std::str::from_utf8(slice).map_or(-1, |s| {
let l = s.len();
l.try_into().unwrap_or(-1)
})
}
49 changes: 49 additions & 0 deletions examples/maven_install.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
"__INPUT_ARTIFACTS_HASH": -152875169,
"__RESOLVED_ARTIFACTS_HASH": 61617868,
"artifacts": {
"net.java.dev.jna:jna": {
"shasums": {
"jar": "34ed1e1f27fa896bca50dbc4e99cf3732967cec387a7a0d5e3486c09673fe8c6"
},
"version": "5.14.0"
},
"org.hamcrest:hamcrest": {
"shasums": {
"jar": "5e62846a89f05cd78cd9c1a553f340d002458380c320455dd1f8fc5497a8a1c1"
},
"version": "2.2"
}
},
"dependencies": {},
"packages": {
"net.java.dev.jna:jna": [
"com.sun.jna",
"com.sun.jna.internal",
"com.sun.jna.ptr",
"com.sun.jna.win32"
],
"org.hamcrest:hamcrest": [
"org.hamcrest",
"org.hamcrest.beans",
"org.hamcrest.collection",
"org.hamcrest.comparator",
"org.hamcrest.core",
"org.hamcrest.internal",
"org.hamcrest.io",
"org.hamcrest.number",
"org.hamcrest.object",
"org.hamcrest.text",
"org.hamcrest.xml"
]
},
"repositories": {
"https://repo1.maven.org/maven2/": [
"net.java.dev.jna:jna",
"org.hamcrest:hamcrest"
]
},
"services": {},
"version": "2"
}

0 comments on commit bb73b64

Please sign in to comment.