JDKPackager Plugin

This plugin builds on Oracle’s javapackager tool to generate native application launchers and installers for macOS, Windows, and Linux. This plugin takes the settings and staged output from Java Application Archetype and passes them through javapackager to create native formats per Oracle’s provided features.

The actual mechanism used by this plugin is the support provided by the lib/ant-javafx.jar Ant task library, which provides more capabilities than the javapackager command line version, but the idea is the same.

This plugin’s most relevant addition to the core sbt-native-packager capabilities is the generation of macOS App bundles and associated .dmg and .pkg package formats. With this plugin complete drag-and-drop installable application bundles are possible, including the embedding of the JRE. It can also generate Windows .exe and .msi installers provided the requisite tools are available on the Windows build platform (see below). While Linux package formats are also possible via this plugin, it is likely the native sbt-native-packager support for .deb and .rpm formats will provide more configurability.

Note

The JDKPackagerPlugin depends on the Universal Plugin, Java Application Archetype and Launcher Plugin

Requirements

The ant-javafx.jar library comes with Oracle JDK 8, found in the lib directory along with tools.jar and friends. If sbt is running under the JVM in Oracle JDK 8, then the plugin should be able to find the path to ant-javafx.jar. If sbt is running under a different JVM, then the path to the tool will have to be specified via the JdkPackager / antPackagerTasks setting.

This plugin must be run on the platform of the target installer. The Oracle tooling does not provide a means of creating, say, Windows installers on MacOS, or MacOS on Linux, etc.

To use create Windows launchers & installers, the either the WIX Toolset (msi) or Inno Setup (exe) is required:

For further details on the capabilities of javapackager, see the Windows and Unix references. (Note: only a few of the possible settings are exposed through this plugin. Please submit a Github issue or pull request if something specific is desired.)

Enabling

The plugin is enabled via the AutoPlugins facility:

enablePlugins(JDKPackagerPlugin)

Build

To use, first get your application working per Java Application Archetype instructions (including the mainClass setting). Once that is working, run

sbt JdkPackager/packageBin

By default, the plugin makes the installer type that is native to the current build platform. The installer is put in the directory target/jdkpackager/bundles. The key jdkPackageType can be used to modify this behavior. Run help jdkPackageType in sbt for details. The most popular setting is likely to be jdkAppIcon.

Settings

For the latest documentation reference the key descriptions in sbt.

jdkPackagerBasename

Filename without the extension for the generated installer package.

jdkPackagerType

Value passed as the native attribute to fx:deploy task.

Per javapackager documentation, this may be one of the following:

  • all: Runs all of the installers for the platform on which it is running, and creates a disk image for the application.

  • installer: Runs all of the installers for the platform on which it is running.

  • image: Creates a disk image for the application. On macOS, the image is the .app file. On Linux, the image is the directory that gets installed.

  • dmg: Generates a DMG file for macOS.

  • pkg: Generates a .pkg package for macOS.

  • mac.appStore: Generates a package for the Mac App Store.

  • rpm: Generates an RPM package for Linux.

  • deb: Generates a Debian package for Linux.

  • exe: Generates a Windows .exe package.

  • msi: Generates a Windows Installer package.

Note

Because only a subset of the possible settings are exposed through the plugin, updates are likely required to fully make use of all formats. dmg is currently the most tested type.

jdkAppIcon

Path to platform-specific application icon:

  • icns: MacOS

  • ico: Windows

  • png: Linux

Defaults to a generically bland Java icon. Oracle javafx ant task reference

jdkPackagerToolkit

GUI toolkit used in app. Either JavaFXToolkit (default) or SwingToolkit

jdkPackagerJVMArgs

Sequence of arguments to pass to the JVM.

Default: Seq("-Xmx768m").

Oracle JVM argument docs

jdkPackagerAppArgs

List of command line arguments to pass to the application on launch.

Default: Seq.empty

Oracle arguments docs

jdkPackagerProperties

Map of System properties to define in application.

Default: Map.empty

Oracle properties docs

jdkPackagerAssociations

Set of application file associations to register for the application.

Example: jdkPackagerAssociations := Seq(FileAssociation("foo", "application/x-foo", Foo Data File", iconPath))

Default: Seq.empty

Note: Requires JDK >= 8 build 40.

Oracle associations docs

Example

To take it for a test spin, run sbt JdkPackager/packageBin in the test-project-jdkpackager directory of the sbt-native-packager source. Then look in the target/jdkpackager/bundles directory for the result (specific name depends on platform built).

Here’s what the build file looks like:

name := "JDKPackagerPlugin Example"

version := "0.1.0"

organization := "com.foo.bar"

libraryDependencies ++= Seq(
    "com.typesafe" % "config" % "1.2.1"
)

Compile / mainClass := Some("ExampleApp")

enablePlugins(JDKPackagerPlugin)

maintainer := "Previously Owned Cats, Inc."

packageSummary := "JDKPackagerPlugin example package thingy"

packageDescription := "A test package using Oracle's JDK bundled javapackager tool."

lazy val iconGlob = sys.props("os.name").toLowerCase match {
  case os if os.contains("mac")  "*.icns"
  case os if os.contains("win")  "*.ico"
  case _  "*.png"
}

jdkAppIcon :=  (sourceDirectory.value ** iconGlob).getPaths.headOption.map(file)

jdkPackagerType := "installer"

jdkPackagerJVMArgs := Seq("-Xmx1g")

jdkPackagerProperties := Map("app.name" -> name.value, "app.version" -> version.value)

jdkPackagerAppArgs := Seq(maintainer.value, packageSummary.value, packageDescription.value)

jdkPackagerAssociations := Seq(
    FileAssociation("foobar", "application/foobar", "Foobar file type"),
    FileAssociation("barbaz", "application/barbaz", "Barbaz file type", jdkAppIcon.value)
)

// Example of specifying a fallback location of `ant-javafx.jar` if plugin can't find it.
(JDKPackager / antPackagerTasks) := (JDKPackager / antPackagerTasks).value orElse {
  for {
    f <- Some(file("/usr/lib/jvm/java-8-oracle/lib/ant-javafx.jar")) if f.exists()
  } yield f
}

Debugging

If you are having trouble figuring out how certain features affect the generated package, you can find the Ant-based build definition file in target/jdkpackager/build.xml. You should be able to run Ant directly in that file assuming JdkPackager / packageBin has been run at least once.