# Toolchains for JVM projects

version 6.7

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.

## Consuming Toolchains

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

buildSrc/src/main/groovy/myproject.java-conventions.gradle

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(14)
        }
    }

buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts

    java {
        toolchain {
            languageVersion.set(JavaLanguageVersion.of(14))
        }
    }

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 locally installed JVMs

3. Gradle chooses a JRE/JDK matching the requirements of the build (in this case a JVM supporting Java 14)

4. If no matching JVM is found, it will automatically download a matching JDK from AdoptOpenJDK

| Toolchain support is only available in the Java plugins and for the tasks they define. Support for Groovy and Scala plugins will be released later. The support will cover the specific compilation and documentation tasks added by these plugins. However running tests can leverage the toolchain support since the `Test` task does support toolchains.

## 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 the default toolchain to use JDK8 for compiling and testing. Additionally, we introduce a new `Test` task that is going to run our unit tests but using a JDK 14.

list/build.gradle

    tasks.withType(JavaCompile).configureEach {
        javaCompiler = javaToolchains.compilerFor {
            languageVersion = JavaLanguageVersion.of(8)
        }
    }
    test {
        javaLauncher = javaToolchains.launcherFor {
            languageVersion = JavaLanguageVersion.of(14)
        }
    }

list/build.gradle.kts

    tasks.withType<JavaCompile>().configureEach {
        javaCompiler.set(javaToolchains.compilerFor {
            languageVersion.set(JavaLanguageVersion.of(8))
        })
    }

    tasks.withType<Test>().configureEach {
        javaLauncher.set(javaToolchains.launcherFor {
            languageVersion.set(JavaLanguageVersion.of(14))
        })
    }

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.

Toolchains tool providers can be obtained from the `javaToolchains` extension.

Three tools are available:

- A `JavaCompiler` which is the tool used by the `JavaCompile` task

- A `JavaLauncher` which is the tool used by the `JavaExec` or `Test` tasks

- A `JavadocTool` which is the tool used by the `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 executable for a Kotlin compile tasks as follows:

build.gradle

    def compiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(11)
    }

    tasks.withType(KotlinJvmCompile).configureEach {
        kotlinOptions.jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
    }

build.gradle.kts

    val compiler = javaToolchains.compilerFor {
        languageVersion.set(JavaLanguageVersion.of(11))
    }

    tasks.withType<KotlinJvmCompile>().configureEach {
        kotlinOptions.jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
    }

Similarly, doing `compiler.get().executablePath` would give you the full path to `javac` for the given toolchain. Please note however that this may realize (and provision) a toolchain eagerly.

## 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 and locations that are supported by the JVM auto detection.

Operation-system specific locations:

- Linux

- MacOs

- Windows

Supported Package Managers:

- Asdf-vm

- Jabba

- SDKMAN!

### 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.

## Auto Provisioning

If Gradle can’t find a locally available toolchain which matches the requirements of the build, it will automatically try to download it from AdoptOpenJDK. By default, it will request a HotSpot JDK matching the current operating system and architecture. Provisioning JDKs are installed in the Gradle User Home directory.

| Gradle will only download JDK versions for GA releases. There is no support for downloading early access versions.

By default, the public AdoptOpenJDK APIs are used to determine and download a matching JDK. 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/

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

### 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.

## 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

## 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. 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.

build.gradle

    import javax.inject.Inject;

    class CustomTaskUsingToolchains extends DefaultTask {

        @Input
        final Property<JavaLauncher> launcher = project.objects.property(JavaLauncher.class);

        @Inject
        CustomTaskUsingToolchains(ObjectFactory factory) {
            // Access the default toolchain
            def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain

            // acquire a provider that returns the launcher for the toolchain
            JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class);
            Provider<JavaLauncher> defaultLauncher = service.launcherFor(toolchain);

            // use it as our default for the property
            launcher.convention(defaultLauncher);
        }

        @TaskAction
        def showConfiguredToolchain() {
            println launcher.get().executablePath
            println launcher.get().metadata.installationPath
        }
    }

    plugins {
        id 'java'
    }

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(8)
        }
    }

    task showDefaultToolchain(type: CustomTaskUsingToolchains)

    task showCustomToolchain(type: CustomTaskUsingToolchains) {
        launcher = javaToolchains.launcherFor {
            languageVersion = JavaLanguageVersion.of(14)
        }
    }

build.gradle.kts

    import javax.inject.Inject;

    open class CustomTaskUsingToolchains : DefaultTask {

        @get:Input
        val launcher: Property<JavaLauncher> = project.objects.property()

        @Inject
        constructor() {
            // Access the default toolchain
            val toolchain = project.extensions.getByType<JavaPluginExtension>().toolchain

            // acquire a provider that returns the launcher for the toolchain
            val service = project.extensions.getByType<JavaToolchainService>()
            val defaultLauncher = service.launcherFor(toolchain)

            // use it as our default for the property
            launcher.convention(defaultLauncher);
        }

        @TaskAction
        fun showConfiguredToolchain() {
            println(launcher.get().executablePath)
            println(launcher.get().metadata.installationPath)
        }
    }

    plugins {
        java
    }

    java {
        toolchain {
            languageVersion.set(JavaLanguageVersion.of(8))
        }
    }

    tasks.register<CustomTaskUsingToolchains>("showDefaultToolchain")

    tasks.register<CustomTaskUsingToolchains>("showCustomToolchain") {
        launcher.set(javaToolchains.launcherFor {
            languageVersion.set(JavaLanguageVersion.of(14))
        })
    }