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

时间:2014-06-23 11:58:33

标签: clojure native leiningen

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

lein项目文件

(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
)

主要方法的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
)

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

public class JavaWrapper{
    public native void setup();
    public static void load(String lib){ System.load(lib);}
}

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

可能相关:
我也无法通过

在JavaWrapper.java中加载wrapper.o
System.loadLibrary("wrapper.o");

我必须使用

System.load("/absolute/path/to/wrapper.o");

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

更好地理解ClassLoaders或特别是一个有用的简单示例非常有用,谢谢。

1 个答案:

答案 0 :(得分:3)

问题是由于我的方法在C标头和jni standard的源文件中出现命名错误。将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 (或.jnilib for mac或.dll for windows但始终带有“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)


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