1

Gradle 6.7 introduced Java toolchains.

In the documentation, they state that Gradle chooses a JRE/JDK matching the requirements of the build ... By default, Gradle prefers installed JDKs over JREs... (from docs.gradle.org: Toolchains for JVM projects).

Thus, the JDK is chosen if we have both, JRE and JDK, installed.

Problem:

Imagine that the user only has a JRE installed.

Yet, we want to run our application via Gradle (JavaExec task) using a Java toolchain, but have to ensure that a JDK is used for running because this application relies on tools.jar, which is not part of a JRE.

Question:

Is it possible to force Gradle to use a JDK for all tasks (including running / launching), not just for compiling, when using a Java toolchain? (see following minimal example with comment)

// This build.gradle should ensure that the application is run using a JDK of version 9 
plugins {
    id 'application'
}
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(9)
        // QUESTION: How to force JDK here? <------
    }
}
// for JavaExec task runJar
tasks.withType(JavaExec).configureEach {
    javaLauncher = javaToolchains.launcherFor(java.toolchain)
}
task runJar(type: JavaExec) {
    classpath = files(jar.archiveFile)
}
...
Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
  • Does this answer your question? [How do I tell Gradle to use specific JDK version?](https://stackoverflow.com/questions/18487406/how-do-i-tell-gradle-to-use-specific-jdk-version) – Martin Zeitler Jul 22 '22 at 10:07
  • @MartinZeitler: No, this does not answer my question. I do not care which Java version is used to run the _Gradle daemon_, neither do I want to manually set the JDK path to a certain location. I want to use Java toolchains for building AND running, and in both cases want to use a JDK, never a JRE. But thanks for the reference to the other question! – Markus Weninger Jul 22 '22 at 10:37
  • The JDK contains the JRE ...and Gradle will link against `rt.jar`, whether you like it or not. I'm pretty certain about it, because my Gradle plugin javadocs wouldn't build without it on classpath. – Martin Zeitler Jul 22 '22 at 17:53

4 Answers4

2

For those of us without internet on build machines per business requirement, sigh, https://docs.gradle.org/current/userguide/toolchains.html describes Selecting toolchains by vendor:

java {
    toolchain {
...
        vendor = JvmVendorSpec.ADOPTIUM // or vendor = JvmVendorSpec.matching("Private Build") in my case. find the Vendor = value you need with ./gradlew -q javaToolchains
    }
}

or even complete Custom toolchain locations:

org.gradle.java.installations.fromEnv=JDK8,JRE17 //from env vars org.gradle.java.installations.paths=/custom/path/jdk1.8,/shared/jre11

Nahshon paz
  • 4,005
  • 2
  • 21
  • 32
0

What is the tool you are using? If it is IntelliJ, then

A feature in IntelliJ that where it would look up if java tool chain is configured and if it is, make it impossible for me to have mismatching configuration within:

Run configurations Project Structure > Project SDK Project Structure > Project Language Level

enter image description here

-1

One always has to add rt.jar first:

task runJar(type: JavaExec) {
    classpath = files(new File(System.getProperty('java.home') + File.separator + 'lib' + File.separator + 'rt.jar'))
    classpath += files(jar.archiveFile)
    ...
}
runJar.onlyIf {
    new File(System.getProperty('java.home') + File.separator + 'lib' + File.separator + 'rt.jar')).exists()
}

This probably would still require a mainClassName inside jar.archiveFile. With JavaExec one can set up whatever environment variable ...incl. JAVA_HOME, so this could be alternated per task. But one can as well use an Exec task on then directly run java -jar some.jar.
I wouldn't even be too certain, that these "runtime only" JRE distributions still exist?
All I know is that there is no tools.jar

Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • "One always has to add rt.jar first" ... That is not true, why should I have to explicitly add rt.jar (which should be on the bootstrap path anyways? The `runJar` task stated in my question runs fine without a problem. And yes, there are "runtime-only" JREs, and this I need to ensure that the Java toolchain choses (and if necessary downloads) a JDK because we rely on JDI located in tools.jar. Sadly your answer does not include anything related to Java toolchain. (Side note: You are right that we have our jar configured to have a main class in its Manifest) – Markus Weninger Jul 24 '22 at 15:27
  • 1
    `java.toolchain` will download a JDK, when none is available; see `~/.gradle/jdks`. And I still haven't found any separate JRE package in my package manager... Oracle/Sun Java were distributed alike this. So it should be generally possible to globally force the use of `~/.gradle/jdks/xyz` (which is initially not there), but one can write `gradle.properties`, to pick up on second run; there's also `org.gradle.java.installations.auto-detect=false`. One even can specify it per task: https://docs.gradle.org/current/userguide/toolchains.html#specify_custom_toolchains_for_individual_tasks – Martin Zeitler Jul 26 '22 at 07:59
  • Plugin code provides some more flexibility (and Java might be easier to write than Groovy): https://docs.gradle.org/current/userguide/toolchains.html#sec:plugins – Martin Zeitler Jul 26 '22 at 08:04
  • And in my example, the `classpath` is only being configured - and not given... which means I can link it to whatever version `rt.jar` I want to. Without the runtime `rt.jar`, nothing will link (despite this is rather related to Javadoc tasks). – Martin Zeitler Jul 26 '22 at 08:09
  • eg. `org.gradle.java.installations.fromEnv=JDK8,JDK11` to only accept JDK installations. – Martin Zeitler Jul 26 '22 at 08:19
  • This just needs `JDK8` and `JDK11` set up as environmental variables, with a path there... then these would be the preferred installations, whenever referring to them with `java.toolchain`. – Martin Zeitler Jul 26 '22 at 08:28
  • "java.toolchain will download a JDK, when none is available" ... This is not true. It will _not_ download a JDK if a simple JRE is installed (e.g., only `openjdk-11-jre` is installed, but not `openjdk-11-jdk`) and no compile task is executed but a simple run task (such as `JavaExec`). Gradle would just use that openjdk-11-jre to run the program (it is important that I am talking about _running_ the already compiled program, not compiling it (which of course would need a JDK with a `javac`)). – Markus Weninger Jul 26 '22 at 09:01
  • **The solution:** I now use `org.gradle.java.installations.auto-detect=false`. This ignores any locally installed versions (both JRE or JDK), will download a *JDK* on first run, and will reuse that JDK on future runs. This ensures that a JDK is used also for `JavaExec` task. – Markus Weninger Jul 26 '22 at 09:02
  • 1
    Just found task `:javaToolchains`, which lists them all and also tells if it's JDK. – Martin Zeitler Jul 30 '22 at 11:16
-1

The solution I finally came up with:

I now use org.gradle.java.installations.auto-detect=false stored in a settings.gradle file in my project. This ignores any locally installed Java versions when using Java toolchains in Gradle.

Thus, it will download a suitable JDK on the first run, and will reuse this JDK on future runs.

Upside: We ensure that even if a local JRE exists, we download a JDK (e.g., we can be sure that tools.jar exists).

Downside: We also download a JDK if a matching one would exist on the machine.

Markus Weninger
  • 11,931
  • 7
  • 64
  • 137