使用Clojure库捆绑本机JNI共享库
我正在为clojure编写一个涉及本机代码的库。 当我将clojure库部署到公共存储库(如clojars)时,如何捆绑共享库(也称为本机依赖项)?
更多信息:
我的项目结构大致如下:
src/ native/ - C code , C Object files and compiled shared libs java/ - Java stuff clojure/ - Clojure stuff
我目前正在使用leineingen。 我试过做:
:jvm-opts [~(str "-Djava.library.path=src/native/:" (System/getenv "$LD_LIBRARY_PATH"))]
如果我在项目中,它可以工作。 但是,如果我将此项目作为依赖项包含在内,我将收到UnsatisfiedLink
错误。
答案取决于您的确切用例。 在最简单的情况下,您需要:
- 将本机库捆绑在库jar中,例如将
native/
文件夹包含在project.clj
中的:resource-paths
中。 - 当您使用库时,可以指定
:native-prefix
选项以指示库jar中的路径,应从中提取本机库。 例如,如果您的库在根文件夹中包含资源“mylib.so”,则可以像这样指定它:[com.foo/bar "1.0.1" :native-prefix ""]
- 您还应该使用
project.clj
的:native-path
选项指定提取的库应该去的位置。 - 最后,你应该使用
:jvm-opts
将你指定的:native-path
添加到java.library.path
。
这些选项记录在样本leiningen project.clj中 。
现在我说它取决于你的用例的原因是,如果你想创建一个包含本机库的uberjar,事情开始变得更加混乱。 主要原因是你无法直接链接到jar中压缩的lib。 如果幸运的话,您将能够在NativeUtils类中使用loadLibraryFromJar
方法。 但是,我记得有ClassLoader
问题导致我无法使用System/load
。 相反,我必须确保库存在于JVM查找的其中一个路径中,以便System/loadLibrary
正确地找到它。 这是我最终做的事情:
- 在运行时从uberjar手动提取本机lib到一个临时文件夹,使用
(-> my-lib io/resource io/input-stream (io/copy my-temp-file))
- 使用
System/setProperty
在运行时更新java.library.path
系统属性以向其添加临时文件夹 - 最后,使用此reflection技巧强制更新库路径
设置很痛苦,但之后效果很好。