Here on Foojay, you can already find some great articles on how to create native executables from your JavaFX code:
- Native Applications for Multiple Devices from a Single JavaFX Project with Gluon Mobile and GitHub Actions
- JavaFX Templates for Desktop Applications
Let's take a look in this article at the current state of what can be done with jpackage
and GitHub Actions.
All the sources and workflow files used here can be found in the JavaPackageDemo
repository.
General Configuration
JavaFX Launch File
A Launcher.java
file is needed as an entry point to your JavaFX application.
A dedicated launcher file, such as Launcher.java
, serves as the entry point for a Java application, handling essential setup tasks before starting the main program. This separation streamlines code management, keeping initialization logic distinct and making the application easier to maintain and expand.
For instance, if your main JavaFX application class is named MainApplication
, create another Java file named Launcher.java and include the following code:
public class Launcher { public static void main(String[] args) { MainApplication.main(args); } }
Prerequisites
The process that is used here, is based on Gradle. Include the following code snippet in your build.gradle
file to generate a standalone FatJar for your project.
A FatJAR is essential because it bundles an entire Java application with all its dependencies into a single file. This eliminates the hassle of managing external libraries separately, ensuring easy deployment and portability across various systems. It simplifies distribution, maintains version consistency, and allows the application to run independently without relying on external dependencies, making it an efficient and self-contained package for seamless execution.
task customFatJar(type: Jar) { manifest { attributes 'Main-Class': 'YOUR.PACKAGE.Launcher' } archiveBaseName = 'PackageDemoJAR' duplicatesStrategy = DuplicatesStrategy.EXCLUDE from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } with jar }
task customFatJar(type: Jar)
: Defines a new task named customFatJar of type Jar, indicating that this task will generate a JAR file.manifest {...}
: Sets the Main-Class attribute in the JAR's manifest file to point to the Launcher class, which serves as the entry point of your application.archiveBaseName = '...'
: Specifies the base name for the generated JAR file.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
: Handles duplicate files encountered during the JAR creation process by excluding them.from {...}
: Collects all dependencies from the runtime classpath and includes them in the JAR.configurations.runtimeClasspath
gathers the project's runtime dependencies, andzipTree
adds each dependency to the JAR.with jar
: Includes the existing JAR file (if any) generated by the defaultjar
task along with the assembled JAR containing dependencies.
This customFatJar
task amalgamates your project's code and its dependencies into a single JAR file. Upon execution, the generated JAR file will be located in the following path: build/libs
To validate the functionality of your JAR file, execute it using the command: java -jar <path to your JAR file>
. This command verifies the proper execution of your Java application encapsulated within the JAR.
Building Executables with jpackage
jpackage
is generally included as part of the JDK starting from Java 14. However, in certain cases or older versions where it might not be bundled or available by default, you can manually download the JDK that includes jpackage
from the official Oracle or OpenJDK website. Ensure you download a JDK version that explicitly mentions support for jpackage
. After downloading and installing the JDK, you should have access to the jpackage
tool in the JDK's bin directory
To confirm whether jpackage
is installed on your system, run the command jpackage --help
on CMD or Terminal. If installed, this command will display the help information for the jpackage
utility, confirming its presence and functionality.
$ jpackage --help Usage: jpackage <options> Sample usages: -------------- Generate an application package suitable for the host system: For a modular application: jpackage -n name -p modulePath -m moduleName/className For a non-modular application: jpackage -i inputDir -n name \ --main-class className --main-jar myJar.jar From a pre-built application image: jpackage -n name --app-image appImageDir ...
Creating a native executable application requires performing the build on the target platform. For each platform, the required additional steps and the jpackage
command are explained.
GitHub Workflows
By using GitHub Actions, we can remove the need to have different platforms available to build native applications. Within the repository of the example project, workflows are provided for each platform which can be used as a guideline if you want to run the same process on your own machine.
Windows
Installing the WiX Toolset, version 3.0 or later for Windows is necessary. After completing the prerequisites, execute the following command:
jpackage --input <direcory of jar file> --name <name> --main-jar <main jar file > --main-class <main class> --type <type> --win-dir-chooser
Example:
jpackage --input build/ --name PackageDemo --main-jar <mPackageDemoJAR.jar > --main-class <com.heshanthenura.packagedemo.Launcher> --type msi --win-dir-chooser
Linux
For Linux, it's required to install fakeroot
. Run the following command to install fake root: apt-get install fakeroot -y
.
For Red Hat Linux, it's necessary to install rpm-build package
. Run the following command to install rpm-build package: apt-get install fakeroot -y
. When using yum (older versions of RHEL or CentOS): sudo yum install rpm-build
.
After completing the prerequisites, execute the following command:
jpackage --input <direcory of jar file> --name <name> --main-jar <main jar file> --main-class <main class> --type <type>
MacOS
For macOS, no additional installations are needed and you can immediately execute the following example command:
jpackage --input build/libs/ --name PackageDemo --main-jar PackageDemoJAR-1.0-SNAPSHOT.jar --main-class com.heshanthenura.packagedemo.Launcher --type dmg --dest build/macos/ --app-version 1.0 --vendor "Heshan Thenura"
Conclusion
Creating a native executable with JavaFX and jpackage
for Windows, macOS, and Linux streamlines deployment across multiple operating systems.
Following a structured set of steps, developers can package Java applications into platform-specific native executables.
This process involves leveraging jpackage
, which allows bundling the application, its dependencies, and a JRE into a standalone package, ensuring ease of installation and execution on various platforms.
Ultimately, this approach simplifies distribution, enhances user accessibility, and provides a consistent user experience across different operating systems for JavaFX applications.