Как загрузить и использовать собственный код c в проекте lein?

#clojure #native #leiningen

#clojure #собственный #leiningen

Вопрос:

Проблема
Я не могу загружать и вызывать методы из скомпилированного класса c в проект leiningen. Мой основной подход заключается в загрузке класса Java, JavaWrapper.java , который использует JNI для вызова некоторых собственных методов в собственном коде, wrapper.o, а затем вызывает методы через этот класс java-оболочки.
Я предполагаю, что есть проблемы с загрузчиком классов при загрузке класса Java, который загружает собственный код из проекта clojure, но, учитывая, что я не могу напрямую получить код 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, я успешно загрузил и использовал собственные методы в wrapper.o через JavaWrapper и JNI.

Возможно, связано:
Я также не могу загрузить wrapper.o в JavaWrapper.java через

 System.loadLibrary("wrapper.o");
  

Я должен использовать

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

Версии инструментов
версия clojure: 1.5.1
версия lein: 2.3.4
jdk: 1.7
ОС: debian7

Лучшее понимание загрузчиков классов или особенно рабочий простой пример были бы очень полезны, спасибо.

Ответ №1:

Проблема была связана с ошибкой именования в моем методе в заголовке C и исходных файлах в соответствии со стандартом jni. Правильный способ использования jni с clojure — создать класс-оболочку Java, как я это сделал, и загрузить динамическую библиотеку с помощью метода clojure.lang.RT.loadLibrary
Поскольку у меня возникли проблемы с поиском хороших примеров для этого, я сделал демонстрацию на github

(clojure.lang.RT.load «/abs/path/to/wrapper.o»);;не удалось найти файл /abs/path/wrapper.o_init.class или wrapper.o.clj

1) Ошибки
Этот метод загрузки не предназначен для использования в машинном коде, он ожидает класс Java или файл clj

(clojure.lang.RT.LoadLibrary «wrapper.o») ;;UnsatisfiedLinkError нет wrapper.o в пути библиотеки Java 2)
Clojure не может найти библиотеку во время соединения, следовательно, UnsatisfiedLinkError — это связано с ошибкой именования

  • сначала библиотека должна быть скомпилирована как динамическая разделяемая библиотека, т. Е. Для компилятора gcc используйте флаг -shared (я на самом деле сделал, но не назвал выходной файл с расширением normal .so)
  • java и, следовательно, clojure ожидают, что собственная библиотека будет называться очень определенным образом: libwrapper.so (или .jnilib для mac или .dll для Windows, но всегда с префиксом «lib»)

3) (JavaWrapper/load «/abs/path/to/wrapper.o») ;;Неудовлетворительная ошибка ссылки com.test.JavaWrapper.setup()
На этот раз ошибка связана с методом в JavaWrapper в файле или библиотеке, поэтому вы знаете, что, по крайней мере, он нашел файл. Ошибка UnsatisfiedLinkError, указывающая конкретный метод в классе Java, подобный этому, всегда должна быть вызвана ошибкой именования между тем, что объявлено собственным методом в файле Java, и тем, что фактически присутствует в исходном или заголовочном файле c.
Обратите внимание на пространство имен «com.test»
При объявлении метода jni в c имя метода должно соответствовать определенному формату,
начиная с 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
Этот метод также не предназначен для загрузки собственного кода