I am writing a library for clojure which involves native code. How can I bundle the shared library (aka native dependencies) when I deploy the clojure libraries to public repositories (like clojars)?
Further Info:
My project structure looks roughly like:
src/
native/ - C code , C Object files and compiled shared libs
java/ - Java stuff
clojure/ - Clojure stuff
I am currently using leineingen. I have tried doing:
:jvm-opts [~(str "-Djava.library.path=src/native/:"
(System/getenv "$LD_LIBRARY_PATH"))]
It works if I am in the project. However, if I include this project as a dependency, I will get a UnsatisfiedLink
error.
The answer depends on your exact use-case. In the simplest situation, you need to:
native/
folder in:resource-paths
in yourproject.clj
.:native-prefix
option to indicate a path inside your library jar from which native libraries should be extracted. For example, if your library contains a resource "mylib.so" in the root folder, you can specify it like this:[com.foo/bar "1.0.1" :native-prefix ""]
:native-path
option in yourproject.clj
.:native-path
you have specified tojava.library.path
, using:jvm-opts
like you said.These options are documented in the sample leiningen project.clj.
Now the reason I said it depends on your use-case is that if you want to create an uberjar that contains native libs, things start getting more messy. The main reason is that you can't directly link to a lib that is zipped inside your jar. If you're lucky, you'll be able to use the
loadLibraryFromJar
method in the NativeUtils class. However, I remember havingClassLoader
-related issues that prevented me from usingSystem/load
. Instead I had to make sure the library was present in one of the paths that the JVM looks for, so thatSystem/loadLibrary
finds it correctly. Here is what I ended up doing:(-> my-lib io/resource io/input-stream (io/copy my-temp-file))
java.library.path
system property at runtime to add the temporary folder to it, usingSystem/setProperty
This is painful to setup, but after that it works pretty well.