JavaCPP, UnsatisfiedLinkError, когда собственная библиотека заархивирована в JAR

Я пытаюсь вызвать код Haskell из Java, используя JavaCPP, чтобы помочь создать необходимую привязку JNI, поскольку уже обсуждалось в этом вопросе.

Вот как я его использую:

<rootdir>
  /javacpp.jar
  /build (destination of libraris)
  /src   (contains Haskell code)
  /com/example/HSCode.java (Java class to load and use native lib)

Содержание HScode.java:

package com.example;

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;

@Platform(include={"<HsFFI.h>","HScode_stub.h"})
public class HScode {
    static { Loader.load(); }
    public static native void hs_init(int[] argc, @Cast("char***") @ByPtrPtr PointerPointer argv);
    public static native String code_hs(String text);

    public static void main(String[] args) throws FileNotFoundException {
        String s = new Scanner(new File("test.txt")).useDelimiter("\\Z").next();
        hs_init(null, null);
        String s1 = code_hs(s);
        System.out.println(s1);
    }
}

Сборник:

cd <rootdir>
ghc --make -isrc -dynamic -shared -fPIC src/HScode.hs \
     -o build/libHScode.so -lHSrts-ghc7.8.4 -optl-Wl,-rpath,.
javac -cp javacpp.jar com/example/HScode.java
java -jar javacpp.jar -d build \
     -Dplatform.compiler=ghc -Dplatform.includepath="src:com/example" \
     -Dplatform.compiler.output="-optl-Wl,-rpath,. -optc-O3 -Wall build/libHScode.so -dynamic -fPIC -shared -lstdc++ -lHSrts-ghc7.8.4 -o " com.example.HScode

Следуя этому подходу, я могу создать libHScode.so и libjniHScode.so, используя javacpp, который отлично работает с:

$ java -cp javacpp.jar:. com.example.HScode

Банка

Теперь следующий шаг заключается в том, что я хочу упаковать все в банку и иметь возможность использовать com.example.HScode этой банки из более крупного проекта Java.

На странице JavaCPP упоминается:

[...] Кроме того, во время выполнения метод Loader.load() автоматически загружает собственные библиотеки из ресурсов Java, которые были помещены в правильный каталог в процессе сборки. Их можно даже заархивировать в файл JAR, это ничего не меняет. Пользователям просто не нужно разбираться, как заставить систему загружать файлы.

Поэтому я подумал, что это должно сработать.

Однако, если я создам банку HScode.jar из содержимого папки build выше, чтобы моя банка содержала как libjniHScode.so, так и libHScode.so, и запустила ее с помощью:

$ java -cp javacpp.jar:HScode.jar:. com.example.HScode

то он не может найти мой собственный код (исключение отредактировано для анонимности):

Exception in thread "main" java.lang.UnsatisfiedLinkError: no jniHScode in java.library.path
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1865)
    at java.lang.Runtime.loadLibrary0(Runtime.java:870)
    at java.lang.System.loadLibrary(System.java:1122)
    at org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:597)
    at org.bytedeco.javacpp.Loader.load(Loader.java:438)
    at org.bytedeco.javacpp.Loader.load(Loader.java:381)
    at com.example.HScode.<clinit>(HScode.java:13)
Caused by: java.lang.UnsatisfiedLinkError: /compilation-path/linux-x86_64/libjniHScode.so: HScode.so: cannot open shared object file: No such file or directory
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1937)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1822)
    at java.lang.Runtime.load0(Runtime.java:809)
    at java.lang.System.load(System.java:1086)
    at org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:580)

Что мне не хватает? Кто-нибудь знает, действительно ли JavaCPP может найти собственный код, когда он заархивирован в банке?


person cornuz    schedule 24.01.2016    source источник


Ответы (1)


Сборка собственных библиотек путем вызова javacpp -jar javacpp.jar com.example.HScode автоматически выводит их в com/example/linux-x86_64/, а Loader загружает их оттуда. Таким образом, при создании нативных библиотек каким-либо другим способом их все равно нужно переместить в com/example/linux-x86_64/, будь то внутри файла JAR или снаружи как обычные файлы, если мы хотим, чтобы Loader их нашел.

person Samuel Audet    schedule 25.01.2016
comment
Спасибо, я думаю, во время вашего ответа я редактировал свой вопрос, удаляя ненужные вещи и добавляя подробности о компиляции. может ли быть проблемой то, что я использую -d build для перемещения созданных библиотек в чистую папку? - person cornuz; 25.01.2016
comment
@cornuz Вы можете использовать Loader.load(URL[] urls, String libnameversion) с URL из ClassLoader.getResource(), если хотите. - person Samuel Audet; 25.01.2016
comment
ваше замечание о сохранении той же структуры в банке имеет смысл. Я хотел бы попробовать использовать стандартный загрузчик. Теперь банка содержит com/example/linux-x86_64/libjniHScode.so. Я до сих пор не знаю, куда поместить нативную библиотеку libHScode.so внутри банки. Пробовал и в com/example/linux-x86_64/ и в руте. Неудачно. Но это работает, когда нативная библиотека находится вне банки, где я вызываю java -cp javacpp.jar:HScode.jar:. com.example.HScode. - person cornuz; 25.01.2016
comment
@cornuz Чтобы его извлечь, он должен быть частью аннотации @Platform либо как link="HScode", либо preload="HScode". - person Samuel Audet; 26.01.2016
comment
Действительно, аннотация link помогает, спасибо! Я предполагал, что это эквивалентно использованию -Dplatform.link="HScode". Использование только последнего заставляет работать параметр -copylibs, но не позволяет извлекать библиотеку из jar во время выполнения. - person cornuz; 26.01.2016