是否可以一起使用Java,SWIG和Rust?

时间:2016-07-08 06:52:44

标签: java java-native-interface rust swig rust-cargo

我正在尝试从Rust调用Java库,我真的想使用SWIG从我写的C头文件生成接口层(我也想让普通的C客户端调用我的库,因此我认为维护一个接口头是有意义的。)

我使用WindowsMingwRust (GNU ABI)上完成所有操作。

我将完全按照我所做的和下面的结果进行,但基本上我最后得到UnsatisfiedLinkError。我认为有些事情可能是错的,但我不确定,我不确定如何(或者如果)我可以修复它们:

  1. SWIG将数字放在函数名称中(如果您编辑它在我的示例中生成的testlib_wrap.c文件,则可以看到这一点。)
  2. JNI documentation说我需要在编译时传递参数-Wl,--add-stdcall-alias但是当我用货物建造时我不知道该怎么做(如果我直接用rustc构建,我可以通过它吗?也许?我在man page
  3. 中看不到任何内容

    简而言之,我的问题是:

    如何使用SWIG从Java调用Rust

    但我觉得我正在摸索解决方案的表面,所以答案可能正在解决上述一点或两点,所以这就是我到目前为止的确切位置......

    我首先使用Cargo创建一个新的Rust库:

    cargo new testlib
    cd testlib
    

    使用以下内容创建testlib.h

    void tell_me_the_answer(void);
    

    使用以下内容创建swig输入文件(testlib.i):

    %module testlib
    %{
    #include "testlib.h"
    %}
    %include "testlib.h"
    

    运行swig以生成一些Java和C:

    mkdir testlib
    swig -outdir testlib -java -package testlib testlib.i
    

    使用以下内容创建一个主java类(Program.java):

    public final class Program {
        static {
            System.loadLibrary("testlib"); 
        }
        public static void main(final String[] args) {
            testlib.testlib.tell_me_the_answer();
        }
    }
    

    编译java:

    javac Program.java testlib\testlib.java testlib\testlibJNI.java
    

    编辑货物为实现该功能而制作的src\lib.rs文件:

    #[no_mangle]
    pub extern "C" fn tell_me_the_answer() {
        println!("The answer is...APPLES!");
    }
    

    创建一个新的build.rs文件,以便通过gcc-rs库来编译swig输出,其中包含:

    extern crate gcc;
    fn main() {
        gcc::Config::new()
                    .file("testlib_wrap.c")
                    .include("C:/Program Files/Java/jdk1.8.0_45/include")
                    .include("C:/Program Files/Java/jdk1.8.0_45/include/win32")
                    .compile("libtestlib.a");
    }
    

    编辑Cargo.toml文件,使其包含:

    [package]
    name = "testlib"
    version = "0.1.0"
    build = "build.rs"
    [lib]
    name = "testlib"
    crate-type = ["dylib"]
    [build-dependencies]
    gcc = "0.3"
    

    编译生锈项目:

    cargo build
    

    运行java应用程序:

    java -Djava.library.path=target\debug Program
    

    收到以下错误:

    Exception in thread "main" java.lang.UnsatisfiedLinkError: testlib.testlibJNI.tell_me_the_answer()V
        at testlib.testlibJNI.tell_me_the_answer(Native Method)
        at testlib.testlib.tell_me_the_answer(testlib.java:13)
        at Program.main(Program.java:6)
    

    我查看了货物在dependency walker中制作的DLL,它看起来很空(就其出口而言)而且单个功能看起来有点奇怪,至少对我而言,由于名称中的 1 @ 部分,我认为--add-stdcall-alias会删除吗?

    Dependency Walker Output

    我是否关闭并且依赖性walker中显示的DLL中的名称是我的问题的根源? 如果是Java_testlib_testlibJNI_tell_me_the_answer会有效吗? 如果是这样,我该怎么做呢(我编辑了_wrap.c文件生成的SWIG以删除1s但我不确定如何摆脱@)?
    如果没有,问题是什么?

1 个答案:

答案 0 :(得分:2)

我有一个解决方案,但它并不是真的很漂亮,所以如果有人能从货物中无缝地实现这是一个更好的解决方案,但批量文件暂时会为我做。这就是我做的......

我放弃了从货物调用gcc,从rust中构建了一个静态库,然后从命令行运行gcc以使用SWIG生成的代码生成生锈的staticlib并创建一个动态库,就像这样...

我将Cargo.toml更改为:

[package]
name = "testlib"
version = "0.1.0"
[lib]
name = "testlib"
crate-type = ["staticlib"]

已删除build.rs

构建静态lib:

cargo build

编制了SWIG输出并将其与刚生产的货物相关联:

gcc -shared -o target\debug\testlib.dll "-LC:/Program Files (x86)/Rust stable GNU 1.9/lib/rustlib/i686-pc-windows-gnu/lib" -Ltarget\debug "-IC:/Program Files/Java/jdk1.8.0_45/include" "-IC:/Program Files/Java/jdk1.8.0_45/include/win32" testlib_wrap.c -ltestlib -lws2_32 -luserenv -lgcc_eh -lshell32 -ladvapi32 -Wl,--add-stdcall-alias

现在我有一个testlib.dll在Dependency Walker中看起来很好,当我运行它时,我看到:

The answer is...APPLES!

这是完全正确的答案。