Search code examples
javajavafx-11

I have no clue what I'm doing trying to export JavaFx as a Jar/Exe


I'm trying my hardest to export my javafx(11) projects, but nothing seems to work. I am on IntelliJ IDE, and for example let's use this project: https://github.com/EveningSt3r/JFX-hotel-project. (I wrote this in 30 minutes but it's simple and fills this question's purpose)

When I use IntelliJ's Build Artifacts function: "fx:deploy is not available" I have no idea how to use jlink, and all tutorials online are stumping me. Most javafx tutorials are from when it was part of the JDK, so that doesn't help. I have no idea how to use Maven and Gradle.

This is coming from a dude who doesn't have a great understanding of how Java actually works, just knows how to make stuff.

I've spent weeks trying to figure this out, so can someone please just dumb it down to baby levels for me, on how to get this into something runnable or an exe? I know I sound stupid but I'm just tired


Solution

  • One option is to use the jpackage tool. That tool was an incubating feature in Java 14-15 and was standardized in Java 16. It creates a self-contained application. In other words, the resulting application contains not only your own code and any dependencies, but an embedded Java Runtime Environment (JRE) as well. That way your end users don't need to worry about having Java or JavaFX installed. Instead, they end up with a native installer/executable.

    There is a user guide for jpackage. There's also a script another user on this site (along with others, if I'm not mistaken) has created that may make things simpler for you: JPackageScriptFX.


    Here is a minimal example of creating a JavaFX application, where the application is packaged with jpackage, entirely from the command line. The example assumes a modular application (i.e. your code and all dependencies have a module-info file), but jpackage is also capable of packaging class-path based applications (i.e. non-modular applications).

    Project Structure

    ├───out
    │   ├───classes
    │   └───package
    └───src
        └───com.example
            │   module-info.java
            │
            └───com
                └───example
                    └───app
                            Main.java
    

    Note I have the source code under a directory named com.example. That is the same name as the module and makes working with javac manually easier (when compiling modules). However, you don't need to do that. In fact, if you're using an IDE then your source code is likely directly under src (or src/main/java if using Maven/Gradle).

    Since you're likely letting your IDE compile the code I would not recommend creating a directory whose name matches the module name.

    Source Files

    Here is the module-info.java file:

    module com.example {
      requires javafx.controls;
    
      exports com.example.app to javafx.graphics;
    }
    

    Here is the Main.java file:

    package com.example.app;
    
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
      public void start(Stage primaryStage) {
        StackPane root = new StackPane(new Label("Hello, World!"));
        Scene scene = new Scene(root, 500, 300);
        primaryStage.setScene(scene);
        primaryStage.setTitle("JPackage Example");
        primaryStage.show();
      }
    }
    

    Compilation

    Here is the command to compile the above code. Note the working directory is the project directory.

    javac --module-source-path src --module com.example -d out\classes
    

    I have a JDK which includes JavaFX. If you do not then you need to make sure to put JavaFX on the module-path. For example:

    javac --module-path <path-to-fx> --module-source-path src --module com.example -d out\classes
    

    But again, you are likely letting your IDE handle compilation.

    Packaging

    Here is the command to package the now-compiled application.

    jpackage --type app-image --name JPackageExample --module-path out/classes --module com.example/com.example.app.Main --dest out/package
    

    The --type determines the application type. On Windows there's three options: app-image (common to all platforms, I believe), exe, and msi. The first will simply create an exploded directory (containing a exe file on Windows). The latter two will create Windows installers for your application. Note you must be on Windows to create Windows packages (same for MacOS and Linux).

    The --module argument tells the tool what the main module and class is (i.e. the entry point of the application). It's format is <main-module>[/<main-class>]. The main class may be omitted if appropriate (e.g. you packaged your code into a JAR file with --main-class specified).

    Again, I have a JDK which includes JavaFX. If you do not then make sure to include JavaFX in the --module-path argument, along with your own code. Important: Unlike during compilation, if you have JavaFX separate from the JDK then make sure to place the JavaFX JMOD files on the module-path, not the normal JAR files; you can get the JMOD files from Gluon (likely same place you got the JavaFX SDK). Keep in mind that JavaFX is platform-specific.


    The above example is entirely from the command-line. You are likely using an IDE so some of the steps may be different. For instance, you can rely on the IDE to compile your code and, if you want, package it into a JAR file. You might even be able to let the IDE handle invoking jpackage as well but I'm not sure.