[英]How do I resolve UnsatisfiedLinkError with JavaCPP native libraries while using JPMS Java modules?
I have a Java 17 project using Gradle with a multitude of JavaCPP libraries.我有一个 Java 17 项目,使用 Gradle 和大量 JavaCPP 库。 I started with a simple demo project that I cloned from the JavaCPP Github repo.
我从一个从 JavaCPP Github 存储库克隆的简单演示项目开始。 This sample project incorporates several native libs such as OpenCV, ffmpeg, etc., so I thought it'd be a good test.
这个示例项目包含了几个原生库,例如 OpenCV、ffmpeg 等,所以我认为这是一个很好的测试。 And, no surprise, it worked just fine.
而且,毫不奇怪,它工作得很好。 It brought up my camera, did the face detection, etc. All very cool.
它打开了我的相机,进行了人脸检测等。一切都非常酷。
My aim - I want to modularize my JavaCPP projects to be JPMS compliant.我的目标 -我想模块化我的 JavaCPP 项目以符合 JPMS。
Not easy.不容易。 So, to troubleshoot, I figure that I would start with good test code, which is why I'm working with the official JavaCPP Gradle demo program.
因此,为了排除故障,我想我会从好的测试代码开始,这就是我使用官方 JavaCPP Gradle 演示程序的原因。
So, I did the following to convert it to be JPMS compliant:因此,我执行了以下操作以将其转换为 JPMS 兼容:
module-info.java
placed down src/main/java
.src/main/java
下创建了一个module-info.java
。 I added the appropriate requires
statements (see below).requires
声明(见下文)。build.gradle
to add several *-platform
dependencies and a few other plugins, including JavaFX.build.gradle
以添加几个*-platform
依赖项和一些其他插件,包括 JavaFX。 The TL;DR - I got it to work (though the camera appears at an angle in the app window, which is just weird, but I'm assuming I'm still missing a library in module-info.java
).长话短说 - 我让它工作了(虽然相机在应用程序 window 中以一定角度出现,这很奇怪,但我假设我仍然缺少
module-info.java
中的库)。 The problem is that it only worked after I not only specified numerous additional *-platform
dependencies in build.gradle
, but also needed to list the actual native platform libraries in module-info.java
.问题是它只有在我不仅在
build.gradle
中指定了许多额外的*-platform
依赖项,而且还需要在module-info.java
中列出实际的本机平台库之后才起作用。 So, for instance, I need to add the following statement:因此,例如,我需要添加以下语句:
requires org.bytedeco.opencv.macosx.x86_64;
If I do not do that, then I get the following error:如果我不这样做,则会收到以下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jniopencv_core in java.library.path: /Users/brk009/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
线程“main”中的异常 java.lang.UnsatisfiedLinkError:java.library.path 中没有 jniopencv_core:/Users/brk009/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System /图书馆/Java/扩展:/usr/lib/java:.
My main question - How can I make a modular JavaCPP project build and execute properly without hard coding the platform dependent native libraries in module-info.java
?我的主要问题 -如何在
module-info.java
中依赖于平台的本机库进行硬编码的情况下正确构建和执行模块化 JavaCPP 项目? I thought that just specifying the *-platform
libraries in module-info.java
would do it, but nope.我认为只需在
module-info.java
中指定*-platform
库就可以了,但是不行。
If this was just a project for my own system, then fine - I'd live with it.如果这只是我自己系统的一个项目,那很好——我愿意接受它。 However, I want to pass some of my example code off to my students.
但是,我想将我的一些示例代码传递给我的学生。 It'd be fine if they all ran Macs.
如果他们都运行 Mac 就好了。 However, my students have quite a heterogeneous platform base (ie a mix of Mac, Windows, and Linux users.) Ideally, it'd be great to have a platform-independent codebase and let my program build and run regardless of the platform.
然而,我的学生有相当多的平台基础(即 Mac、Windows 和 Linux 用户的混合体)。理想情况下,拥有一个独立于平台的代码库并让我的程序构建和运行而不管平台如何。 Heck, I'd even be happy if I only needed to specify the platform as a parameter for
gradlew
as a command-line argument, such as indicated here , where I could just specify -PjavacppPlatform=linux-x86_64
.哎呀,如果我只需要将平台指定为
gradlew
的参数作为命令行参数,我什至会很高兴,如此处 所示,我可以只指定-PjavacppPlatform=linux-x86_64
。 But that did not work either.但这也不起作用。
I did verify that Loader.Detector.getPlatform()
returns the correct platform string, and Loader.getCacheDir()
returns ~/.javacpp/cache as you would expect.我确实验证了
Loader.Detector.getPlatform()
返回正确的平台字符串,并且Loader.getCacheDir()
返回 ~/.javacpp/cache 正如您所期望的那样。
Any help/guidance would be immensely appreciated.任何帮助/指导将不胜感激。 Thank you kindly.
非常感谢你。
module-info.java模块信息.java
module HelloJavaCPP {
requires java.base;
requires java.desktop;
requires org.bytedeco.javacpp;
requires org.bytedeco.javacpp.macosx.x86_64; // I do NOT WANT to hard code any platform!
requires org.bytedeco.javacv;
requires org.bytedeco.opencv;
requires org.bytedeco.opencv.macosx.x86_64;
requires org.bytedeco.ffmpeg;
requires org.bytedeco.ffmpeg.macosx.x86_64;
requires org.bytedeco.openblas;
requires org.bytedeco.openblas.macosx.x86_64;
}
build.gradle build.gradle
plugins {
id 'application'
id 'java'
id 'java-library'
id 'org.openjfx.javafxplugin' version '0.0.12'
id 'org.javamodularity.moduleplugin' version '1.8.10'
id 'org.bytedeco.gradle-javacpp-platform' version '1.5.7'
}
group = 'org.hello'
version = '1.5.7'
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
javafx {
version = "17.0.2"
modules = [ 'javafx.graphics','javafx.controls', 'javafx.fxml' ]
}
dependencies {
api "org.bytedeco:javacv-platform:1.5.7"
api 'org.bytedeco:opencv-platform:4.5.5-1.5.7'
// api "org.bytedeco:opencv-platform-gpu:4.5.5-$version"
api "org.bytedeco:ffmpeg-platform-gpl:5.0-$version"
api 'org.bytedeco:openblas-platform:0.3.19-1.5.7'
testImplementation 'junit:junit:4.13.2'
}
application {
mainModule = "$moduleName"
mainClass = "org.hello.Demo"
}
settings.gradle I'm including this just incase. settings.gradle我包括这个以防万一。
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
gradlePluginPortal()
}
}
rootProject.name = 'HelloJavaCPP'
gradle.rootProject { ext.javacppVersion = '1.5.7' }
Those links posted by Samuel above were immensely helpful. Samuel 上面发布的那些链接非常有帮助。 It turns out there are some modularity peculiarities with JavaFX that can wreck havoc when using JavaFX with non-JavaFX modules such as those in JavaCPP.
事实证明,JavaFX 存在一些模块化特性,当将 JavaFX 与非 JavaFX 模块(例如 JavaCPP 中的模块)一起使用时,这些特性可能会造成严重破坏。 See here .
看这里。
The key part that was important:重要的关键部分:
We can either use the run goal of the JavaFX Maven plugin, the java goal of the Exec Maven plugin, or manually launch java with a module path computed from Maven dependencies and option
--add-modules ALL-MODULE-PATH
.我们可以使用 JavaFX Maven 插件的运行目标,Exec Maven 插件的 java 目标,或者使用从 Maven 依赖项和选项计算的模块路径手动启动 java 和选项
--add-modules ALL-MODULE-PATH
DULE-DUPALETHPA
Once I figured out how to add a JVM argument in Gradle, I was able to remove all hard-coded architecture requires
statements, and I can now use gradlew run
, let JavaCPP's Loader
class do all the work of discovering the architecture itself and loading the appropriate native libraries!一旦我弄清楚如何在 Gradle 中添加一个 JVM 参数,我就能够删除所有硬编码的体系结构
requires
语句,我现在可以使用gradlew run
,让 JavaCPP 的Loader
class 完成发现体系结构本身和加载合适的原生库!
The two most important files that need to change:需要更改的两个最重要的文件:
module-info.java模块信息.java
Notice how much simpler this becomes, and it has NO hard-coded platform system architecture information, which is exactly what we want:请注意这变得多么简单,并且它没有硬编码的平台系统架构信息,这正是我们想要的:
module HelloJavaCPP {
requires java.base;
requires java.desktop;
requires org.bytedeco.javacpp;
requires org.bytedeco.javacv;
requires org.bytedeco.opencv;
requires org.bytedeco.ffmpeg;
requires org.bytedeco.openblas;
}
build.gradle build.gradle
The most important change I needed to make (which I got from information posted here was to add a run
configuration, and specify the JVM argument `--add-modules我需要做的最重要的改变(我从这里发布的信息中得到的是添加一个
run
配置,并指定 JVM 参数 `--add-modules
plugins {
id 'application'
id 'java'
id 'java-library'
id 'org.openjfx.javafxplugin' version '0.0.12'
id 'org.javamodularity.moduleplugin' version '1.8.10'
id 'org.bytedeco.gradle-javacpp-platform' version "$javacppVersion"
}
group = 'org.hello'
version = '1.5.7'
repositories {
mavenCentral()
}
ext {
// javacppPlatform - should be autodetected, but can also specify on cmd line
// as -PjavacppPlatform=macosx-x86_64
// javacppPlatform = 'linux-x86_64,macosx-x86_64,windows-x86_64,etc' // defaults to Loader.getPlatform()
javacppPlatform = 'macosx-x86_64' // defaults to Loader.getPlatform()
}
javafx {
version = "17.0.2"
modules = [ 'javafx.graphics','javafx.controls', 'javafx.fxml' ]
}
dependencies {
api "org.bytedeco:javacpp-platform:$javacppVersion"
api "org.bytedeco:javacv-platform:$javacppVersion"
api "org.bytedeco:opencv-platform:4.5.5-1.5.7"
api "org.bytedeco:ffmpeg-platform-gpl:5.0-1.5.7"
api "org.bytedeco:openblas-platform:0.3.19-1.5.7"
testImplementation 'junit:junit:4.13.2'
}
application {
mainModule = "$moduleName"
mainClass = "org.hello.Demo"
}
// THIS WAS THE PRIMARY CHANGE:
run {
jvmArgs = ['--add-modules', 'ALL-MODULE-PATH']
}
It's worth noting that I was able to remove the ext
configuration in build.gradle
, which allows Loader.getPlatform()
to do the work of determining the platform at runtime, and it worked just fine.值得注意的是,我能够删除
build.gradle
中的ext
配置,它允许Loader.getPlatform()
在运行时完成确定平台的工作,并且它工作得很好。 (I left it in place just for reference purposes.) (我把它留在原地仅供参考。)
I hope this helps others.我希望这对其他人有帮助。 I did NOT test out building an image, as judging from what I read, that is quite an additional level of complexity.
我没有测试构建图像,从我读到的内容来看,这是一个额外的复杂程度。 We'll tackle that another time.
我们下次再解决这个问题。
Thank you again.再次感谢你。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.