如何在lein项目中加载和使用本机c代码?

use*_*496 6 native clojure leiningen

问题
我无法加载并将已编译的c类中的方法调用到leiningen项目中.我的基本方法是加载一个Java类JavaWrapper.java,它使用JNI调用本机代码中的一些本机方法,wrapper.o然后通过这个java包装类调用方法.
我想有一个classLoader问题,加载一个java类,它从clojure项目加载本机代码,但鉴于我似乎无法直接获取clojure代码在库路径上找到wrapper.o,我不确定如何处理这个.

列宁项目文件

(defproject lein-native-test "0.1.0-SNAPSHOT"
...
:java-source-paths ["java-path"]
:jvm-opts ["-Djava.library.path=.:./native:/absolute/path/to/native"] ;;not sure what format it wants
)
Run Code Online (Sandbox Code Playgroud)

使用main方法的clojure文件
我尝试使用四种方法稍微修改它们,所有这些都包含在下面的代码中以及注释中的相应错误.

(ns lein-native-test.core
(:import (com.test JavaWrapper)))
(def -main []
;;four things I've tried and their errors
(clojure.lang.RT.load "/abs/path/to/wrapper.o") ;;could not find file /abs/path/wrapper.o_init.class or wrapper.o.clj
(clojure.lang.RT.loadLibrary "wrapper.o") ;;UnsatisfiedLinkError no wrapper.o in java library path
(JavaWrapper/load "/abs/path/to/wrapper.o") ;;UnsatisfiedLinkError com.test.JavaWrapper.setup()
(assembly-load "/abs/path/to/wrapper.o") ;;unable to resolvesymbol: assembly-load
)
Run Code Online (Sandbox Code Playgroud)

使用JNI,JavaWrapper.java的本机方法的Java代码

public class JavaWrapper{
    public native void setup();
    public static void load(String lib){ System.load(lib);}
}
Run Code Online (Sandbox Code Playgroud)

在尝试使用clojure和lein之前,我通过JavaWrapper和JNI成功加载并使用wrapper.o中的本机方法.

可能相关:
我也无法在JavaWrapper.java中加载wrapper.o

System.loadLibrary("wrapper.o");
Run Code Online (Sandbox Code Playgroud)

我必须使用

System.load("/absolute/path/to/wrapper.o");
Run Code Online (Sandbox Code Playgroud)

工具版本的
clojure版本:1.5.1
lein版本:2.3.4
jdk:1.7
os:debian7

更好地理解ClassLoaders或特别是一个简单的工作例子非常有用,谢谢.

use*_*496 4

该问题是由于我的方法在 C 标头和源文件中按照jni 标准的命名错误造成的。将 jni 与 clojure 一起使用的正确方法是创建一个 Java 包装类,就像我所做的那样,并使用该方法加载动态库 clojure.lang.RT.loadLibrary
因为我很难找到好的例子,所以我在github上做了一个演示

错误
1) ( clojure.lang.RT.load "/abs/path/to/wrapper.o") ;;无法找到文件 /abs/path/wrapper.o_init.class 或wrapper.o.clj
此加载方法并不意味着用于本机代码,它需要一个 java 类或 clj 文件

2) (clojure.lang.RT.loadLibrary "wrapper.o") ;;UnsatisfiedLinkError java 库路径中没有wrapper.o
Clojure 无法在链接时找到该库,因此 UnsatisfiedLinkError --- 这是由于命名错误

  • 首先,该库应该编译为动态共享库,即对于 gcc 编译器使用 -shared标志(我实际上这样做了,但没有使用正常的 .so 扩展名命名输出文件)
  • java 和 clojure 期望本机库以非常特定的方式命名:libwrapper.so(对于 mac 为 .jnilib,对于 Windows 为 .dll,但始终带有“lib”前缀)

3) (JavaWrapper/load "/abs/path/to/wrapper.o") ;;UnsatisfiedLinkError com.test.JavaWrapper.setup()
这次错误出在文件或库中 JavaWrapper 内的方法上,因此您知道至少它找到了该文件。指定 Java 类中的特定方法(如本例)的 UnsatisfiedLinkError 应该始终是由于 Java 文件中声明的本机方法与 c 源文件或头文件中实际存在的内容之间的命名错误造成的。
注意命名空间“com.test”
在c中声明jni方法时,方法名称必须遵循特定格式,
来自http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design .html
“动态链接器根据名称解析条目。本机方法名称由以下组件连接而成:”

  • 前缀 Java_
  • 损坏的完全限定类名
  • 下划线 (_) 分隔符
  • 损坏的方法名称
  • 对于重载的本机方法,两个下划线 (__) 后跟损坏的参数签名

在这种情况下,完整的 c 源方法签名将是

void Java_com_test_setup(JNIEnv *env, jobject obj)
Run Code Online (Sandbox Code Playgroud)


4)(程序集加载“/abs/path/to/wrapper.o”);;无法解析符号:程序集加载
此方法也不意味着加载本机代码