How to make Rust JNI bindings with Bazel

Viewed 187

Introduction

I have a Rust library that I want to be able to call from Java through JNI. Using Maven, I can use it, but I'm stuck when I try to move to Bazel.

I run bazel run //my_lib/java:java_binary and I get the following

Exception in thread "main" java.lang.UnsatisfiedLinkError: no rust_bindings in java.library.path: [/private/var/tmp/_bazel_XXXXXXXXX/XXXXXXXXXXXXX/execroot/__main__/bazel-out/darwin-fastbuild/bin/my_lib/java/java_binary.runfiles/__main__/my_lib/rust]
    at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2680)
    at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:807)
    at java.base/java.lang.System.loadLibrary(System.java:1907)
    at Main.<clinit>(Main.java:8)

which means the Rust shared library library isn't available. Indeed when I tree /private/var/tmp/_bazel_XXXXXXXXX/XXXXXXXXXXXXX/execroot/__main__/bazel-out/darwin-fastbuild/bin/my_lib/java/java_binary.runfiles/__main__/my_lib, I get

└── java
    ├── java_binary -> /private/var/tmp/_bazel_XXXXXXXXX/XXXXXXXXXXXXX/execroot/__main__/bazel-out/darwin-fastbuild/bin/stackoverflow/java/java_binary
    └── java_binary.jar -> /private/var/tmp/_bazel_XXXXXXXXX/XXXXXXXXXXXXX/execroot/__main__/bazel-out/darwin-fastbuild/bin/stackoverflow/java/java_binary.jar

How come bazel doesn't build the Rust shared library despite the fact it's a dependancy of the Java binary?

The code

$ tree .
├── java
│   ├── BUILD.bazel
│   ├── Main.class
│   └── Main.java
└── rust
    ├── BUILD.bazel
    ├── CARGO
    │   └── BUILD.bazel
    ├── Cargo.toml
    └── src
        └── lib.rs

java/Main.java

public class Main {

    static native void rust();

    static {
        System.loadLibrary("rust_bindings");
    }

    public static void main(String[] args) {
        System.out.println("Hello from Java");
        rust();
    }
}

java/BUILD.bazel

package(default_visibility = ["//visibility:public"])

load("@rules_java//java:defs.bzl", "java_binary")

java_binary(
    name = "java_binary",
    srcs = glob(["*.java"]),
    main_class = "Main",
    deps = [
        "//my_lib/rust:rust_bindings"
    ],
)

rust/Cargo.toml

[package]
name = "rust_bindings"
version = "0.1.0"
edition = "2018"

[lib]
name="rust_bindings"
crate-type = ["cdylib"]

[dependencies]
jni = "0.19.0"

rust/src/lib.rs

use jni::JNIEnv;
use jni::objects::{JClass, JString};

#[no_mangle]
pub extern "system" fn Java_Main_rust(env: JNIEnv,class: JClass)
{
    println!("Hello from Rust");
}

rust/BUILD.bazel

package(default_visibility = ["//visibility:public"])

load("@rules_rust//rust:defs.bzl", "rust_shared_library")
load("//cargo:crates.bzl", "all_crate_deps")

rust_shared_library(
    name = "rust_bindings",
    srcs = glob(["src/**/*.rs"]),
    edition = "2018",
    deps = all_crate_deps(), // Import the JNI crate using Bazel
)

Edit: simplified the code

1 Answers

I didn't manage to make it work with rust_shared_library, so as workaround I wrapped it in cc_binary (relevant commit).

In your example try to change rust/BUILD.bazel:

load("@rules_rust//rust:defs.bzl", "rust_shared_library")
load("//cargo:crates.bzl", "all_crate_deps")

rust_shared_library(
    name = "rust_lib",
    srcs = glob(["src/**/*.rs"]),
    edition = "2018",
    deps = all_crate_deps(), // Import the JNI crate using Bazel
)

cc_binary(
    name = "rust_bindings",
    linkshared = True,
    visibility = ["//visibility:public"],
    deps = [":rust_lib"],
)
Related