• Contents
  • Consuming Toolchains
  • Specify custom toolchains for individual tasks
  • Auto detection of installed toolchains
  • Auto Provisioning
  • Custom Toolchain locations
  • Precedence
  • Toolchains for plugin authors

默认来说,Gradle 使用运行Gradle 自己的Java 版本来构建JVM项目 ..
这并总是想要 …
在不同开发者的机器上使用不同的Java版本构建项目以及CI服务器上可能都会出现问题 ..
除此之外,你也许想要使用一个不支持运行Gradle 的Java 版本构建项目 …
Java Toolchain 由此而生(简单指的是工具链) 是一个工具的集合,通常来自本地JRE / JDK 安装被用来配置构建的不同方面 …
构建任务也许使用javac 作为它们的编译器,测试以及执行任务也许使用java 命令,并且javadoc 被用来生成文档 …

Consuming Toolchains(*)

一个构建能够全局定义工具链的目标是什么 - 通过指定Java 语言版本和厂商

  1. java {
  2. toolchain {
  3. languageVersion = JavaLanguageVersion.of(11)
  4. }
  5. }

执行这个构建(例如 gradle check) 现在将处理你以及其他人运行你的构建的各种事情 …

  • 配置所有的编译,测试以及javadoc 任务去使用定义的工具链(可以不同于运行Gradle自己使用的Java 版本)
  • Gradle 检测本地安装的JVMs
  • Gradle 选择一个JRE/JDK 匹配构建的需求(例如JVM 支持JAVA 11)
  • 如果匹配的JVM 不存在,它将会自动的下载匹配匹配的JDK(从AdoptOpenJdk)

    工具链在Java 插件中得到支持(并且可以在任务中定义) 对于Groovy 插件,编译支持但是可能不支持Groovydoc 生成 .. 对于Scala 插件, 编译以及Scaladoc 生成都是支持的 …

Using toolchains by specific vendors

对于你的构建需要特定的JRE/JDK,你需要想为工具链定义特定厂商
JvmVendorSpec 拥有已知知名的JVM 厂商(它们由Gradle 组织并维护) .
这是一个优势Gradle 能够处理任何不一致的JDK 版本(通过完整的编码厂商信息)

  1. java {
  2. toolchain {
  3. languageVersion = JavaLanguageVersion.of(11)
  4. vendor = JvmVendorSpec.ADOPTIUM
  5. }
  6. }

如果这个厂商你想要换成一个其他不知名的厂商,你也能够限制工具链去匹配必要的java.vendor系统属性的工具链 …
例如,匹配一个自定义厂商的工具链(它关心java.vendor属性是否包含匹配的字符串)
匹配时大小写不敏感的 ..

  1. java {
  2. toolchain {
  3. languageVersion = JavaLanguageVersion.of(11)
  4. vendor = JvmVendorSpec.matching("customString")
  5. }
  6. }

Selecting toolchains by their 虚拟机实现

如果你的项目需要一个特定的实现,你能够基于实现过滤,当前可用的实现可以是:

  • VENDOR_SPECIFIC

表现为一个占位符并且可匹配任何任何厂商的实现(例如: hotspot,zulu …)

  • J9

仅匹配使用OpenJ9 / IBM J9运行时引擎的虚拟机实现
例如,为了使用IBM Semeru JVM,通过AdoptOpenJDK发行的 .. 你能够指定过滤器如下所述:

  1. java {
  2. toolchain {
  3. languageVersion = JavaLanguageVersion.of(11)
  4. vendor = JvmVendorSpec.IBM_SEMERU
  5. implementation = JvmImplementation.J9
  6. }
  7. }

Java 主版本,厂商(如果需要) 以及实现(如果指定) 将会作为编译的输入以及测试执行的输入 ..

Specify custom toolchains for individual tasks

当你想要为某个特定的任务实现特定的工具链,你能够进行指定 ..
例如,Test任务暴露了一个JavaLauncher 属性 - 它能够定义启动测试的java 执行器是什么 …
如下所示,我们配置所有的java 编译任务使用JDK 8. 除此之外,我们引入了一个新的Test任务(它将通过使用JDK 14运行单元测试)

  1. tasks.withType(JavaCompile).configureEach {
  2. javaCompiler = javaToolchains.compilerFor {
  3. languageVersion = JavaLanguageVersion.of(8)
  4. }
  5. }
  6. task('testsOn14', type: Test) {
  7. javaLauncher = javaToolchains.launcherFor {
  8. languageVersion = JavaLanguageVersion.of(14)
  9. }
  10. }

除此之外,在application子项目中,我们增加了额外的Java 执行任务去使用JDK 14运行我们的应用 ..

  1. task('runOn14', type: JavaExec) {
  2. javaLauncher = javaToolchains.launcherFor {
  3. languageVersion = JavaLanguageVersion.of(14)
  4. }
  5. classpath = sourceSets.main.runtimeClasspath
  6. mainClass = application.mainClass
  7. }

依赖于这个任务,一个JRE 可能对于其他任务来说已经足够,然而对于(例如编译)需要一个JDK,默认来说Gradle 更加偏好安装JDK 而不是JRE(如果它们满足这些需求)
工具链工具提供器能够通过javaToolchains 扩展抓取 …
三个工具是可用的:

  • JavaCompiler (由JavaCompile任务使用)
  • JavaLauncher (由JavaExec 或者 Test 任务使用)
  • JavadocTool 由Javadoc 任务使用

    依靠Java executable 或者 Java home 与任务集成

    任何任务能够配置Java Executable 的路径或者Java home 位置,能够从工具链中受益 ..
    然而你或许不能应该直接关联一个工具链,它们有一些元数据 - 能够访问它们的全路径或者它们所属的Java 安装的路径 … ```groovy def compiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(11) }

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

  1. 类似的,通过compiler.get().executablePath 能够获取给定工具链的javac 的完整路径 ..<br />但是请注意,这可能会急切地实现(并提供)工具链。
  2. <a name="ObQUl"></a>
  3. # Auto detection of installed toolchains
  4. 默认来说,Gradle 自动检测本地的JRE/JDK 安装,因此对于用户来说不需要额外的配置 ...<br />以下列出了通常的包管理,工具以及由JVM自动检测的位置 ...<br />JVM 自动检测已知工作方式:
  5. - 操作系统特定的位置: Linux / MacOS / Windows
  6. - 包管理器: [Asdf-vm](https://asdf-vm.com/#/),[Jabba](https://github.com/shyiko/jabba),[SDKMAN](https://sdkman.io/)
  7. - [Maven Toolchain](https://maven.apache.org/guides/mini/guide-using-toolchains.html) 规范
  8. 在所有已检测的JRE /JDK 安装的集合中,根据[Toolchain Precedence Rules](https://docs.gradle.org/current/userguide/toolchains.html#sec:precedence) 抓取一个 ...
  9. <a name="Wo116"></a>
  10. ## How to disable auto-detection
  11. 为了禁用自动检测,你能够使用 org.gradle.java.installations.auto-detect Gradle 属性 ...
  12. - 要么 启动gradle 时提供参数 -Porg.gradle.java.installations.auto-detect=false
  13. - 要么 放置org.gradle.java.installations.auto-detect=false gradle.properties文件中
  14. <a name="G2zUQ"></a>
  15. # 自动提供
  16. 如果Gradle 没有发现本地可用的工具链匹配构建的需求,它将自动的尝试从AdoptOpenJDK中下载. 默认他将请求一个HotSpot JDK(匹配当前操作系统以及架构的 JDK),提供的JDK 自动安装到 [Gradle User Home directory](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home) .
  17. > Gradle 仅仅会下载GA 发行版的JDK,这不支持任何早期访问版本(实验版本)
  18. 一旦提供的JDK 安装到Gradle 用户家目录中,那么这些JDK 对于自动建额就可见了(自动选择)并且能够被后续的构建使用,就像安装到系统上的其他JDK一样,因此自动提供仅仅在自动检测发现一个合适的JDK失败之后触发,自动提供仅仅会下载新的JDK 并且 不会涉及已经安装的JDK的更新 ...<br />任何自动配置的 JDK 都不会被自动配置重新访问和自动更新,即使有一个新的微小变化的版本可用 ...<br />默认来说,公共的[AdoptOpenJDK APIs](https://api.adoptopenjdk.net/) 能够用来检测并下载匹配的JDK ..
  19. > 由于AdoptOpenJDK的改变并且它迁移了项目到 [Eclipse Adoptium](https://adoptium.net/),现在从Eclipse Adoptium 提供JDK的下载端点(或者IBM Semeru)下载,不再从 AdoptOpenJDK 构建
  20. > 使用JvmVendorSpec.ADOPTOPENJDK 并且自动解析将会导致启用的警告 ...
  21. 如果你想要使用其他服务器(它们是兼容AdoptOpenJDK v3版本的API)进行自动提供,你能够指定Gradle 去使用不同的host ...<br />Gradle 属性使用示例如下:
  22. ```groovy
  23. org.gradle.jvm.toolchain.install.adoptopenjdk.baseUri=https://api.company.net/

仅仅安全协议https是支持的,这需要确保在下载期间没有任何一个能够被干预(篡改) ..

Viewing and debugging toolchains

gradle 能够显示所有已经检测的工具链以及它们包含的元数据 …
举个例子,为了展示一个项目的所有工具链,可以运行:

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

这有助于调试哪些工具链可用于构建、如何检测它们以及 Gradle 了解这些工具链的元数据类型。

How to disable auto provisioning

为了禁用自动提供,你能够使用org.gradle.java.installations.auto-download Gradle 属性..

  • 要么 通过 -Porg.gradle.java.installations.auto-download=false
  • 要么放置org.gradle.java.installations.auto-download= false 到gradle.properties中

Custom Toolchains locations

如果自动检测本地工具链是不足够的或者被禁用,它们也有额外的方式能够让Gradle 直到安装的toolchains ..
如果你的配置已经提供了指向安装的JVM的环境变量,你能够让Gradle 知道那些环境变量能够被它关心 … 加上环境变量 JDK8 / JRE14指向一个有效的java 安装,以下的指令能够让Gradle 知道解析这些环境变量并考虑这些安装(当查询一个匹配的工具链时)

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

除此之外,你能够提供一个逗号分隔的列表(路径的列表 它们指定的使用org.gradle.java.installations.paths的属性)
例如在你的gradle.properties中可以指定Gradle 在检测 JVM时所查看的那些目录 …
Gradle 将会处理这些目录(尽可能的)作为安装,但是不会检测任何内嵌的目录 …

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

自定义工具链不是优于自动检测的.如果自动检测打开,那么自定义工具链仍然能够被指定,但是它们将会扩展所有检测的JRE/JDK 位置 .. 在这些位置之间,还是通过工具链优先级规则 选择一个 …

Precedence

Gradle 将会排序所有的JDK/JRE安装 (根据构建的匹配工具链的规范) 并且将拿取第一个
排序规则基于以下规则:

  • 运行Gradle 本身的安装作为首选(如果可以)
  • JDK 优于 JRE
  • 某些厂商优于未知名厂商
    1. ADOPTIUM
    2. ADOPTOPENJDK
    3. AMAZON
    4. APPLE
    5. AZUL
    6. BELLSOFT
    7. GRAAL_VM
    8. HEWLETT_PACKARD
    9. IBM
    10. IBM_SEMERU
    11. MICROSOFT
    12. ORACLE
    13. SAP
    14. everything else
  • 更高的主版本优先
  • 更高的微版本优先
  • 根据词典顺序排序安装路径(用于确定性地决定相同类型、来自相同供应商和具有相同版本的安装的最后手段标准)

所有的规则形成了多个级别的分类标准,在它们所展示的顺序中,让我们通过例子来说明,
例如我们只有一个条件(仅仅只想要使用JAVA 17)并且我们的机制上安装了已检测到的
Oracle JRE v17.0.0,Oracle v17.0.1,Microsoft JDK 17.0.0,Microsoft JRE17.0.1,Microsoft JDK17.0.1,让我们假设Gradle 通过使用不同的主版本进行运行,
因此它不匹配工具链的规范(所以它不符合工具链规范(这是重点,否则该安装将优先于其他所有安装,并且我们的示例将提供较少的信息,那就是我们可能直接安装JAVA17并使用)
当我们应用上述的规则,它们进行排序我们将得到以下列表:

  1. Microsoft JDK 17.0.1
  2. Microsoft JDK 17.0.0
  3. Oracle JDK v17.0.0
  4. Microsoft JRE v17.0.1
  5. Oracle JRE v17.0.1

JRE结尾(优于应用了JDK 优于JRE的规则),然后在这两个类别中,在应用优先选择该供应商而不是 Oracle 的规则之后,Microsoft 安装首先结束 ..
然后相同的Microsoft JDK版本排序根据主版本优先的结果,应用再多的规则也不会改变这个最终顺序 …(只能获取一个此列表的子集)

Toolchains for plugin authors

为插件构建工具链 …
自定义任务需要来自JDK的工具(应该暴露一个Property 将想要的工具作为一个泛型)
这个属性应该被声明一个@Nested input.
通过注册JavaToolchainService到插件或者任务中,它能够关联一个约定到这些属性中(通过包含此项目的来自java扩展的JavaToolchainSpec)
以下例子展示了如何使用默认的工具链作为约定(当允许用户独立的配置每一个任务的工具链时) ..

import javax.inject.Inject;

abstract class CustomTaskUsingToolchains extends DefaultTask {

    @Nested
    abstract Property<JavaLauncher> getLauncher()

    @Inject
    CustomTaskUsingToolchains() {
        // 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)
    }
}

tasks.register('showDefaultToolchain', CustomTaskUsingToolchains)

tasks.register('showCustomToolchain', CustomTaskUsingToolchains) {
    launcher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(16)
    }
}

当属性正确的配置为@Nested, 他将自动的跟踪Java 主版本以及厂商以及实现(作为输入) 如果厂商和实现指定 …