Hello!
I solved an annoying problem related to creating a nice packaged .exe file for my game today, and I wanted to share that.
First up, I’m using Kotlin with Gradle and LibGDX to develop my game. A couple days ago, I tried to share the game with a friend, but because I was using Java 11 to compile it and they only had Java 8, they weren’t able to run it. Sure, they could update the JVM, but that’s annoying and I want the game to just work.
After some research, it seemed that the most modern solution to this was the jpackage
tool introduced in Java 14. I downloaded the most recent JDK, 15.0.1, and put a copy of it in my project folder so I wouldn’t have to worry about environment variables or whatever. With a little bit of Gradle Groovy scripting, I was able to create a task that packages the game with a Java runtime and gives an executable and some folders for distribution.
task jpackage(type: Exec) { //exec tasks=command line tasks
project(":client") { // go inside of the client project
doFirst() {
project.delete("${buildDir}/distribution/powerworks") // delete results of last bundling
}
workingDir project.projectDir // set cmd line working directory to the project dir
def commands = [ //new list of cmd arguments, will be separated by spaces
"${project.projectDir}/jdk-15.0.1/bin/jpackage", // first, the command itself. jpackage.exe is located in jdk/bin
'--type', 'app-image', // select app-image here to just create the runnable file, otherwise it defaults to runnable installer
'--dest', "${buildDir}/distribution", // destination for files
'--input', "${buildDir}/libs", // this should be the folder containing just the game's jar file
'--name', 'powerworks', // name of app
'--main-class', project.mainClassName, // references the project's main class, if you don't set this above somewhere just add it manually
'--main-jar', jar.archiveFile.get().asFile.getName(), //references the jar task, this can also be set manually
]
if (osName.contains('windows')) { //just platform specific icon stuff and the mac xstartonfirstthread
commands << '--icon'
commands << "${project.projectDir}/logo.ico"
} else if (osName.contains('linux')) {
commands << '--icon'
commands << "${project.projectDir}/logo.png"
} else if (osName.contains('mac')) {
commands << '--icon'
commands << "${project.projectDir}/logo.icns"
commands << '--java-options'
commands << "-XstartOnFirstThread"
}
commandLine = commands // this is what actually executes the command
}
}
Every other time I’ve tried to do this I failed miserably, but this time I succeeded. I hope this can be of some small help to someone. One final note, you can select the JVM runtime that it uses by using the --runtime-image
option, but if you don’t want to it will automatically create one for you with jlink. I tried to select it but it was causing problems so I just removed the argument entirely.
I partially wanted to make this because every other guide seemed to be out of date with the latest version of jpackage, so I promise this one works with JDK 15.0.1.