// Copyright 2020 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[toolchains]]
= Toolchains for JVM projects

By default, Gradle uses the same Java version for running Gradle itself and building JVM projects.

This is not always desirable.
Building projects with different Java versions on different developer machines and CI servers may lead to unexpected issues.
Additionally, you may want to build a project using a Java version that is not supported for running Gradle.

A Java Toolchain (from now on referred to simply as toolchain) is a set of tools, usually taken from a local JRE/JDK installation that are used to configure different aspects of a build.
Compile tasks may use `javac` as their compiler, test and exec tasks may use the `java` command while `javadoc` will be used to generate documentation.

[[sec:consuming]]
== Consuming Toolchains

A build can globally define what toolchain it targets by stating the Java Language version it needs and optionally the vendor:

====
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/groovy/",files="buildSrc/src/main/groovy/myproject.java-conventions.gradle[tags=toolchain]"]
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin/",files="buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts[tags=toolchain]"]
====

Executing the build (e.g. using `gradle check`) will now handle several things for you and others running your build:

1. Setup all compile, test and javadoc tasks to use the defined toolchain which may be different than the one Gradle itself uses
2. Gradle detects <<#sec:auto_detection,locally installed JVMs>>
3. Gradle chooses a JRE/JDK matching the requirements of the build (in this case a JVM supporting Java 11)
4. If no matching JVM is found, it will automatically download a matching JDK from https://adoptopenjdk.net/[AdoptOpenJDK]

[NOTE]
====
Toolchain support is available in the Java plugins and for the tasks they define.
For the Groovy plugin, compilation is supported but not yet Groovydoc generation.
For the Scala plugin, compilation and Scaladoc generation are supported.
====

[[sec:vendors]]
=== Using toolchains by specific vendors

In case your build has specific requirements from the used JRE/JDK, you may want to define the vendor for the toolchain as well.
link:{javadocPath}/org/gradle/jvm/toolchain/JvmVendorSpec.html[`JvmVendorSpec`] has a list of well-known JVM vendors recognized by Gradle.
The advantage is that Gradle can handle any inconsistencies across JDK versions in how exactly the JVM encodes the vendor information.

====
include::sample[dir="snippets/java/toolchain-filters/groovy/",files="build.gradle[tags=toolchain-known-vendor]"]
include::sample[dir="snippets/java/toolchain-filters/kotlin/",files="build.gradle.kts[tags=toolchain-known-vendor]"]
====

If the vendor you want to target is not a known vendor, you can still restrict the toolchain to those matching the `java.vendor` system property of the available toolchains.

The following snippet uses filtering to include a subset of available toolchains. This example only includes toolchains whose `java.vendor` property contains the given match string.
The matching is done in a case-insensitive manner.

====
include::sample[dir="snippets/java/toolchain-filters/groovy/",files="build.gradle[tags=toolchain-matching-vendor]"]
include::sample[dir="snippets/java/toolchain-filters/kotlin/",files="build.gradle.kts[tags=toolchain-matching-vendor]"]
====

=== Selecting toolchains by their virtual machine implementation

If your project requires a specific implementation, you can filter based on the implementation as well.
Currently available implementations to choose from are:

`VENDOR_SPECIFIC`::
Acts as a placeholder and matches any implementation from any vendor (e.g. hotspot, zulu, ...)
`J9`::
Matches only virtual machine implementations using the OpenJ9/IBM J9 runtime engine.

For example, to use an https://www.eclipse.org/openj9/[IBM Semeru] JVM, distributed via https://adoptopenjdk.net/[AdoptOpenJDK],
you can specify the filter as shown in the example below.

====
include::sample[dir="snippets/java/toolchain-filters/groovy/",files="build.gradle[tags=toolchain-matching-implementation]"]
include::sample[dir="snippets/java/toolchain-filters/kotlin/",files="build.gradle.kts[tags=toolchain-matching-implementation]"]
====

[NOTE]
====
The Java major version, the vendor (if specified) and implementation (if specified)
will be tracked as an input for compilation and test execution.
====

[[sec:configuring_toolchain_specifications]]
=== Configuring toolchain specifications

Gradle allows configuring multiple properties that affect the selection of a toolchain, such as language version or vendor.
Even though these properties can be configured independently, the configuration must follow certain rules in order to form a _valid_ specification.

A `JavaToolchainSpec` is considered _valid_ in two cases:

1. when no properties have been set, i.e. the specification is _empty_;
2. when `languageVersion` has been set, optionally followed by setting any other property.

In other words, if a vendor or an implementation are specified, they must be accompanied by the language version.
Gradle distinguishes between toolchain specifications that configure the language version and the ones that do not.
A specification without a language version, in most cases, would be treated as a one that selects the toolchain of the current build.
If the language version is not set then Gradle will ignore values provided for other properties of the specification.

Usage of _invalid_ instances of `JavaToolchainSpec` is deprecated and will be removed in the future versions of Gradle.


== Specify custom toolchains for individual tasks

In case you want to tweak which toolchain is used for a specific task, you can specify the exact tool a task is using.
For example, the `Test` task exposes a `JavaLauncher` property that defines which java executable to use for launching the tests.

In the example below, we configure all java compilation tasks to use JDK8.
Additionally, we introduce a new `Test` task that is going to run our unit tests but using a JDK 14.

====
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/groovy/",files="list/build.gradle[tags=customToolchain]"]
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin",files="list/build.gradle.kts[tags=customToolchain]"]
====

In addition, in the `application` subproject, we add another Java execution task to run our application with JDK 14.

====
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/groovy/",files="application/build.gradle[tags=customExec]"]
include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin",files="application/build.gradle.kts[tags=customExec]"]
====

Depending on the task, a JRE might be enough while for other tasks (e.g. compilation), a JDK is required.
By default, Gradle prefers installed JDKs over JREs if they can satisfy the requirements.

Toolchains tool providers can be obtained from the link:{javadocPath}/org/gradle/jvm/toolchain/JavaToolchainService.html[`javaToolchains`] extension.

Three tools are available:

* A `JavaCompiler` which is the tool used by the link:{groovyDslPath}/org.gradle.api.tasks.compile.JavaCompile.html#org.gradle.api.tasks.compile.JavaCompile:javaCompiler[JavaCompile] task
* A `JavaLauncher` which is the tool used by the link:{groovyDslPath}/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:javaLauncher[JavaExec] or link:{groovyDslPath}/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:javaLauncher[Test] tasks
* A `JavadocTool` which is the tool used by the link:{groovyDslPath}/org.gradle.api.tasks.javadoc.Javadoc.html#org.gradle.api.tasks.javadoc.Javadoc:javadocTool[Javadoc] task

=== Integration with tasks relying on a Java executable or Java home

Any tasks that can be configured with a path to a Java executable, or a Java home location, can benefit from toolchains.

While you will not be able to wire a toolchain tool directly, they all have metadata that gives access to their full path or to the path of the Java installation they belong to.

For example, you can configure the `java` executable for a task as follows:

====
include::sample[dir="snippets/java/toolchain-config-task/groovy/",files="build.gradle[tags=java-executable]"]
include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.gradle.kts[tags=java-executable]"]
====

Another example, you can configure the _Java Home_ for a task as follows:

====
include::sample[dir="snippets/java/toolchain-config-task/groovy/",files="build.gradle[tags=java-home]"]
include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.gradle.kts[tags=java-home]"]
====

Yet another example, you can configure the Java _compiler_ executable for a task as follows:

====
include::sample[dir="snippets/java/toolchain-config-task/groovy/",files="build.gradle[tags=java-compiler]"]
include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.gradle.kts[tags=java-compiler]"]
====

[WARNING]
====
The examples above use tasks with `RegularFileProperty` and `DirectoryProperty` properties which allow lazy configuration.

Doing respectively `launcher.get().executablePath`, `launcher.get().metadata.installationPath` or `compiler.get().executablePath` instead will give you the full path for the given toolchain but note that this may realize (and provision) a toolchain eagerly.
====

[[sec:auto_detection]]
== Auto detection of installed toolchains

By default, Gradle automatically detects local JRE/JDK installations so no further configuration is required by the user.
The following is a list of common package managers, tools, and locations that are supported by the JVM auto-detection.

JVM auto-detection knows how to work with:

* Operation-system specific locations: Linux, macOS, Windows
* Package Managers: https://asdf-vm.com/#/[Asdf-vm], https://github.com/shyiko/jabba[Jabba], https://sdkman.io/[SDKMAN!]
* https://maven.apache.org/guides/mini/guide-using-toolchains.html[Maven Toolchain] specifications
* https://www.jetbrains.com/help/idea/sdk.html#jdk-from-ide[IntelliJ IDEA] installations

Among the set of all detected JRE/JDK installations, one will be picked according to the <<sec:precedence,Toolchain Precedence Rules>>.

[NOTE]
====
Whether you are using toolchain auto-detection or you are configuring <<sec:custom_loc>>, installations that are
non-existing or without a `bin/java` executable will be ignored with a warning, but they won't generate an error.
====

[[sub:disable_auto_detect]]
=== How to disable auto-detection

In order to disable auto-detection, you can use the `org.gradle.java.installations.auto-detect` Gradle property:

* Either start gradle using `-Porg.gradle.java.installations.auto-detect=false`
* Or put `org.gradle.java.installations.auto-detect=false` into your `gradle.properties` file.

[[sec:provisioning]]
== Auto-Provisioning

If Gradle can't find a locally available toolchain that matches the requirements of the build, it can automatically download one.
By default, Gradle downloads an Adoptium or AdoptOpenJDK that matches the current operating system and architecture.
Gradle installs these JDKs in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home directory>>.
For further details see the <<#sub:download_repositories,Toolchain Download Repositories>> section below.

[NOTE]
====
Gradle only downloads JDK versions for GA releases.
There is no support for downloading early access versions.
====

Once installed in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home directory>>, a provisioned JDK becomes one of the JDKs visible to <<#sec:auto_detection,auto-detection>>
and can be used by any subsequent builds, just like any other JDK installed on the system.
Since auto-provisioning only kicks in when auto-detection fails to find a matching JDK, auto-provisioning can only download new JDKs and is in no way involved in updating any of the already installed ones.
None of the auto-provisioned JDKs will ever be revisited and automatically updated by auto-provisioning, even if there is a newer minor version available for them.

[[sub:adoptium_and_adopt_open_jdk]]
=== Default toolchain resolvers

Gradle uses the public https://api.adoptium.net/[Adoptium] and https://api.adoptopenjdk.net/[AdoptOpenJDK] APIs
by default to determine and download a matching JDK.
Gradle first attempts to download a JDK from Adoptium. If Adoptium does not provide a matching JDK, Gradle attempts to download from AdoptOpenJDK.

[WARNING]
====
Due to changes in AdoptOpenJDK and the migration to https://adoptium.net/[Eclipse Adoptium], the endpoint now serves JDKs from https://adoptium.net/[Eclipse Adoptium] or https://developer.ibm.com/languages/java/semeru-runtimes/[IBM Semeru] and no longer builds from AdoptOpenJDK.
Using `JvmVendorSpec.ADOPTOPENJDK` and having it resolved through auto-provisioning results in a deprecation warning.
====

In case you want to use another server that is compatible with v3 of the AdoptOpenJDK API, you can point Gradle to use a different host.
For that you use the Gradle property as in the example below:

----
org.gradle.jvm.toolchain.install.adoptopenjdk.baseUri=https://api.company.net/
----

You can also override the Adoptium API host with `org.gradle.jvm.toolchain.install.adoptium.baseUri`.

[WARNING]
====
Only secure protocols like `https` are accepted.
This is required to make sure no one can tamper with the download in flight.
====

[[sub:download_repositories]]
=== Toolchain Download Repositories

Starting in Gradle 7.6, you can configure arbitrary toolchain repositories.
For details on writing plugins for toolchain provisioning, consult the <<toolchain_plugins.adoc#toolchain_plugins,Toolchain Resolver Plugins>> page.

[IMPORTANT]
====
When you configure a toolchain repository, Gradle no longer uses the built-in default Adoptium and OpenJDK toolchain respositories.
====

One example of a toolchain resolver plugin is the https://github.com/gradle/disco-toolchains[Disco Toolchains Plugin], based on the https://github.com/foojayio/discoapi[foojay Disco API].

Beyond applying toolchain resolver plugins, the toolchain download resolvers provided by them also need to be configured.
Let's illustrate with an example.
Consider two toolchain resolver plugins applied by the build:

* One is the Disco plugin mentioned above, which downloads toolchains via the `DiscoToolchainResolver` it provides.
* The other contains a resolver named `AdoptiumResolver`, which duplicates the legacy toolchain resolver from past Gradle versions.

The following example uses these toolchain resolvers in a build via the `toolchainManagement` block in the settings file:

====
include::sample[dir="snippets/java/toolchain-management/kotlin/",files="settings.gradle.kts[tags=toolchain-management]"]
include::sample[dir="snippets/java/toolchain-management/groovy/",files="settings.gradle[tags=toolchain-management]"]
====
<1> In the `toolchainManagement` block, the `jvm` block contains configuration for Java toolchains.
<2> The `javaRepositories` block defines named Java toolchain repository configurations.
Use the `resolverClass` property to link these configurations to plugins.
<3> Toolchain declaration order matters.
Gradle downloads from the first repository that provides a match, starting with the first repository in the list.
<4> You can configure toolchain repositories with the same set of <<declaring_repositories.adoc#sec:authentication_schemes,authentication and authorization options>> used for dependency management.

[WARNING]
====
The `jvm` block in `toolchainManagement` only resolves after applying a toolchain resolver plugin.
====

[[sub:viewing_toolchains]]
=== Viewing and debugging toolchains

Gradle can display the list of all detected toolchains including their metadata.

For example, to show all toolchains of a project, run:

```
gradle -q javaToolchains
```

.Output of **`gradle -q javaToolchains`**
----
> gradle -q javaToolchains

 + Options
     | Auto-detection:     Enabled
     | Auto-download:      Enabled

 + AdoptOpenJDK 1.8.0_242
     | Location:           /Users/username/myJavaInstalls/8.0.242.hs-adpt/jre
     | Language Version:   8
     | Vendor:             AdoptOpenJDK
     | Architecture:       x86_64
     | Is JDK:             false
     | Detected by:        system property 'org.gradle.java.installations.paths'

 + Microsoft JDK 16.0.2+7
     | Location:           /Users/username/.sdkman/candidates/java/16.0.2.7.1-ms
     | Language Version:   16
     | Vendor:             Microsoft
     | Architecture:       aarch64
     | Is JDK:             true
     | Detected by:        SDKMAN!

 + OpenJDK 15-ea
     | Location:           /Users/user/customJdks/15.ea.21-open
     | Language Version:   15
     | Vendor:             AdoptOpenJDK
     | Architecture:       x86_64
     | Is JDK:             true
     | Detected by:        environment variable 'JDK16'

 + Oracle JDK 1.7.0_80
     | Location:           /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre
     | Language Version:   7
     | Vendor:             Oracle
     | Architecture:       x86_64
     | Is JDK:             false
     | Detected by:        macOS java_home
----

This can help to debug which toolchains are available to the build, how they are detected and what kind of metadata Gradle knows about those toolchains.

[[sub:disable_auto_provision]]
=== How to disable auto provisioning

In order to disable auto-provisioning, you can use the `org.gradle.java.installations.auto-download` Gradle property:

* Either start gradle using `-Porg.gradle.java.installations.auto-download=false`
* Or put `org.gradle.java.installations.auto-download=false` into a `gradle.properties` file.

[[sec:custom_loc]]
== Custom Toolchain locations

If auto-detecting local toolchains is not sufficient or disabled, there are additional ways you can let Gradle know about installed toolchains.

If your setup already provides environment variables pointing to installed JVMs, you can also let Gradle know about which environment variables to take into account.
Assuming the environment variables `JDK8` and `JRE14` point to valid java installations, the following instructs Gradle to resolve those environment variables and consider those installations when looking for a matching toolchain.

----
org.gradle.java.installations.fromEnv=JDK8,JRE14
----

Additionally, you can provide a comma-separated list of paths to specific installations using the `org.gradle.java.installations.paths` property.
For example, using the following in your `gradle.properties` will let Gradle know which directories to look at when detecting JVMs.
Gradle will treat these directories as possible installations but will not descend into any nested directories.

----
org.gradle.java.installations.paths=/custom/path/jdk1.8,/shared/jre11
----

[NOTE]
====
Gradle does not prioritize custom toolchains over <<sec:auto_detection,auto-detected>> toolchains.
If you enable auto-detection in your build, custom toolchains extend the set of toolchain locations.
Gradle picks a toolchain according to the <<sec:precedence,Toolchain Precedence Rules>>.
====

[[sec:precedence]]
== Precedence

Gradle will sort all the JDK/JRE installations matching the toolchain specification of the build and will pick the first one.
Sorting is done based on the following rules:

. the installation currently running Gradle is preferred over any other
. JDK installations are preferred over JRE ones
. certain vendors take precedence over others; their ordering (from the highest priority to lowest):
.. ADOPTIUM
.. ADOPTOPENJDK
.. AMAZON
.. APPLE
.. AZUL
.. BELLSOFT
.. GRAAL_VM
.. HEWLETT_PACKARD
.. IBM
.. MICROSOFT
.. ORACLE
.. SAP
.. everything else
. higher major versions take precedence over lower ones
. higher minor versions take precedence over lower ones
. installation paths take precedence according to their lexicographic ordering (last resort criteria for deterministically deciding
between installations of the same type, from the same vendor and with the same version)

All these rules are applied as multilevel sorting criteria, *in the order shown*.
Let's illustrate with an example.
A toolchain specification requests Java version 17. Gradle detects the following matching installations:

* Oracle JRE v17.0.0
* Oracle JDK v17.0.1
* Microsoft JDK 17.0.0
* Microsoft JRE 17.0.1
* Microsoft JDK 17.0.1

Assume that Gradle runs on a major Java version other than 17. Otherwise, that installation would have priority.

When we apply the above rules to sort this set we will end up with following ordering:

. Microsoft JDK 17.0.1
. Microsoft JDK 17.0.0
. Oracle JDK v17.0.0
. Microsoft JRE v17.0.1
. Oracle JRE v17.0.1

Gradle prefers JDKs over JREs, so the JREs come last.
Gradle prefers the Microsoft vendor over Oracle, so the Microsoft installations come first.
Gradle prefers higher version numbers, so JDK 17.0.1 comes before JDK 17.0.0.

So Gradle picks the first match in this order: Microsoft JDK 17.0.1.

[[sec:plugins]]
== Toolchains for plugin authors

Custom tasks that require a tool from the JDK should expose a `Property<T>` with the desired tool as generic type.
The property should be declared as a <<more_about_tasks.adoc#sec:task_input_nested_inputs,`@Nested` input>>.
By injecting the `JavaToolchainService` in the plugin or task, it is also possible to wire a convention in those properties by obtaining the `JavaToolchainSpec` from the `java` extension on the project.
The example below showcases how to use the default toolchain as convention while allowing users to individually configure the toolchain per task.

====
include::sample[dir="snippets/java/toolchain-task/groovy/",files="build.gradle"]
include::sample[dir="snippets/java/toolchain-task/kotlin/",files="build.gradle.kts"]
====

[NOTE]
====
With the property correctly configured as `@Nested`, it will automatically track the Java major version, the vendor (if specified) and implementation (if specified) as an input.
====
