如何在Java中调用Rust编写的库包?在这个例子中,我们将看到如何:
- 构建一个简单的 Rust 库,它公开一个 C API(巴拿马 FFI 可以链接到它)。
- 使用cbindgen来生成这个库中的C头文件。
- 用于jextract从头文件生成 java 绑定。
- 创建一个简单的 java 程序,通过绑定调用 rust 库。
步骤 1. 设置项目
$ mkdir rust-panama-helloworld |
步骤 2. 编写一个简单的 Rust 库
编辑src/lib.rs并将内容更改为:
#[no_mangle] |
#[no_mangle]需要该属性以确保函数在库中可见,并extern "C"用于确保函数具有正确的 ABI(特定平台的 C ABI)。
步骤 3. 添加所需的项目配置
进入Cargo.toml并添加以下内容:
[build-dependencies] |
cbindgen用于从 rust 源生成 C 头文件,该文件将被输入到巴拿马的jextract工具中以生成 java 绑定。
步骤 4. 创建一个调用 cbindgen 的构建脚本
build.rs在顶级目录中创建一个文件并添加以下内容:
extern crate cbindgen; |
这是一个简单的构建脚本,它调用 cbindgen 并将输出写入lib.h顶级目录中的文件。
步骤 5. 构建 Rust 库并生成 C 头文件
$ cargo build
这应该在target/debug的文件夹中创建该文件(lib)rust_panama_helloworld.(dll/so/dylib)。
构建还应调用cbindgen并生成lib.h顶级目录中的文件。该文件的内容应如下所示:
include <stdarg.h> |
步骤 6. 确保panama jdk 在路径上
$ java --version |
在撰写本文时,我正在使用http://jdk.java.net/panama/上提供的最新快照。
步骤 7. 使用 jextract 生成 java 绑定
$ jextract -d classes -t org.openjdk --include-function hello_world -l rust_panama_helloworld -- lib.h |
在这个命令中:
- -d classes 指定生成的绑定的输出目录。
- -t org.openjdk 指定生成的类所在的 java 包。
- --include-function hello_world所以我们只包含hello_world我们在 rust 中定义的函数。
- -l rust_panama_helloworld使生成的绑定将rust_panama_helloworld自动加载库。
- -- 是一个分隔符,用于指示要处理的头文件列表的开始。
- 最后lib.h是在步骤 5 中生成的头文件。
一个classes目录应创建包含由jextract产生了一些文件:
./classes |
步骤 8. 创建一个调用我们的库的 java 程序
在顶级目录中创建一个Main.java文件并将以下内容放入其中:
import static org.openjdk.lib_h.*; |
步骤 9. 运行 java 程序
$ java --add-modules jdk.incubator.foreign --enable-native-access=ALL-UNNAMED -Djava.library.path=./target/debug -cp classes Main.java |
在这个命令中:
- --add-modules jdk.incubator.foreign将jdk.incubator.foreign模块添加到模块图中(默认情况下不解析孵化器模块)。
- --enable-native-access=ALL-UNNAMED 启用对未命名模块的本机访问,这是巴拿马 API 工作所必需的。
- -Djava.library.path=./target/debug将包含我们 rust 库的目录添加到库路径中,以便System.loadLibrary在运行时可以找到它。
- -cp classes 使用我们生成的 java 绑定类(jextract 的输出)指定类路径。
- Main.java 指定我们的主类,它将被动态编译,然后运行。
步骤 10. 观察输出
WARNING: Using incubator modules: jdk.incubator.foreign |