Skip to content

Commit 50828fa

Browse files
committed
feat: graalvm jdk21 toolchain
- feat: support for jvm21+ toolchain - feat: support for gradle toolchains - feat: pass `-PnativeArch=native` to build with `-march=native` - feat: `-Os` optimization for native builds - fix: remove mrjar classes at >jvm17 from fatjars - fix: use jdk21 to run the tests (needed for `Unsafe.ensureInitialized`) - fix: truffle svm dependency is required after graalvm `24.0.0` - fix: warnings for gvm flag usage, renamed truffle svm macro - fix: build with `--add-modules=jdk.unsupported` where needed - fix: don't use `gu` tool for modern graalvm versions - chore: buildinfo changes for JVM targets, toolchains - chore: enforce testing at exactly jdk21 - chore: enforce build tooling at jdk21+ - chore: bump graalvm/truffle libs → `24.1.2` Known Issues: - `pkl-gradle`: Tests are broken at class init (caused by below) - `pkl-config-{java,kotlin}`: Class init problems in fatjars Signed-off-by: Sam Gammon <sam@elide.dev>
1 parent 75bd214 commit 50828fa

File tree

10 files changed

+186
-34
lines changed

10 files changed

+186
-34
lines changed

buildSrc/src/main/kotlin/BuildInfo.kt

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,7 +19,34 @@ import java.io.File
1919
import org.gradle.api.Project
2020
import org.gradle.api.artifacts.VersionCatalog
2121
import org.gradle.api.artifacts.VersionCatalogsExtension
22+
import org.gradle.api.provider.Provider
23+
import org.gradle.jvm.toolchain.*
2224
import org.gradle.kotlin.dsl.getByType
25+
import org.gradle.kotlin.dsl.support.serviceOf
26+
27+
/**
28+
* JVM bytecode target; this is pinned at a reasonable version, because downstream JVM projects
29+
* which consume Pkl will need a minimum Bytecode level at or above this one.
30+
*
31+
* Kotlin and Java need matching bytecode targets, so this is expressed as a build setting and
32+
* constant default. To override, pass `-PpklJvmTarget=X` to the Gradle command line, where X is a
33+
* major Java version.
34+
*/
35+
private const val PKL_JVM_TARGET_DEFAULT_MAXIMUM = 17
36+
37+
/**
38+
* The Pkl build requires JDK 21+ to build, because JDK 17 is no longer within the default set of
39+
* supported JDKs for GraalVM. This is a build-time requirement, not a runtime requirement.
40+
*
41+
* See `settings.gradle.kts`, where this is enforced.
42+
*/
43+
private const val PKL_JDK_VERSION_MIN = 21
44+
45+
/**
46+
* The Pkl build must use exactly JDK 21 to run tests, because certain methods in use by Truffle are
47+
* not present in JDK 22+; thus, this is pinned to [PKL_JDK_VERSION_MIN] (JDK 21).
48+
*/
49+
private const val PKL_TEST_JDK_TARGET = PKL_JDK_VERSION_MIN
2350

2451
// `buildInfo` in main build scripts
2552
// `project.extensions.getByType<BuildInfo>()` in precompiled script plugins
@@ -80,6 +107,35 @@ open class BuildInfo(project: Project) {
80107

81108
val isReleaseBuild: Boolean by lazy { java.lang.Boolean.getBoolean("releaseBuild") }
82109

110+
val isNativeArch: Boolean by lazy { java.lang.Boolean.getBoolean("nativeArch") }
111+
112+
val jvmTarget: Int by lazy {
113+
System.getProperty("pklJvmTarget")?.toInt() ?: PKL_JVM_TARGET_DEFAULT_MAXIMUM
114+
}
115+
116+
val jdkVendor: JvmVendorSpec = JvmVendorSpec.GRAAL_VM
117+
118+
val jdkToolchainVersion: JavaLanguageVersion by lazy {
119+
JavaLanguageVersion.of(System.getProperty("pklJdkToolchain")?.toInt() ?: PKL_JDK_VERSION_MIN)
120+
}
121+
122+
private fun JavaToolchainSpec.pklJdkToolchain() {
123+
languageVersion.set(jdkToolchainVersion)
124+
vendor.set(jdkVendor)
125+
}
126+
127+
val javaCompiler: Provider<JavaCompiler> by lazy {
128+
project.serviceOf<JavaToolchainService>().let { toolchainService ->
129+
toolchainService.compilerFor { pklJdkToolchain() }
130+
}
131+
}
132+
133+
val javaTestLauncher: Provider<JavaLauncher> by lazy {
134+
project.serviceOf<JavaToolchainService>().let { toolchainService ->
135+
toolchainService.launcherFor { pklJdkToolchain() }
136+
}
137+
}
138+
83139
val hasMuslToolchain: Boolean by lazy {
84140
// see "install musl" in .circleci/jobs/BuildNativeJob.pkl
85141
File(System.getProperty("user.home"), "staticdeps/bin/x86_64-linux-musl-gcc").exists()

buildSrc/src/main/kotlin/InstallGraalVm.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ abstract class InstallGraalVm
3030
@Inject
3131
constructor(
3232
private val fileOperations: FileOperations,
33-
private val execOperations: ExecOperations
33+
private val execOperations: ExecOperations,
3434
) : DefaultTask() {
3535
@get:Input abstract val graalVm: Property<BuildInfo.GraalVm>
3636

@@ -58,10 +58,16 @@ constructor(
5858
if (os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin")
5959

6060
println("Installing native-image into $distroDir")
61-
execOperations.exec {
62-
val executableName = if (os.isWindows) "gu.cmd" else "gu"
63-
executable = distroBinDir.resolve(executableName).toString()
64-
args("install", "--no-progress", "native-image")
61+
val gvmVersionMajor =
62+
requireNotNull(graalVm.get().version.split(".").first().toIntOrNull()) {
63+
"Invalid GraalVM JDK version: ${graalVm.get().graalVmJdkVersion}"
64+
}
65+
if (gvmVersionMajor < 24) {
66+
execOperations.exec {
67+
val executableName = if (os.isWindows) "gu.cmd" else "gu"
68+
executable = distroBinDir.resolve(executableName).toString()
69+
args("install", "--no-progress", "native-image")
70+
}
6571
}
6672

6773
println("Creating symlink ${graalVm.get().installDir} for $distroDir")

buildSrc/src/main/kotlin/pklFatJar.gradle.kts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -82,7 +82,29 @@ tasks.shadowJar {
8282

8383
exclude("META-INF/maven/**")
8484
exclude("META-INF/upgrade/**")
85-
exclude("META-INF/versions/19/**")
85+
86+
val info = project.extensions.getByType<BuildInfo>()
87+
val minimumJvmTarget = JavaVersion.toVersion(info.jvmTarget)
88+
89+
// effectively, this results in calls excluding:
90+
// `META-INF/versions/{17-25}/**`
91+
// at the time of this writing; multi-release JARs beyond JDK 21 break the current
92+
// version of the Shadow plugin, and aren't needed for Truffle's use by Pkl.
93+
sortedSetOf(
94+
// minimum jvm target + 1
95+
JavaVersion.toVersion(minimumJvmTarget.majorVersion.toInt() + 1)
96+
)
97+
.plus(
98+
// all subsequent versions
99+
JavaVersion
100+
.values()
101+
.filter { it.isCompatibleWith(minimumJvmTarget) }
102+
.toSortedSet()
103+
)
104+
.forEach {
105+
// exclude all such versions up to the supported maximum for the build toolchain
106+
exclude("META-INF/versions/${it.majorVersion}/**")
107+
}
86108

87109
// org.antlr.v4.runtime.misc.RuleDependencyProcessor
88110
exclude("META-INF/services/javax.annotation.processing.Processor")

buildSrc/src/main/kotlin/pklJavaLibrary.gradle.kts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import org.gradle.accessors.dm.LibrariesForLibs
1919

2020
plugins {
2121
`java-library`
22+
id("pklJvmToolchain")
2223
id("pklKotlinTest")
2324
id("com.diffplug.spotless")
2425
}
@@ -29,9 +30,22 @@ val sourcesJarConfiguration = configurations.register("sourcesJar")
2930
// Version Catalog library symbols.
3031
val libs = the<LibrariesForLibs>()
3132

33+
// Build configuration.
34+
val info = project.extensions.getByType<BuildInfo>()
35+
3236
java {
37+
val jvmTarget = JavaVersion.toVersion(info.jvmTarget)
38+
3339
withSourcesJar() // creates `sourcesJar` task
3440
withJavadocJar()
41+
42+
sourceCompatibility = jvmTarget
43+
targetCompatibility = jvmTarget
44+
45+
toolchain {
46+
languageVersion = info.jdkToolchainVersion
47+
vendor = info.jdkVendor
48+
}
3549
}
3650

3751
artifacts {
@@ -80,9 +94,34 @@ val workAroundKotlinGradlePluginBug by
8094
}
8195
}
8296

97+
val truffleJavacArgs =
98+
listOf(
99+
// TODO: determine correct limits for Truffle specializations
100+
// (see https://graalvm.slack.com/archives/CNQSB2DHD/p1712380902746829)
101+
"-Atruffle.dsl.SuppressWarnings=truffle-limit"
102+
)
103+
104+
val jpmsJavacArgs = listOf("--add-modules=jdk.unsupported")
105+
val javacArgsProvider = CommandLineArgumentProvider { jpmsJavacArgs }
106+
83107
tasks.compileJava {
108+
javaCompiler = info.javaCompiler
84109
dependsOn(workAroundKotlinGradlePluginBug)
85-
// TODO: determine correct limits for Truffle specializations
86-
// (see https://graalvm.slack.com/archives/CNQSB2DHD/p1712380902746829)
87-
options.compilerArgs.add("-Atruffle.dsl.SuppressWarnings=truffle-limit")
110+
options.compilerArgumentProviders.add(CommandLineArgumentProvider { truffleJavacArgs })
111+
}
112+
113+
tasks.withType<JavaCompile>().configureEach {
114+
val jvmTarget = JavaVersion.toVersion(info.jvmTarget)
115+
javaCompiler = info.javaCompiler
116+
sourceCompatibility = jvmTarget.majorVersion
117+
targetCompatibility = jvmTarget.majorVersion
118+
119+
options.compilerArgumentProviders.add(javacArgsProvider)
120+
}
121+
122+
tasks.withType<JavaExec>().configureEach { jvmArgs(jpmsJavacArgs) }
123+
124+
tasks.test {
125+
javaLauncher = info.javaTestLauncher
126+
jvmArgumentProviders.add(javacArgsProvider)
88127
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plugins {
2+
`jvm-toolchains`
3+
}

buildSrc/src/main/kotlin/pklKotlinLibrary.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,13 +14,14 @@
1414
* limitations under the License.
1515
*/
1616
import org.gradle.accessors.dm.LibrariesForLibs
17+
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
1718

1819
plugins {
1920
id("pklJavaLibrary")
20-
2121
kotlin("jvm")
2222
}
2323

24+
// Build configuration.
2425
val buildInfo = project.extensions.getByType<BuildInfo>()
2526

2627
// Version Catalog library symbols.
@@ -38,3 +39,7 @@ dependencies {
3839
tasks.compileKotlin {
3940
enabled = true // disabled by pklJavaLibrary
4041
}
42+
43+
tasks.withType<KotlinJvmCompile>().configureEach {
44+
kotlinOptions { jvmTarget = JavaVersion.toVersion(buildInfo.jvmTarget).majorVersion }
45+
}

gradle/libs.versions.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ geantyref = "1.+"
99
googleJavaFormat = "1.25.2"
1010
# must not use `+` because used in download URL
1111
# 23.1.x requires JDK 20+
12-
graalVm = "23.0.6"
13-
graalVmJdkVersion = "17.0.12"
12+
graalVm = "24.1.2"
13+
graalVmJdkVersion = "21.0.5"
1414
# slightly hacky but convenient place so we remember to update the checksum
1515
graalVmSha256-macos-x64 = "3ecac1471f3fa95a56c5b75c65db9e60ac4551f56eda09eb9da95e6049ea77d7"
1616
graalVmSha256-macos-aarch64 = "4cdfdc6c9395f6773efcd191b6605f1b7c8e1b78ab900ab5cff34720a3feffc5"
@@ -96,6 +96,8 @@ spotlessPlugin = { group = "com.diffplug.spotless", name = "spotless-plugin-grad
9696
svm = { group = "org.graalvm.nativeimage", name = "svm", version.ref = "graalVm" }
9797
truffleApi = { group = "org.graalvm.truffle", name = "truffle-api", version.ref = "graalVm" }
9898
truffleDslProcessor = { group = "org.graalvm.truffle", name = "truffle-dsl-processor", version.ref = "graalVm" }
99+
truffleSvm = { group = "org.graalvm.nativeimage", name = "truffle-runtime-svm", version.ref = "graalVm" }
100+
truffleRuntime = { group = "org.graalvm.truffle", name = "truffle-runtime", version.ref = "graalVm" }
99101
wiremock = { group = "org.wiremock", name = "wiremock", version.ref = "wiremock" }
100102

101103
[plugins] # ordered alphabetically

pkl-cli/pkl-cli.gradle.kts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ val stagedWindowsAmd64Executable: Configuration by configurations.creating
5050

5151
dependencies {
5252
compileOnly(libs.svm)
53+
compileOnly(libs.truffleSvm)
54+
implementation(libs.truffleRuntime)
55+
compileOnly(libs.graalSdk)
5356

5457
// CliEvaluator exposes PClass
5558
api(projects.pklCore)
@@ -157,7 +160,7 @@ tasks.check { dependsOn(testStartJavaExecutable) }
157160
fun Exec.configureExecutable(
158161
graalVm: BuildInfo.GraalVm,
159162
outputFile: Provider<RegularFile>,
160-
extraArgs: List<String> = listOf()
163+
extraArgs: List<String> = listOf(),
161164
) {
162165
inputs
163166
.files(sourceSets.main.map { it.output })
@@ -179,11 +182,13 @@ fun Exec.configureExecutable(
179182
executable = "${graalVm.baseDir}/bin/$nativeImageCommandName"
180183

181184
// JARs to exclude from the class path for the native-image build.
182-
val exclusions = listOf(libs.truffleApi, libs.graalSdk).map { it.get().module.name }
185+
val exclusions = listOf(libs.graalSdk).map { it.get().module.name }
183186
// https://www.graalvm.org/22.0/reference-manual/native-image/Options/
184187
argumentProviders.add(
185188
CommandLineArgumentProvider {
186189
buildList {
190+
// must be emitted before any experimental options are used
191+
add("-H:+UnlockExperimentalVMOptions")
187192
// currently gives a deprecation warning, but we've been told
188193
// that the "initialize everything at build time" *CLI* option is likely here to stay
189194
add("--initialize-at-build-time=")
@@ -194,9 +199,9 @@ fun Exec.configureExecutable(
194199
add("-H:IncludeResources=org/jline/utils/.*")
195200
add("-H:IncludeResourceBundles=org.pkl.core.errorMessages")
196201
add("-H:IncludeResources=org/pkl/commons/cli/PklCARoots.pem")
197-
add("--macro:truffle")
198202
add("-H:Class=org.pkl.cli.Main")
199-
add("-H:Name=${outputFile.get().asFile.name}")
203+
add("-o")
204+
add(outputFile.get().asFile.name)
200205
// the actual limit (currently) used by native-image is this number + 1400 (idea is to
201206
// compensate for Truffle's own nodes)
202207
add("-H:MaxRuntimeCompileMethods=1800")
@@ -210,8 +215,14 @@ fun Exec.configureExecutable(
210215
// executable
211216
if (!buildInfo.isReleaseBuild) {
212217
add("-Ob")
218+
} else {
219+
add("-Os")
220+
}
221+
if (buildInfo.isNativeArch) {
222+
add("-march=native")
223+
} else {
224+
add("-march=compatibility")
213225
}
214-
add("-march=compatibility")
215226
// native-image rejects non-existing class path entries -> filter
216227
add("--class-path")
217228
val pathInput =
@@ -240,7 +251,7 @@ val macExecutableAmd64: TaskProvider<Exec> by
240251
dependsOn(":installGraalVmAmd64")
241252
configureExecutable(
242253
buildInfo.graalVmAmd64,
243-
layout.buildDirectory.file("executable/pkl-macos-amd64")
254+
layout.buildDirectory.file("executable/pkl-macos-amd64"),
244255
)
245256
}
246257

@@ -251,7 +262,7 @@ val macExecutableAarch64: TaskProvider<Exec> by
251262
configureExecutable(
252263
buildInfo.graalVmAarch64,
253264
layout.buildDirectory.file("executable/pkl-macos-aarch64"),
254-
listOf("-H:+AllowDeprecatedBuilderClassesOnImageClasspath")
265+
listOf("-H:+AllowDeprecatedBuilderClassesOnImageClasspath"),
255266
)
256267
}
257268

@@ -261,7 +272,7 @@ val linuxExecutableAmd64: TaskProvider<Exec> by
261272
dependsOn(":installGraalVmAmd64")
262273
configureExecutable(
263274
buildInfo.graalVmAmd64,
264-
layout.buildDirectory.file("executable/pkl-linux-amd64")
275+
layout.buildDirectory.file("executable/pkl-linux-amd64"),
265276
)
266277
}
267278

@@ -281,7 +292,7 @@ val linuxExecutableAarch64: TaskProvider<Exec> by
281292
// Ensure compatibility for kernels with page size set to 4k, 16k and 64k
282293
// (e.g. Raspberry Pi 5, Asahi Linux)
283294
"-H:PageSize=65536"
284-
)
295+
),
285296
)
286297
}
287298

@@ -297,7 +308,7 @@ val alpineExecutableAmd64: TaskProvider<Exec> by
297308
configureExecutable(
298309
buildInfo.graalVmAmd64,
299310
layout.buildDirectory.file("executable/pkl-alpine-linux-amd64"),
300-
listOf("--static", "--libc=musl")
311+
listOf("--static", "--libc=musl"),
301312
)
302313
}
303314

@@ -307,7 +318,7 @@ val windowsExecutableAmd64: TaskProvider<Exec> by
307318
configureExecutable(
308319
buildInfo.graalVmAmd64,
309320
layout.buildDirectory.file("executable/pkl-windows-amd64"),
310-
listOf("-Dfile.encoding=UTF-8")
321+
listOf("-Dfile.encoding=UTF-8"),
311322
)
312323
}
313324

0 commit comments

Comments
 (0)