-
-
Notifications
You must be signed in to change notification settings - Fork 571
Bundling Java apps
To be written. Thanks @overheadhunter for the hint.
For self-contained applications, the Java Packager for JDK 9 packages applications with a JDK 9 runtime image generated by the jlink tool. To package a JDK 8 or JDK 7 JRE with your application, use the JDK 8 Java Packager.
Source: https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
Here is what I did for my project. It is not a perfect solution, but it works for me. Please feel free to improve and/or generalize this information, but it should be a good starting point.
I couldn't find a standalone version of the OpenJRE to download, but I used the Open JRE from Ubuntu and it works on every linux distribution I tested, following the testing guidelines given by appimage (old debian, etc.)
The command cp -rL /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre linux-jre
did the trick for me. -r
is for recursive. -L
replaces sym links with the link targets.
It would almost certainly be better to unpack the java runtime .deb file rather than using the method I outlined above, but I spent a little time trying to get that to work and couldn't. I think you may need to merge the contents of more than one deb together to get the full jre. If you get that working, please let me know or edit it in.
If you are going to release a 64 bit and 32 bit version, you need to do this on a 32 bit linux distribution as well.
Anyway, save that JRE for later.
Here's mine. It's simple and direct. We're going to launch this using appimage's built in launcher script, so it doesn't need to do anything fancy:
#!/bin/bash
DIR="$(dirname "$(readlink -f "$0")")"
cd $DIR
./jre/bin/java -jar <any jvm arguments you need> target.jar "$@" &
disown
exit 0
The first thing it does it change the working directory to the directory of this script. My program expects that and you can't really change the working directory in java, so I do it here.
Then I launch java, calling my program, passing the commandline arguments with $@
. Notice the &
at the end of the command. This means "run in the background".
Then we disown
the java process, meaning we can exit this script without terminating the jvm and our program.
Then we exit. Done!
Here's mine:
[Desktop Entry]
Name=Hypnos
Exec=hypnos %F
Icon=hypnos
Type=Application
Categories=Audio;AudioVideo;
Comment=Music Player and Library
MimeType=inode/directory;audio/flac;
Name[en]=Hypnos
Terminal=false
StartupNotify=true
NoDisplay=false
Save this as [program name].desktop and keep it for the next step.
Here's how mine is structured. I'm using my actual file names because it's easier and more clear than typing [program name]
all the time.
Hypnos.AppDir/
usr/
bin/
jre/ <-- our jre folder from step 1
hypnos <-- our launch script from step 2
hypnos.jar <-- your program's jar file
<Whatever other resources your program uses>
<this is your program's main directory>
hypnos.desktop
Hypnos.png
AppRun <-- provided by app image
Use the appimage tool to build your app image with a command that looks like this:
./appimagetool-x86_64.AppImage Hypnos.AppDir Hypnos.AppImage
If everything works, voila! You have an appimage with an embedded JRE. If you're having trouble, join the IRC channel and look for me (JoshuaD) and I'll see what I can do to help you.
If you use the above method, your program will run, but in ps
and in the taskmananger, it will be named java
rather than hypnos
, which is annoying.
To fix this, rename your embedded jre/bin/java
to jre/bin/hypnos
. Yes it's a hack, but no one else is using this JRE and it seems to work perfectly for me. You'll have to update your launch script (step 2) as well.
I looked for a long time for much more clever solutions and had nothing work. I have been very satisfied with this simple solution.
You need to get a 32bit JRE, and then use the appimagetool option --runtime-file runtime-i686
. You can get the updated runtime file from the appimage project.
Here is very simplified version of my ant build file, which may be helpful to you:
<project name="Hypnos Music Player" default="compile" basedir=".">
<property name="src" location="src"/>
<property name="build" location="bin"/>
<property name="stage" location="stage" />
<property name="dist" location="distribution/" />
<property name="temp" location="temp" />
<property name="packaging" location="packaging/" />
<property name="jarFile" location="${stage}/hypnos.jar" />
<property name="appImageTool" location="${packaging}/appimagetool-x86_64.AppImage" />
<buildnumber file="${packaging}/build.num"/>
<path id="class.path">
<fileset dir="${stage}/lib">
<include name="**/*.jar" />
</fileset>
<pathelement location="${jarFile}" />
</path>
<target name="init">
<tstamp />
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init" description="compile the source">
<javac fork="yes" target="1.8" source ="1.8" includeantruntime="false" srcdir="." destdir="${build}">
<classpath refid="class.path" />
</javac>
</target>
<target name="jar" depends="compile" description="Create a jar.">
<jar destfile="${jarFile}" basedir="${build}">
<manifest>
<attribute name="Main-Class" value="net.joshuad.hypnos.Hypnos" />
<attribute name="Class-Path" value="... items removed for brevity ... " />
</manifest>
</jar>
</target>
<target name="dist-nix-64bit" depends="jar" description="Make an AppImage for 64 bit Linux">
<sequential>
<delete dir="${temp}" />
<mkdir dir="${temp}/" />
<copy todir="${temp}/Hypnos.AppDir" >
<fileset dir="packaging/Hypnos.AppDir" />
</copy>
<copy todir="${temp}/Hypnos.AppDir/usr/bin" >
<fileset dir="stage" >
<exclude name="**/bin/**" />
</fileset>
</copy>
<copy todir="${temp}/Hypnos.AppDir/usr/bin/jre" >
<fileset dir="${packaging}/jres/linux-64bit" />
</copy>
<exec executable="${appImageTool}">
<arg value="${temp}/Hypnos.AppDir" />
<arg value="${dist}/Hypnos-nix-64bit.AppImage" />
</exec>
<delete dir="${temp}" />
</sequential>
</target>
<target name="dist-nix-32bit" depends="jar" description="Make an AppImage for 32 bit Linux">
<sequential>
<delete dir="${temp}" />
<mkdir dir="${temp}/" />
<copy todir="${temp}/Hypnos.AppDir" >
<fileset dir="packaging/Hypnos.AppDir" />
</copy>
<copy todir="${temp}/Hypnos.AppDir/usr/bin" >
<fileset dir="stage" >
<exclude name="**/bin/**" />
</fileset>
</copy>
<copy todir="${temp}/Hypnos.AppDir/usr/bin/jre" >
<fileset dir="${packaging}/jres/linux-32bit" />
</copy>
<exec executable="${appImageTool}">
<arg value="${temp}/Hypnos.AppDir" />
<arg value="--runtime-file" />
<arg value="${packaging}/runtime-i686" />
<arg value="${dist}/Hypnos-nix-32bit.AppImage" />
</exec>
<delete dir="${temp}" />
</sequential>
</target>
</project>