I have created a Java application runtime image using jlink. I would like to be able to ship the software as an executable to different platforms. (Preferably by building on one platform, like cross-compiling.)
Ideally, it would be a single application file that users may double-click to launch, without installing anything.
How can this be accomplished?
Yes, as of Java 8 there are two ways of doing this, using the javapackager tool, or the JavaFX Ant Tasks (which aren't actually specific to JavaFX, and are provided with the Java 8 JDK).
Here's an example of packaging an application as a Jar, setting the main-class and classpath attributes, and copying all the dependencies into a folder, using Maven.
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${project.groupId}.${project.artifactId}.DemoCLI</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
And here is the Ant build file to package the standalone application (exe for Windows, .app and .dmg on OS X, .deb and .rpm on Linux).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<!--
Uses the JavaFX Ant Tasks to build native application bundles
(specific to the platform which it is built on).
See https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/javafx_ant_tasks.html
These tasks are distributed with version 8 of the Oracle JDK,
Amazon Corretto JDK, RedHat JDK, probably others, though they
do not seem to be the OpenJDK on Debian Linux
-->
<project
name="fxwebclient" default="default" basedir="."
xmlns:fx="javafx:com.sun.javafx.tools.ant">
<!-- In Java 8, java.home is typically the JRE of a JDK -->
<property name="jdk.lib.dir" value="${java.home}/../lib" />
<!-- Where to build our app bundles -->
<property name="build.dist.dir" value="${basedir}/target/dist" />
<echo>Using Java from ${java.home}</echo>
<target name="default" depends="clean">
<!-- get the ant-jfx.jar from the JDK -->
<taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
uri="javafx:com.sun.javafx.tools.ant"
classpath="${jdk.lib.dir}/ant-javafx.jar" />
<!-- Define our application entry point -->
<fx:application id="demo" name="demo"
mainClass="yourpackage.DemoCLI" />
<!-- Our jar and copied dependency jars (see pom.xml) -->
<fx:resources id="appRes">
<fx:fileset dir="${basedir}/target" includes="*.jar"/>
<fx:fileset dir="${basedir}/target/lib" includes="*.jar" />
</fx:resources>
<!-- Create app bundles [platform specific] -->
<fx:deploy nativeBundles="all"
outdir="${build.dist.dir}" outfile="DemoWebClient">
<fx:application refid="demo" />
<fx:resources refid="appRes" />
</fx:deploy>
</target>
<!-- clean up -->
<target name="clean">
<delete dir="${build.dist.dir}" includeEmptyDirs="true" />
<delete file="${basedir}/target/${ant.project.name}.jar" />
</target>
</project>
What you're describing is what's called a native executable. There are programs that will wrap your Java application into an executable file but because Java runs it's code on the Java Virtual Machine (JVM), your users will need to have it pre-installed for your program to work out of the box. You can code an installer for your application in something like C++ or C# (C# runs on the .NET Runtime which comes pre-installed on all Windows machines) that installs the JVM and possibly your application alongside it, and then compile that code to a native executable. That way, the end user doesn’t need to go looking around for Java downloads. This is the approach that Minecraft takes I believe.
Wrap your Java executable into a native executable using any of:
Launch4J (Windows)
Oracle Docs (MacOS)
Discourse (Linux)
Quarkus (native executable, no installer)
Warp Packer (self-extracting executable, no installer)
Also, have a look at SubstrateVM. This is not a true Java, however, it may help you in some cases like simple command line applications.
Substrate VM is a framework that allows ahead-of-time (AOT) compilation of Java applications under closed-world assumption into executable images or shared objects (ELF-64 or 64-bit Mach-O).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With