CS 346 (W23)
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage


As discussed on the console application section, scripts don’t work well when we require more complex runtime components. For example, using the script for a JavaFX application produces an error:

$ ./clock_advanced
Error: JavaFX runtime components are missing, and are required to run this application

JavaFX applications

Scripts like this are really only suitable for console applications i.e. applications without a GUI. If you are building a JavaFX or Compose desktop application, you need to either use jlink or jpackage to build an installer.

JLink will let you build a custom runtime that will handle the module dependencies for JavaFX. The simplest way to do this is to add the JLink plugin to your build.gradle file and let Gradle handle it.

plugins {
    id 'org.beryx.jlink' version '2.25.0'

You can also configure it in the build.gradle file as well. For a full set of options see the Badass-JLink plugin page.

    launcher {
        name = "clock"

We can rebuild the clock sample using Gradle - build - jLink to produce a runtime script in build/image

Here’s the resulting directory structure. Notice that it includes a number of libraries that our application needs to run.

$ tree build/image -L 2
├── bin
│   ├── clock_advanced
│   ├── clock_advanced.bat
│   ├── java
│   ├── jrunscript
│   └── keytool
├── conf
│   ├── net.properties
│   ├── security
│   └── sound.properties
├── include
│   ├── classfile_constants.h
│   ├── darwin
│   ├── jawt.h
│   ├── jni.h
│   ├── jvmti.h
│   └── jvmticmlr.h
├── legal
│   ├── java.base
│   ├── java.datatransfer
│   ├── java.desktop
│   ├── java.prefs
│   ├── java.scripting
│   ├── java.xml
│   └── jdk.unsupported
├── lib
│   ├── classlist
│   ├── fontconfig.bfc
│   ├── fontconfig.properties.src
│   ├── jrt-fs.jar
│   ├── jspawnhelper
│   ├── jvm.cfg
│   ├── libawt.dylib
..... (continues)

Running the top-level bin/clock_advanced image will execute our application.

$ ./clock_advanced


Creating installers

Finally, we can use jpackage to create native installers for a number of supported operating systems. JPackage is included as a console application in Java JDK 16 or higher, and will work with any JVM language (e.g. Java, Kotlin, Scala). The full guide is here.

An installer is an application that when executed, installs a different application for the user. We need installers because most applications consists of many different files: executables, libraries, resources (images, sound files), preference files and so on. These need to be installed in the correct location, and sometimes registered, to function correctly.

Tasks that the installer performs include:

  • Copying application files to the correct location.
  • Installing and registering system libraries.
  • Making changes to the system registry (or similar system databases).
  • Creating icons on the desktop, or applications folder.
  • Prompting the user if any of these tasks require elevated privileges.

Instead of running jpackage manually, we will install a plugin into IntelliJ and use that environment to generate our installers. We can do this by installing the Badass-JLink plugin page. To use the plugin, include the following in your gradle.build script:

plugins {
  id 'org.beryx.jlink' version '2.25.0'

JPackage itself has a number of other options that you can specify in the build.gradle file. The full list of options is on the plugin website.

// build.gradle file options for jpackage
jlink {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
        name = 'hello'
        jvmArgs = ['-Dlog4j.configurationFile=./log4j2.xml']

If you install the plugin correctly, then you should see the jpackage command in Gradle - build - jpackage. Run this and it will create platform installers in the build/distribution directory.

macOS installer

This is a standard macOS installer. Drag the clock_advanced icon to the Applications folder. You can then run it from that folder.

Installers are meant for graphical applications. If you are building a JavaFX or Compose desktop application, this is the right choice. If you’re building a console application, you probably want a script instead (see previous step) so that you can execute it from the console directly.


We expect the output of this process to be an installer (or scripts/custom deliverable) for each platform.

An installer does the following:

  • Installs your software
  • Installs libraries
  • Sets the application icon
  • Creates shortcuts
  • Installs readme.txt
  • Installs license.txt