javajavafxjava-modulejlinkjdeps

Problems with jlinking javafx modules to make stand alone aplication


You can checkout my whole project on github. I currently don't use any dependency managers in my projects and I would like to keep it that way. I am using a library that I made in java and I am using javaFX for GUI. As the title states I want to make my app a standalone app so someone can run it without having java installed.

I make my jar file like this in VSCode: enter image description here

And after this I can run my application with .jar file with this command:

java --module-path %JAVAFX% --add-modules=javafx.controls,javafx.fxml -jar match_three_solver.jar

So I try to run this command jdeps -s --module-path %JAVAFX% match_three_solver.jar to check which packages I have to include in my jlink command and it returns me this error:

Exception in thread "main" java.lang.module.FindException: Error reading module: match_three_solver.jar
        at java.base/jdk.internal.module.ModulePath.readModule(ModulePath.java:351)
        at java.base/jdk.internal.module.ModulePath.scan(ModulePath.java:238)
        at java.base/jdk.internal.module.ModulePath.scanNextEntry(ModulePath.java:191)
        at java.base/jdk.internal.module.ModulePath.findAll(ModulePath.java:167)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsConfiguration$Builder.build(JdepsConfiguration.java:521)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.buildConfig(JdepsTask.java:607)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:561)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:537)
        at jdk.jdeps/com.sun.tools.jdeps.Main.main(Main.java:50)
Caused by: java.lang.module.InvalidModuleDescriptorException: Main.class found in top-level directory (unnamed package not allowed in module)
        at java.base/jdk.internal.module.ModulePath.toPackageName(ModulePath.java:720)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
        at java.base/java.util.zip.ZipFile$EntrySpliterator.tryAdvance(ZipFile.java:573)
        at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
        at java.base/jdk.internal.module.ModulePath.jarPackages(ModulePath.java:621)
        at java.base/jdk.internal.module.ModulePath.lambda$readJar$6(ModulePath.java:653)
        at java.base/jdk.internal.module.ModuleInfo.doRead(ModuleInfo.java:311)
        at java.base/jdk.internal.module.ModuleInfo.read(ModuleInfo.java:129)
        at java.base/jdk.internal.module.ModulePath.readJar(ModulePath.java:652)
        at java.base/jdk.internal.module.ModulePath.readModule(ModulePath.java:332)
        ... 8 more

If I understand this correctly (I don't know really what I am doing cuz I'm doing this for the first time)... I have a non-modular .jar file so I can't use jdeps. If I want to use jdeps command I have to have module-info.java in my project. My issue is that wherever I create module-info.java file... the compiler starts screaming errors and it also doesn't recognize javafx modules: enter image description here

So if you have any idea how to fix this (if I have to completely restructure the current folder to make it work) please let me know. Also what is the point that I can't use jdeps without my .jar file being a modular.

Also I tired to just make my standalone app with this command

jlink --module-path ../jmods;lib/javaFX/lib 
--add-modules javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.web 
--output jreFX2

and run it like this (in its standalone environment) jreFX2\bin\java -jar match_three_solver.jar... And it gives me this error:

Graphics Device initialization failed for :  d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:283)
        at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:253)
        at javafx.graphics@21.0.2/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:263)
        at javafx.graphics@21.0.2/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:290)
        at javafx.graphics@21.0.2/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:162)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:651)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1135)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:95)
        at javafx.graphics@21.0.2/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1135)
Caused by: java.lang.RuntimeException: No toolkit found
        at javafx.graphics@21.0.2/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:275)
        at javafx.graphics@21.0.2/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:290)
        at javafx.graphics@21.0.2/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:162)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:651)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics@21.0.2/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        ... 2 more

Any help would be greatly appreciated. Thanks in advance.


Solution

  • The following steps outline the process of creating a custom JRE:

    JDK

    Download JDK

    https://adoptium.net/temurin/releases/?os=windows&arch=x64&package=jdk

    get OpenJDK21U-jdk_x64_windows_hotspot_21.0.6_7.zip

    Unzip JDK

    unzip OpenJDK21U-jdk_x64_windows_hotspot_21.0.6_7.zip

    move jdk-21.0.6+7 to C:\TOOLS

    final path is C:\TOOLS\jdk-21.0.6+7

    Config User Environment Variables

    JavaFX JMODS

    Download JavaFX

    https://gluonhq.com/products/javafx/

    get openjfx-21.0.6_windows-x64_bin-jmods.zip

    Unzip JavaFX

    unzip openjfx-21.0.6_windows-x64_bin-jmods.zip

    move javafx-jmods-21.0.6 into C:\TOOLS

    final path is C:\TOOLS\javafx-jmods-21.0.6

    Config User Environment Variables

    Create custom JRE

    open cmd.exe

    run command:

    jlink --module-path %JAVA_JMODS%;%JAVAFX_JMODS% ^
          --add-modules javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.swing,java.desktop,java.base ^
          --output jreFX2
    

    Distibution Directory

    match_three_solver_app
    ├── jreFX2
    ├── match_three_solver.jar
    └── runMe.bat
    

    Create app directory

    Create app directory : match_three_solver_app

    Your app jar

    runMe.bat

    create runMe.bat into app directory : match_three_solver_app

    start /B "" jreFX2\bin\javaw -jar match_three_solver.jar
    exit
    

    Final Distibution Directory

    match_three_solver_app
    ├── jreFX2
    ├── match_three_solver.jar
    └── runMe.bat
    

    Now you can compress the match_three_solver_app directory into a zip archive.

    match_three_solver_app.zip

    Others only need to get match_three_solver_app.zip, unzip it, and click runMe.bat in the directory to run match_three_solver.

    Summarize

    Here the directory size of custom JRE: jreFX2 is 106.3 MB