How to use the jpackage command with Java 14 (and JavaFX)

In 2024, assuming that you’re using (a) a modern macOS version and (b) a modern Java version, and (c) you want to bundle a Java/JAR file as a Mac/macOS application, then (d) you’ll want to use the Java jpackage command to bundle your Java/Scala/Kotlin/JVM application into something that works like a native application. Here’s a quick example that works with the following assumptions:

  • You’ve bundled your complete application into one jar file
  • You have an icon for that jar file
  • You want to create an application image (i.e., something that looks like a native application on MacOS, Windows, and Linux)
  • Your input files are in a directory named input
  • You want to write your output files to a directory named output
  • Very importantly, you’ve installed JDK/SE 14 or newer
  • You’ve installed the JavaFX jmods from this web page

Prerequisite: Installing the JavaFX jmods

My default JDK 14 jmods are installed in this directory on my MacOS system:

~/Library/Caches/Coursier/jvm/adopt@1.14.0-1/Contents/Home/jmods

An important part of this process is knowing that you have to install the JavaFX jmods into this folder as well.

My jpackage command for my JavaFX app

Given that background, here’s the JDK 14 jpackage command I just used to bundle my Scala/Java application into a native MacOS application:

jpackage \
  --type app-image \
  --verbose \
  --input input \
  --dest output \
  --name CliffsNotes \
  --main-jar CliffsNotesEditor-0.1.jar \
  --main-class com.alvinalexander.cliffsnotes.CliffsNotesGui \
  --icon CliffsNotes.icns \
  --module-path /Users/al/bin/jdk-14.0.1.jdk/Contents/Home/jmods \
  --add-modules java.base,javafx.controls,javafx.web,javafx.graphics,javafx.media,java.datatransfer,java.desktop,java.scripting,java.xml,jdk.jsobject,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,javafx.fxml,java.naming,java.sql,jdk.charsets \
  --mac-package-name CliffsNotes \
  --mac-package-identifier com.alvinalexander.cliffsnotes.CliffsNotesGui \
  --java-options -Xmx2048m

In that command:

  • I put my input jar file and icon in a subdirectory named input
  • My application is named CliffsNotes
  • My jar file is named CliffsNotesEditor-0.1.jar
  • The main class for my application — the class inside that jar file — is named com.alvinalexander.cliffsnotes.CliffsNotesGui
  • I installed JDK 14 in a directory named /Users/al/bin/jdk-14.0.1.jdk
  • I’m creating a native Mac/MacOS image, so I add a few Mac-specific options at the end of that command

In my case I add all of those Java modules because my application is a JavaFX application. I don’t think I really need all of those modules, so my next task is to try to figure out which modules I need in that command. For instance, I don’t use any scripting or XML, so I shouldn’t need java.scripting or java.xml. I think I might be able to figure out which modules are needed using a command like jlink, but I haven’t figured that out yet.

November, 2022: I confirmed today that it IS NOT necessary to list all those Java modules for a Java Swing application on macOS Monterey (12.6).

Note: jpackage replaces jpackager

Note that in JDK 7 and 8, there was a command named jpackager that worked similar to jpackage. Somewhere between Java 8 and Java 11 that command was removed from the JDK distribution, and finally jpackage is now available with JDK 14. I tried to build an app using an unsupported version of jpackage with Java 11, and I couldn’t get that to work. Once I upgraded to Java 14, its jpackage command worked fine.

Late 2024 update

I just used this shell script to get a Java/Swing app running on macOS 14.4 with Java 17, and I can confirm that it works:

#
# 2024-10-27:
# - this command now works with Java 17.
# - i haven’t tested to see if 'xattr' is 100% necessary yet.
# - i used this today to get this app working, and it's now running from the macOS 14.4 Dock.
#

jpackage \
  --type app-image \
  --verbose \
  --input input \
  --dest output \
  --name Imagen \
  --main-jar Imagen3-assembly-0.1.jar \
  --main-class com.valleyprogramming.imagen.Imagen3 \
  --icon input/Imagen.icns \
  --mac-package-name Imagen \
  --mac-package-identifier com.valleyprogramming.imagen.Imagen3 \
  --java-options -Xmx2048m

echo ""
echo "AL: using 'xattr' to 'remove all attributes'"
xattr -cr output/Imagen.app

Note that the “Mac app signing” process is a big deal these days, and this command DOES NOT use signing. You’ll see messages to that effect, like these:

[15:23:01.164] Running /usr/bin/codesign
[15:23:01.216] Command [PID: 36089]:
    /usr/bin/codesign -s - -vvvv --force output/Imagen.app/Contents/runtime
[15:23:01.216] Output:
    output/Imagen.app/Contents/runtime: replacing existing signature
    output/Imagen.app/Contents/runtime: signed bundle with Mach-O thin (arm64) [com.oracle.java.com.valleyprogramming.imagen.Imagen3]
[15:23:01.216] Returned: 0

[15:23:01.216] Running /usr/bin/codesign
[15:23:01.269] Command [PID: 36090]:
    /usr/bin/codesign -s - -vvvv --force output/Imagen.app
[15:23:01.269] Output:
    output/Imagen.app: replacing existing signature
    output/Imagen.app: signed app bundle with Mach-O thin (arm64) [com.valleyprogramming.imagen.Imagen3]
[15:23:01.269] Returned: 0

[15:23:01.269] Succeeded in building Mac Application Image package

jpackage arguments and options

The jpackage command has many command-line options and arguments. For some purposes you can probably get by with just a few, like this:

jpackage \
  --type app-image \
  --input input \
  --dest output \
  --name CliffsNotes \
  --main-jar CliffsNotesEditor-0.1.jar \
  --main-class com.alvinalexander.cliffsnotes.CliffsNotesGui \
  --icon CliffsNotes.icns

That being said, if you’re trying to package something nice for other people you may want to use more jpackage command-line options. Here’s a list of those options/arguments with JDK 14.0.1:

$ jpackage -h
WARNING: Using incubator modules: jdk.incubator.jpackage
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
    Generate an application image:
        For a modular application:
            jpackage --type app-image -n name -p modulePath \
                -m moduleName/className
        For a non-modular application:
            jpackage --type app-image -i inputDir -n name \
                --main-class className --main-jar myJar.jar
        To provide your own options to jlink, run jlink separately:
            jlink --output appRuntimeImage -p modulePath -m moduleName \
                --no-header-files [<additional jlink options>...]
            jpackage --type app-image -n name \
                -m moduleName/className --runtime-image appRuntimeImage
    Generate a Java runtime package:
        jpackage -n name --runtime-image <runtime-image>

Generic Options:
  @<filename> 
          Read options and/or mode from a file 
          This option can be used multiple times.
  --type -t <type> 
          The type of package to create
          Valid values are: {"app-image", "dmg", "pkg"} 
          If this option is not specified a platform dependent
          default type will be created.
  --app-version <version>
          Version of the application and/or package
  --copyright <copyright string>
          Copyright for the application
  --description <description string>
          Description of the application
  --help -h 
          Print the usage text with a list and description of each valid
          option for the current platform to the output stream, and exit
  --name -n <name>
          Name of the application and/or package
  --dest -d <destination path>
          Path where generated output file is placed
          Defaults to the current working directory.
          (absolute path or relative to the current directory)
  --temp <file path>
          Path of a new or empty directory used to create temporary files
          (absolute path or relative to the current directory)
          If specified, the temp dir will not be removed upon the task
          completion and must be removed manually
          If not specified, a temporary directory will be created and
          removed upon the task completion.
  --vendor <vendor string>
          Vendor of the application
  --verbose
          Enables verbose output
  --version
          Print the product version to the output stream and exit

Options for creating the runtime image:
  --add-modules <module name>[,<module name>...]
          A comma (",") separated list of modules to add.
          This module list, along with the main module (if specified)
          will be passed to jlink as the --add-module argument.
          if not specified, either just the main module (if --module is
          specified), or the default set of modules (if --main-jar is 
          specified) are used.
          This option can be used multiple times.
  --module-path -p <module path>...
          A : separated list of paths
          Each path is either a directory of modules or the path to a
          modular jar.
          (each path is absolute or relative to the current directory)
          This option can be used multiple times.
  --bind-services 
          Pass on --bind-services option to jlink (which will link in 
          service provider modules and their dependences) 
  --runtime-image <file path>
          Path of the predefined runtime image that will be copied into
          the application image
          (absolute path or relative to the current directory)
          If --runtime-image is not specified, jpackage will run jlink to
          create the runtime image using options:
          --strip-debug, --no-header-files, --no-man-pages, and
          --strip-native-commands.

Options for creating the application image:
  --icon <icon file path>
          Path of the icon of the application package
          (absolute path or relative to the current directory)
  --input -i <input path>
          Path of the input directory that contains the files to be packaged
          (absolute path or relative to the current directory)
          All files in the input directory will be packaged into the
          application image.

Options for creating the application launcher(s):
  --add-launcher <launcher name>=<file path>
          Name of launcher, and a path to a Properties file that contains
          a list of key, value pairs
          (absolute path or relative to the current directory)
          The keys "module", "main-jar", "main-class",
          "arguments", "java-options", "app-version", "icon", and
          "win-console" can be used.
          These options are added to, or used to overwrite, the original
          command line options to build an additional alternative launcher.
          The main application launcher will be built from the command line
          options. Additional alternative launchers can be built using
          this option, and this option can be used multiple times to
          build multiple additional launchers. 
  --arguments <main class arguments>
          Command line arguments to pass to the main class if no command
          line arguments are given to the launcher
          This option can be used multiple times.
  --java-options <java options>
          Options to pass to the Java runtime
          This option can be used multiple times.
  --main-class <class name>
          Qualified name of the application main class to execute
          This option can only be used if --main-jar is specified.
  --main-jar <main jar file>
          The main JAR of the application; containing the main class
          (specified as a path relative to the input path)
          Either --module or --main-jar option can be specified but not
          both.
  --module -m <module name>[/<main class>]
          The main module (and optionally main class) of the application
          This module must be located on the module path.
          When this option is specified, the main module will be linked
          in the Java runtime image.  Either --module or --main-jar
          option can be specified but not both.
  --mac-package-identifier <ID string>
          An identifier that uniquely identifies the application for macOS
          Defaults to the main class name.
          May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),
          and period (.) characters.
  --mac-package-name <name string>
          Name of the application as it appears in the Menu Bar
          This can be different from the application name.
          This name must be less than 16 characters long and be suitable for
          displaying in the menu bar and the application Info window.
          Defaults to the application name.
  --mac-package-signing-prefix <prefix string>
          When signing the application package, this value is prefixed
          to all components that need to be signed that don't have
          an existing package identifier.
  --mac-sign
          Request that the package be signed
  --mac-signing-keychain <file path>
          Path of the keychain to search for the signing identity
          (absolute path or relative to the current directory).
          If not specified, the standard keychains are used.
  --mac-signing-key-user-name <team name>
          Team name portion in Apple signing identities' names.
          For example "Developer ID Application: "

Options for creating the application package:
  --app-image <file path>
          Location of the predefined application image that is used
          to build an installable package
          (absolute path or relative to the current directory)
  --file-associations <file path>
          Path to a Properties file that contains list of key, value pairs
          (absolute path or relative to the current directory)
          The keys "extension", "mime-type", "icon", and "description"
          can be used to describe the association.
          This option can be used multiple times.
  --install-dir <file path>
          Absolute path of the installation directory of the application
  --license-file <file path>
          Path to the license file
          (absolute path or relative to the current directory)
  --resource-dir <path>
          Path to override jpackage resources
          Icons, template files, and other resources of jpackage can be
          over-ridden by adding replacement resources to this directory.
          (absolute path or relative to the current directory)
  --runtime-image <file-path>
          Path of the predefined runtime image to install
          (absolute path or relative to the current directory)
          Option is required when creating a runtime package.

In summary, if you want to package your applications to be native MacOS, Windows, or Linux applications, I hope these examples are helpful.