Spring Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.3 KiB

/**
* Apply the JVM Toolchain conventions
* See https://docs.gradle.org/current/userguide/toolchains.html
*
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
* when available.
2 years ago
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=20"} will use:
* <ul>
* <li>a JDK17 toolchain for compiling the main SourceSet
2 years ago
* <li>a JDK20 toolchain for compiling and running the test SourceSet
* </ul>
*
* By default, the build will fall back to using the current JDK and 17 language level for all sourceSets.
*
* Gradle will automatically detect JDK distributions in well-known locations.
* The following command will list the detected JDKs on the host.
* {@code
* $ ./gradlew -q javaToolchains
* }
*
* We can also configure ENV variables and let Gradle know about them:
* {@code
* $ echo JDK17
* /opt/openjdk/java17
2 years ago
* $ echo JDK20
* /opt/openjdk/java20
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 check
* }
*
* @author Brian Clozel
* @author Sam Brannen
*/
def mainToolchainConfigured() {
return project.hasProperty('mainToolchain') && project.mainToolchain
}
def testToolchainConfigured() {
return project.hasProperty('testToolchain') && project.testToolchain
}
def mainToolchainLanguageVersion() {
if (mainToolchainConfigured()) {
return JavaLanguageVersion.of(project.mainToolchain.toString())
}
return JavaLanguageVersion.of(17)
}
def testToolchainLanguageVersion() {
if (testToolchainConfigured()) {
return JavaLanguageVersion.of(project.testToolchain.toString())
}
return mainToolchainLanguageVersion()
}
plugins.withType(JavaPlugin) {
// Configure the Java Toolchain if the 'mainToolchain' is configured
if (mainToolchainConfigured()) {
java {
toolchain {
languageVersion = mainToolchainLanguageVersion()
}
}
}
else {
// Fallback to JDK17
java {
sourceCompatibility = JavaVersion.VERSION_17
}
}
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
}
tasks.withType(Test).configureEach{
javaLauncher = javaToolchains.launcherFor {
languageVersion = testLanguageVersion
}
Allow JDK 20 builds to pass by using legacy locale data After setting up a JDK 20 CI build pipeline, numerous tests involving date/time parsing and formatting began to fail [0]. (The failing JPA tests are not specific to JDK 20.) For example, we encounter visually confusing assertion failures such as the following. org.opentest4j.AssertionFailedError: expected: "12:00 PM" but was: "12:00 PM" The expected string contains a normal space (which has always been the case prior to JDK 20); whereas, the actual string now contains a narrow non-breaking space. The cause of this is mentioned in the JDK 20 Release Notes [1] as "NBSP prefixed to a, instead of a normal space". Note, however, that the links for the first two bullet points in that section are mixed up. "NBSP prefixed to a, instead of a normal space" should point to [2]. Furthermore, the new whitespace character is not a non-breaking space (NBSP) but rather a narrow non-breaking space (NNBSP). In addition, the second bullet point should technically read "NNBSP prefixed to `a`, instead of a normal space" -- even though `a` provides limited value to most readers. The downside for the Java community is that this constitutes a breaking change for parsing and formatting date/time values that include "AM" and "PM" units (any may potentially apply to other date/time parsing/formatting scenarios). In Spring Framework's test suite we have witnessed this in conjunction with Spring's @DateTimeFormat and DateTimeFormatterFactory infrastructure as well as with Google's Gson-to-JSON support. A colleague who works at Oracle graciously informed me that one can use "legacy locale data" by supplying `-Djava.locale.providers=COMPAT` as a JVM argument, noting however that this option limits some newer functionalities (but without enumerating which new functionalities one might be missing when using this option). In any case, this commit adds that JVM argument to our Gradle toolchain builds so that our test suite passes on JDK 20, and we will continue to investigate further options for our builds and for our users. Note, however, that one must manually configure the `-Djava.locale.providers=COMPAT` JVM argument when running affected tests within an IDE. See gh-30185 [0] https://ge.spring.io/s/kmiq2bz2afafs/tests/overview?outcome=failed [1] https://jdk.java.net/20/release-notes#JDK-8284840 [2] https://unicode-org.atlassian.net/browse/CLDR-14032
2 years ago
jvmArgs += ['-Djava.locale.providers=COMPAT']
}
}
}
plugins.withType(GroovyPlugin) {
// Fallback to JDK17
if (!mainToolchainConfigured()) {
compileGroovy {
sourceCompatibility = JavaVersion.VERSION_17
}
}
}
pluginManager.withPlugin("kotlin") {
// Fallback to JDK17
compileKotlin {
kotlinOptions {
jvmTarget = '17'
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '17'
}
}
}
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
pluginManager.withPlugin("me.champeau.jmh") {
if (mainToolchainConfigured() || testToolchainConfigured()) {
tasks.matching { it.name.contains('jmh') && it.hasProperty('javaLauncher') }.configureEach {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(testToolchainLanguageVersion())
})
}
tasks.withType(JavaCompile).matching { it.name.contains("Jmh") }.configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testToolchainLanguageVersion()
}
}
}
}
// Store resolved Toolchain JVM information as custom values in the build scan.
rootProject.ext {
resolvedMainToolchain = false
resolvedTestToolchain = false
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (mainToolchainConfigured() && !resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) {
def metadata = task.javaCompiler.get().metadata
task.project.buildScan.value('Main toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
resolvedMainToolchain = true
}
if (testToolchainConfigured() && !resolvedTestToolchain && task instanceof Test && task.javaLauncher.isPresent()) {
def metadata = task.javaLauncher.get().metadata
task.project.buildScan.value('Test toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
resolvedTestToolchain = true
}
}