I would like to build a .msi, .deb, and .pkg from the same source tree and on the same machine.
Distributable runtimes for Java9+ are no longer downloadable, so perfectly sane solutions like launch4j+nsis no longer work.
javapackager has been abandoned by Oracle.
OpenJDK's jpackager can't (and will never) cross compile for different build projects, and it isn't even a real product yet.
Is there a way to build installers for win/linux/macos from the same machine?
Is the promise of "compile once, run everywhere" is truly dead and buried?
I have a legacy java application that is now in limbo, since MacOS java8 doesn't support java.awt.desktop, which requires java9+
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
jlinkto build the runtime image andjpackageto build the app image for Windows, Linux, and macOS (on those respective systems) then copy those app images to macOS and build annsisinstaller (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
jpackagethat has options, classpath, etc., which you can change without a need to rebuild the runtime.greeter.jarfor this example and place in the build directory, calledtargetfor this example)jilnkto build a runtime. The following command usesjlinkfrom JDK11 and puts the result in a directory calledruntime. This example includes all modules on the module path, but you can usejdepsto 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.jpackageto build an app image suitable for packaging in an installer. This uses jpackage from JDK14 early access (the only version of the JDK that hasjpackageat the time of this writing). The command line option—win-consoleis 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..\Greeter\Greeter.exeThe resulting app image (in the
app-imagedirectory) 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/appdirectory and updateapp.classpathin theGreeter/app/Greeter.cfgfile. Again, all this can be done on any platform, no need to start up the target platform (Windows in my case).Also,
jpackageis an officially supported tool but only available in EA JDK 14 (it's Feb 2020 as I write). JDK 14 may be downloaded andjpackagecan 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
jpackagehas been marked "Closed/Delivered" suggesting the tool is mature and just waiting for JDK 14 to be released: https://openjdk.java.net/jeps/343There is an example project on GitHub that has a lot of useful command line examples on how to run
jlinkandjpackage: https://github.com/jtconnors/SocketClientFX Though this project uses outdated command options. You can runjpackage --helpto 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
-helpoption to get good reference information