#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
Этот метод также не предназначен для загрузки собственного кода