The answer to your question is not short. But I'll try to be brief and point to all relevant information.
The short answer is: you can do this.
The longer answer is: you still have to build a runtime for each target environment from within that target environment, but you only have to do this once. You can then save that runtime and reuse it to automatically build installers with your latest Java jars/code in a single environment. For example, use jlink
to build the runtime image and jpackage
to build the app image for Windows, Linux, and macOS (on those respective systems) then copy those app images to macOS and build an nsis
installer (or installer builder of your choice) for each platform from within macOS.
When you update your code and recompile, you can just copy the new jars into the pre-built app image. You'll have to copy in all your dependencies, too, but that would be necessary for any installer. There is a config file in the runtime built by jpackage
that has options, classpath, etc., which you can change without a need to rebuild the runtime.
- Create a runable program, something as simple as
package com.example;
public class Greeter {
public static void main(String[] args) {
System.out.println("Hi, I'm the greeter. Welcome.");
}
}
- compile the program and place in a jar (call it
greeter.jar
for this example and place in the build directory, called target
for this example)
- run
jilnk
to build a runtime. The following command uses jlink
from JDK11 and puts the result in a directory called runtime
. This example includes all modules on the module path, but you can use jdeps
to get just the modules you need. I suggest including all modules if you do not want to ever have to rebuid this runtime when your project evolves and depends on more of the Java runtime. Not to mention transitive dependencies on the JRE.
> set JLINK=C:\Program Files\Java\jdk-11.0.6\bin\jlink.exe
> "%JLINK%" --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules ALL-MODULE-PATH --output runtime
- run
jpackage
to build an app image suitable for packaging in an installer. This uses jpackage from JDK14 early access (the only version of the JDK that has jpackage
at the time of this writing). The command line option —win-console
is only for Windows and is only necessary if the program does something with stdin/stdout (the console). Our example writes to the console, so we need this. This argument will likely sometimes open a console window when running your application, so remove it if you have a pure windows based (gui) application.
> set JPKG=C:\Program Files\Java\jdk-14-ea\bin\jpackage.exe
> "%JPKG%" --type app-image -i target —win-console -n Greeter --main-class com.example.Greeter --main-jar greeter.jar --runtime-image runtime
- run the application with
.\Greeter\Greeter.exe
The resulting app image (in the app-image
directory) can be used to build an installer with your favorite install builder (I use NSIS). You can do this on any platform. Furthermore, when you update you program you only have to copy your new jars into the app image. There is no need to rebuild the app image or the runtime. This copy of the jars can take place on any platform, and there is no need for Windows to be run in order to build a new installer for a new version of your application.
If your application has jar dependencies (say from Maven central), you’ll need to copy those jars to the Greeter/app
directory and update app.classpath
in the Greeter/app/Greeter.cfg
file. Again, all this can be done on any platform, no need to start up the target platform (Windows in my case).
Also, jpackage
is an officially supported tool but only available in EA JDK 14 (it's Feb 2020 as I write). JDK 14 may be downloaded and jpackage
can be used with other versions of JDK (like JDK 11 LTS).
See https://blogs.oracle.com/jtc/a-brief-example-using-the-early-access-jpackage-utility
The JEP for jpackage
has been marked "Closed/Delivered" suggesting the tool is mature and just waiting for JDK 14 to be released: https://openjdk.java.net/jeps/343
There is an example project on GitHub that has a lot of useful command line examples on how to run jlink
and jpackage
: https://github.com/jtconnors/SocketClientFX
Though this project uses outdated command options. You can run jpackage --help
to get the new options.
Useful Links:
JDK 14 (early access until March 17th, 2020): http://jdk.java.net/14/
Explains non-modular usage of jlink: https://medium.com/azulsystems/using-jlink-to-build-java-runtimes-for-non-modular-applications-9568c5e70ef4
jlink manual: https://docs.oracle.com/javase/9/tools/jlink.htm#JSWOR-GUID-CECAC52B-CFEE-46CB-8166-F17A8E9280E9
jpackage - run with the -help
option to get good reference information