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.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
In summary, if you want to package your applications to be native MacOS, Windows, or Linux applications, I hope these examples are helpful.