在使用 JPMS Java 模块时,如何解决 JavaCPP 本机库的 UnsatisfiedLinkError?
我有一个使用 Gradle 和大量 JavaCPP 库的 Java 17 项目。我从克隆的 简单演示项目开始来自 JavaCPP Github 存储库。这个示例项目包含了几个本机库,例如 OpenCV、ffmpeg 等,所以我认为这是一个很好的测试。而且,毫不奇怪,它运行得很好。它调出了我的相机,进行了面部检测等。一切都非常酷。
我的目标 - 我想模块化我的 JavaCPP 项目以使其符合 JPMS。
这并不容易。因此,为了排除故障,我认为我应该从良好的测试代码开始,这就是我使用官方 JavaCPP Gradle 演示程序的原因。
因此,我执行了以下操作将其转换为符合 JPMS:
- 创建了一个放置在
src/main/java
下的module-info.java
。我添加了适当的requires
语句(见下文)。 - 修改了
build.gradle
以添加多个*-platform
依赖项和一些其他插件,包括 JavaFX。
TL;DR - 我让它工作了(尽管相机在应用程序窗口中以一定角度出现,这很奇怪,但我假设我仍然缺少 module-info.java< 中的库) /代码>)。问题是,它只有在我不仅在
build.gradle
中指定了许多额外的 *-platform
依赖项,而且还需要在 中列出实际的本机平台库之后才起作用。 >模块信息.java
。因此,例如,我需要添加以下语句:
requires org.bytedeco.opencv.macosx.x86_64;
如果我不这样做,则会收到以下错误:
线程“main”中出现异常java.lang.UnsatisfiedLinkError:java.library.path中没有jniopencv_core:/Users/brk009/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions :/System/Library/Java/Extensions:/usr/lib/java:.
我的主要问题 - 如何在不对 module-info.java
中的平台相关本机库进行硬编码的情况下正确构建和执行模块化 JavaCPP 项目? strong> 我认为只需在 module-info.java
中指定 *-platform
库就可以了,但事实并非如此。
如果这只是我自己系统的一个项目,那么很好 - 我会接受它。但是,我想将一些示例代码传递给我的学生。如果他们都运行 Mac 就好了。然而,我的学生拥有相当异构的平台基础(即 Mac、Windows 和 Linux 用户的混合体)。理想情况下,拥有一个独立于平台的代码库并让我的程序构建和运行而不管平台如何,那就太好了。哎呀,如果我只需要将平台指定为 gradlew 的参数作为命令行参数,我什至会很高兴,例如所示的 此处,我可以在其中指定 -PjavacppPlatform=linux-x86_64
。但这也不起作用。
我确实验证了 Loader.Detector.getPlatform()
返回正确的平台字符串,并且 Loader.getCacheDir()
返回 ~/.javacpp/cache 正如您所期望的。
任何帮助/指导将不胜感激!谢谢您。
module-info.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
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 我把这个包括在内只是为了以防万一。
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
gradlePluginPortal()
}
}
rootProject.name = 'HelloJavaCPP'
gradle.rootProject { ext.javacppVersion = '1.5.7' }
I have a Java 17 project using Gradle with a multitude of JavaCPP libraries. I started with a simple demo project that I cloned from the JavaCPP Github repo. This sample project incorporates several native libs such as OpenCV, ffmpeg, etc., so I thought it'd be a good test. 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.
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.
So, I did the following to convert it to be JPMS compliant:
- Created a
module-info.java
placed downsrc/main/java
. I added the appropriaterequires
statements (see below). - Modified
build.gradle
to add several*-platform
dependencies and a few other plugins, including 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
). 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
. 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:.
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
? I thought that just specifying the *-platform
libraries in module-info.java
would do it, but nope.
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. However, my students have quite a heterogeneous platform base (i.e. 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. 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
. 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.
Any help/guidance would be immensely appreciated! Thank you kindly.
module-info.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
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.
pluginManagement {
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
gradlePluginPortal()
}
}
rootProject.name = 'HelloJavaCPP'
gradle.rootProject { ext.javacppVersion = '1.5.7' }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
塞缪尔上面发布的那些链接非常有帮助。事实证明,JavaFX 存在一些模块化特性,当 JavaFX 与非 JavaFX 模块(例如 JavaCPP 中的模块)一起使用时,这些特性可能会造成严重破坏。请参阅此处。
重要的关键部分:
一旦我弄清楚如何在 Gradle 中添加 JVM 参数,我就能够删除所有硬编码架构
requires
语句,并且我现在可以使用gradlew run
,让 JavaCPP 的Loader
类来完成发现架构本身并加载适当的本机库的所有工作!需要更改的两个最重要的文件:
module-info.java
请注意,这变得多么简单,并且它没有硬编码的平台系统架构信息,这正是我们想要的:
build.gradle
我需要做出的最重要的更改(我从发布的信息 这里是添加一个
run
配置,并指定JVM参数`--add-modules值得注意的是,我能够删除
ext
在build.gradle
中进行配置,它允许 Loader.getPlatform() 在运行时完成确定平台的工作,而且效果很好(我把它留在了!仅供参考。)我希望这有帮助从我读到的内容来看,我没有测试构建图像,这相当复杂,我们会再次解决这个问题
。
Those links posted by Samuel above were immensely helpful. 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. See here.
The key part that was important:
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 usegradlew run
, let JavaCPP'sLoader
class do all the work of discovering the architecture itself and loading the appropriate native libraries!The two most important files that need to change:
module-info.java
Notice how much simpler this becomes, and it has NO hard-coded platform system architecture information, which is exactly what we want:
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-modulesIt's worth noting that I was able to remove the
ext
configuration inbuild.gradle
, which allowsLoader.getPlatform()
to do the work of determining the platform at runtime, and it worked just fine! (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.