Compare commits

...
14 Commits
Author SHA1 Message Date
Jen Basch 9a2a0610de Remove extra checkout from java executable build jobs (#1925) 2026-10-21 13:36:26 -07:00
Jen Basch 19e7a92fb7 Prepare 0.32.2 release 2026-10-21 10:39:00 -07:00
Jen Basch 57c13a208e Prepare 0.32.2 release (#1924) 2026-10-21 10:36:26 -07:00
Jen Basch 96eeffb587 Update bundled CA certs (#1923) 2026-10-21 09:46:45 -07:00
David TeslerandDan Chao 3cbd01f6ec Fix negative caret width rendering multi-line expression errors (#1793)
The expression preamble length was subtracted from endColumn even
when endColumn came from the already-stripped sourceLine.length(),
so multi-line frames got a negative caret width and String.repeat
threw. Apply the offset only to the frame-derived columns.

---------

Co-authored-by: Dan Chao <dan.chao@apple.com>
2026-10-21 09:40:45 -07:00
Islon Scherer 9ed02459c1 Prepare "0.32.1" release 2026-07-23 18:31:14 +02:00
Daniel Chao b26ec4af93 Prepare 0.32.1 release (#1789) 2026-07-23 09:00:53 -07:00
Daniel Chao ae78112205 Fix variable resolution of object body params (#1788)
Fixes a regression introduced in Pkl 0.32.
2026-07-22 13:05:00 -07:00
Daniel Chao 4ba3d8e7d6 Migrate Gradle delegated properties (#1782)
There were some more that we missed last time around
2026-07-16 16:16:33 -07:00
Daniel Chao d9bc5a5e5d Migrate Gradle deprecations (#1745) 2026-07-16 16:15:24 -07:00
Kushal Pisavadia fedafc47b0 Close intermediate redirect response bodies before following (#1749)
When following redirects, `RequestRewritingClient` discarded each 3xx
response without closing its body, leaking the underlying connection.

Close the body as soon as we know the status is a redirect and won't
be returned, so the too-many-redirects, missing-`Location`,
invalid-URI and downgrade error paths all release the connection too.

This prevents leaking one `BodyHandlers.ofInputStream()` connection
per hop. Now the code closes the body before following the redirect.
2026-07-16 16:14:56 -07:00
Daniel Chao 0237938b3b Fix eval -x of local members (#1760)
This fixes a regression where local members cannot be seen by the
expression evaluator.

Because variable are now parse-time resolved, this builds a truffle node
by first constructing a synthetic module of all defined local members,
and calls AstBuilder on this synthetic module first.
2026-07-10 21:01:39 -07:00
Dan Chao 6caa75344d Update changelog with release notes link 2026-07-08 09:59:40 -07:00
Dan Chao e51fb2f917 Prepare 0.32.0 release 2026-07-08 09:49:12 -07:00
79 changed files with 4394 additions and 3758 deletions

No files matched your search

-1
View File
@@ -16,7 +16,6 @@ local command =
}.join(" ") }.join(" ")
steps { steps {
catalog.`actions/checkout@v6`
new { new {
name = "gradle build java executables" name = "gradle build java executables"
shell = "bash" shell = "bash"
-3
View File
@@ -185,9 +185,6 @@ jobs:
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi fi
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: gradle build java executables - name: gradle build java executables
shell: bash shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build
-3
View File
@@ -183,9 +183,6 @@ jobs:
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi fi
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: gradle build java executables - name: gradle build java executables
shell: bash shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build
-3
View File
@@ -183,9 +183,6 @@ jobs:
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi fi
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: gradle build java executables - name: gradle build java executables
shell: bash shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build
-3
View File
@@ -184,9 +184,6 @@ jobs:
if [[ "$RUNNER_DEBUG" -eq 1 ]]; then if [[ "$RUNNER_DEBUG" -eq 1 ]]; then
echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV echo "DEBUG_ARGS=--info --stacktrace" >> $GITHUB_ENV
fi fi
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: gradle build java executables - name: gradle build java executables
shell: bash shell: bash
run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -DreleaseBuild=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build run: ./gradlew $DEBUG_ARGS --no-daemon -DpklMultiJdkTesting=true -DreleaseBuild=true pkl-doc:build pkl-cli:build pkl-codegen-java:build pkl-codegen-kotlin:build
+2 -2
View File
@@ -19,8 +19,8 @@ plugins {
id("me.champeau.jmh") id("me.champeau.jmh")
} }
val truffle: Configuration by configurations.creating val truffle: Configuration = configurations.create("truffle")
val graal: Configuration by configurations.creating val graal: Configuration = configurations.create("graal")
dependencies { dependencies {
jmh(projects.pklCore) jmh(projects.pklCore)
+38 -36
View File
@@ -63,58 +63,60 @@ fun Project.configurePklPomMetadata() {
/** Configures POM validation task to check for unresolved versions and snapshots in releases. */ /** Configures POM validation task to check for unresolved versions and snapshots in releases. */
fun Project.configurePomValidation() { fun Project.configurePomValidation() {
val validatePom by tasks.registering { val validatePom =
if (tasks.findByName("generatePomFileForLibraryPublication") == null) { tasks.register("validatePom") {
return@registering if (tasks.findByName("generatePomFileForLibraryPublication") == null) {
} return@register
val generatePomFileForLibraryPublication by tasks.existing(GenerateMavenPom::class) }
val outputFile = val generatePomFileForLibraryPublication =
layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check tasks.named<GenerateMavenPom>("generatePomFileForLibraryPublication")
val outputFile =
layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check
dependsOn(generatePomFileForLibraryPublication) dependsOn(generatePomFileForLibraryPublication)
inputs.file(generatePomFileForLibraryPublication.get().destination) inputs.file(generatePomFileForLibraryPublication.get().destination)
outputs.file(outputFile) outputs.file(outputFile)
doLast { doLast {
outputFile.get().asFile.delete() outputFile.get().asFile.delete()
val pomFile = generatePomFileForLibraryPublication.get().destination val pomFile = generatePomFileForLibraryPublication.get().destination
assert(pomFile.exists()) assert(pomFile.exists())
val text = pomFile.readText() val text = pomFile.readText()
run { run {
val unresolvedVersion = Regex("<version>.*[+,()\\[\\]].*</version>") val unresolvedVersion = Regex("<version>.*[+,()\\[\\]].*</version>")
val matches = unresolvedVersion.findAll(text).toList() val matches = unresolvedVersion.findAll(text).toList()
if (matches.isNotEmpty()) { if (matches.isNotEmpty()) {
throw org.gradle.api.GradleException( throw org.gradle.api.GradleException(
""" """
Found unresolved version selector(s) in generated POM: Found unresolved version selector(s) in generated POM:
${matches.joinToString("\n") { it.groupValues[0] }} ${matches.joinToString("\n") { it.groupValues[0] }}
""" """
.trimIndent() .trimIndent()
) )
}
} }
}
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
if (buildInfo.isReleaseBuild) { if (buildInfo.isReleaseBuild) {
val snapshotVersion = Regex("<version>.*-SNAPSHOT</version>") val snapshotVersion = Regex("<version>.*-SNAPSHOT</version>")
val matches = snapshotVersion.findAll(text).toList() val matches = snapshotVersion.findAll(text).toList()
if (matches.isNotEmpty()) { if (matches.isNotEmpty()) {
throw org.gradle.api.GradleException( throw org.gradle.api.GradleException(
""" """
Found snapshot version(s) in generated POM of Pkl release version: Found snapshot version(s) in generated POM of Pkl release version:
${matches.joinToString("\n") { it.groupValues[0] }} ${matches.joinToString("\n") { it.groupValues[0] }}
""" """
.trimIndent() .trimIndent()
) )
}
} }
}
outputFile.get().asFile.writeText("OK") outputFile.get().asFile.writeText("OK")
}
} }
}
tasks.named("publish") { dependsOn(validatePom) } tasks.named("publish") { dependsOn(validatePom) }
} }
@@ -79,7 +79,7 @@ plugins.withType(MavenPublishPlugin::class).configureEach {
} }
} }
val allDependencies by tasks.registering(DependencyReportTask::class) val allDependencies = tasks.register<DependencyReportTask>("allDependencies")
tasks.withType(Test::class).configureEach { tasks.withType(Test::class).configureEach {
System.getProperty("testReportsDir")?.let { reportsDir -> System.getProperty("testReportsDir")?.let { reportsDir ->
@@ -132,8 +132,8 @@ tasks.shadowJar {
shadow { addShadowVariantIntoJavaComponent = false } shadow { addShadowVariantIntoJavaComponent = false }
val testFatJar by val testFatJar =
tasks.registering(Test::class) { tasks.register<Test>("testFatJar") {
testClassesDirs = files(tasks.test.get().testClassesDirs) testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath = classpath =
// compiled test classes // compiled test classes
@@ -148,47 +148,48 @@ val testFatJar by
tasks.check { dependsOn(testFatJar) } tasks.check { dependsOn(testFatJar) }
val validateFatJar by tasks.registering { val validateFatJar =
val outputFile = layout.buildDirectory.file("validateFatJar/result.txt") tasks.register("validateFatJar") {
inputs.files(tasks.shadowJar) val outputFile = layout.buildDirectory.file("validateFatJar/result.txt")
inputs.property("nonRelocations", nonRelocations) inputs.files(tasks.shadowJar)
outputs.file(outputFile) inputs.property("nonRelocations", nonRelocations)
outputs.file(outputFile)
doLast { doLast {
val unshadowedFiles = mutableListOf<String>() val unshadowedFiles = mutableListOf<String>()
zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit { zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit {
val fileDetails = this val fileDetails = this
val path = fileDetails.relativePath.pathString val path = fileDetails.relativePath.pathString
if ( if (
!(fileDetails.isDirectory || !(fileDetails.isDirectory ||
path.startsWith("org/pkl/") || path.startsWith("org/pkl/") ||
path.startsWith("META-INF/") || path.startsWith("META-INF/") ||
nonRelocations.any { path.startsWith(it) }) nonRelocations.any { path.startsWith(it) })
) { ) {
// don't throw exception inside `visit` // don't throw exception inside `visit`
// as this gives a misleading "Could not expand ZIP" error message // as this gives a misleading "Could not expand ZIP" error message
unshadowedFiles.add(path) unshadowedFiles.add(path)
}
}
if (unshadowedFiles.isEmpty()) {
outputFile.get().asFile.writeText("SUCCESS")
} else {
outputFile.get().asFile.writeText("FAILURE")
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
} }
} }
if (unshadowedFiles.isEmpty()) {
outputFile.get().asFile.writeText("SUCCESS")
} else {
outputFile.get().asFile.writeText("FAILURE")
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
}
} }
}
tasks.check { dependsOn(validateFatJar) } tasks.check { dependsOn(validateFatJar) }
val resolveSourcesJars by val resolveSourcesJars =
tasks.registering(ResolveSourcesJars::class) { tasks.register<ResolveSourcesJars>("resolveSourcesJars") {
configuration.set(configurations.runtimeClasspath) configuration.set(configurations.runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("resolveSourcesJars")) outputDir.set(layout.buildDirectory.dir("resolveSourcesJars"))
} }
val fatSourcesJar by val fatSourcesJar =
tasks.registering(MergeSourcesJars::class) { tasks.register<MergeSourcesJars>("fatSourcesJar") {
plugins.withId("pklJavaLibrary") { inputJars.from(tasks.named("sourcesJar")) } plugins.withId("pklJavaLibrary") { inputJars.from(tasks.named("sourcesJar")) }
inputJars.from(firstPartySourcesJarsConfiguration) inputJars.from(firstPartySourcesJarsConfiguration)
inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) }) inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) })
@@ -213,7 +214,7 @@ publishing {
artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { classifier = "sources" } artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { classifier = "sources" }
plugins.withId("pklJavaLibrary") { plugins.withId("pklJavaLibrary") {
val javadocJar by tasks.existing(Jar::class) val javadocJar = tasks.named<Jar>("javadocJar")
// Javadoc Jar is not fat (didn't invest effort) // Javadoc Jar is not fat (didn't invest effort)
artifact(javadocJar.flatMap { it.archiveFile }) { classifier = "javadoc" } artifact(javadocJar.flatMap { it.archiveFile }) { classifier = "javadoc" }
} }
@@ -21,11 +21,15 @@ plugins { id("de.undercouch.download") }
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
// tries to minimize chance of corruption by download-to-temp-file-and-move // tries to minimize chance of corruption by download-to-temp-file-and-move
val downloadGraalVmAarch64 by val downloadGraalVmAarch64 =
tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAarch64) } tasks.register<Download>("downloadGraalVmAarch64") {
configureDownloadGraalVm(buildInfo.graalVmAarch64)
}
val downloadGraalVmAmd64 by val downloadGraalVmAmd64 =
tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAmd64) } tasks.register<Download>("downloadGraalVmAmd64") {
configureDownloadGraalVm(buildInfo.graalVmAmd64)
}
fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) { fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
onlyIf { !graalvm.installDir.exists() } onlyIf { !graalvm.installDir.exists() }
@@ -37,14 +41,14 @@ fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
tempAndMove(true) tempAndMove(true)
} }
val verifyGraalVmAarch64 by val verifyGraalVmAarch64 =
tasks.registering(Verify::class) { tasks.register<Verify>("verifyGraalVmAarch64") {
configureVerifyGraalVm(buildInfo.graalVmAarch64) configureVerifyGraalVm(buildInfo.graalVmAarch64)
dependsOn(downloadGraalVmAarch64) dependsOn(downloadGraalVmAarch64)
} }
val verifyGraalVmAmd64 by val verifyGraalVmAmd64 =
tasks.registering(Verify::class) { tasks.register<Verify>("verifyGraalVmAmd64") {
configureVerifyGraalVm(buildInfo.graalVmAmd64) configureVerifyGraalVm(buildInfo.graalVmAmd64)
dependsOn(downloadGraalVmAmd64) dependsOn(downloadGraalVmAmd64)
} }
@@ -60,15 +64,15 @@ fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) {
} }
@Suppress("unused") @Suppress("unused")
val installGraalVmAarch64 by val installGraalVmAarch64 =
tasks.registering(InstallGraalVm::class) { tasks.register<InstallGraalVm>("installGraalVmAarch64") {
dependsOn(verifyGraalVmAarch64) dependsOn(verifyGraalVmAarch64)
graalVm = buildInfo.graalVmAarch64 graalVm = buildInfo.graalVmAarch64
} }
@Suppress("unused") @Suppress("unused")
val installGraalVmAmd64 by val installGraalVmAmd64 =
tasks.registering(InstallGraalVm::class) { tasks.register<InstallGraalVm>("installGraalVmAmd64") {
dependsOn(verifyGraalVmAmd64) dependsOn(verifyGraalVmAmd64)
graalVm = buildInfo.graalVmAmd64 graalVm = buildInfo.graalVmAmd64
} }
@@ -47,8 +47,8 @@ dependencies {
} }
} }
val validateHtml by val validateHtml =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("validateHtml") {
val resultFile = layout.buildDirectory.file("validateHtml/result.txt") val resultFile = layout.buildDirectory.file("validateHtml/result.txt")
inputs.files(htmlValidator.sources) inputs.files(htmlValidator.sources)
outputs.file(resultFile) outputs.file(resultFile)
@@ -26,8 +26,8 @@ plugins {
val executableSpec = project.extensions.create("executable", ExecutableSpec::class.java) val executableSpec = project.extensions.create("executable", ExecutableSpec::class.java)
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
val javaExecutable by val javaExecutable =
tasks.registering(ExecutableJar::class) { tasks.register<ExecutableJar>("javaExecutable") {
group = "build" group = "build"
dependsOn(tasks.jar) dependsOn(tasks.jar)
inJar = tasks.shadowJar.flatMap { it.archiveFile } inJar = tasks.shadowJar.flatMap { it.archiveFile }
@@ -77,7 +77,8 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider<JavaLauncher>? = null)
} }
} }
val testStartJavaExecutable by tasks.registering { setupTestStartJavaExecutable() } val testStartJavaExecutable =
tasks.register("testStartJavaExecutable") { setupTestStartJavaExecutable() }
// Setup `testStartJavaExecutable` tasks for multi-JDK testing. // Setup `testStartJavaExecutable` tasks for multi-JDK testing.
val testStartJavaExecutableOnOtherJdks = val testStartJavaExecutableOnOtherJdks =
@@ -30,22 +30,26 @@ plugins {
val executableSpec = project.extensions.getByType<ExecutableSpec>() val executableSpec = project.extensions.getByType<ExecutableSpec>()
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
val stagedMacAmd64Executable: Configuration by configurations.creating val stagedMacAmd64Executable: Configuration = configurations.create("stagedMacAmd64Executable")
val stagedMacAarch64Executable: Configuration by configurations.creating val stagedMacAarch64Executable: Configuration = configurations.create("stagedMacAarch64Executable")
val stagedLinuxAmd64Executable: Configuration by configurations.creating val stagedLinuxAmd64Executable: Configuration = configurations.create("stagedLinuxAmd64Executable")
val stagedLinuxAarch64Executable: Configuration by configurations.creating val stagedLinuxAarch64Executable: Configuration =
val stagedAlpineLinuxAmd64Executable: Configuration by configurations.creating configurations.create("stagedLinuxAarch64Executable")
val stagedWindowsAmd64Executable: Configuration by configurations.creating val stagedAlpineLinuxAmd64Executable: Configuration =
configurations.create("stagedAlpineLinuxAmd64Executable")
val stagedWindowsAmd64Executable: Configuration =
configurations.create("stagedWindowsAmd64Executable")
val nativeImageClasspath by configurations.creating { val nativeImageClasspath =
extendsFrom(configurations.runtimeClasspath.get()) configurations.create("nativeImageClasspath") {
// Ensure native-image version uses GraalVM C SDKs instead of Java FFI or JNA extendsFrom(configurations.runtimeClasspath.get())
// (comes from artifact `mordant-jvm-graal-ffi`). // Ensure native-image version uses GraalVM C SDKs instead of Java FFI or JNA
exclude("com.github.ajalt.mordant", "mordant-jvm-ffm") // (comes from artifact `mordant-jvm-graal-ffi`).
exclude("com.github.ajalt.mordant", "mordant-jvm-ffm-jvm") exclude("com.github.ajalt.mordant", "mordant-jvm-ffm")
exclude("com.github.ajalt.mordant", "mordant-jvm-jna") exclude("com.github.ajalt.mordant", "mordant-jvm-ffm-jvm")
exclude("com.github.ajalt.mordant", "mordant-jvm-jna-jvm") exclude("com.github.ajalt.mordant", "mordant-jvm-jna")
} exclude("com.github.ajalt.mordant", "mordant-jvm-jna-jvm")
}
val libs = the<LibrariesForLibs>() val libs = the<LibrariesForLibs>()
@@ -85,32 +89,32 @@ private fun NativeImageBuild.setClasspath() {
classpath.from(nativeImageClasspath) classpath.from(nativeImageClasspath)
} }
val macExecutableAmd64 by val macExecutableAmd64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("macExecutableAmd64") {
imageName = executableSpec.name.map { "$it-macos-amd64" } imageName = executableSpec.name.map { "$it-macos-amd64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
amd64() amd64()
setClasspath() setClasspath()
} }
val macExecutableAarch64 by val macExecutableAarch64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("macExecutableAarch64") {
imageName = executableSpec.name.map { "$it-macos-aarch64" } imageName = executableSpec.name.map { "$it-macos-aarch64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
aarch64() aarch64()
setClasspath() setClasspath()
} }
val linuxExecutableAmd64 by val linuxExecutableAmd64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("linuxExecutableAmd64") {
imageName = executableSpec.name.map { "$it-linux-amd64" } imageName = executableSpec.name.map { "$it-linux-amd64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
amd64() amd64()
setClasspath() setClasspath()
} }
val linuxExecutableAarch64 by val linuxExecutableAarch64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("linuxExecutableAarch64") {
imageName = executableSpec.name.map { "$it-linux-aarch64" } imageName = executableSpec.name.map { "$it-linux-aarch64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
aarch64() aarch64()
@@ -120,8 +124,8 @@ val linuxExecutableAarch64 by
extraNativeImageArgs.add("-H:PageSize=65536") extraNativeImageArgs.add("-H:PageSize=65536")
} }
val alpineExecutableAmd64 by val alpineExecutableAmd64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("alpineExecutableAmd64") {
imageName = executableSpec.name.map { "$it-alpine-linux-amd64" } imageName = executableSpec.name.map { "$it-alpine-linux-amd64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
amd64() amd64()
@@ -129,73 +133,75 @@ val alpineExecutableAmd64 by
extraNativeImageArgs.addAll(listOf("--static", "--libc=musl")) extraNativeImageArgs.addAll(listOf("--static", "--libc=musl"))
} }
val windowsExecutableAmd64 by val windowsExecutableAmd64 =
tasks.registering(NativeImageBuild::class) { tasks.register<NativeImageBuild>("windowsExecutableAmd64") {
imageName = executableSpec.name.map { "$it-windows-amd64" } imageName = executableSpec.name.map { "$it-windows-amd64" }
mainClass = executableSpec.mainClass mainClass = executableSpec.mainClass
amd64() amd64()
setClasspath() setClasspath()
} }
val assembleNative by tasks.existing val assembleNative = tasks.named("assembleNative")
val testStartNativeExecutable by tasks.registering { val testStartNativeExecutable =
dependsOn(assembleNative) tasks.register("testStartNativeExecutable") {
dependsOn(assembleNative)
// dummy file for up-to-date checking // dummy file for up-to-date checking
val outputFile = project.layout.buildDirectory.file("testStartNativeExecutable/output.txt") val outputFile = project.layout.buildDirectory.file("testStartNativeExecutable/output.txt")
outputs.file(outputFile) outputs.file(outputFile)
val execOutput = providers.exec { val execOutput = providers.exec {
commandLine(assembleNative.get().outputs.files.singleFile, "--version") commandLine(assembleNative.get().outputs.files.singleFile, "--version")
}
doLast {
val outputText = execOutput.standardOutput.asText.get()
if (!outputText.contains(buildInfo.pklVersionNonUnique)) {
throw GradleException(
"Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'"
)
} }
outputFile.get().asFile.toPath().apply {
try { doLast {
parent.createDirectories() val outputText = execOutput.standardOutput.asText.get()
} catch (_: java.nio.file.FileAlreadyExistsException) {} if (!outputText.contains(buildInfo.pklVersionNonUnique)) {
writeText("OK") throw GradleException(
"Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'"
)
}
outputFile.get().asFile.toPath().apply {
try {
parent.createDirectories()
} catch (_: java.nio.file.FileAlreadyExistsException) {}
writeText("OK")
}
} }
} }
}
val requiredGlibcVersion: Version = Version.parse("2.17") val requiredGlibcVersion: Version = Version.parse("2.17")
val checkGlibc by tasks.registering { val checkGlibc =
enabled = buildInfo.os.isLinux && !buildInfo.musl tasks.register("checkGlibc") {
dependsOn(assembleNative) enabled = buildInfo.os.isLinux && !buildInfo.musl
doLast { dependsOn(assembleNative)
val exec = providers.exec { doLast {
commandLine("objdump", "-T", assembleNative.get().outputs.files.singleFile) val exec = providers.exec {
} commandLine("objdump", "-T", assembleNative.get().outputs.files.singleFile)
val output = exec.standardOutput.asText.get() }
val minimumGlibcVersion = val output = exec.standardOutput.asText.get()
output val minimumGlibcVersion =
.split("\n") output
.mapNotNull { line -> .split("\n")
val match = Regex("GLIBC_([.0-9]*)").find(line) .mapNotNull { line ->
match?.groups[1]?.let { Version.parse(it.value) } val match = Regex("GLIBC_([.0-9]*)").find(line)
} match?.groups[1]?.let { Version.parse(it.value) }
.maxOrNull() }
if (minimumGlibcVersion == null) { .maxOrNull()
throw GradleException( if (minimumGlibcVersion == null) {
"Could not determine glibc version from executable. objdump output: $output" throw GradleException(
) "Could not determine glibc version from executable. objdump output: $output"
} )
if (minimumGlibcVersion > requiredGlibcVersion) { }
throw GradleException( if (minimumGlibcVersion > requiredGlibcVersion) {
"Incorrect glibc version. Found: $minimumGlibcVersion, required: $requiredGlibcVersion" throw GradleException(
) "Incorrect glibc version. Found: $minimumGlibcVersion, required: $requiredGlibcVersion"
)
}
} }
} }
}
// Expose underlying task's outputs // Expose underlying task's outputs
private fun <T : Task> Task.wraps(other: TaskProvider<T>) { private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
@@ -203,19 +209,24 @@ private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
outputs.files(other) outputs.files(other)
} }
val testNative by tasks.existing { dependsOn(testStartNativeExecutable, checkGlibc) } val testNative = tasks.named("testNative") { dependsOn(testStartNativeExecutable, checkGlibc) }
val assembleNativeMacOsAarch64 by tasks.existing { wraps(macExecutableAarch64) } val assembleNativeMacOsAarch64 =
tasks.named("assembleNativeMacOsAarch64") { wraps(macExecutableAarch64) }
val assembleNativeMacOsAmd64 by tasks.existing { wraps(macExecutableAmd64) } val assembleNativeMacOsAmd64 = tasks.named("assembleNativeMacOsAmd64") { wraps(macExecutableAmd64) }
val assembleNativeLinuxAarch64 by tasks.existing { wraps(linuxExecutableAarch64) } val assembleNativeLinuxAarch64 =
tasks.named("assembleNativeLinuxAarch64") { wraps(linuxExecutableAarch64) }
val assembleNativeLinuxAmd64 by tasks.existing { wraps(linuxExecutableAmd64) } val assembleNativeLinuxAmd64 =
tasks.named("assembleNativeLinuxAmd64") { wraps(linuxExecutableAmd64) }
val assembleNativeAlpineLinuxAmd64 by tasks.existing { wraps(alpineExecutableAmd64) } val assembleNativeAlpineLinuxAmd64 =
tasks.named("assembleNativeAlpineLinuxAmd64") { wraps(alpineExecutableAmd64) }
val assembleNativeWindowsAmd64 by tasks.existing { wraps(windowsExecutableAmd64) } val assembleNativeWindowsAmd64 =
tasks.named("assembleNativeWindowsAmd64") { wraps(windowsExecutableAmd64) }
publishing { publishing {
publications { publications {
@@ -13,29 +13,31 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
val assembleNativeMacOsAarch64 by tasks.registering { group = "build" } val assembleNativeMacOsAarch64 = tasks.register("assembleNativeMacOsAarch64") { group = "build" }
val assembleNativeMacOsAmd64 by tasks.registering { group = "build" } val assembleNativeMacOsAmd64 = tasks.register("assembleNativeMacOsAmd64") { group = "build" }
val assembleNativeLinuxAarch64 by tasks.registering { group = "build" } val assembleNativeLinuxAarch64 = tasks.register("assembleNativeLinuxAarch64") { group = "build" }
val assembleNativeLinuxAmd64 by tasks.registering { group = "build" } val assembleNativeLinuxAmd64 = tasks.register("assembleNativeLinuxAmd64") { group = "build" }
val assembleNativeAlpineLinuxAmd64 by tasks.registering { group = "build" } val assembleNativeAlpineLinuxAmd64 =
tasks.register("assembleNativeAlpineLinuxAmd64") { group = "build" }
val assembleNativeWindowsAmd64 by tasks.registering { group = "build" } val assembleNativeWindowsAmd64 = tasks.register("assembleNativeWindowsAmd64") { group = "build" }
val testNativeMacOsAarch64 by tasks.registering { group = "verification" } val testNativeMacOsAarch64 = tasks.register("testNativeMacOsAarch64") { group = "verification" }
val testNativeMacOsAmd64 by tasks.registering { group = "verification" } val testNativeMacOsAmd64 = tasks.register("testNativeMacOsAmd64") { group = "verification" }
val testNativeLinuxAarch64 by tasks.registering { group = "verification" } val testNativeLinuxAarch64 = tasks.register("testNativeLinuxAarch64") { group = "verification" }
val testNativeLinuxAmd64 by tasks.registering { group = "verification" } val testNativeLinuxAmd64 = tasks.register("testNativeLinuxAmd64") { group = "verification" }
val testNativeAlpineLinuxAmd64 by tasks.registering { group = "verification" } val testNativeAlpineLinuxAmd64 =
tasks.register("testNativeAlpineLinuxAmd64") { group = "verification" }
val testNativeWindowsAmd64 by tasks.registering { group = "verification" } val testNativeWindowsAmd64 = tasks.register("testNativeWindowsAmd64") { group = "verification" }
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
@@ -44,79 +46,85 @@ private fun <T : Task> Task.wraps(other: TaskProvider<T>) {
outputs.files(other) outputs.files(other)
} }
val assembleNative by tasks.registering { val assembleNative =
group = "build" tasks.register("assembleNative") {
group = "build"
if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) { if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) {
throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}") throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}")
} }
when { when {
buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> { buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> {
wraps(assembleNativeMacOsAarch64) wraps(assembleNativeMacOsAarch64)
} }
buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> { buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> {
wraps(assembleNativeMacOsAmd64) wraps(assembleNativeMacOsAmd64)
} }
buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> { buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> {
wraps(assembleNativeLinuxAarch64) wraps(assembleNativeLinuxAarch64)
} }
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> { buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) wraps(assembleNativeAlpineLinuxAmd64) else wraps(assembleNativeLinuxAmd64) if (buildInfo.musl) wraps(assembleNativeAlpineLinuxAmd64)
} else wraps(assembleNativeLinuxAmd64)
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> { }
wraps(assembleNativeWindowsAmd64) buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
} wraps(assembleNativeWindowsAmd64)
else -> { }
doLast { else -> {
throw GradleException( doLast {
"Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}" throw GradleException(
) "Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}"
)
}
} }
} }
} }
}
val testNative by tasks.registering { val testNative =
group = "verification" tasks.register("testNative") {
dependsOn(assembleNative) group = "verification"
dependsOn(assembleNative)
if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) { if (!buildInfo.isCrossArchSupported && buildInfo.isCrossArch) {
throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}") throw GradleException("Cross-arch builds are not supported on ${buildInfo.os.name}")
} }
when { when {
buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> { buildInfo.os.isMacOsX && buildInfo.targetArch == "aarch64" -> {
dependsOn(testNativeMacOsAarch64) dependsOn(testNativeMacOsAarch64)
} }
buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> { buildInfo.os.isMacOsX && buildInfo.targetArch == "amd64" -> {
dependsOn(testNativeMacOsAmd64) dependsOn(testNativeMacOsAmd64)
} }
buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> { buildInfo.os.isLinux && buildInfo.targetArch == "aarch64" -> {
dependsOn(testNativeLinuxAarch64) dependsOn(testNativeLinuxAarch64)
} }
buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> { buildInfo.os.isLinux && buildInfo.targetArch == "amd64" -> {
if (buildInfo.musl) dependsOn(testNativeAlpineLinuxAmd64) else dependsOn(testNativeLinuxAmd64) if (buildInfo.musl) dependsOn(testNativeAlpineLinuxAmd64)
} else dependsOn(testNativeLinuxAmd64)
buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> { }
dependsOn(testNativeWindowsAmd64) buildInfo.os.isWindows && buildInfo.targetArch == "amd64" -> {
} dependsOn(testNativeWindowsAmd64)
else -> { }
doLast { else -> {
throw GradleException( doLast {
"Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}" throw GradleException(
) "Cannot build targeting ${buildInfo.os.name}/${buildInfo.targetArch} with musl=${buildInfo.musl}"
)
}
} }
} }
} }
}
val checkNative by tasks.registering { val checkNative =
group = "verification" tasks.register("checkNative") {
dependsOn(testNative) group = "verification"
} dependsOn(testNative)
}
val buildNative by tasks.registering { val buildNative =
group = "build" tasks.register("buildNative") {
dependsOn(checkNative) group = "build"
} dependsOn(checkNative)
}
@@ -15,7 +15,7 @@
*/ */
plugins { id("com.diffplug.spotless") } plugins { id("com.diffplug.spotless") }
val pklFormatter by configurations.creating val pklFormatter = configurations.create("pklFormatter")
dependencies { pklFormatter(rootProject.project("pkl-formatter")) } dependencies { pklFormatter(rootProject.project("pkl-formatter")) }
+4 -3
View File
@@ -37,7 +37,8 @@ nexusPublishing {
} }
} }
val configureLateInitAnnotation by tasks.registering(ConfigureLateInitAnnotation::class) val configureLateInitAnnotation =
tasks.register<ConfigureLateInitAnnotation>("configureLateInitAnnotation")
idea { idea {
project { project {
@@ -53,9 +54,9 @@ idea {
} }
} }
val clean by tasks.existing { delete(layout.buildDirectory) } val clean = tasks.named("clean") { delete(layout.buildDirectory) }
val printVersion by tasks.registering { doFirst { println(buildInfo.pklVersion) } } val printVersion = tasks.register("printVersion") { doFirst { println(buildInfo.pklVersion) } }
val message = val message =
""" """
+2 -2
View File
@@ -1,6 +1,6 @@
name: main name: main
title: Main Project title: Main Project
version: 0.32.0-dev version: 0.32.2
prerelease: true prerelease: false
nav: nav:
- nav.adoc - nav.adoc
@@ -3,10 +3,10 @@
// the following attributes must be updated immediately before a release // the following attributes must be updated immediately before a release
// pkl version corresponding to current git commit without -dev suffix or git hash // pkl version corresponding to current git commit without -dev suffix or git hash
:pkl-version-no-suffix: 0.32.0 :pkl-version-no-suffix: 0.32.2
// tells whether pkl version corresponding to current git commit // tells whether pkl version corresponding to current git commit
// is a release version (:is-release-version: '') or dev version (:!is-release-version:) // is a release version (:is-release-version: '') or dev version (:!is-release-version:)
:!is-release-version: :is-release-version: ''
// the remaining attributes do not need to be updated regularly // the remaining attributes do not need to be updated regularly
+1 -1
View File
@@ -1,6 +1,6 @@
= Pkl 0.32 Release Notes = Pkl 0.32 Release Notes
:version: 0.32 :version: 0.32
:version-minor: 0.32.0 :version-minor: 0.32.2
:release-date: July 8th, 2026 :release-date: July 8th, 2026
:version-next: 0.33 :version-next: 0.33
:version-next-date: October 2026 :version-next-date: October 2026
@@ -1,8 +1,44 @@
= Changelog = Changelog
include::ROOT:partial$component-attributes.adoc[] include::ROOT:partial$component-attributes.adoc[]
[[release-0.32.2]]
== 0.32.2 (2026-10-21)
=== Security Fixes [small]#🔒#
* Update fallback CA certificate trust and remove trust for the issuer of compromised intermediate CAs (pr:https://github.com/apple/pkl/pull/1923).
=== Fixes
* Fix rendering of stack traces for evaluated multi-line expressions (pr:https://github.com/apple/pkl/pull/1793).
=== Contributors ❤️
Thank you to all the contributors for this release!
* https://github.com/HT154[@HT154]
* https://github.com/protobufel2[@protobufel2]
[[release-0.32.1]]
== 0.32.1 (2026-07-23)
=== Fixes
* Fix closing intermediate response bodies when following HTTP redirects (https://github.com/apple/pkl/pull/1749[#1749]).
* Fix `eval -x` of local members (https://github.com/apple/pkl/pull/1760[#1760]).
* Fix variable resolution of object body params (https://github.com/apple/pkl/pull/1788[#1788]).
=== Contributors ❤️
Thank you to all the contributors for this release!
* https://github.com/bioball[@bioball]
* https://github.com/KushalP[@KushalP]
[[release-0.32.0]] [[release-0.32.0]]
== 0.32.0 (UNRELEASED) == 0.32.0 (2026-07-08)
xref:0.32.adoc[Release Notes]
[[release-0.31.1]] [[release-0.31.1]]
== 0.31.1 (2026-03-26) == 0.31.1 (2026-03-26)
@@ -38,6 +74,7 @@ Thank you to all the contributors for this release!
== 0.31.0 (2026-02-26) == 0.31.0 (2026-02-26)
xref:0.31.adoc[Release Notes] xref:0.31.adoc[Release Notes]
[[release-0.30.2]] [[release-0.30.2]]
== 0.30.2 (2025-12-15) == 0.30.2 (2025-12-15)
+1 -1
View File
@@ -1,7 +1,7 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
group=org.pkl-lang group=org.pkl-lang
version=0.32.0 version=0.32.2
# google-java-format requires jdk.compiler exports # google-java-format requires jdk.compiler exports
org.gradle.jvmargs= \ org.gradle.jvmargs= \
+3 -3
View File
@@ -2,15 +2,15 @@
"catalogs": {}, "catalogs": {},
"aliases": { "aliases": {
"pkl": { "pkl": {
"script-ref": "org.pkl-lang:pkl-cli-java:0.31.1", "script-ref": "org.pkl-lang:pkl-cli-java:0.32.2",
"java-agents": [] "java-agents": []
}, },
"pkl-codegen-java": { "pkl-codegen-java": {
"script-ref": "org.pkl-lang:pkl-codegen-java:0.31.1", "script-ref": "org.pkl-lang:pkl-codegen-java:0.32.2",
"java-agents": [] "java-agents": []
}, },
"pkl-codegen-kotlin": { "pkl-codegen-kotlin": {
"script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.31.1", "script-ref": "org.pkl-lang:pkl-codegen-kotlin:0.32.2",
"java-agents": [] "java-agents": []
} }
}, },
+3 -3
View File
@@ -83,8 +83,8 @@ tasks.shadowJar {
exclude("module-info.*") exclude("module-info.*")
} }
val testJavaExecutable by val testJavaExecutable =
tasks.registering(Test::class) { tasks.register<Test>("testJavaExecutable") {
testClassesDirs = tasks.test.get().testClassesDirs testClassesDirs = tasks.test.get().testClassesDirs
classpath = classpath =
// compiled test classes // compiled test classes
@@ -138,7 +138,7 @@ fun Exec.useRootDirAndSuppressOutput() {
// 0.28 Preparing for JDK21 toolchains revealed that `testStartJavaExecutable` may pass, even though // 0.28 Preparing for JDK21 toolchains revealed that `testStartJavaExecutable` may pass, even though
// the evaluator fails. To catch this, we eval a simple expression using the fat jar. // the evaluator fails. To catch this, we eval a simple expression using the fat jar.
val testEvalJavaExecutable by val testEvalJavaExecutable =
setupJavaExecutableRun("testEvalJavaExecutable", evalTestFlags) { useRootDirAndSuppressOutput() } setupJavaExecutableRun("testEvalJavaExecutable", evalTestFlags) { useRootDirAndSuppressOutput() }
// Run the same evaluator tests on all configured JDK test versions. // Run the same evaluator tests on all configured JDK test versions.
@@ -1777,7 +1777,7 @@ result = someLib.x
PackageServer.populateCacheDir(tempDir) PackageServer.populateCacheDir(tempDir)
val projectPath = val projectPath =
FileTestUtils.rootProjectDir.resolve( FileTestUtils.rootProjectDir.resolve(
"pkl-commons-cli/src/main/resources/org/pkl/commons/cli/project1/" "pkl-commons-cli/src/test/resources/org/pkl/commons/cli/project1/"
) )
val options = val options =
CliEvaluatorOptions( CliEvaluatorOptions(
+3 -2
View File
@@ -19,10 +19,11 @@ plugins {
id("pklPublishLibrary") id("pklPublishLibrary")
} }
val svmClasspath: Configuration by configurations.creating val svmClasspath: Configuration = configurations.create("svmClasspath")
// used by pklNativeExecutable.gradle.kts // used by pklNativeExecutable.gradle.kts
@Suppress("unused") val svm: SourceSet by sourceSets.creating { compileClasspath = svmClasspath } @Suppress("unused")
val svm: SourceSet = sourceSets.create("svm") { compileClasspath = svmClasspath }
dependencies { dependencies {
api(projects.pklCore) api(projects.pklCore)
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -37,7 +37,7 @@ dependencies {
* These packages are used by PackageServer to serve assets when running LanguageSnippetTests and * These packages are used by PackageServer to serve assets when running LanguageSnippetTests and
* PackageResolversTest. * PackageResolversTest.
*/ */
val createTestPackages by tasks.registering val createTestPackages = tasks.register("createTestPackages")
// make sure that declaring a dependency on this project suffices to have test fixtures generated // make sure that declaring a dependency on this project suffices to have test fixtures generated
tasks.processResources { tasks.processResources {
@@ -92,8 +92,8 @@ val keystoreName = "localhost.p12"
val keystoreFile = keystoreDir.map { it.file(keystoreName) } val keystoreFile = keystoreDir.map { it.file(keystoreName) }
val certsFileName = "localhost.pem" val certsFileName = "localhost.pem"
val generateKeys by val generateKeys =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("generateKeys") {
outputs.file(keystoreFile) outputs.file(keystoreFile)
mainClass.set("sun.security.tools.keytool.Main") mainClass.set("sun.security.tools.keytool.Main")
args = args =
@@ -117,8 +117,8 @@ val generateKeys by
} }
} }
val exportCerts by val exportCerts =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("exportCerts") {
val outputFile = keystoreDir.map { it.file(certsFileName) } val outputFile = keystoreDir.map { it.file(certsFileName) }
dependsOn(generateKeys) dependsOn(generateKeys)
inputs.file(keystoreFile) inputs.file(keystoreFile)
+7 -7
View File
@@ -22,11 +22,11 @@ plugins {
signing signing
} }
val pklCodegenJava: Configuration by configurations.creating val pklCodegenJava: Configuration = configurations.create("pklCodegenJava")
val firstPartySourcesJars by configurations.existing val firstPartySourcesJars = configurations.named("firstPartySourcesJars")
val generateTestConfigClasses by val generateTestConfigClasses =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("generateTestConfigClasses") {
val outputDir = layout.buildDirectory.dir("testConfigClasses") val outputDir = layout.buildDirectory.dir("testConfigClasses")
outputs.dir(outputDir) outputs.dir(outputDir)
inputs.dir("src/test/resources/codegenPkl") inputs.dir("src/test/resources/codegenPkl")
@@ -45,13 +45,13 @@ tasks.processTestResources { dependsOn(generateTestConfigClasses) }
tasks.compileTestKotlin { dependsOn(generateTestConfigClasses) } tasks.compileTestKotlin { dependsOn(generateTestConfigClasses) }
val bundleTests by tasks.registering(Jar::class) { from(sourceSets.test.get().output) } val bundleTests = tasks.register<Jar>("bundleTests") { from(sourceSets.test.get().output) }
// Runs unit tests using jar'd class files as a source. // Runs unit tests using jar'd class files as a source.
// This is to test loading the ClassRegistry from within a jar, as opposed to directly from the file // This is to test loading the ClassRegistry from within a jar, as opposed to directly from the file
// system. // system.
val testFromJar by val testFromJar =
tasks.registering(Test::class) { tasks.register<Test>("testFromJar") {
dependsOn(bundleTests) dependsOn(bundleTests)
testClassesDirs = files(tasks.test.get().testClassesDirs) testClassesDirs = files(tasks.test.get().testClassesDirs)
@@ -19,11 +19,11 @@ plugins {
id("pklPublishLibrary") id("pklPublishLibrary")
} }
val pklConfigJava: Configuration by configurations.creating val pklConfigJava: Configuration = configurations.create("pklConfigJava")
val pklConfigJavaAll: Configuration by configurations.creating val pklConfigJavaAll: Configuration = configurations.create("pklConfigJavaAll")
val pklCodegenKotlin: Configuration by configurations.creating val pklCodegenKotlin: Configuration = configurations.create("pklCodegenKotlin")
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
@@ -49,8 +49,8 @@ dependencies {
testImplementation(libs.geantyref) testImplementation(libs.geantyref)
} }
val generateTestConfigClasses by val generateTestConfigClasses =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("generateTestConfigClasses") {
val outputDir = layout.buildDirectory.dir("testConfigClasses") val outputDir = layout.buildDirectory.dir("testConfigClasses")
outputs.dir(outputDir) outputs.dir(outputDir)
inputs.dir("src/test/resources/codegenPkl") inputs.dir("src/test/resources/codegenPkl")
+42 -40
View File
@@ -35,9 +35,10 @@ val externalReaderFixtureSourceSet: NamedDomainObjectProvider<SourceSet> =
runtimeClasspath += sourceSets.test.get().output + sourceSets.test.get().runtimeClasspath runtimeClasspath += sourceSets.test.get().output + sourceSets.test.get().runtimeClasspath
} }
val externalReaderFixtureImplementation: Configuration by configurations.getting { val externalReaderFixtureImplementation: Configuration =
extendsFrom(configurations.testImplementation.get()) configurations.getByName("externalReaderFixtureImplementation") {
} extendsFrom(configurations.testImplementation.get())
}
idea { idea {
module { module {
@@ -137,36 +138,37 @@ tasks.withType<JavaCompile>().configureEach {
tasks.compileKotlin { enabled = false } tasks.compileKotlin { enabled = false }
val externalReaderFixture by tasks.registering { val externalReaderFixture =
group = "build" tasks.register("externalReaderFixture") {
dependsOn(tasks.named("compileExternalReaderFixtureJava")) group = "build"
inputs.files(externalReaderFixtureSourceSet.map { it.output }) dependsOn(tasks.named("compileExternalReaderFixtureJava"))
val fileName = if (buildInfo.os.isWindows) "externalreader.bat" else "externalreader" inputs.files(externalReaderFixtureSourceSet.map { it.output })
val outputFile = layout.buildDirectory.file("fixtures/$fileName") val fileName = if (buildInfo.os.isWindows) "externalreader.bat" else "externalreader"
outputs.file(outputFile) val outputFile = layout.buildDirectory.file("fixtures/$fileName")
doLast { outputs.file(outputFile)
val classpath = externalReaderFixtureSourceSet.get().runtimeClasspath.asPath doLast {
val scriptContent = val classpath = externalReaderFixtureSourceSet.get().runtimeClasspath.asPath
if (buildInfo.os.isWindows) { val scriptContent =
""" if (buildInfo.os.isWindows) {
"""
@echo off @echo off
java -cp $classpath org.pkl.core.externalreaderfixture.Main java -cp $classpath org.pkl.core.externalreaderfixture.Main
""" """
.trimIndent() .trimIndent()
} else { } else {
""" """
#!/usr/bin/env bash #!/usr/bin/env bash
java -cp $classpath org.pkl.core.externalreaderfixture.Main java -cp $classpath org.pkl.core.externalreaderfixture.Main
""" """
.trimIndent() .trimIndent()
} }
outputFile.get().asFile.writeText(scriptContent) outputFile.get().asFile.writeText(scriptContent)
outputFile.get().asFile.setExecutable(true) outputFile.get().asFile.setExecutable(true)
println("Created external reader ${outputFile.get().asFile.absolutePath}") println("Created external reader ${outputFile.get().asFile.absolutePath}")
}
} }
}
tasks.test { tasks.test {
configureTest() configureTest()
@@ -194,8 +196,8 @@ tasks.test {
) )
} }
val generateBaseModuleMembers by val generateBaseModuleMembers =
tasks.registering(JavaExec::class) { tasks.register<JavaExec>("generateBaseModuleMembers") {
group = "build" group = "build"
val outputDir = layout.buildDirectory.dir("generated/sources/baseModuleMembers") val outputDir = layout.buildDirectory.dir("generated/sources/baseModuleMembers")
@@ -226,8 +228,8 @@ tasks.compileJava { dependsOn(generateBaseModuleMembers) }
tasks.sourcesJar { dependsOn(generateBaseModuleMembers) } tasks.sourcesJar { dependsOn(generateBaseModuleMembers) }
val testJavaExecutable by val testJavaExecutable =
tasks.registering(Test::class) { tasks.register<Test>("testJavaExecutable") {
configureExecutableTest("LanguageSnippetTestsEngine") configureExecutableTest("LanguageSnippetTestsEngine")
classpath = classpath =
// compiled test classes // compiled test classes
@@ -246,38 +248,38 @@ val testJavaExecutable by
tasks.check { dependsOn(testJavaExecutable) } tasks.check { dependsOn(testJavaExecutable) }
val testMacExecutableAmd64 by val testMacExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testMacExecutableAmd64") {
dependsOn(":pkl-cli:macExecutableAmd64") dependsOn(":pkl-cli:macExecutableAmd64")
configureExecutableTest("MacAmd64LanguageSnippetTestsEngine") configureExecutableTest("MacAmd64LanguageSnippetTestsEngine")
} }
val testMacExecutableAarch64 by val testMacExecutableAarch64 =
tasks.registering(Test::class) { tasks.register<Test>("testMacExecutableAarch64") {
dependsOn(":pkl-cli:macExecutableAarch64") dependsOn(":pkl-cli:macExecutableAarch64")
configureExecutableTest("MacAarch64LanguageSnippetTestsEngine") configureExecutableTest("MacAarch64LanguageSnippetTestsEngine")
} }
val testLinuxExecutableAmd64 by val testLinuxExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testLinuxExecutableAmd64") {
dependsOn(":pkl-cli:linuxExecutableAmd64") dependsOn(":pkl-cli:linuxExecutableAmd64")
configureExecutableTest("LinuxAmd64LanguageSnippetTestsEngine") configureExecutableTest("LinuxAmd64LanguageSnippetTestsEngine")
} }
val testLinuxExecutableAarch64 by val testLinuxExecutableAarch64 =
tasks.registering(Test::class) { tasks.register<Test>("testLinuxExecutableAarch64") {
dependsOn(":pkl-cli:linuxExecutableAarch64") dependsOn(":pkl-cli:linuxExecutableAarch64")
configureExecutableTest("LinuxAarch64LanguageSnippetTestsEngine") configureExecutableTest("LinuxAarch64LanguageSnippetTestsEngine")
} }
val testAlpineExecutableAmd64 by val testAlpineExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testAlpineExecutableAmd64") {
dependsOn(":pkl-cli:alpineExecutableAmd64") dependsOn(":pkl-cli:alpineExecutableAmd64")
configureExecutableTest("AlpineLanguageSnippetTestsEngine") configureExecutableTest("AlpineLanguageSnippetTestsEngine")
} }
val testWindowsExecutableAmd64 by val testWindowsExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testWindowsExecutableAmd64") {
dependsOn(":pkl-cli:windowsExecutableAmd64") dependsOn(":pkl-cli:windowsExecutableAmd64")
configureExecutableTest("WindowsLanguageSnippetTestsEngine") configureExecutableTest("WindowsLanguageSnippetTestsEngine")
} }
@@ -523,6 +523,13 @@ public final class SymbolTable {
if (scope instanceof LexicalScope lex) { if (scope instanceof LexicalScope lex) {
if (shouldSkip && !(scope instanceof ForGeneratorScope)) { if (shouldSkip && !(scope instanceof ForGeneratorScope)) {
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) { if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
// skipping the object scope should now see the object body params, because the params
// are one level higher than the body itself.
var result = fun.apply(objectScope, levelsUp);
if (result instanceof Parameter parameter) {
//noinspection unchecked
return (R) new Parameter(parameter.slot(), parameter.levelsUp() - 1);
}
levelsUp++; levelsUp++;
} }
// An EagerGeneratorScope (for `when` predicates) skipped an ObjectScope. // An EagerGeneratorScope (for `when` predicates) skipped an ObjectScope.
@@ -16,6 +16,7 @@
package org.pkl.core.http; package org.pkl.core.http;
import com.google.errorprone.annotations.ThreadSafe; import com.google.errorprone.annotations.ThreadSafe;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@@ -118,6 +119,9 @@ final class RequestRewritingClient implements HttpClient {
if (!HttpUtils.isRedirectStatusCode(response.statusCode())) { if (!HttpUtils.isRedirectStatusCode(response.statusCode())) {
return response; return response;
} }
if (response.body() instanceof Closeable closeable) {
closeable.close();
}
if (redirectCount >= MAX_HTTP_REDIRECTS) { if (redirectCount >= MAX_HTTP_REDIRECTS) {
throw new HttpClientException( throw new HttpClientException(
ErrorMessages.create("httpTooManyRedirects", MAX_HTTP_REDIRECTS)); ErrorMessages.create("httpTooManyRedirects", MAX_HTTP_REDIRECTS));
@@ -50,7 +50,6 @@ import org.pkl.core.util.AnsiStringBuilder;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.IoUtils; import org.pkl.core.util.IoUtils;
import org.pkl.core.util.MutableReference; import org.pkl.core.util.MutableReference;
import org.pkl.core.util.Pair;
import org.pkl.core.util.SyntaxHighlighter; import org.pkl.core.util.SyntaxHighlighter;
import org.pkl.parser.Parser; import org.pkl.parser.Parser;
import org.pkl.parser.ParserError; import org.pkl.parser.ParserError;
@@ -183,51 +182,6 @@ public class ReplServer implements AutoCloseable {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/**
* Create a fake module that declares all the local properties that exist in ReplState, so that
* AstBuilder can correctly resolve variables. Additionally, return a {@link Node} with its span
* calculated in terms of this fake module.
*
* <p>This created module is never executed.
*/
private Pair<String, Node> buildSyntheticModuleText(Node syntaxNode, String srcText) {
if (syntaxNode instanceof ModuleDecl) {
return Pair.of(srcText, syntaxNode);
}
var sb = new StringBuilder();
var nodeText = syntaxNode.text(srcText.toCharArray());
var adjustedNode = syntaxNode;
if (syntaxNode instanceof Expr) {
sb.append(expressionPreamble).append(nodeText).append('\n');
} else {
sb.append(nodeText).append('\n');
}
var mod = parser.parseModule(sb.toString());
if (syntaxNode instanceof Expr) {
adjustedNode = mod.getProperties().get(0).getExpr();
} else if (syntaxNode instanceof ImportClause) {
adjustedNode = mod.getImports().get(0);
} else if (syntaxNode instanceof ClassProperty) {
adjustedNode = mod.getProperties().get(0);
} else if (syntaxNode instanceof Class) {
adjustedNode = mod.getClasses().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.TypeAlias) {
adjustedNode = mod.getTypeAliases().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassMethod) {
adjustedNode = mod.getMethods().get(0);
}
var cursor = EconomicMaps.getEntries(replState.module.getMembers());
while (cursor.advance()) {
var key = cursor.getKey();
var value = cursor.getValue();
if (value.isLocal()) {
sb.append("local ").append(key).append(" = Undefined()\n\n");
}
}
assert adjustedNode != null;
return Pair.of(sb.toString(), adjustedNode);
}
@SuppressWarnings("StatementWithEmptyBody") @SuppressWarnings("StatementWithEmptyBody")
private void handleNode( private void handleNode(
ReplState replState, ReplState replState,
@@ -237,7 +191,7 @@ public class ReplServer implements AutoCloseable {
String srcText, String srcText,
boolean evalDefinitions, boolean evalDefinitions,
boolean forceResults) { boolean forceResults) {
var pair = buildSyntheticModuleText(node, srcText); var pair = VmUtils.buildSyntheticModuleText(node, replState.module, srcText);
var syntheticModuleText = pair.first; var syntheticModuleText = pair.first;
var adjustedNode = pair.second; var adjustedNode = pair.second;
var module = ModuleKeys.synthetic(uri, workingDir.toUri(), uri, syntheticModuleText, false); var module = ModuleKeys.synthetic(uri, workingDir.toUri(), uri, syntheticModuleText, false);
@@ -283,8 +237,9 @@ public class ReplServer implements AutoCloseable {
new ReplResponse.InternalError(new IllegalStateException("Unexpected parse result"))); new ReplResponse.InternalError(new IllegalStateException("Unexpected parse result")));
} }
} catch (VmException e) { } catch (VmException e) {
e.setForExpressionInput(true);
// TODO: patch stack trace for constants // TODO: patch stack trace for constants
results.add(new EvalError(renderException(e))); results.add(new EvalError(errorRenderer.render(e)));
} }
} }
@@ -28,6 +28,8 @@ import org.pkl.core.util.SyntaxHighlighter;
public final class StackTraceRenderer { public final class StackTraceRenderer {
private final Function<StackFrame, StackFrame> frameTransformer; private final Function<StackFrame, StackFrame> frameTransformer;
private static final int PREAMBLE_LENGTH = VmUtils.EXPRESSION_PREAMBLE.length();
public StackTraceRenderer(Function<StackFrame, StackFrame> frameTransformer) { public StackTraceRenderer(Function<StackFrame, StackFrame> frameTransformer) {
this.frameTransformer = frameTransformer; this.frameTransformer = frameTransformer;
} }
@@ -36,9 +38,10 @@ public final class StackTraceRenderer {
List<StackFrame> frames, List<StackFrame> frames,
@Nullable String hint, @Nullable String hint,
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
AnsiStringBuilder out) { AnsiStringBuilder out,
boolean forExpressionInput) {
var compressed = compressFrames(frames); var compressed = compressFrames(frames);
doRender(compressed, hint, hintBuilder, out, "", true); doRender(compressed, hint, hintBuilder, out, "", true, forExpressionInput);
} }
// non-private for testing // non-private for testing
@@ -48,12 +51,13 @@ public final class StackTraceRenderer {
@Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder, @Nullable BiConsumer<AnsiStringBuilder, Boolean> hintBuilder,
AnsiStringBuilder out, AnsiStringBuilder out,
String leftMargin, String leftMargin,
boolean isFirstElement) { boolean isFirstElement,
boolean forExpressionInput) {
for (var frame : frames) { for (var frame : frames) {
if (frame instanceof StackFrameLoop loop) { if (frame instanceof StackFrameLoop loop) {
// ensure a cycle of length 1 doesn't get rendered as a loop // ensure a cycle of length 1 doesn't get rendered as a loop
if (loop.count == 1) { if (loop.count == 1) {
doRender(loop.frames, null, null, out, leftMargin, isFirstElement); doRender(loop.frames, null, null, out, leftMargin, isFirstElement, forExpressionInput);
} else { } else {
if (!isFirstElement) { if (!isFirstElement) {
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n'); out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
@@ -63,7 +67,7 @@ public final class StackTraceRenderer {
.append(AnsiTheme.STACK_TRACE_LOOP_COUNT, loop.count) .append(AnsiTheme.STACK_TRACE_LOOP_COUNT, loop.count)
.append(" repetitions of:\n"); .append(" repetitions of:\n");
var newLeftMargin = leftMargin + "│ "; var newLeftMargin = leftMargin + "│ ";
doRender(loop.frames, null, null, out, newLeftMargin, isFirstElement); doRender(loop.frames, null, null, out, newLeftMargin, isFirstElement, forExpressionInput);
if (isFirstElement) { if (isFirstElement) {
renderHint(hint, hintBuilder, out, newLeftMargin); renderHint(hint, hintBuilder, out, newLeftMargin);
isFirstElement = false; isFirstElement = false;
@@ -74,7 +78,7 @@ public final class StackTraceRenderer {
if (!isFirstElement) { if (!isFirstElement) {
out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n'); out.append(AnsiTheme.STACK_TRACE_MARGIN, leftMargin).append('\n');
} }
renderFrame((StackFrame) frame, out, leftMargin); renderFrame((StackFrame) frame, out, leftMargin, forExpressionInput);
} }
if (isFirstElement) { if (isFirstElement) {
@@ -84,9 +88,10 @@ public final class StackTraceRenderer {
} }
} }
private void renderFrame(StackFrame frame, AnsiStringBuilder out, String leftMargin) { private void renderFrame(
StackFrame frame, AnsiStringBuilder out, String leftMargin, boolean forExpressionInput) {
var transformed = frameTransformer.apply(frame); var transformed = frameTransformer.apply(frame);
renderSourceLine(transformed, out, leftMargin); renderSourceLine(transformed, out, leftMargin, forExpressionInput);
renderSourceLocation(transformed, out, leftMargin); renderSourceLocation(transformed, out, leftMargin);
} }
@@ -113,14 +118,21 @@ public final class StackTraceRenderer {
out.append('\n'); out.append('\n');
} }
private void renderSourceLine(StackFrame frame, AnsiStringBuilder out, String leftMargin) { private void renderSourceLine(
StackFrame frame, AnsiStringBuilder out, String leftMargin, boolean forExpressionInput) {
var originalSourceLine = frame.getSourceLines().get(0); var originalSourceLine = frame.getSourceLines().get(0);
var leadingWhitespace = VmUtils.countLeadingWhitespace(originalSourceLine); var leadingWhitespace = VmUtils.countLeadingWhitespace(originalSourceLine);
var sourceLine = originalSourceLine.strip(); var sourceLine = originalSourceLine.strip();
var startColumn = frame.getStartColumn() - leadingWhitespace; var hasPreamble = false;
if (forExpressionInput && sourceLine.startsWith(VmUtils.EXPRESSION_PREAMBLE)) {
sourceLine = sourceLine.substring(PREAMBLE_LENGTH);
hasPreamble = true;
}
var preambleOffset = hasPreamble ? PREAMBLE_LENGTH : 0;
var startColumn = frame.getStartColumn() - leadingWhitespace - preambleOffset;
var endColumn = var endColumn =
frame.getStartLine() == frame.getEndLine() frame.getStartLine() == frame.getEndLine()
? frame.getEndColumn() - leadingWhitespace ? frame.getEndColumn() - leadingWhitespace - preambleOffset
: sourceLine.length(); : sourceLine.length();
var prefix = frame.getStartLine() + " | "; var prefix = frame.getStartLine() + " | ";
@@ -37,6 +37,7 @@ public abstract class VmException extends AbstractTruffleException {
private final List<StackFrame> leadingStackFrames; private final List<StackFrame> leadingStackFrames;
@Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder; @Nullable private final BiConsumer<AnsiStringBuilder, Boolean> messageBuilder;
@Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder; @Nullable protected BiConsumer<AnsiStringBuilder, Boolean> hintBuilder;
private boolean forExpressionInput;
public VmException( public VmException(
@Nullable String message, @Nullable String message,
@@ -62,6 +63,7 @@ public abstract class VmException extends AbstractTruffleException {
this.insertedStackFrames = insertedStackFrames; this.insertedStackFrames = insertedStackFrames;
this.leadingStackFrames = leadingStackFrames; this.leadingStackFrames = leadingStackFrames;
this.hintBuilder = hintBuilder; this.hintBuilder = hintBuilder;
this.forExpressionInput = forExpressionInput;
} }
public final boolean isExternalMessage() { public final boolean isExternalMessage() {
@@ -112,6 +114,14 @@ public abstract class VmException extends AbstractTruffleException {
this.hintBuilder = ((builder, aBoolean) -> builder.append(hint)); this.hintBuilder = ((builder, aBoolean) -> builder.append(hint));
} }
public void setForExpressionInput(boolean forExpressionInput) {
this.forExpressionInput = forExpressionInput;
}
public boolean isForExpressionInput() {
return forExpressionInput;
}
public enum Kind { public enum Kind {
EVAL_ERROR, EVAL_ERROR,
UNDEFINED_VALUE, UNDEFINED_VALUE,
@@ -144,7 +144,8 @@ public final class VmExceptionRenderer {
var hintBuilder = exception.getHintBuilder(); var hintBuilder = exception.getHintBuilder();
if (!frames.isEmpty()) { if (!frames.isEmpty()) {
stackTraceRenderer.render(frames, hint, hintBuilder, out.append('\n')); stackTraceRenderer.render(
frames, hint, hintBuilder, out.append('\n'), exception.isForExpressionInput());
} else if (hint != null) { } else if (hint != null) {
// render hint if there are no stack frames // render hint if there are no stack frames
out.append('\n').append(AnsiTheme.ERROR_MESSAGE_HINT, hint); out.append('\n').append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
@@ -34,7 +34,7 @@ import org.pkl.parser.syntax.Module;
@TruffleLanguage.Registration( @TruffleLanguage.Registration(
id = "pkl", id = "pkl",
name = "Pkl", name = "Pkl",
version = "0.32.0-dev", version = "0.32.2",
characterMimeTypes = VmLanguage.MIME_TYPE, characterMimeTypes = VmLanguage.MIME_TYPE,
contextPolicy = ContextPolicy.SHARED) contextPolicy = ContextPolicy.SHARED)
@ProvidedTags(PklTags.Expression.class) @ProvidedTags(PklTags.Expression.class)
@@ -61,9 +61,12 @@ import org.pkl.core.module.ModuleKey;
import org.pkl.core.module.ModuleKeys; import org.pkl.core.module.ModuleKeys;
import org.pkl.core.module.ResolvedModuleKey; import org.pkl.core.module.ResolvedModuleKey;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.Pair;
import org.pkl.parser.Parser; import org.pkl.parser.Parser;
import org.pkl.parser.ParserError; import org.pkl.parser.ParserError;
import org.pkl.parser.syntax.Expr; import org.pkl.parser.syntax.Expr;
import org.pkl.parser.syntax.ImportClause;
import org.pkl.parser.syntax.ModuleDecl;
public final class VmUtils { public final class VmUtils {
/** See {@link VmUtils#shouldRunTypeCheck(VirtualFrame)}. */ /** See {@link VmUtils#shouldRunTypeCheck(VirtualFrame)}. */
@@ -92,6 +95,10 @@ public final class VmUtils {
private static final DecimalFormatSymbols ROOT_DECIMAL_FORMAT_SYMBOLS = private static final DecimalFormatSymbols ROOT_DECIMAL_FORMAT_SYMBOLS =
DecimalFormatSymbols.getInstance(Locale.ROOT); DecimalFormatSymbols.getInstance(Locale.ROOT);
public static final String EXPRESSION_PREAMBLE = "`THE REPL TEXT EXPR` = ";
private static final Parser parser = new Parser();
private VmUtils() {} private VmUtils() {}
static VmTyped createEmptyModule() { static VmTyped createEmptyModule() {
@@ -930,12 +937,69 @@ public final class VmUtils {
} }
} }
/**
* Create a fake module that declares all the local properties that exist in {@code module}, so
* that AstBuilder can correctly resolve variables.
*
* <p>Additionally, return a {@link org.pkl.parser.syntax.Node} with its span calculated in terms
* of this fake module.
*
* <p>This created module is never executed.
*/
public static Pair<String, org.pkl.parser.syntax.Node> buildSyntheticModuleText(
org.pkl.parser.syntax.Node syntaxNode, VmTyped module, String srcText) {
if (syntaxNode instanceof ModuleDecl) {
return Pair.of(srcText, syntaxNode);
}
var sb = new StringBuilder();
var nodeText = syntaxNode.text(srcText.toCharArray());
var adjustedNode = syntaxNode;
if (syntaxNode instanceof Expr) {
sb.append(EXPRESSION_PREAMBLE).append(nodeText).append('\n');
} else {
sb.append(nodeText).append('\n');
}
var mod = parser.parseModule(sb.toString());
if (syntaxNode instanceof Expr) {
adjustedNode = mod.getProperties().get(0).getExpr();
} else if (syntaxNode instanceof ImportClause) {
adjustedNode = mod.getImports().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassProperty) {
adjustedNode = mod.getProperties().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.Class) {
adjustedNode = mod.getClasses().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.TypeAlias) {
adjustedNode = mod.getTypeAliases().get(0);
} else if (syntaxNode instanceof org.pkl.parser.syntax.ClassMethod) {
adjustedNode = mod.getMethods().get(0);
}
var cursor = EconomicMaps.getEntries(module.getMembers());
while (cursor.advance()) {
var key = cursor.getKey();
var value = cursor.getValue();
if (value.isLocal()) {
sb.append("local ").append(key).append(" = Undefined()\n\n");
}
}
assert adjustedNode != null;
return Pair.of(sb.toString(), adjustedNode);
}
public static Object evaluateExpression( public static Object evaluateExpression(
VmTyped module, VmTyped module,
String expression, String expression,
SecurityManager securityManager, SecurityManager securityManager,
ModuleResolver moduleResolver) { ModuleResolver moduleResolver) {
var syntheticModule = ModuleKeys.synthetic(REPL_TEXT_URI, expression); org.pkl.parser.syntax.Node node;
try {
node = parser.parseExpressionInput(expression);
} catch (ParserError e) {
throw toVmException(e, expression, REPL_TEXT_URI, "");
}
var pair = buildSyntheticModuleText(node, module, expression);
var syntheticModuleText = pair.first;
var adjustedNode = pair.second;
var syntheticModule = ModuleKeys.synthetic(REPL_TEXT_URI, syntheticModuleText);
ResolvedModuleKey resolvedModule; ResolvedModuleKey resolvedModule;
try { try {
resolvedModule = syntheticModule.resolve(securityManager); resolvedModule = syntheticModule.resolve(securityManager);
@@ -955,9 +1019,10 @@ public final class VmUtils {
resolvedModule, resolvedModule,
false); false);
var language = VmLanguage.get(null); var language = VmLanguage.get(null);
var parsedExpression = parseExpressionNode(expression, source);
var builder = new AstBuilder(source, language, moduleInfo, moduleResolver); var builder = new AstBuilder(source, language, moduleInfo, moduleResolver);
var exprNode = builder.visitExpr(parsedExpression); var mod = parser.parseModule(syntheticModuleText);
builder.visitModule(mod);
var exprNode = builder.visitExpr((Expr) adjustedNode);
var rootNode = var rootNode =
new SimpleRootNode( new SimpleRootNode(
language, language,
@@ -966,7 +1031,12 @@ public final class VmUtils {
"", "",
exprNode); exprNode);
var callNode = Truffle.getRuntime().createIndirectCallNode(); var callNode = Truffle.getRuntime().createIndirectCallNode();
return callNode.call(rootNode.getCallTarget(), module, module); try {
return callNode.call(rootNode.getCallTarget(), module, module);
} catch (VmException e) {
e.setForExpressionInput(true);
throw e;
}
} }
public static int findCustomThisSlot(VirtualFrame frame) { public static int findCustomThisSlot(VirtualFrame frame) {
@@ -0,0 +1,3 @@
// make sure that we don't trim this identifier when not using the expression evaluator
// see VmUtils.buildSyntheticModuleText()
`THE REPL TEXT EXPR` = throw("uh oh")
@@ -0,0 +1,9 @@
foo = new Mapping {
default { key ->
for (idx, char in key.split("")) {
["\(char)_\(idx)"] = "hi"
}
}
["zzz"] {}
}
@@ -0,0 +1,8 @@
foo = new Mapping {
default { key ->
when (key is String) {
bar = key
}
}
["x"] {}
}
@@ -0,0 +1,14 @@
–– Pkl Error ––
uh oh
x | `THE REPL TEXT EXPR` = throw("uh oh")
^^^^^^^^^^^^^^
at replTextPreamble#`THE REPL TEXT EXPR` (file:///$snippetsDir/input/errors/replTextPreamble.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)
@@ -0,0 +1,7 @@
foo {
["zzz"] {
["z_0"] = "hi"
["z_1"] = "hi"
["z_2"] = "hi"
}
}
@@ -0,0 +1,5 @@
foo {
["x"] {
bar = "x"
}
}
@@ -134,4 +134,77 @@ class EvaluateExpressionTest {
assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`") assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`")
} }
@Test
fun `evaluate local property`() {
val result = evaluate("local foo = 1", "foo")
assertThat(result).isEqualTo(1L)
}
@Test
fun `evaluate throwing local property trims expression preamble when rendering stack frames`() {
val error = assertThrows<PklException> { evaluate("local foo = throw(\"uh oh\")", "foo") }
assertThat(error.message)
.isEqualTo(
"""
–– Pkl Error ––
uh oh
1 | local foo = throw("uh oh")
^^^^^^^^^^^^^^
at text#foo (repl:text)
1 | foo
^^^
at (repl:text)
"""
.trimIndent()
)
}
@Test
fun `evaluate throwing multi-line expression renders without crashing`() {
val error =
assertThrows<PklException> {
evaluate(
"x = 1",
"""
let (_a = 1)
let (_b = 2)
throw("boom")
"""
.trimIndent(),
)
}
assertThat(error.message)
.isEqualTo(
"""
–– Pkl Error ––
boom
3 | throw("boom")
^^^^^^^^^^^^^
at (repl:text)
1 | let (_a = 1)
^^^^^^^^^^^^
at repl:text.<let expr> (repl:text)
"""
.trimIndent()
)
}
@Test
fun `evaluate import`() {
val result = evaluate("import \"pkl:base\"", "base")
assertThat(result).isInstanceOf(PModule::class.java)
result as PModule
assertThat(result.moduleName).isEqualTo("pkl.base")
}
} }
@@ -15,6 +15,8 @@
*/ */
package org.pkl.core.http package org.pkl.core.http
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.net.URI import java.net.URI
import java.net.http.HttpClient as JdkHttpClient import java.net.http.HttpClient as JdkHttpClient
import java.net.http.HttpRequest import java.net.http.HttpRequest
@@ -25,6 +27,7 @@ import java.util.Locale
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatList import org.assertj.core.api.Assertions.assertThatList
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.commons.test.FakeHttpResponse
import org.pkl.core.util.GlobResolver import org.pkl.core.util.GlobResolver
import org.pkl.core.util.IoUtils import org.pkl.core.util.IoUtils
@@ -441,4 +444,38 @@ class RequestRewritingClientTest {
assertThatList(captured.request.headers().allValues("user-agent")) assertThatList(captured.request.headers().allValues("user-agent"))
.containsExactly("My User Agent") .containsExactly("My User Agent")
} }
@Test
fun `closes intermediate redirect response body before following the redirect`() {
val intermediateBody = CloseTrackingInputStream()
val delegate =
ReplayingClient(
FakeHttpResponse<InputStream>().apply {
statusCode = 302
headers = httpHeaders("Location", "/bar.pkl")
body = intermediateBody
},
FakeHttpResponse<InputStream>(),
)
val client =
RequestRewritingClient("Pkl", Duration.ofSeconds(42), -1, delegate, mapOf(), mapOf())
val request = HttpRequest.newBuilder(URI("https://example.com/foo.pkl")).build()
client.send(request, BodyHandlers.ofInputStream(), NoopChecker)
assertThat(intermediateBody.closed)
.`as`("intermediate 3xx response body must be closed to release its connection")
.isTrue
}
/** @ByteArrayInputStream that records whether it was closed. */
private class CloseTrackingInputStream : ByteArrayInputStream(ByteArray(0)) {
var closed = false
private set
override fun close() {
closed = true
super.close()
}
}
} }
@@ -16,6 +16,9 @@
package org.pkl.core.http package org.pkl.core.http
import java.net.URI import java.net.URI
import java.net.http.HttpHeaders
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.util.regex.Pattern import java.util.regex.Pattern
data class HttpSettings( data class HttpSettings(
@@ -32,3 +35,21 @@ fun HttpClient.getConfiguredSettings(): HttpSettings {
object NoopChecker : HttpClient.HttpRequestChecker { object NoopChecker : HttpClient.HttpRequestChecker {
override fun check(uri: URI) {} override fun check(uri: URI) {}
} }
fun httpHeaders(name: String, value: String): HttpHeaders =
HttpHeaders.of(mapOf(name to listOf(value))) { _, _ -> true }
class ReplayingClient(vararg responses: HttpResponse<*>) : HttpClient {
private val responses = ArrayDeque(responses.toList())
@Suppress("UNCHECKED_CAST")
override fun <T : Any> send(
request: HttpRequest,
responseBodyHandler: HttpResponse.BodyHandler<T>,
httpRequestChecker: HttpClient.HttpRequestChecker,
): HttpResponse<T> {
return responses.removeFirst() as HttpResponse<T>
}
override fun close() {}
}
@@ -192,7 +192,7 @@ class StackTraceRendererTest {
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1) val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop) val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
val formatter = AnsiStringBuilder(false) val formatter = AnsiStringBuilder(false)
renderer.doRender(frames, null, null, formatter, "", true) renderer.doRender(frames, null, null, formatter, "", true, true)
val renderedFrames = formatter.toString() val renderedFrames = formatter.toString()
assertThat(renderedFrames) assertThat(renderedFrames)
.isEqualTo( .isEqualTo(
+4 -4
View File
@@ -78,8 +78,8 @@ tasks.test {
inputs.dir("src/test/files/SinglePackageTest/output") inputs.dir("src/test/files/SinglePackageTest/output")
} }
val testNativeExecutable by val testNativeExecutable =
tasks.registering(Test::class) { tasks.register<Test>("testNativeExecutable") {
dependsOn(tasks.assembleNative) dependsOn(tasks.assembleNative)
testClassesDirs = sourceSets.test.get().output.classesDirs testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath classpath = sourceSets.test.get().runtimeClasspath
@@ -91,8 +91,8 @@ val testNativeExecutable by
filter { includeTestsMatching("org.pkl.doc.NativeExecutableTest") } filter { includeTestsMatching("org.pkl.doc.NativeExecutableTest") }
} }
val testJavaExecutable by val testJavaExecutable =
tasks.registering(Test::class) { tasks.register<Test>("testJavaExecutable") {
testClassesDirs = sourceSets.test.get().output.classesDirs testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath classpath = sourceSets.test.get().runtimeClasspath
+29 -27
View File
@@ -23,8 +23,8 @@ plugins {
id("pklJSpecify") id("pklJSpecify")
} }
val pklDistributionCurrent: Configuration by configurations.creating val pklDistributionCurrent: Configuration = configurations.create("pklDistributionCurrent")
val pklHistoricalDistributions: Configuration by configurations.creating val pklHistoricalDistributions: Configuration = configurations.create("pklHistoricalDistributions")
// Because pkl-executor doesn't depend on other Pkl modules // Because pkl-executor doesn't depend on other Pkl modules
// (nor has overlapping dependencies that could cause a version conflict), // (nor has overlapping dependencies that could cause a version conflict),
@@ -66,37 +66,39 @@ publishing {
sourceSets { main { java { srcDir("src/main/java") } } } sourceSets { main { java { srcDir("src/main/java") } } }
val prepareHistoricalDistributions by tasks.registering { val prepareHistoricalDistributions =
val outputDir = layout.buildDirectory.dir("pklHistoricalDistributions") tasks.register("prepareHistoricalDistributions") {
inputs.files(pklHistoricalDistributions.files) val outputDir = layout.buildDirectory.dir("pklHistoricalDistributions")
outputs.dir(outputDir) inputs.files(pklHistoricalDistributions.files)
doLast { outputs.dir(outputDir)
val distributionDir = outputDir.get().asFile.toPath().also(Files::createDirectories) doLast {
for (file in pklHistoricalDistributions.files) { val distributionDir = outputDir.get().asFile.toPath().also(Files::createDirectories)
val target = distributionDir.resolve(file.name) for (file in pklHistoricalDistributions.files) {
// Create normal files on Windows, symlink on macOS/linux (need admin privileges to create val target = distributionDir.resolve(file.name)
// symlinks on Windows) // Create normal files on Windows, symlink on macOS/linux (need admin privileges to create
if (buildInfo.os.isWindows) { // symlinks on Windows)
if (!Files.isRegularFile(target, LinkOption.NOFOLLOW_LINKS)) { if (buildInfo.os.isWindows) {
if (Files.exists(target)) { if (!Files.isRegularFile(target, LinkOption.NOFOLLOW_LINKS)) {
Files.delete(target) if (Files.exists(target)) {
Files.delete(target)
}
Files.copy(file.toPath(), target)
} }
Files.copy(file.toPath(), target) } else {
} if (!Files.isSymbolicLink(target)) {
} else { if (Files.exists(target)) {
if (!Files.isSymbolicLink(target)) { Files.delete(target)
if (Files.exists(target)) { }
Files.delete(target) Files.createSymbolicLink(target, file.toPath())
} }
Files.createSymbolicLink(target, file.toPath())
} }
} }
} }
} }
}
val prepareTest by tasks.registering { val prepareTest =
dependsOn(pklDistributionCurrent, prepareHistoricalDistributions) tasks.register("prepareTest") {
} dependsOn(pklDistributionCurrent, prepareHistoricalDistributions)
}
tasks.test { dependsOn(prepareTest) } tasks.test { dependsOn(prepareTest) }
+3 -3
View File
@@ -69,12 +69,12 @@ sourceSets {
// Then the path to the jar file and the toolchain's `java` binary // Then the path to the jar file and the toolchain's `java` binary
// are injected into tests as properties. // are injected into tests as properties.
val externalReader by sourceSets.creating {} val externalReader = sourceSets.create("externalReader") {}
dependencies { "externalReaderImplementation"(libs.msgpack) } dependencies { "externalReaderImplementation"(libs.msgpack) }
val externalReaderJar by val externalReaderJar =
tasks.registering(Jar::class) { tasks.register<Jar>("externalReaderJar") {
description = "Builds an external reader executable jar file" description = "Builds an external reader executable jar file"
archiveBaseName = "external-reader" archiveBaseName = "external-reader"
archiveVersion = "" archiveVersion = ""
+12 -12
View File
@@ -36,38 +36,38 @@ private fun Test.configureNativeTest() {
include("**/NativeServerTest.*") include("**/NativeServerTest.*")
} }
val testMacExecutableAarch64 by val testMacExecutableAarch64 =
tasks.registering(Test::class) { tasks.register<Test>("testMacExecutableAarch64") {
dependsOn(":pkl-cli:macExecutableAarch64") dependsOn(":pkl-cli:macExecutableAarch64")
configureNativeTest() configureNativeTest()
} }
val testMacExecutableAmd64 by val testMacExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testMacExecutableAmd64") {
dependsOn(":pkl-cli:macExecutableAmd64") dependsOn(":pkl-cli:macExecutableAmd64")
configureNativeTest() configureNativeTest()
} }
val testLinuxExecutableAmd64 by val testLinuxExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testLinuxExecutableAmd64") {
dependsOn(":pkl-cli:linuxExecutableAmd64") dependsOn(":pkl-cli:linuxExecutableAmd64")
configureNativeTest() configureNativeTest()
} }
val testLinuxExecutableAarch64 by val testLinuxExecutableAarch64 =
tasks.registering(Test::class) { tasks.register<Test>("testLinuxExecutableAarch64") {
dependsOn(":pkl-cli:linuxExecutableAarch64") dependsOn(":pkl-cli:linuxExecutableAarch64")
configureNativeTest() configureNativeTest()
} }
val testAlpineExecutableAmd64 by val testAlpineExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testAlpineExecutableAmd64") {
dependsOn(":pkl-cli:alpineExecutableAmd64") dependsOn(":pkl-cli:alpineExecutableAmd64")
configureNativeTest() configureNativeTest()
} }
val testWindowsExecutableAmd64 by val testWindowsExecutableAmd64 =
tasks.registering(Test::class) { tasks.register<Test>("testWindowsExecutableAmd64") {
dependsOn(":pkl-cli:windowsExecutableAmd64") dependsOn(":pkl-cli:windowsExecutableAmd64")
configureNativeTest() configureNativeTest()
} }
+8 -7
View File
@@ -25,9 +25,9 @@ plugins {
signing signing
} }
val placeholder: SourceSet by sourceSets.creating val placeholder = sourceSets.create("placeholder")
val firstPartySourcesJars by configurations.existing val firstPartySourcesJars = configurations.named("firstPartySourcesJars")
// Note: pkl-tools cannot (easily) contain pkl-config-kotlin // Note: pkl-tools cannot (easily) contain pkl-config-kotlin
// because pkl-tools ships with a shaded Kotlin stdlib. // because pkl-tools ships with a shaded Kotlin stdlib.
@@ -55,12 +55,13 @@ dependencies {
// TODO: need to figure out how to properly generate javadoc here. // TODO: need to figure out how to properly generate javadoc here.
// For now, we'll include a placeholder javadoc jar. // For now, we'll include a placeholder javadoc jar.
val javadocPlaceholder by tasks.registering(Javadoc::class) { source = placeholder.allJava } val javadocPlaceholder =
tasks.register<Javadoc>("javadocPlaceholder") { source = placeholder.allJava }
java { withJavadocJar() } java { withJavadocJar() }
val javadocJar by val javadocJar =
tasks.existing(Jar::class) { tasks.named<Jar>("javadocJar") {
from(javadocPlaceholder) from(javadocPlaceholder)
archiveBaseName.set("pkl-tools-all") archiveBaseName.set("pkl-tools-all")
archiveClassifier.set("javadoc") archiveClassifier.set("javadoc")
@@ -105,8 +106,8 @@ private fun Exec.configureTestStartFatJar(launcher: Provider<JavaLauncher>) {
} }
} }
val testStartFatJar by val testStartFatJar =
tasks.registering(Exec::class) { configureTestStartFatJar(buildInfo.javaTestLauncher) } tasks.register<Exec>("testStartFatJar") { configureTestStartFatJar(buildInfo.javaTestLauncher) }
tasks.validateFatJar { dependsOn(testStartFatJar) } tasks.validateFatJar { dependsOn(testStartFatJar) }
+1 -1
View File
@@ -36,7 +36,7 @@
/// ///
/// Warning: Although this module is ready for initial use, /// Warning: Although this module is ready for initial use,
/// benchmark results may be inaccurate or inconsistent. /// benchmark results may be inaccurate or inconsistent.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.Benchmark module pkl.Benchmark
import "pkl:platform" as _platform import "pkl:platform" as _platform
+1 -1
View File
@@ -29,7 +29,7 @@
/// // ... /// // ...
/// } /// }
/// ``` /// ```
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
open module pkl.Command open module pkl.Command
import "pkl:Command" import "pkl:Command"
+1 -1
View File
@@ -63,7 +63,7 @@
/// @Deprecated { message = "Use `com.example.Birds.Parrot` instead" } /// @Deprecated { message = "Use `com.example.Birds.Parrot` instead" }
/// amends "pkl:PackageInfo" /// amends "pkl:PackageInfo"
/// ``` /// ```
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.DocPackageInfo module pkl.DocPackageInfo
// used by doc comments // used by doc comments
+1 -1
View File
@@ -31,7 +31,7 @@
/// ///
/// title = "Title displayed in the header of each page" /// title = "Title displayed in the header of each page"
/// ``` /// ```
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.DocsiteInfo module pkl.DocsiteInfo
import "pkl:reflect" import "pkl:reflect"
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Common settings for Pkl's own evaluator. /// Common settings for Pkl's own evaluator.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
@Since { version = "0.26.0" } @Since { version = "0.26.0" }
module pkl.EvaluatorSettings module pkl.EvaluatorSettings
+1 -1
View File
@@ -64,7 +64,7 @@
/// value = project /// value = project
/// } /// }
/// ``` /// ```
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.Project module pkl.Project
import "pkl:EvaluatorSettings" as EvaluatorSettingsModule import "pkl:EvaluatorSettings" as EvaluatorSettingsModule
+1 -1
View File
@@ -19,7 +19,7 @@
/// These tools differentiate from [pkl:reflect][reflect] in that they parse Pkl modules, but do not /// These tools differentiate from [pkl:reflect][reflect] in that they parse Pkl modules, but do not
/// execute any code within these modules. /// execute any code within these modules.
@Since { version = "0.27.0" } @Since { version = "0.27.0" }
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.analyze module pkl.analyze
// used by doc comments // used by doc comments
+1 -1
View File
@@ -17,7 +17,7 @@
/// Fundamental properties, methods, and classes for writing Pkl programs. /// Fundamental properties, methods, and classes for writing Pkl programs.
/// ///
/// Members of this module are automatically available in every Pkl module. /// Members of this module are automatically available in every Pkl module.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.base module pkl.base
import "pkl:json" import "pkl:json"
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// A JSON parser. /// A JSON parser.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.json module pkl.json
/// Annotate properties of classes and modules with this class to override how a [JsonRenderer] /// Annotate properties of classes and modules with this class to override how a [JsonRenderer]
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// A [Jsonnet](https://jsonnet.org) renderer. /// A [Jsonnet](https://jsonnet.org) renderer.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.jsonnet module pkl.jsonnet
/// Constructs an [ImportStr]. /// Constructs an [ImportStr].
+1 -1
View File
@@ -18,7 +18,7 @@
/// ///
/// Note that some mathematical functions, such as `sign()`, `abs()`, and `round()`, /// Note that some mathematical functions, such as `sign()`, `abs()`, and `round()`,
/// are directly defined in classes [Number], [Int], and [Float]. /// are directly defined in classes [Number], [Int], and [Float].
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.math module pkl.math
/// The minimum [Int] value: `-9223372036854775808`. /// The minimum [Int] value: `-9223372036854775808`.
+1 -1
View File
@@ -28,7 +28,7 @@
/// The `pkl-binary` format uses MessagePack encoding. /// The `pkl-binary` format uses MessagePack encoding.
/// Its specification is available at /// Its specification is available at
/// <https://pkl-lang.org/main/current/bindings-specification/binary-encoding.html>. /// <https://pkl-lang.org/main/current/bindings-specification/binary-encoding.html>.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.pklbinary module pkl.pklbinary
/// Render values as `pkl-binary`. /// Render values as `pkl-binary`.
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Information about the platform that the current program runs on. /// Information about the platform that the current program runs on.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.platform module pkl.platform
/// The platform that the current program runs on. /// The platform that the current program runs on.
+1 -1
View File
@@ -16,7 +16,7 @@
/// A renderer for [Protocol Buffers](https://developers.google.com/protocol-buffers). /// A renderer for [Protocol Buffers](https://developers.google.com/protocol-buffers).
/// Note: This module is _experimental_ and not ready for production use. /// Note: This module is _experimental_ and not ready for production use.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.protobuf module pkl.protobuf
import "pkl:reflect" import "pkl:reflect"
+1 -1
View File
@@ -27,7 +27,7 @@
/// * Workflow tools /// * Workflow tools
/// ///
/// _**WARNING**_: This module is _experimental_ and not ready for production use. /// _**WARNING**_: This module is _experimental_ and not ready for production use.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
@Since { version = "0.32.0" } @Since { version = "0.32.0" }
module pkl.ref module pkl.ref
+1 -1
View File
@@ -26,7 +26,7 @@
/// - Documentation generators (such as *Pkldoc*) /// - Documentation generators (such as *Pkldoc*)
/// - Code generators (such as *pkl-codegen-java* and *pkl-codegen-kotlin*) /// - Code generators (such as *pkl-codegen-java* and *pkl-codegen-kotlin*)
/// - Domain-specific schema validators /// - Domain-specific schema validators
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.reflect module pkl.reflect
import "pkl:base" import "pkl:base"
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Information about the Pkl release that the current program runs on. /// Information about the Pkl release that the current program runs on.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.release module pkl.release
import "pkl:semver" import "pkl:semver"
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Parsing, comparison, and manipulation of [semantic version](https://semver.org/spec/v2.0.0.html) numbers. /// Parsing, comparison, and manipulation of [semantic version](https://semver.org/spec/v2.0.0.html) numbers.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.semver module pkl.semver
/// Tells whether [version] is a valid semantic version number. /// Tells whether [version] is a valid semantic version number.
+1 -1
View File
@@ -19,7 +19,7 @@
/// Every settings file must amend this module. /// Every settings file must amend this module.
/// Unless CLI commands and build tool plugins are explicitly configured with a settings file, /// Unless CLI commands and build tool plugins are explicitly configured with a settings file,
/// they will use `~/.pkl/settings.pkl` or the defaults specified in this module. /// they will use `~/.pkl/settings.pkl` or the defaults specified in this module.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.settings module pkl.settings
import "pkl:EvaluatorSettings" import "pkl:EvaluatorSettings"
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Utilities for generating shell scripts. /// Utilities for generating shell scripts.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.shell module pkl.shell
/// Escapes [str] by enclosing it in single quotes. /// Escapes [str] by enclosing it in single quotes.
+2 -2
View File
@@ -24,8 +24,8 @@ plugins {
// create and publish a self-contained stdlib archive // create and publish a self-contained stdlib archive
// purpose is to provide non-jvm tools/projects with a versioned stdlib // purpose is to provide non-jvm tools/projects with a versioned stdlib
val stdlibZip by val stdlibZip =
tasks.registering(Zip::class) { tasks.register<Zip>("stdlibZip") {
destinationDirectory.set(layout.buildDirectory.dir("libs")) destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveBaseName.set("pkl-stdlib") archiveBaseName.set("pkl-stdlib")
archiveVersion.set(project.version as String) archiveVersion.set(project.version as String)
+1 -1
View File
@@ -18,7 +18,7 @@
/// ///
/// To write tests, amend this module and define [facts] or [examples] (or both). /// To write tests, amend this module and define [facts] or [examples] (or both).
/// To run tests, use the `pkl test` command to evaluate the amended module. /// To run tests, use the `pkl test` command to evaluate the amended module.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
open module pkl.test open module pkl.test
/// Named groups of boolean expressions that are expected to evaluate to [true]. /// Named groups of boolean expressions that are expected to evaluate to [true].
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// An XML renderer. /// An XML renderer.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.xml module pkl.xml
/// Renders values as XML. /// Renders values as XML.
+1 -1
View File
@@ -15,7 +15,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// A YAML 1.2 compliant YAML parser. /// A YAML 1.2 compliant YAML parser.
@ModuleInfo { minPklVersion = "0.32.0" } @ModuleInfo { minPklVersion = "0.32.2" }
module pkl.yaml module pkl.yaml
/// Annotate properties of classes and modules with this class to override how a [YamlRenderer] /// Annotate properties of classes and modules with this class to override how a [YamlRenderer]