Search code examples
jarjava-native-interfaceshared-librariesjavacpp

JavaCPP, UnsatisfiedLinkError when native library is archived in JAR


I'm trying to call Haskell code from Java, using JavaCPP to help create the necessary JNI binding, as already discussed in this question.

This is how I'm using it:

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

Content of 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);
    }
}

Compilation:

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

Following this approach, I can create a libHScode.so and a libjniHScode.so using javacpp, which runs fine with:

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

Jar

Now, the following step is that I want to package everything in a jar, and be able to use this jar's com.example.HScode from a larger java project.

JavaCPP's page mentions:

[...] Moreover, at runtime, the Loader.load() method automatically loads the native libraries from Java resources, which were placed in the right directory by the building process. They can even be archived in a JAR file, it changes nothing. Users simply do not need to figure out how to make the system load the files.

So I thought this should work.

However, if I make a jar HScode.jar out of the content of the build folder above, so that my jar contains both libjniHScode.so and libHScode.so, and run it with:

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

then it cannot find my native code (exception edited for anonymization):

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)

What am I missing? Does anyone know whether JavaCPP can actually find the native code when it's archived in a jar?


Solution

  • Building for the native libraries by calling javacpp -jar javacpp.jar com.example.HScode outputs them in com/example/linux-x86_64/ automatically and the Loader loads them from there. So when building the native libraries by some other means, they still need to be moved to com/example/linux-x86_64/, whether inside a JAR file or outside as normal files, if we want the Loader to find them.