The way it works now is, you convert your program to a module, then “link” it to all the other modules it requires.
The result of this linking process is what’s known as an image. An image is actually a file tree, which includes a bin
directory with one or more ready-to-run executables. This tree is what you distribute, typically as a zip or tar.gz.
The steps are:
- Create a module-info.java
- Compile with a module path instead of a classpath
- Create a jar from classes, as usual
- Convert jar to a jmod with the JDK’s
jmod
tool
- Link that jmod, and the modules on which it depends, into an image
Writing a module descriptor
The first step is to turn the application into a module. Minimally, this requires creating a module-info.java
at the top of your source tree (that is, in the empty package). Every module has a name, which is often the same as a package name but doesn’t have to be. So, your module-info.java might look like this:
module com.mcs75.businessapp {
exports com.mcs75.desktop.businessapp;
requires java.logging;
requires transitive javafx.graphics;
requires transitive javafx.controls;
}
Building
When you build, you don’t specify a classpath at all. Instead, you specify a module path.
A module path is a list of directories, not files. Each directory contains, not surprisingly, modules. The jmods
directory of the JDK is included implicitly. All you need to include is directories containing the non-JDK modules you need. In your case, that at least means Gluon’s JavaFX:
javac -Xlint -g -d build/classes --module-path /opt/gluon-javafx/lib
src/java/com/mcs75/desktop/businessapp/*.java
Then you create a jar the usual way:
jar -c -f build/mybusinessapp.jar -C build/classes .
A jar file with a module-info.class in it is considered a modular jar.
Making a jmod
Creating a jmod is usually a simple process:
mkdir build/modules
jmod create --class-path build/mybusinessapp.jar
--main-class com.mcs75.desktop.businessapp.BusinessApplication
build/modules/mybusinessapp.jmod
Linking
Finally, you use the JDK’s jlink
command to assemble everything:
jlink --output build/image
--module-path build/modules:/opt/gluon-javafx/lib
--add-modules com.mcs75.businessapp
--launcher MyBusinessApp=com.mcs75.businessapp
jlink
creates a minimal JRE, which contains only the modules you explicitly add (and the modules those explicit modules require). --add-modules
is the required option that specifies what to add.
As with other JDK tools, --module-path
specifies directories containing modules.
The --launcher
option causes the final image tree to have an additional executable script in its bin
directory with the given name (the part before the equals). So, MyBusinessApp=com.mcs75.businessapp
means “create an executable named MyBusinessApp, which executes the module com.mcs75.businessapp.”
Because the jmod create
command included a --main-class
option, Java will know what to execute, just like declaring a Main-Class attribute in a manifest. It is also possible to explicitly declare a class to execute in the --launcher
option if desired.
Distributing
What you will want to distribute is a zip or tar.gz of the entire image file tree. The executable that the user should run is in the image’s bin
directory. Of course, you are free to add your own executables. You are also free to place this into any kind of installer, as long as the image tree’s structure is preserved.
A future JDK will have a packaging tool for creating full-fledged native installers. As of Java 14, the JDK has a jpackage tool which can create native installers. For example:
jpackage -n MyBusinessApp --runtime-image build/image
-m com.mcs75.businessapp/com.mcs75.desktop.businessapp.BusinessApplication
-n
specifies the name of the program. --runtime-image
specifies the location of the existing jlink’d image. -m
is the module and class within the jlink’d image to execute, much like the portion after the =
in jlink’s --launcher
option.
Cross-building
Since the image includes native binaries, you will need to create an image for each platform. Obviously, one option is to build the image on a Linux system, and again on a Windows system, and again on a Mac, etc.
But you can also use jmod
and jlink
to create images for other platforms, regardless of where you’re building.
There’s only a few additional steps needed. First, you will need the JDKs for those other platforms. Download them as archives (zip or tar.gz), not installers, and unpack them to directories of your choice.
Each JDK defines a platform string. This is normally of the form <os>-<arch>. The platform is a property of the java.base
module; you can see any JDK’s platform by examining that module:
jmod describe path-to-foreign-jdk/jmods/java.base.jmod | grep '^platform'
Pass that platform string to your jmod command using the --target-platform
option:
mkdir build/modules
jmod create --target-platform windows-amd64
--class-path build/mybusinessapp.jar
--main-class com.mcs75.desktop.businessapp.BusinessApplication
build/modules/mybusinessapp.jmod
Finally, when linking, you want to explicitly include the other JDK’s jmods
directory, so jlink won’t implicitly include its own JDK’s modules:
jlink --output build/image
--module-path path-to-foreign-jdk/jmods:build/modules:/opt/gluon-javafx-windows/lib
--add-modules com.mcs75.businessapp
--launcher MyBusinessApp=com.mcs75.businessapp