JavaFX + jpackage + Maven template project for generating native desktop applications.
- Build nice, small cross-platform JavaFX-based desktop apps with native installers
- Apx 20-30mb .dmg, .msi and .deb installers - check out the example builds in releases.
- Just use Maven - no shell scripts required. Use standard Maven dependency system to automatically manage ordinary JAR dependencies (including transitive Maven dependencies).
- Generate macOS (.dmg) and Windows (.msi) installers automatically with GitHub Actions
The jlink & jpackage tools provide nice, small installers, using the Java module system to generate embedded, stripped down JVMs. Unfortunately, modules are in practice a bit difficult to use, as it expects Java libraries to add module information ( typically either via extra manifest entries or a new, compiled module-info.java/.class).
In the worst-case scenario, every time a developer adds a standard Maven dependency, the entire build process breaks due to module problems. This presents a grim choice - either give up on tools like jpackage or give up on Maven dependency management and return to the bad old days of shell scripts and manually managing library paths.
In this sample project, these problems are managed through the use of a single application-scoped shaded jar. The application's normal Maven dependencies are merged into a single JAR, and then jdeps automatically generates the module-info.java.
While this is a terrible strategy for libraries, it works just fine for end-user desktop applications.
So, what we have here is a pretty small, simple Maven project template that can be used to build nice, small native installers for a JavaFX application using only Maven, Java 15, and jpackage. In addition, on macOS XCode is required, and on Windows the free WiX Toolset.
On macOS, Windows and Linux these installers are coming in at around 30-40mb. This demonstration app includes several native desktop demonstration features - for example, drag-and-drop from the Finder/Explorer, as well as a few macOS Dock integration examples. The "Hello World" versions are closer to 30mb than 40mb.
This also provides an eventual path for migrating to fully modularized libraries. As libraries are converted to modules, eventually the build could be migrated to Maven-managed modules, perhaps eventually leveraging Maven copy-dependencies.
Here are few cool things in this template:
- Only uses Maven. No shell scripts required.
- Includes sample GitHub Actions to build macOS, Windows and Linux installers
- Demonstrates setting the application icon
- Builds a .dmg on macOS, .msi on Windows, and .deb on Linux
- Now bundles the JavaFX SDK & modules to simplify getting started.
- Template includes examples of many JavaFX / native desktop integration for macOS & Windows (Linux varies).
- Drag & drop with Finder / Explorer
- Change the Dock icon dynamically on macOS
- Menu on the top for macOS, in the window itself on Windows
- Request user attention (bouncing dock icon) on macOS
This project is sponsored by ChangeNode.com - if you would like to add easy automatic updates, crash reporting, analytics, etc. to your Java/JavaFX desktop application, go check it out.
Once everything is installed (see below) it's really easy to use:
To generate an installer, just run...
mvn clean install
To do everything up until the actual installer generation...
mvn clean package
- Install Java 15 or later.
- Verify by opening a fresh Terminal/Command Prompt and typing
java --version
.
- Verify by opening a fresh Terminal/Command Prompt and typing
- Install Apache Maven 3.6.3 or later and make sure it's on your path.
- Verify this by opening a fresh Terminal/Command Prompt and typing
mvn --version
.
- Verify this by opening a fresh Terminal/Command Prompt and typing
- Clone/download this project.
- On Java 15, add the jpackage configuration to your MAVEN_OPTS for your shell environment (described in more detail
below).
- On Java 15, verify this is working by typing
mvn --version
and notice the warning about using an incubator project. - Java 16 is expected to bundle jpackage, which will allow you to skip this step.
- Here's what the output looks like on Windows - notice the first line WARNING.
- On Java 15, verify this is working by typing
C:\Users\wiver\src\shade-test>mvn --version
WARNING: Using incubator modules: jdk.incubator.jpackage
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Users\wiver\devenv\apache-maven-3.6.3\apache-maven-3.6.3\bin\..
Java version: 15.0.1, vendor: AdoptOpenJDK, runtime: C:\Program Files\AdoptOpenJDK\jdk-15.0.1.9-hotspot
Default locale: en_US, platform encoding: Cp1252
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
- macOS: verify XCode is installed and needed agreements accepted.
- Just launch XCode and accept, or verify in Terminal with the command
sudo xcodebuild -license
.
- Just launch XCode and accept, or verify in Terminal with the command
- Windows: install Wix 3 binaries.
- As of this writing, merely installing Wix via the installer was sufficient for jpackage to find it.
- Final step: run
mvn clean install
from the root of the project to generate thetarget\TestApp.dmg
ortarget\TestApp.msi
(installer).- For reference, here is a complete run log for a successful run on Windows.
Because these builds use stripped down JVM images, the generated installers are in the 30-40mb range.
This project relies on jtoolprovider-plugin
to perform key build steps. To generate the actual installers, the jpackage tool must be available to the ToolProvider
API. For most people, adding a MAVEN_OPTS
environment variable will work nicely.
For example, on macOS, this can be done by adding to the following line to the ~/.zshrc
file.
export MAVEN_OPTS="--add-modules jdk.incubator.jpackage"
Current versions of Windows 10 have a nice UI for adding an environment variable. You can find it in the modern control panel via search - just start a search for "env" and that should bring up the appropriate control panel. Note that on Windows, you don't need the quote marks - here's a screenshot illustrating the proper configuration for a Windows 10 environment .
Unfortunately, as of this writing adding this entry to the IntelliJ options for Maven (either in the IntelliJ Maven JVM
importer UI or via project-directory/.mvn/jvm.config
) will break the Maven sync. This bug is tracked by JetBrains as
IDEA-246963. There is a .mvn/Xjvm.config file
in this project -
once the bug is fixed, or if you use a different editor, just try renaming that file to jvm.config
. Or, presumably,
when Java 16 ships and jpackage is no longer in incubation this will just go away as an issue.
This version of the project uses Maven Profiles to set a platform property. This property is then used to generate paths to the proper version of the JavaFX modules and the configuration for the platform-specific jpackage execution.
If you are not familiar with the standard Maven build lifecycle, you are highly encouraged to review the documentation "Introduction to the Build Lifecycle" to understand how Maven builds work.
The project automatically activate platform-specific
profiles, setting the
properties javafx.libs
and javafx.mods
to the proper locations, and also specifying the platform-specific
configuration file.
- Use maven-shade-plugin to generate a merged JAR containing
all the Maven managed dependencies. It strips out all the
existing module-info, unless you
exclude it that manually via the filters. This generated jar file is saved in the
target/shaded-jar/
folder. - Uses the maven-antrun-plugin to unzip the generated JAR into
a
target/unpacked-shade/
- Runs jtoolprovider-plugin to
run jdeps on the
target/unpacked-shade/
directory to generate a new module-info file for the project. - Runs moditect-maven-plugin to compile and add the module-info back into the shaded jar file.
- (Optional: requires activating profile add-jlink-package, e.g.
mvn clean verify -Padd-jlink-package
) jtoolprovider-plugin will run jlink to generate a native image. This is mostly helpful for local development only - end-users generally prefer an installer.
- Tells the maven-install-plugin to NOT install the files for this project in the local .m2 repository. No need to waste time and disk space copying unneeded files.
- If Maven detects the build is running on macOS or Windows, it will automatically add running jpackage via jtoolprovider-plugin to the install phase.
Problems? Make sure everything is installed and working right!
- Compiler not recognizing the --release option? Probably on an old JDK.
- Can't find jdeps? Probably on an old JDK.
- Unrecognized option: --add-modules jdk.incubator.jpackage
- Probably don't have MAVEN_OPTS set correctly.
If you need consulting support, feel free to reach out to ChangeNode.com.
A: GitHub won't allow cloning a template if the source has files over 10mb in size. The javafx.web components basically bundle a full native web browser under the covers. As of JavaFX 15 the javafx.web.jmod is roughly 25mb in size. If you need it, you can download it and install it in the JavaFX projects in your local project.
A: Yeah. There are a lot of moving parts in a build script like this. Most notably, the name of the generated application is set to Test-App-1.0 in a few places. Unfortunately, this is kind of complicated - for example, the name of the build application exists both in the GitHub Actions and the pom.xml. I tried to strike a balance between using various properties to avoid duplication and (worst case) degenerating into a sea of strange expressions. Hopefully you can follow everything, but if you have questions or need consulting support you can always reach out.
A: The current GitHub Workflow for the Linux build runs on a GitHub Ubuntu instance, and by default it generates a amd64.deb file. jpackage supports other distribution formats, including rpm, so if you want a different packaging format you can tweak the GitHub Action for the Ubuntu build and the jpackage command for Unix to generate whatever you need. As long as you can find the right combination of configuration flags for jpackage and can set up the GitHub Actions runner to match, you can generate whatever packaging you might need. If you need, you could set up several combinations of Maven profile and GitHub Action to generate as many different builds as you wish to support. For example, you could support generating macOS .dmg and .pkg files, Windows .msi and .exe, Linux .deb and .rpm in several different binary formats.
A: The Windows GitHub workflow for this project downloads the Wix Installer toolkit and adds it to the path to automatically build the Windows installer. On your local dev machine just install WiX Toolset locally instead - it'll be a lot faster.
As of this writing, the Windows options will work, but the lack of a version number means that right now you have to manually uninstall and reinstall via the Windows Control Panel to update to a new version. Not a big deal for development, but when you go to install an update you'll have to uninstall/reinstall manually.
In a proper CI build, the installer version should be set by the CI system. Use this project as a starting point and add the version info based on your CI and versioning approach. Perhaps some combination of a GUID and/or timestamp.
As an aside, ChangeNode supports forcing reinstalls of .msi regardless of the installer version - for example, if you need to revert a deployment to an earlier version due to a regression.
A: You will likely need to add additional options to ship properly on macOS - most notably, you will want to sign and notarize your app for macOS to make everything work without end user warnings. Check out tools such as Gon or this command-line signing tutorial.
Q: Can I generate macOS installers on Windows, or Windows installers on macOS? Or macOS/Windows on Linux?
A: No, but this project uses GitHub workflows to generate macOS and Windows installers automatically, regardless of your development platform. This means that (for example) you could do your dev work on Linux and rely on the GitHub Actions to generate macOS and Windows builds. If you need help, reach out to ChangeNode.com.
You still should (of course) do QA on your apps, but that's a different topic.
A: No... for that, you should check out ChangeNode.com!
A: Cool - check out JPackageScriptFX - the original shell scripts used as a reference when I initially started work on this project.
Sure - here's my personal list of cool JavaFX resources, which includes links to a few other big lists of resources.