mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 16:19:20 +02:00
Upgrade JVM toolchain to 25 and Kotlin toolchain to 2.3.20 (#1516)
Motivation - Enable correct NullAway analysis - Pick up toolchain fixes and improvements Toolchains - Require JDK 25 for JVM toolchain (keep Java 17 runtime compatibility) - Require Kotlin 2.3.20 for Kotlin toolchain (keep Kotlin 2.2 runtime compatibility) - Require JDK 25 for Gradle daemon JVM (via gradle-daemon-jvm.properties) - Fix javac and kotlinc warnings from toolchain upgrades CI - Bump GitHub workflows to JDK 25 Building Kotlin - Bump Kotlin language level to 2.2 to match stdlib version - Consolidate build logic into pklKotlinBase.gradle.kts - Adopt modern Kotlin plugin syntax - Fix new kotlinc warnings - Update ktfmt to 0.62 - first version compatible with Kotlin 2.3.20 - changes formatting compared to 0.61 - Replace dependency resolution rule with BOM alignment - rule was too broad and interfered with toolchain/runtime separation Testing - Expand matrix to JDK 25 (LTS) and 26 - Ensure each matrix task can be run independently - Fix KotlinCodeGeneratorsTest and EmbeddedExecutorsTest on affected JDKs - Disable one test in CliCommandTest on affected JDKs (failure cause unknown) Compatibility fixes - Fix reflective access in DocGenerator on affected JDKs Build fixes - Fix misuse of `task.enabled` vs. `report.required` - Fix `gradlew tasks` on Windows - Downgrade Spotless to 8.3.0 to (hopefully) work around sporadic NoClassDefFoundError Result - NullAway runs correctly - Broader JDK test coverage - More reproducible and potentially faster builds
This commit is contained in:
@@ -31,7 +31,7 @@ import org.gradle.process.CommandLineArgumentProvider
|
||||
|
||||
/**
|
||||
* JVM bytecode target; this is pinned at a reasonable version, because downstream JVM projects
|
||||
* which consume Pkl will need a minimum Bytecode level at or above this one.
|
||||
* which consume Pkl will need a minimum bytecode level at or above this one.
|
||||
*
|
||||
* Kotlin and Java need matching bytecode targets, so this is expressed as a build setting and
|
||||
* constant default. To override, pass `-DpklJdkToolchain=X` to the Gradle command line, where X is
|
||||
@@ -40,10 +40,13 @@ import org.gradle.process.CommandLineArgumentProvider
|
||||
const val PKL_JVM_TARGET_DEFAULT_MAXIMUM = 17
|
||||
|
||||
/**
|
||||
* The Pkl build requires JDK 21+ to build, because JDK 17 is no longer within the default set of
|
||||
* supported JDKs for GraalVM. This is a build-time requirement, not a runtime requirement.
|
||||
* The Pkl build requires JDK 25+; otherwise, NullAway will not work correctly.
|
||||
*
|
||||
* This is a build-time requirement, not a runtime requirement. To avoid the provisioning of
|
||||
* multiple JDKs and other build issues, keep this value in sync with the JVM toolchain versions in
|
||||
* `buildSrc/build.gradle.kts` and `gradle-daemon-jvm.properties`.
|
||||
*/
|
||||
const val PKL_JDK_VERSION_MIN = 21
|
||||
const val PKL_JDK_VERSION_MIN = 25
|
||||
|
||||
/**
|
||||
* The JDK minimum is set to match the bytecode minimum, to guarantee that fat JARs work against the
|
||||
@@ -52,14 +55,15 @@ const val PKL_JDK_VERSION_MIN = 21
|
||||
const val PKL_TEST_JDK_MINIMUM = PKL_JVM_TARGET_DEFAULT_MAXIMUM
|
||||
|
||||
/**
|
||||
* Maximum JDK version which Pkl is tested with; this should be bumped when new JDK stable releases
|
||||
* are issued. At the time of this writing, JDK 23 is the latest available release.
|
||||
* Maximum JDK version which Pkl is tested with; this should be bumped when new JDK releases are
|
||||
* issued.
|
||||
*/
|
||||
const val PKL_TEST_JDK_MAXIMUM = 23
|
||||
const val PKL_TEST_JDK_MAXIMUM = 26
|
||||
|
||||
/**
|
||||
* Test the full suite of JDKs between [PKL_TEST_JDK_MINIMUM] and [PKL_TEST_JDK_MAXIMUM]; if this is
|
||||
* set to `false` (or overridden on the command line), only LTS releases are tested by default.
|
||||
* If `true`, all JDK releases between [PKL_TEST_JDK_MINIMUM] and [PKL_TEST_JDK_MAXIMUM] are tested.
|
||||
* If `false`, only LTS releases within that range are tested. To override, pass
|
||||
* `-DpklTestAllJdks=true` on the Gradle command line.
|
||||
*/
|
||||
const val PKL_TEST_ALL_JDKS = false
|
||||
|
||||
@@ -197,8 +201,8 @@ open class BuildInfo(private val project: Project) {
|
||||
}
|
||||
|
||||
val testJdkVendors: Sequence<JvmVendorSpec> by lazy {
|
||||
// By default, only OpenJDK is tested during multi-JDK testing. Flip `-DpklTestAllVendors=true`
|
||||
// to additionally test against a suite of JDK vendors, including Azul, Oracle, and GraalVM.
|
||||
// By default, only Adoptium is tested during multi-JDK testing. Flip `-DpklTestAllVendors=true`
|
||||
// to additionally test against GraalVM and Oracle.
|
||||
when (System.getProperty("pklTestAllVendors")?.toBoolean()) {
|
||||
true -> sequenceOf(JvmVendorSpec.ADOPTIUM, JvmVendorSpec.GRAAL_VM, JvmVendorSpec.ORACLE)
|
||||
else -> sequenceOf(JvmVendorSpec.ADOPTIUM)
|
||||
@@ -278,8 +282,9 @@ open class BuildInfo(private val project: Project) {
|
||||
val namer = testNamer(baseNameProvider)
|
||||
val applyConfig: MultiJdkTestConfigurator = { (version, jdk) ->
|
||||
// 1) copy configurations from the template task
|
||||
dependsOn(templateTask)
|
||||
templateTask.get().let { template ->
|
||||
// copy explicit dependencies not inferred from task inputs
|
||||
dependsOn(template.dependsOn)
|
||||
classpath = template.classpath
|
||||
testClassesDirs = template.testClassesDirs
|
||||
jvmArgs.addAll(template.jvmArgs)
|
||||
@@ -305,8 +310,8 @@ open class BuildInfo(private val project: Project) {
|
||||
// multiply out by jdk vendor
|
||||
testJdkVendors.map { vendor -> (targetVersion to vendor) }
|
||||
}
|
||||
.map { (jdkTarget, vendor) ->
|
||||
if (jdkToolchainVersion == jdkTarget)
|
||||
.mapNotNull { (jdkTarget, vendor) ->
|
||||
if (jdkToolchainVersion == jdkTarget) {
|
||||
tasks.register(namer(jdkTarget, vendor)) {
|
||||
// alias to `test`
|
||||
dependsOn(templateTask)
|
||||
@@ -314,20 +319,24 @@ open class BuildInfo(private val project: Project) {
|
||||
description =
|
||||
"Alias for regular '${baseNameProvider()}' task, on JDK ${jdkTarget.asInt()}"
|
||||
}
|
||||
else
|
||||
tasks.register(namer(jdkTarget, vendor.takeIf { isMultiVendor }), Test::class) {
|
||||
enabled = jdkTarget.isEnabled
|
||||
group = Category.VERIFICATION
|
||||
description = "Run tests against JDK ${jdkTarget.asInt()}"
|
||||
applyConfig(jdkTarget to toolchains.launcherFor { languageVersion = jdkTarget })
|
||||
// fix: on jdk17, we must force the polyglot module on to the modulepath
|
||||
if (jdkTarget.asInt() == 17)
|
||||
jvmArgumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
buildList { listOf("--add-modules=org.graalvm.polyglot") }
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Always register and enable the task so it can be run explicitly,
|
||||
// but only return it if it should be included in "check".
|
||||
val task =
|
||||
tasks.register(namer(jdkTarget, vendor.takeIf { isMultiVendor }), Test::class) {
|
||||
group = Category.VERIFICATION
|
||||
description = "Run tests against JDK ${jdkTarget.asInt()}"
|
||||
applyConfig(jdkTarget to toolchains.launcherFor { languageVersion = jdkTarget })
|
||||
// fix: on jdk17, we must force the polyglot module on to the modulepath
|
||||
if (jdkTarget.asInt() == 17)
|
||||
jvmArgumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
buildList { listOf("--add-modules=org.graalvm.polyglot") }
|
||||
}
|
||||
)
|
||||
}
|
||||
task.takeIf { jdkTarget.isEnabled }
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ private val ltsReleases =
|
||||
JavaLanguageVersion.of(11),
|
||||
JavaLanguageVersion.of(17),
|
||||
JavaLanguageVersion.of(21),
|
||||
JavaLanguageVersion.of(25),
|
||||
)
|
||||
|
||||
/** Describes an inclusive range of JVM versions, based on the [JavaLanguageVersion] type. */
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
import com.diffplug.gradle.spotless.KotlinGradleExtension
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
plugins { id("com.diffplug.spotless") }
|
||||
|
||||
@@ -42,25 +40,9 @@ configurations {
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency {
|
||||
if (requested.group == "org.jetbrains.kotlin") {
|
||||
// prevent transitive deps from bumping Koltin version
|
||||
useVersion(libs.versions.kotlin.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withType(JavaPlugin::class).configureEach {
|
||||
tasks.withType<JavaCompile>().configureEach { options.release = buildInfo.jvmTarget }
|
||||
}
|
||||
|
||||
tasks.withType<KotlinJvmCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.fromTarget(buildInfo.jvmTarget.toString())
|
||||
freeCompilerArgs.addAll("-Xjsr305=strict", "-Xjvm-default=all")
|
||||
freeCompilerArgs.add("-Xjdk-release=${buildInfo.jvmTarget}")
|
||||
}
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
javaCompiler = buildInfo.javaCompiler
|
||||
options.release = buildInfo.jvmTarget
|
||||
}
|
||||
|
||||
plugins.withType(IdeaPlugin::class).configureEach {
|
||||
@@ -97,10 +79,9 @@ plugins.withType(MavenPublishPlugin::class).configureEach {
|
||||
|
||||
// settings.gradle.kts sets `--write-locks`
|
||||
// if Gradle command line contains this task name
|
||||
val updateDependencyLocks by
|
||||
tasks.registering {
|
||||
doLast { configurations.filter { it.isCanBeResolved }.forEach { it.resolve() } }
|
||||
}
|
||||
val updateDependencyLocks by tasks.registering {
|
||||
doLast { configurations.filter { it.isCanBeResolved }.forEach { it.resolve() } }
|
||||
}
|
||||
|
||||
val allDependencies by tasks.registering(DependencyReportTask::class)
|
||||
|
||||
|
||||
@@ -58,17 +58,8 @@ spotless {
|
||||
target("src/*/java/**/*.java")
|
||||
licenseHeaderFile(rootProject.file("buildSrc/src/main/resources/license-header.star-block.txt"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
addStep(revertYearOnlyChanges)
|
||||
ktfmt(libs.versions.ktfmt.get()).googleStyle()
|
||||
target("src/*/kotlin/**/*.kt")
|
||||
licenseHeaderFile(rootProject.file("buildSrc/src/main/resources/license-header.star-block.txt"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.compileKotlin { enabled = false }
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes +=
|
||||
@@ -86,19 +77,6 @@ tasks.javadoc {
|
||||
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
|
||||
}
|
||||
|
||||
val workAroundKotlinGradlePluginBug by
|
||||
tasks.registering {
|
||||
doLast {
|
||||
// Works around this problem, which sporadically appears and disappears in different
|
||||
// subprojects:
|
||||
// A problem was found with the configuration of task ':pkl-executor:compileJava' (type
|
||||
// 'JavaCompile').
|
||||
// > Directory '[...]/pkl/pkl-executor/build/classes/kotlin/main'
|
||||
// specified for property 'compileKotlinOutputClasses' does not exist.
|
||||
layout.buildDirectory.dir("classes/kotlin/main").get().asFile.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
val truffleJavacArgs =
|
||||
listOf(
|
||||
// TODO: determine correct limits for Truffle specializations
|
||||
@@ -108,7 +86,6 @@ val truffleJavacArgs =
|
||||
|
||||
tasks.compileJava {
|
||||
javaCompiler = info.javaCompiler
|
||||
dependsOn(workAroundKotlinGradlePluginBug)
|
||||
options.compilerArgs.addAll(truffleJavacArgs + info.jpmsAddModulesFlags)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.kotlin
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
|
||||
plugins {
|
||||
java
|
||||
kotlin("jvm")
|
||||
id("com.diffplug.spotless")
|
||||
}
|
||||
|
||||
val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
|
||||
val libs = the<LibrariesForLibs>()
|
||||
|
||||
dependencies {
|
||||
// Align versions of Kotlin modules during dependency resolution.
|
||||
// Do NOT align "api", as this would affect consumers' builds.
|
||||
implementation(platform(libs.kotlinBom))
|
||||
testImplementation(platform(libs.kotlinBom))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
val kotlinTarget = KotlinVersion.fromVersion(libs.versions.kotlinTarget.get())
|
||||
languageVersion.set(kotlinTarget)
|
||||
apiVersion.set(kotlinTarget)
|
||||
jvmTarget = JvmTarget.fromTarget(buildInfo.jvmTarget.toString())
|
||||
jvmToolchain {
|
||||
languageVersion.set(buildInfo.jdkToolchainVersion)
|
||||
vendor.set(buildInfo.jdkVendor)
|
||||
}
|
||||
freeCompilerArgs.addAll(
|
||||
"-jvm-default=no-compatibility", // was: -Xjvm-default=all
|
||||
"-Xjdk-release=${buildInfo.jvmTarget}",
|
||||
"-Xjsr305=strict",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
spotless {
|
||||
val revertYearOnlyChanges = RevertYearOnlyChangesStep(rootProject.rootDir, ratchetFrom!!).create()
|
||||
|
||||
kotlin {
|
||||
addStep(revertYearOnlyChanges)
|
||||
ktfmt(libs.versions.ktfmt.get()).googleStyle()
|
||||
target("src/*/kotlin/**/*.kt")
|
||||
licenseHeaderFile(rootProject.file("buildSrc/src/main/resources/license-header.star-block.txt"))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,38 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
plugins {
|
||||
id("pklJavaLibrary")
|
||||
kotlin("jvm")
|
||||
}
|
||||
plugins { id("pklJavaLibrary") }
|
||||
|
||||
// Build configuration.
|
||||
val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
|
||||
// Version Catalog library symbols.
|
||||
val libs = the<LibrariesForLibs>()
|
||||
|
||||
dependencies {
|
||||
// At least some of our kotlin APIs contain Kotlin stdlib types
|
||||
// that aren't compiled away by kotlinc (e.g., `kotlin.Function`).
|
||||
// So let's be conservative and default to `api` for now.
|
||||
// For Kotlin APIs that only target Kotlin users (e.g., pkl-config-kotlin),
|
||||
// it won't make a difference.
|
||||
api(buildInfo.libs.findLibrary("kotlinStdLib").get())
|
||||
}
|
||||
|
||||
tasks.compileKotlin {
|
||||
enabled = true // disabled by pklJavaLibrary
|
||||
}
|
||||
|
||||
tasks.withType<KotlinJvmCompile>().configureEach {
|
||||
compilerOptions {
|
||||
languageVersion = KotlinVersion.KOTLIN_2_1
|
||||
jvmTarget = JvmTarget.fromTarget(buildInfo.jvmTarget.toString())
|
||||
freeCompilerArgs.addAll("-Xjdk-release=${buildInfo.jvmTarget}")
|
||||
}
|
||||
// Kotlin libraries typically expose stdlib types in their public APIs.
|
||||
// Therefore, the stdlib must be available on the consumer's compile classpath,
|
||||
// and "implementation" is not sufficient.
|
||||
api(libs.kotlinStdLib)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||
|
||||
plugins {
|
||||
`jvm-test-suite`
|
||||
kotlin("jvm")
|
||||
id("pklKotlinBase")
|
||||
}
|
||||
|
||||
val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
@@ -27,10 +27,11 @@ val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
val libs = the<LibrariesForLibs>()
|
||||
|
||||
dependencies {
|
||||
testImplementation(libs.kotlinStdLib)
|
||||
|
||||
testImplementation(libs.assertj)
|
||||
testImplementation(libs.junitApi)
|
||||
testImplementation(libs.junitParams)
|
||||
testImplementation(libs.kotlinStdLib)
|
||||
|
||||
testRuntimeOnly(libs.junitEngine)
|
||||
testRuntimeOnly(libs.junitLauncher)
|
||||
@@ -44,7 +45,7 @@ tasks.withType<Test>().configureEach {
|
||||
// enable checking of stdlib return types
|
||||
systemProperty("org.pkl.testMode", "true")
|
||||
|
||||
reports.named("html") { enabled = true }
|
||||
reports.named("html") { required = true }
|
||||
|
||||
testLogging { exceptionFormat = TestExceptionFormat.FULL }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user