Adds the [Java 
Toolchain](https://docs.gradle.org/current/userguide/toolchains.html#sec:using-java-toolchains)
 to replace manual executions:

| Old | New
|------|------|
| `JAVA` | 
[JavaLauncher](https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JavaLauncher.html)
 |
| `JAVAC` | 
[JavaCompiler](https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JavaCompiler.html)
 |
| `JAVADOC` | 
[JavadocTool](https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JavadocTool.html)
 |

## Instructions for reviewers

The build runs on Windows and Linux (Ubuntu), but the analysis was done on 
Windows.

### JVM detection and selection
The toolchain selects the JDKs to use based on all those that are made 
available to it. As detailed in the link above, JDKs can come from various 
sources on the user machine, including being auto-provisioned. The daemon JDK 
is always available (usually `JAVA_HOME`), and `gradle.properties` made 
`JDK_HOME` available in #2240.
Selection of the toolchain build/worker JDK from all available ones is done 
internally (as detailed in the link) based on the restrictions configured in 
the toolchain. I have restricted the major version to that of `jdkVersionInfo` 
(exact version, not minimum), which is determined by the *build file's JDK 
resolution*:

def envJavaHome = cygpath(System.getenv("JDK_HOME"))
if (envJavaHome == null || envJavaHome.equals("")) envJavaHome = 
cygpath(System.getenv("JAVA_HOME"))
def javaHome = envJavaHome == null || envJavaHome.equals("") ? 
System.getProperty("java.home") : envJavaHome

However **the selected toolchain JDK might not be the same as the resolved 
build's JDK**. The toolchain always prefers the daemon JVM to avoid forking, so 
if it meets the restrictions, it's selected. Consider the following scenarios:

| `JAVA_HOME` | `JDK_HOME` | Selection                                          
                    |
|----------------|---------------|------------------------------------------------------|
| 25                  | 26                 | `JDK_HOME` - only is passes the 
restrictions       |
| 26                  | 25                 | `JDK_HOME` - only is passes the 
restrictions       |
| 25/26 Path A | 25/26 Path B | `JAVA_HOME` - internal toolchain preference >:( 
|
| 25/26 Path A | 25/26 Path A | `JAVA_HOME`==`JDK_HOME`                         
      |

For local testing:
1. Stop the daemon with `gradlew --stop`.
2. Show the detected toolchains and detection configuration with `gradlew 
javaToolchains`. The display order is not the selection preference order 
necessarily.
3. Find which one is used by running `gradlew :base:compileJava -i --rerun` 
(fastest execution that still compiles Java) and looking for something like 
`Compiling with toolchain 'C:\Program Files\Java\jdk-26'` near the end, and 
compare with step 2.

You will also see there `Compiling with JDK Java compiler API` instead of 
`Compiling with Java command line compiler 'C:\Program 
Files\Java\jdk-26\bin\javac.exe'` - this is the toolchain replacing the manual 
executions.

If you change your env vars, while the daemon is running, stop it so that it 
picks up the changes (1) and then verify the changes (2).

I think that the 3rd scenario will be very rare in practice, and problems 
arising from it even more so.

See also the related 
[JDK-8087537](https://bugs.openjdk.org/browse/JDK-8087537), although it's very 
old, so might be a coincidence that it's still relevant.

### Forking
The build specifies `compile.options.fork = true` to force forking. You will 
see `Compilation mode: forking compiler` (also at the end). If this option is 
removed and the same JDK can be used for both the daemon and the build (rows 3 
and 4 above), you will see `Compilation mode: in-process compilation` because 
the same JVM is used. If different JDKs are required (rows 1 and 2), you will 
see `Compilation mode: default, forking compiler` because when the JVMs differ, 
the default is to fork.

It is kept to preserve the current behavior, but isn't strictly necessary. It 
does help with giving the build JVM its own memory rather than sharing it with 
the daemon.

### Memory
Using the CLI execution, Gradle had no say in memory allocation, and the 
default 1/4th of physical RAM was used (machine-dependent). When Gradle 
controls the build JVM, it defaults to 512MB (so does the daemon, but this is 
unchanged). I tested a full `sdk` build and didn't see a difference in running 
time, so it probably doesn't matter. It can be configured by adding, e.g., 
`compile.options.forkOptions.memoryMaximumSize = 2g` in the build file if you 
want to test it locally. I haven't tested if it's enough when running in the 
same daemon JVM because currently I'm forcing the forking.

### Native tasks
Native tasks such as Decora and Prism still use `executable = JAVA`, which will 
be resolved from the `JDK_HOME` path. As discussed above, the toolchain might 
be using a different JDK (of the same major version).

---------
- [x] I confirm that I make this contribution in accordance with the [OpenJDK 
Interim AI Policy](https://openjdk.org/legal/ai).

-------------

Commit messages:
 - Whitespaces
 - Remove CLI execs
 - Add the toolchain

Changes: https://git.openjdk.org/jfx/pull/2294/files
  Webrev: https://webrevs.openjdk.org/?repo=jfx&pr=2294&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8391822
  Stats: 12 lines in 2 files changed: 6 ins; 4 del; 2 mod
  Patch: https://git.openjdk.org/jfx/pull/2294.diff
  Fetch: git fetch https://git.openjdk.org/jfx.git pull/2294/head:pull/2294

PR: https://git.openjdk.org/jfx/pull/2294

Reply via email to