Introduction

The badass-runtime plugin allows you to create custom runtime images for non-modular applications. It also lets you create an application installer with the jpackage tool.

For modular applications use the Badass-JLink plugin.

Creating a custom runtime image for a non-modular application involves the following steps:

  • create a classic (pre-Java-9-style) distribution using a task such as installDist or installShadowDist

  • use jlink to create a custom JRE containing only the modules required by the application

  • merge the pre-Java-9-style distribution into the custom JRE and adjust the start scripts with appropriate JVM settings flags such as --module-path.

The plugin provides a runtime task that automates the above steps and a runtimeZip task that additionally creates a zip archive of the custom runtime image. In addition, a suggestModules task helps you choose the modules to include in your custom JRE. With the jpackage task you can create a platform-specific installer for your application.

The plugin requires Java 11 and Gradle 4.8 or newer. While it might work with some combinations of older Java and Gradle versions, these are not officially supported.

To use the plugin, include the following in your build script:

plugins {
    id 'org.beryx.runtime' version '1.12.7'
}

Applying this plugin also implicitly applies the Application plugin.

The badass-runtime plugin adds an extension named runtime. The sample below shows a few configuration options.

runtime {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    modules = ['java.naming', 'java.xml']
}

The next sections provide detailed information on how to configure the plugin.

The source code is available on GitHub and is licensed under the Apache-2.0 license.

User Guide

This plugin works with non-modular applications, that is, applications that run on the class-path and don’t make use of module descriptors. Nevertheless, in order to use this plugin, your non-modular application must be compatible with the Java version of your target JRE (11 or newer).

Tasks

runtime

Creates an image containing your application, a custom JRE, and appropriate start scripts.
If the property distDir is not set, this task depends on either installDist or installShadowDist (if the Shadow plugin is applied).

runtimeZip

Creates a zip archive of the custom runtime image.
depends on: runtime

suggestModules

Displays a list of modules that are probably required by your application. This list will be used when creating the custom JRE if you don’t configure the modules property explicitly within the runtime extension.
Setting the modules property explicitly is strongly recommended. The list of modules suggested by this task is a good value to start with, but it may miss some required modules or include unnecessary ones, so you may need to adjust it.
depends on: jar

jpackageImage

Uses the jpackage tool to create a platform-specific application image.
depends on: runtime

jpackage

Uses the jpackage tool to create a platform-specific application installer.
depends on: jpackageImage

Properties

distDir

The directory containing the application pre-Java-9-style distribution.
You rarely need to set this property explicitly, because the plugin automatically initializes it with the directory in which the installDist or the installShadowDist task (if the Shadow plugin is applied) creates the distribution.
usage example: distDir = file("$buildDir/install/myapp")

imageDir

The directory into which the custom runtime image should be generated.
(If you use the targetPlatform method to generate images for other platforms, the corresponding images will be created in subdirectories of imageDir.)
defaultValue: buildDir/image
usage example: imageDir = file("$buildDir/myapp-image")

imageZip

The file into which a zip archive of the custom runtime image should be created.
defaultValue: buildDir/image.zip"
usage example: imageZip = file("$buildDir/myapp-image.zip")

options

A list of options to be passed to jlink when creating the custom JRE.
defaultValue: empty list
usage example: options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']

javaHome

The path to the JDK providing the tools needed by the plugin (javac, jar, jlink etc.).
defaultValue: the first non-empty value from:
     - the badass.runtime.java.home system property
     - the BADASS_RUNTIME_JAVA_HOME environment variable
     - the Java toolchain configured in the Gradle script
     - the java.home system property (only if it points to a JRE containing the javac, jar, and jlink tools)
     - the JAVA_HOME environment variable
usage example: javaHome = '/usr/lib/jvm/open-jdk'

modules

The list of modules to be included in the custom JRE.
defaultValue: empty list (the modules provided by the suggestModules task will be used)
usage example: modules = ['java.naming', 'java.xml']

additive

In many cases, the list of modules provided by the suggestModules task contains exactly the modules required by your application. This means that you don’t need to configure the modules property, because the plugin can figure out by itself which modules are required. In some other cases, the "suggested" list of modules is almost right, in the sense that it only misses one or a few modules. In these cases, you are allowed to specify only the missing modules in the modules property, and you instruct the plugin to add them to the list of suggested modules, by setting the property additive to true.
defaultValue: false
usage example: additive = true

Methods

addOptions(String…​ options)

Adds options to be passed to jlink. It is an alternative way of setting the options property. You can call this method multiple times.
usage example: addOptions '--no-header-files', '--no-man-pages'

targetPlatform(String name, String jdkHome, List<String> options = [])

Instructs the plugin to use jlink to generate an application image for a specific platform.
This method is not for configuring the installable packages produced by jpackage. See details
By default, the plugin generates an image for the platform it runs on. To create images for other platforms, you need to call the targetPlatform method (one call per target platform).
name: an identifier of your choice that will be appended to the imageDir and imageZip properties to determine the location of the image directory and of the image archive.
jdkHome: the path to the target platform JDK.
options: an optional list of platform-specific options. These options will pe passed to jlink in addition to those provided by the options property of the runtime extension.
NOTE: This is only a convenience method. There is a more powerful targetPlatform method (described below), which allows configuring additional parameters of the target platform.

Usage example
runtime {
    ...
    targetPlatform('linux-x64', '/usr/lib/jvm/jdk_x64_linux_hotspot_11_28')
    targetPlatform('linux-s390x', '/usr/lib/jvm/jdk_s390x_linux_hotspot_11_28',
                                                               ['--endian', 'big'])
    ...
}

For a project named hello, executing the runtimeZip task with the above configuration, and assuming default values for the other properties, the plugin will generate the platform-specific images in the directories build/image/hello-linux-x64 and build/image/hello-linux-s390x. The archived images will be available in build/image-linux-x64.zip and build/image-linux-s390x.zip.

targetPlatform(String name, Action<TargetPlatform> action)

This more powerful version of the targetPlatform method allows configuring the target platform parameters using a script block.
name: an identifier of your choice that will be appended to the imageDir and imageZip properties to determine the location of the image directory and of the image archive.
action: a script block for configuring the target platform parameters.
     Parameters:
         jdkHome: the path to the target platform JDK.
         options: an optional list of platform-specific options.
     Methods:
         addOptions(String…​ options): an alternative way of setting the options property.
         jdkDownload(String downloadUrl, Closure downloadConfig=null): helper method for setting jdkHome.
             It downloads and unpacks a JDK distribution from the given URL.
             The optional closure allows configuring the following parameters:
               - downloadDir: the directory in which the distribution is downloaded and unpacked.
                   defaultValue: buildDir/jdks/targetPlatform-name
               - archiveName: the name under which the archived distribution should be saved.
                   defaultValue: jdk
               - archiveExtension: accepted values: tar.gz and zip.
                   defaultValue: null (inferred from the URL)
               - pathToHome: the relative path to the JDK home in the unpacked distribution.
                   defaultValue: null (inferred by scanning the unpacked distribution)
               - overwrite: if true, the plugin overwrites an already existing distribution.
                   defaultValue: false

Usage example
runtime {
    ...
    targetPlatform("linux-s390x") {
        jdkHome = "/usr/lib/jvm/linux-s390x/jdk-14.0.1_7"
        addOptions("--endian", "big")
    }

    targetPlatform("win") {
        jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7.1/OpenJDK14U-jdk_x64_windows_hotspot_14.0.1_7.zip")
    }

    targetPlatform("mac") {
        jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7/OpenJDK14U-jdk_x64_mac_hotspot_14.0.1_7.tar.gz") {
            downloadDir = "$buildDir/myMac"
            archiveName = "my-mac-jdk"
            archiveExtension = "tar.gz"
            pathToHome = "jdk-14.0.1+7/Contents/Home"
            overwrite = true
        }
    }
    ...
}
enableCds(Action<CdsData> action = null)

Experimental - requires Java 13 or newer
Enables Class Data Sharing (CDS).
action: an optional script block for configuring the class data sharing.
     Parameters:
         sharedArchiveFile: the path and name of the class data sharing archive file.
             It supports the Mustache syntax and placeholders described in the launcher section.
             defaultValue:
                 lib/server/<appName>.jsa on Unix-like systems
                 bin\server\<appName>.jsa on Windows

Usage example
runtime {
    ...
    enableCds()
}

     or

runtime {
    ...
    enableCds {
        sharedArchiveFile = "{{HOME_DIR}}/${applicationName}.jsa"
    }
}

When the enableCds method is used, the plugin creates a base CDS archive of the image by executing $imageDir/bin/java -Xshare:dump. This means that you cannot use enableCds when targeting another platform.

The plugin also configures a dynamic AppCDS archive in the start scripts. If no file is found at the sharedArchiveFile location, the application is started with the -XX:ArchiveClassesAtExit option, which will create a dynamic AppCDS archive at this location. Otherwise, the application is started with the -XX:SharedArchiveFile option and uses the existing AppCDS archive.

NOTE: Start scripts are not included in the installable packages generated by jpackage. As a result, only the base CDS archive of the image is used by the packaged application.

Script blocks

The runtime extension can also contain the script blocks detailed below.

launcher

The plugin generates script files for launching your application. These script files can be customized by configuring the launcher block.

Environment variables can be included by using the Mustache syntax, that is, by enclosing their name between {{ and }}. Additionally, you can use the following placeholders:

  • {{BIN_DIR}} - the bin directory of the custom runtime image

  • {{HOME_DIR}} - user’s home directory ($HOME on Unix-like systems, %USERPROFILE% on Windows)

    jvmArgs

    list of JVM arguments to be passed to the java executable.
    defaultValue: the arguments configured in the applicationDefaultJvmArgs property of the application extension

    noConsole

    This boolean property has an effect only on Windows. It is ignored on other platforms.
    If true, the application will be launched without an associated console window (using javaw instead of java).
    defaultValue: false

    runInBinDir

    If true, the start script will cd in the bin directory of the image before executing the application.
    defaultValue: false

    unixScriptTemplate

    the template for generating the script file for Unix-like systems.
    defaultValue: null (the plugin uses its own template)

    windowsScriptTemplate

    the template for generating the script file for Windows-based systems.
    defaultValue: null (the plugin uses its own template)

The plugin uses Groovy’s SimpleTemplateEngine to parse the templates, with the following variables available:

  • applicationName

  • mainClassName

Usage example

Groovy
runtime {
    ...
    launcher {
        jvmArgs = [
            '-Dlog4j.debug=true', '-Dlog4j.configurationFile={{BIN_DIR}}/log4j2.xml',
            '-DdbHost', '{{PGHOST}}'
        ]
        unixScriptTemplate = file('unixStartScript.txt')
        windowsScriptTemplate = file('windowsStartScript.txt')
    }
    ...
}
Kotlin
runtime {
    ...
    launcher {
        jvmArgs = listOf(
            "-Dlog4j.debug=true", "-Dlog4j.configurationFile={{BIN_DIR}}/log4j2.xml",
            "-DdbHost", "{{PGHOST}}"
        )
        unixScriptTemplate = file("unixStartScript.txt")
        windowsScriptTemplate = file("windowsStartScript.txt")
    }
    ...
}

jpackage

This script block allows you to customize the jpackage-based generation of platform-specific installable packages.

jpackageHome

The path to the JDK providing the jpackage tool.
defaultValue: the first non-empty value from:
     - the badass.runtime.jpackage.home system property
     - the BADASS_RUNTIME_JPACKAGE_HOME environment variable
     - the Java toolchain configured in the Gradle script
     - the java.home system property (only if it points to a JRE containing the jpackage tool)
     - the JAVA_HOME environment variable
usage example: jpackageHome = "/usr/lib/jvm/jdk16"

outputDir

Convenience property for setting both imageOutputDir and installerOutputDir with the value buildDir/outputDir.
defaultValue: "jpackage"
usage example: outputDir = "my-packaging"

imageOutputDir

the directory passed as argument to the --output option when running jpackage to create an application image.
defaultValue: buildDir/outputDir
usage example: imageOutputDir = file("$buildDir/my-packaging-image")

imageName

the argument passed to the --name option when running jpackage to create an application image.
defaultValue: project.name
usage example: imageName = "MyApp"

imageOptions

list of additional options to be passed to the jpackage executable when creating the appliction image.
defaultValue: empty list
usage example: imageOptions = ["--win-console"]

resourceDir

the directory passed as argument to the --resource-dir option when running jpackage to create an application installer. It is also applicable when creating an application image when you want your own application image instead of the default java image.
usage example: resourceDir = file("$buildDir/my-packaging-resources")

skipInstaller

boolean value that lets you generate only the platform-specific application image and skip the generation of the platform-specific application installer.
defaultValue: false
usage example: skipInstaller = true

installerType

the type of installer to be generated.
defaultValue: null (all supported types for the current platform will be generated)
usage example: installerType = "rpm"

installerOutputDir

the directory passed as argument to the --output option when running jpackage to create an application installer.
defaultValue: buildDir/outputDir
usage example: installerOutputDir = file("$buildDir/my-packaging-installer")

installerName

the argument passed to the --name option when running jpackage to create an application installer.
defaultValue: project.name
usage example: installerName = "MyApp"

jvmArgs

list of JVM arguments to be passed to the virtual machine.
defaultValue: the jvmArgs value configured in the launcher block, or the arguments configured in the applicationDefaultJvmArgs property of the application extension.
NOTE: If the default value is used, and it contains the placeholder {{BIN_DIR}}, the plugin replaces this placeholder with $APPDIR/.. when passing the arguments to jpackage. This is the correct approach in most cases. If it doesn’t work in your case, you need to explicitly configure jvmArgs in the jpackage block.
Currently, jpackage doesn’t support environment variables in --java-options. Therefore, you cannot use environment variable names enclosed between {{ and }} in jvmArgs.

args

list of arguments to be passed to the application.
defaultValue: the arguments configured in the args property of the run task

appVersion

the argument passed to the --app-version option when running jpackage when executing the jpackage and jpackageImage tasks.
defaultValue: the project version
usage example: appVersion = "1.0.0"

installerOptions

list of additional options to be passed to the jpackage executable when creating the application installer.
defaultValue: empty list
usage example: installerOptions = ["--win-console"]

targetPlatformName

This property is required only when using the targetPlatform method. It specifies which of the images produced by jlink should be used as runtime image by jpackage. Its value must match the name provided in one of the calls to the targetPlatform method.
defaultValue: null
usage example: targetPlatformName = "linux"

In contrast to jlink, jpackage is not able to produce installers for other platforms. For example, to create an installer for Linux, you must run jpackage on a Linux machine. You cannot do it on a Windows or Mac platform.
If you need to create installers for more than one platform, it’s probably better not to use targetPlatform. Instead, you run the same build on different machines. If your project is on GitHub, you can automate this by using GitHub Actions, as seen in this example.
mainJar

the argument passed to the --main-jar option when running jpackage to create an application image.
Usually, you don’t need to set this property, unless you also explicitly set distDir.
defaultValue: the name of the JAR file produced by the installDist or the installShadowDist task
usage example: mainJar = "my-app-1.0.1.jar"

mainClass

the argument passed to the --main-class option when running jpackage to create an application image.
Usually, you don’t need to set this property, unless you also explicitly set distDir.
defaultValue: the mainClassName configured for the application plugin
usage example: mainClass = "org.example.hello.Greeter"

Usage example

Groovy
runtime {
    ...
    jpackage {
        jpackageHome = '/usr/lib/jvm/jdk16'
        outputDir = 'my-packaging'
        // imageOutputDir = file("$buildDir/my-packaging-image")
        // installerOutputDir = file("$buildDir/my-packaging-installer")
        imageName = 'MyApp'
        imageOptions = ['--win-console']
        skipInstaller = false
        installerName = 'MyApp'
        installerType = 'msi'
        installerOptions = ['--win-menu', '--win-shortcut']
    }
    ...
}
Kotlin
runtime {
    ...
    jpackage {
        jpackageHome = "/usr/lib/jvm/jdk16"
        outputDir = "my-packaging"
        // imageOutputDir = file("$buildDir/my-packaging-image")
        // installerOutputDir = file("$buildDir/my-packaging-installer")
        imageName = "MyApp"
        imageOptions = listOf("--win-console")
        skipInstaller = false
        installerName = "MyApp"
        installerType = "msi"
        installerOptions = listOf("--win-menu", "--win-shortcut")
    }
    ...
}

Examples

The following projects illustrate how to use this plugin to create custom runtime images: