You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用JNA加载本地库及其传递性本地依赖?

如何使用JNA加载本地库及其传递性本地依赖?

兄弟,我太懂你这种头疼的感觉了——明明主库加载成功了,可它的依赖死活找不到,调试日志还偏偏不按你预期的路径去搜,简直离谱!我来帮你捋清楚这里面的门道,以及怎么解决这个问题。

首先得搞明白核心原因:你用JNA加载主库的时候,JNA确实会用jna.library.path来找主库,但主库的传递依赖是由系统的动态链接器(macOS上是dyld)负责加载的,它根本不知道JNA的jna.library.path是什么!你日志里看到的@executable_path/../Frameworks是主库编译时硬编码的依赖路径,dyld会严格按照这个路径+系统默认路径去搜,自然不会去你的临时目录找。

下面给你几个可行的解决方案,按推荐程度排序:

方法一:修改主库的依赖路径(最彻底)

如果能拿到主库的编译权限,或者可以修改二进制文件,这是一劳永逸的办法:

  • 用macOS的install_name_tool工具修改主库中依赖的路径,把硬编码的@executable_path/../Frameworks/libcfitsio.4.dylib改成@rpath/libcfitsio.4.dylib
    install_name_tool -change @executable_path/../Frameworks/libcfitsio.4.dylib @rpath/libcfitsio.4.dylib libstellarsolver.dylib
    
  • 然后给主库添加rpath指向你的依赖目录(也就是你解压的临时目录):
    install_name_tool -add_rpath /var/folders/cv/xl07kvgs0q91fv5w0x1y5mbh0000gn/T/snafu17797529543521833103 libstellarsolver.dylib
    

这样dyld加载主库时,就会自动去你指定的rpath目录找依赖了。

方法二:手动提前加载所有传递依赖

如果没法修改主库,那可以让JNA先把所有依赖库加载到进程中,这样主库需要依赖时,直接用进程里已加载的版本就行:

// 注意加载顺序:先加载被依赖的库,再加载主库
// 比如先加载libcfitsio,再加载libstellarsolver
NativeLibrary.load("libcfitsio.4.dylib", NativeLibrary.DEFAULT_OPTIONS);
// 如果还有其他传递依赖,按依赖顺序依次加载
// NativeLibrary.load("other_dependency.dylib", NativeLibrary.DEFAULT_OPTIONS);
// 最后加载主库
StellarSolverInterface solver = Native.load("libstellarsolver.dylib", StellarSolverInterface.class);

这种方法不需要修改任何系统变量或库文件,就是需要你搞清楚所有依赖的先后顺序(比如A依赖B,就得先加载B)。

方法三:设置系统动态链接器的搜索路径

macOS上可以设置DYLD_LIBRARY_PATH环境变量,指向你的依赖目录,让dyld去那里找依赖。注意这个变量必须在JVM启动前设置,比如在启动Java程序的脚本里:

export DYLD_LIBRARY_PATH=/var/folders/cv/xl07kvgs0q91fv5w0x1y5mbh0000gn/T/snafu17797529543521833103
java -jar your_application.jar

不过要注意,macOS的Gatekeeper可能会限制这个变量的作用,尤其是在签名的应用里,所以这种方法不一定总能生效。

你提到的StackOverflow里说要给每个依赖设路径,其实不是必须的——要么你手动加载每个依赖,要么让dyld能找到它们的统一路径,不用给每个依赖单独配置JNA的路径。

备注:内容来源于stack exchange,提问作者David Maffitt

火山引擎 最新活动