簡體   English   中英

在使用 JPMS Java 模塊時,如何使用 JavaCPP 本機庫解決 UnsatisfiedLinkError?

[英]How do I resolve UnsatisfiedLinkError with JavaCPP native libraries while using JPMS Java modules?

我有一個 Java 17 項目,使用 Gradle 和大量 JavaCPP 庫。 我從一個從 JavaCPP Github 存儲庫克隆的簡單演示項目開始。 這個示例項目包含了幾個原生庫,例如 OpenCV、ffmpeg 等,所以我認為這是一個很好的測試。 而且,毫不奇怪,它工作得很好。 它打開了我的相機,進行了人臉檢測等。一切都非常酷。

我的目標 -我想模塊化我的 JavaCPP 項目以符合 JPMS。

不容易。 因此,為了排除故障,我想我會從好的測試代碼開始,這就是我使用官方 JavaCPP Gradle 演示程序的原因。

因此,我執行了以下操作以將其轉換為 JPMS 兼容:

  1. src/main/java下創建了一個module-info.java 我添加了適當的requires聲明(見下文)。
  2. 修改build.gradle以添加幾個*-platform依賴項和一些其他插件,包括 JavaFX。

長話短說 - 我讓它工作了(雖然相機在應用程序 window 中以一定角度出現,這很奇怪,但我假設我仍然缺少module-info.java中的庫)。 問題是它只有在我不僅在build.gradle中指定了許多額外的*-platform依賴項,而且還需要在module-info.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 /圖書館/Java/擴展:/usr/lib/java:.

我的主要問題 -如何在module-info.java中依賴於平台的本機庫進行硬編碼的情況下正確構建和執行模塊化 JavaCPP 項目? 我認為只需在module-info.java中指定*-platform庫就可以了,但是不行。

如果這只是我自己系統的一個項目,那很好——我願意接受它。 但是,我想將我的一些示例代碼傳遞給我的學生。 如果他們都運行 Mac 就好了。 然而,我的學生有相當多的平台基礎(即 Mac、Windows 和 Linux 用戶的混合體)。理想情況下,擁有一個獨立於平台的代碼庫並讓我的程序構建和運行而不管平台如何。 哎呀,如果我只需要將平台指定為gradlew的參數作為命令行參數,我什至會很高興,如此處 所示,我可以只指定-PjavacppPlatform=linux-x86_64 但這也不起作用。

我確實驗證了Loader.Detector.getPlatform()返回正確的平台字符串,並且Loader.getCacheDir()返回 ~/.javacpp/cache 正如您所期望的那樣。

任何幫助/指導將不勝感激。 非常感謝你。


模塊信息.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' }

Samuel 上面發布的那些鏈接非常有幫助。 事實證明,JavaFX 存在一些模塊化特性,當將 JavaFX 與非 JavaFX 模塊(例如 JavaCPP 中的模塊)一起使用時,這些特性可能會造成嚴重破壞。 這里

重要的關鍵部分:

我們可以使用 JavaFX Maven 插件的運行目標,Exec Maven 插件的 java 目標,或者使用從 Maven 依賴項和選項計算的模塊路徑手動啟動 java 和選項--add-modules ALL-MODULE-PATH DULE-DUPALETHPA

一旦我弄清楚如何在 Gradle 中添加一個 JVM 參數,我就能夠刪除所有硬編碼的體系結構requires語句我現在可以使用gradlew run ,讓 JavaCPP 的Loader class 完成發現體系結構本身和加載合適的原生庫!

需要更改的兩個最重要的文件:


模塊信息.java

請注意這變得多么簡單,並且它沒有硬編碼的平台系統架構信息,這正是我們想要的:

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

我需要做的最重要的改變(我從這里發布的信息中得到的是添加一個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']
}

值得注意的是,我能夠刪除build.gradle中的ext配置,它允許Loader.getPlatform()在運行時完成確定平台的工作,並且它工作得很好。 (我把它留在原地僅供參考。)

我希望這對其他人有幫助。 我沒有測試構建圖像,從我讀到的內容來看,這是一個額外的復雜程度。 我們下次再解決這個問題。

再次感謝你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM