在Java中使用panama FFI调用Rust库

在这里插入图片描述

如何在Java中调用Rust编写的库包?在这个例子中,我们将看到如何:

  • 构建一个简单的 Rust 库,它公开一个 C API(巴拿马 FFI 可以链接到它)。
  • 使用cbindgen来生成这个库中的C头文件。
  • 用于jextract从头文件生成 java 绑定。
  • 创建一个简单的 java 程序,通过绑定调用 rust 库。

步骤 1. 设置项目

$ mkdir rust-panama-helloworld
$ cd rust-panama-helloworld
$ cargo init --lib

步骤 2. 编写一个简单的 Rust 库

编辑src/lib.rs并将内容更改为:

#[no_mangle]
pub extern <font>"C"</font><font> fn hello_world() {
    println!(</font><font>"Hello, world!"</font><font>);
}
</font>

#[no_mangle]需要该属性以确保函数在库中可见,并extern "C"用于确保函数具有正确的 ABI(特定平台的 C ABI)。

步骤 3. 添加所需的项目配置

进入Cargo.toml并添加以下内容:

[build-dependencies]
cbindgen = <font>"0.20.0"</font><font>

<p>[lib]
crate_type = [</font><font>"cdylib"</font><font>]
</font>

cbindgen 用于从 rust 源生成 C 头文件,该文件将被输入到巴拿马的jextract工具中以生成 java 绑定。

步骤 4. 创建一个调用 cbindgen 的构建脚本

build.rs在顶级目录中创建一个文件并添加以下内容:

extern crate cbindgen;

use std::env;

fn main() {
    let crate_dir = env::<b>var</b>(<font>"CARGO_MANIFEST_DIR"</font><font>).unwrap();

    cbindgen::Builder::<b>new</b>()
      .with_crate(crate_dir)
      .with_language(cbindgen::Language::C)
      .generate()
      .expect(</font><font>"Unable to generate bindings"</font><font>)
      .write_to_file(</font><font>"lib.h"</font><font>);
}
</font>

这是一个简单的构建脚本,它调用 cbindgen 并将输出写入lib.h顶级目录中的文件。

步骤 5. 构建 Rust 库并生成 C 头文件

$ cargo build

这应该在target/debug的文件夹中创建该文件(lib)rust_panama_helloworld.(dll/so/dylib)。

构建还应调用cbindgen并生成lib.h顶级目录中的文件。该文件的内容应如下所示:

<a href="/query/searchAction.shtml?query=include"><b>include</b></a> <stdarg.h>
<a href="/query/searchAction.shtml?query=include"><b>include</b></a> <stdbool.h>
<a href="/query/searchAction.shtml?query=include"><b>include</b></a> <stdint.h>
<a href="/query/searchAction.shtml?query=include"><b>include</b></a> <stdlib.h>

<b>void</b> hello_world(<b>void</b>);

步骤 6. 确保panama jdk 在路径上

$ java --version
openjdk 17-panama 2021-09-14
OpenJDK Runtime Environment (build 17-panama+3-167)
OpenJDK 64-Bit Server VM (build 17-panama+3-167, mixed mode, sharing)

在撰写本文时,我正在使用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
└───org
    └───openjdk
            constants$0.<b>class</b>
            lib_h.<b>class</b>
            RuntimeHelper$VarargsInvoker.<b>class</b>
            RuntimeHelper.<b>class</b>

步骤 8. 创建一个调用我们的库的 java 程序

在顶级目录中创建一个Main.java文件并将以下内容放入其中:

<b>import</b> <b>static</b> org.openjdk.lib_h.*;

<b>public</b> <b>class</b> Main {
    <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
        hello_world();
    }
}

步骤 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
warning: using incubating module(s): jdk.incubator.foreign
1 warning
Hello, world!

在这里插入图片描述
如果对软件测试、接口测试、自动化测试、持续集成、面试经验。感兴趣可以进到806549072,群内会有不定期的分享测试资料。还会有技术大牛,业内同行一起交流技术


版权声明:本文为m0_61655732原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。