Bundle JRE with JME app using Gradle?

Hi

Curious to know what tool or Gradle plugin folks use for bundling a minimum JRE with their app. Is there an official Gradle plugin for this?

Thanks

5 Likes

I donā€™t know the answer to your question but I look forward to seeing what other folks say. I will also need to know this soon.

1 Like

Because I upload to steam I donā€™t need an installer (or even a zip) so what I do is I have jres for windows and linux committed to git (I hate this, I wish there was a maven dependency) and then just use gradle to include that in the target directory

build.gradle

task addJreToDistributable(type: Copy) {
    from zipTree("resources/desktop-deployment/OpenJDK11U-jre_x64_windows_hotspot_11.0.4_11.zip")
    destinationDir = file("$buildDir/windowsDistribution/oneMillionWorlds")
}

createWindowsDist.dependsOn addJreToDistributable

Then my steam upload uploads directly from the target directory (using steam pipe command line utility)

Steam SDK downloaded from: Steamworks (requires login)

Uploading to steam documentation: Uploading to Steam (Steamworks Documentation)

There is also launch4j, this doesnā€™t bundle a jre (or at least, doesnā€™t help you bundling one; if you yourself bundle one and tell launch4j about it it will use it, but thatā€™s a bit pointless, might as well use a bat file for that) but will prompt the user to download one from the internet if they donā€™t have one already. I ultimately decided I didnā€™t like that but it may work for other people

3 Likes

You could create a separate project that builds your custom JRE. (Only your selected GC, skip out support for stuff that you donā€™t need, ie. RMI, etc.) The Gradle Maven plugin includes tasks that allow generating a Maven artifact that can be installed to your local repository, and then any/all of your JME builds can pull in the artifact. Configuring for specific platforms should be doable.

If I ever create a private maven repository thatā€™s what Iā€™ll do. (MavenLocal isnt really good enough because I have build servers and want things to work consistently.)

(I could of course just run jlink as part of the build, but that just pushes the problem one step along, Iā€™d still need source JDKs then, again which I donā€™t think are available as maven resources)

Part of me wants to put up complete JREs for popular operating systems onto maven central, but the fact no one else has done that makes me think thatā€™s not ok. That is what we have done in our private maven repo at the company I work at

Okay. Would it be possible to pre-populate a MavanLocal on the server image? Iā€™d expect the JRE to change much less often than the rest of the code.

I suspect that itā€™s more of a practicality issue. Once youā€™re past the point where ā€œpackage the latest Temurin buildā€ is the appropriate response, you really should be looking at a fully custom build. Building JDK-17.0.1-ga from source gives:

  • ~ 30 JVM features
  • Times number of supported OS/Arch combos
  • Plus the various options that can be passed to the native build toolchain

= Combinatorial explosion of artifacts.

Well thatā€™s very very interesting. The fact you can pull down adoptium JREs from their API is news to me (and very very welcome news). See Eclipse Temurin JREs are back! | Adoptium Blog for anyone else who is interested.

It feels like with that it becomes possible for our gradle templates to include bundled JREs; thereby matching a nice feature the JMonkey SDK has.

1 Like

Currently I use

I tried some other solutions but I think this one is currently the best

6 Likes

I do it with The Badass Runtime Plugin here. All I have to do is to run the runtime task and voilĆ , a build/image directory is created containing both my project and a JRE. It also creates a starter shell script/batch file to run the application using the bundled JRE.

4 Likes

Thanks, guys.

@Pesegato is it possible to bundle it for Windows from Linux OS with JavaPackager?

A question from Mac users!

Do I need to do codesign and notarize my app for running it on Mac?

There will be no installer. It is going to be a bash start script to execute the runnable jar with an embedded JRE. Do I still need to codesign?

@fba is there a proper way to specify a directory to copy all JRE stuff inside it in the Badass Runtime Plugin? so that it does not mix JRE libs with my app libs.

I use jlink directly (I believe the Badass Runtime Plugin is also using jlink internally) from a Gradle task, invoking it the same way it would be used from a CLI. It gets bundled in a runtime/ directory in my distributable package (the distributable is built via running jpackage the same way, using the output from jlink as its runtime image) along with a lib/ directory (where all my JARs + dependencies go), and a launcher script that invokes the bundled JRE.

Iā€™ve found this to be a very simple and robust way to bundle the JRE, and itā€™s easy to control pretty much anything you want to.

2 Likes

By default the Badass Runtime Plugin will place the JRE under build/jre, your project files under build/lib and the start up scripts in build/scripts. The build/image directory is simply a merge of those three directories.

Thatā€™s correct, the plugin uses jlink and jpackage under the hood.

3 Likes

Yeah, here I want to have a bit of control over it but I could not find a proper way to set it via the plugin.

I ended up adding this, so when it merges them, it copies JRE stuff into a subdirectory called ā€œjreā€.

tasks.runtime.doLast {
    if(runtime.targetPlatforms) {
        runtime.getTargetPlatforms().get().values().forEach { platform ->
            File jreDir = new File(runtime.jreDir.get().asFile, "$project.name-$platform.name")
            File imageDir = new File(runtime.imageDir.get().asFile, "$project.name-$platform.name")
            createRuntime(jreDir, imageDir)
        }
    } else {
        createRuntime(runtime.jreDir.get().asFile, runtime.imageDir.get().asFile)
    }
}

void createRuntime(File jreDir, File imageDir) {
    project.delete(imageDir)
    copyJre(jreDir, imageDir)
    copyAppTo(imageDir)
}

void copyJre(File jreDir, File imageDir) {
    project.copy {
        from jreDir
        into new File(imageDir, "jre")
    }
}

void copyAppTo(File imageDir) {
    project.copy {
        from("$buildDir/install/$project.name")
        into imageDir
    }
}

Also, I needed to update the start scripts template as well to support this change.

Edit:

See

2 Likes

Oh, and I should add that to build for different platforms I just run 3 builds (Windows, Mac, Linux) on GitHub Actions. Thatā€™s necessary if youā€™re using jpackage since it has to run on the platform itā€™s packaging for, but if you just want to link a runtime and, say, distribute it in a zip you could just use jlink in a GitHub Actions build and only run it when you need to build a new runtime. As long as you donā€™t use too many build minutes, your project does not need to be open source to build on GitHub for free.

3 Likes

Didnā€™t try but unlikely. I think it bundles with a locally installed jreā€¦

Pretty cool! Now I ask for an open source example :wink:

I have a project where I build on a linux server for windows with the maven-jlink-plugin.
It supports a ā€˜sourceJdkModulesā€™ parameter that points to the jdk you want to bundle, so if you add a windows jdk there, the bundled project will run on windows.
See https://github.com/etherblood/etherworld/blob/master/jlink/pom.xml#L32 for an example.

3 Likes

My installer is Windows only (for now). I use a combination of

  • maven-shade-plugin (to bundle everything into a single executable jar)
  • launch4j (to turn that jar into a single .exe file)
  • Inno Setup (to create a windows installer).

I include my own JRE and it gets zipped up in the installer.

3 Likes