From 1cc20b611f26466faeac758f8d863328eb61c51a Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Fri, 5 Jun 2026 16:31:40 -0700 Subject: [PATCH] Revert configuration cache (#1659) This reverts the commits that enabled Gradle's configuration cache feature. IMO: this feature is too hard to use. We don't know if a task is valid for the configuration cache until it runs, and it's very hard to tell if something is safe when authoring Gradle code. For example, our publish tasks are currently failing; I don't know how I would fix this without running the publish task again on my dev machine. Also, some of our build scripts become more brittle because of this; for example, see https://github.com/apple/pkl/blob/bb07589eae0b3195a589559a3245cbc12c29b394/build-logic/src/main/kotlin/BuildInfo.kt#L291-L296 --- build-logic/src/main/kotlin/BuildInfo.kt | 43 +++++++------- build-logic/src/main/kotlin/InstallGraalVm.kt | 33 ++++------- .../src/main/kotlin/NativeImageBuild.kt | 57 ++++++++++-------- .../src/main/kotlin/PklFormatterSpotless.kt | 34 +++-------- build-logic/src/main/kotlin/PklPublishing.kt | 2 +- .../src/main/kotlin/pklFatJar.gradle.kts | 14 ++--- .../src/main/kotlin/pklGraalVm.gradle.kts | 25 ++------ .../kotlin/pklGradlePluginTest.gradle.kts | 16 ++--- .../main/kotlin/pklHtmlValidator.gradle.kts | 2 +- .../main/kotlin/pklJavaExecutable.gradle.kts | 8 +-- .../kotlin/pklNativeExecutable.gradle.kts | 46 +++------------ build.gradle.kts | 58 ++++++------------- gradle.properties | 2 - pkl-cli/pkl-cli.gradle.kts | 16 ++--- pkl-commons-test/pkl-commons-test.gradle.kts | 43 ++++++++------ pkl-config-java/pkl-config-java.gradle.kts | 3 +- .../pkl-config-kotlin.gradle.kts | 3 +- pkl-core/pkl-core.gradle.kts | 30 +++++----- pkl-executor/pkl-executor.gradle.kts | 7 +-- pkl-gradle/pkl-gradle.gradle.kts | 38 ++++-------- pkl-tools/pkl-tools.gradle.kts | 7 +-- 21 files changed, 180 insertions(+), 307 deletions(-) diff --git a/build-logic/src/main/kotlin/BuildInfo.kt b/build-logic/src/main/kotlin/BuildInfo.kt index 6cfd0ac72..766f9a39a 100644 --- a/build-logic/src/main/kotlin/BuildInfo.kt +++ b/build-logic/src/main/kotlin/BuildInfo.kt @@ -288,12 +288,7 @@ open class BuildInfo(private val project: Project) { classpath = template.classpath testClassesDirs = template.testClassesDirs jvmArgs.addAll(template.jvmArgs) - // jvmArgumentProviders are NOT copied: providers added by plugins (e.g. - // java-gradle-plugin's GradleJvmCommandLineArgumentProvider) hold a direct - // reference to the task they were registered on. Copying them to derived tasks - // causes those tasks to capture a foreign task reference, which the - // configuration cache cannot serialize. Each derived task receives its own - // providers via withType().configureEach in the subproject. + jvmArgumentProviders.addAll(template.jvmArgumentProviders) forkEvery = template.forkEvery maxParallelForks = template.maxParallelForks minHeapSize = template.minHeapSize @@ -377,33 +372,35 @@ open class BuildInfo(private val project: Project) { org.gradle.internal.os.OperatingSystem.current() } - private val computedCommitId: Provider = + // could be `commitId: Provider = project.provider { ... }` + val commitId: String by lazy { + // allow -DcommitId=abc123 for build environments that don't have git. + System.getProperty("commitId").let { if (it != null) return@lazy it } // only run command once per build invocation if (project.path == project.rootProject.path) { - project.providers - .exec { commandLine("git", "rev-parse", "--short", "HEAD") } - .standardOutput - .asText - .map { it.trim() } + val process = + ProcessBuilder() + .command("git", "rev-parse", "--short", "HEAD") + .directory(project.rootDir) + .start() + process.waitFor().also { exitCode -> + if (exitCode == -1) throw RuntimeException(process.errorStream.reader().readText()) + } + process.inputStream.reader().readText().trim() } else { project.rootProject.extensions.getByType(BuildInfo::class.java).commitId } + } - val commitId: Provider = - // allow -DcommitId=abc123 for build environments that don't have git. - System.getProperty("commitId")?.let { project.providers.provider { it } } ?: computedCommitId + val commitish: String by lazy { if (isReleaseBuild) project.version.toString() else commitId } - val commitish: Provider = - if (isReleaseBuild) project.providers.provider { project.version.toString() } else commitId - - val pklVersion: Provider = + val pklVersion: String by lazy { if (isReleaseBuild) { - project.providers.provider { project.version.toString() } + project.version.toString() } else { - project.providers - .provider { project.version.toString() } - .zip(commitId) { version, id -> version.replace("-SNAPSHOT", "-dev+$id") } + project.version.toString().replace("-SNAPSHOT", "-dev+$commitId") } + } val pklVersionNonUnique: String by lazy { if (isReleaseBuild) { diff --git a/build-logic/src/main/kotlin/InstallGraalVm.kt b/build-logic/src/main/kotlin/InstallGraalVm.kt index 188a16360..d7a9dbc63 100644 --- a/build-logic/src/main/kotlin/InstallGraalVm.kt +++ b/build-logic/src/main/kotlin/InstallGraalVm.kt @@ -20,13 +20,9 @@ import java.util.* import javax.inject.Inject import kotlin.io.path.createDirectories import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty import org.gradle.api.internal.file.FileOperations import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.gradle.process.ExecOperations @@ -36,32 +32,25 @@ constructor( private val fileOperations: FileOperations, private val execOperations: ExecOperations, ) : DefaultTask() { - @get:Input abstract val homeDir: Property - - @get:InputFile abstract val downloadFile: RegularFileProperty - - @get:Input abstract val version: Property - - @get:Input abstract val graalVmJdkVersion: Property - - @get:Internal abstract val installDir: DirectoryProperty + @get:Input abstract val graalVm: Property init { - @Suppress("LeakingThis") onlyIf("GraalVM not installed") { !installDir.get().asFile.exists() } + @Suppress("LeakingThis") onlyIf("GraalVM not installed") { !graalVm.get().installDir.exists() } } @TaskAction @Suppress("unused") fun run() { - val distroDir = Paths.get(homeDir.get(), UUID.randomUUID().toString()) + // minimize chance of corruption by extract-to-random-dir-and-flip-symlink + val distroDir = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString()) try { distroDir.createDirectories() - println("Extracting ${downloadFile.get().asFile} into $distroDir") + println("Extracting ${graalVm.get().downloadFile} into $distroDir") // faster and more reliable than Gradle's `copy { from tarTree() }` execOperations.exec { workingDir = distroDir.toFile() executable = "tar" - args("--strip-components=1", "-xzf", downloadFile.get().asFile) + args("--strip-components=1", "-xzf", graalVm.get().downloadFile) } val os = org.gradle.internal.os.OperatingSystem.current() @@ -70,8 +59,8 @@ constructor( println("Installing native-image into $distroDir") val gvmVersionMajor = - requireNotNull(version.get().split(".").first().toIntOrNull()) { - "Invalid GraalVM JDK version: ${graalVmJdkVersion.get()}" + requireNotNull(graalVm.get().version.split(".").first().toIntOrNull()) { + "Invalid GraalVM JDK version: ${graalVm.get().graalVmJdkVersion}" } if (gvmVersionMajor < 24) { execOperations.exec { @@ -81,11 +70,11 @@ constructor( } } - println("Creating symlink ${installDir.get().asFile} for $distroDir") - val tempLink = Paths.get(homeDir.get(), UUID.randomUUID().toString()) + println("Creating symlink ${graalVm.get().installDir} for $distroDir") + val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString()) Files.createSymbolicLink(tempLink, distroDir) try { - Files.move(tempLink, installDir.get().asFile.toPath(), StandardCopyOption.ATOMIC_MOVE) + Files.move(tempLink, graalVm.get().installDir.toPath(), StandardCopyOption.ATOMIC_MOVE) } catch (e: Exception) { try { fileOperations.delete(tempLink.toFile()) diff --git a/build-logic/src/main/kotlin/NativeImageBuild.kt b/build-logic/src/main/kotlin/NativeImageBuild.kt index d6827b79b..f9a2be005 100644 --- a/build-logic/src/main/kotlin/NativeImageBuild.kt +++ b/build-logic/src/main/kotlin/NativeImageBuild.kt @@ -16,18 +16,18 @@ import javax.inject.Inject import org.gradle.api.DefaultTask import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters -import org.gradle.api.services.ServiceReference import org.gradle.api.tasks.ClasspathNormalizer import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.registerIfAbsent import org.gradle.kotlin.dsl.withNormalizer import org.gradle.process.ExecOperations @@ -49,31 +49,25 @@ abstract class NativeImageBuild : DefaultTask() { @get:InputFiles abstract val classpath: ConfigurableFileCollection - /** Path to the `native-image` binary (e.g. `/bin/native-image`). */ - @get:Input abstract val nativeImageExecutable: Property + private val outputDir = project.layout.buildDirectory.dir("executable") - @get:Input abstract val graalSdkLibraryName: Property - - @get:Input abstract val releaseBuild: Property - - @get:Input abstract val nativeArch: Property - - /** Divisor applied to `availableProcessors` to throttle native-image CPU usage. */ - @get:Input abstract val processorDivisor: Property + @get:OutputFile val outputFile = outputDir.flatMap { it.file(imageName) } @get:Inject protected abstract val execOperations: ExecOperations - @get:Inject protected abstract val layout: ProjectLayout + private val graalVm: Provider = arch.map { a -> + when (a) { + Architecture.AMD64 -> buildInfo.graalVmAmd64 + Architecture.AARCH64 -> buildInfo.graalVmAarch64 + } + } - private val outputDir - get() = layout.buildDirectory.dir("executable") + private val buildInfo: BuildInfo = project.extensions.getByType(BuildInfo::class.java) - @get:OutputFile - val outputFile - get() = outputDir.flatMap { it.file(imageName) } + private val nativeImageCommandName = + if (buildInfo.os.isWindows) "native-image.cmd" else "native-image" - @get:ServiceReference("nativeImageBuildService") - abstract val buildService: Property + private val nativeImageExecutable = graalVm.map { "${it.baseDir}/bin/$nativeImageCommandName" } private val extraArgsFromProperties by lazy { System.getProperties() @@ -81,7 +75,19 @@ abstract class NativeImageBuild : DefaultTask() { .map { "${it.key}=${it.value}".substring("pkl.native".length) } } + private val buildService = + project.gradle.sharedServices.registerIfAbsent( + "nativeImageBuildService", + NativeImageBuildService::class, + ) { + maxParallelUsages.set(1) + } + init { + // ensure native-image builds run in serial (prevent `gw buildNative` from consuming all host + // CPU resources). + usesService(buildService) + group = "build" inputs @@ -98,7 +104,8 @@ abstract class NativeImageBuild : DefaultTask() { @Suppress("unused") protected fun run() { execOperations.exec { - val exclusions = listOf(graalSdkLibraryName.get()) + val exclusions = + listOf(buildInfo.libs.findLibrary("graalSdk").get()).map { it.get().module.name } executable = nativeImageExecutable.get() workingDir(outputDir) @@ -133,10 +140,10 @@ abstract class NativeImageBuild : DefaultTask() { add("-H:-ParseRuntimeOptions") // quick build mode: 40% faster compilation, 20% smaller (but presumably also slower) // executable - if (!releaseBuild.get()) { + if (!buildInfo.isReleaseBuild) { add("-Ob") } - if (nativeArch.get()) { + if (buildInfo.isNativeArch) { add("-march=native") } else { add("-march=compatibility") @@ -148,7 +155,9 @@ abstract class NativeImageBuild : DefaultTask() { } add(pathInput.asPath) // make sure dev machine stays responsive (15% slowdown on my laptop) - val processors = Runtime.getRuntime().availableProcessors() / processorDivisor.get() + val processors = + Runtime.getRuntime().availableProcessors() / + if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1 add("-J-XX:ActiveProcessorCount=${processors}") // Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed // tools. diff --git a/build-logic/src/main/kotlin/PklFormatterSpotless.kt b/build-logic/src/main/kotlin/PklFormatterSpotless.kt index d726c504d..737366069 100644 --- a/build-logic/src/main/kotlin/PklFormatterSpotless.kt +++ b/build-logic/src/main/kotlin/PklFormatterSpotless.kt @@ -15,10 +15,8 @@ */ import com.diffplug.spotless.FormatterFunc import com.diffplug.spotless.FormatterStep -import java.io.File import java.io.Serial import java.io.Serializable -import java.lang.reflect.Method import java.net.URLClassLoader import org.gradle.api.artifacts.Configuration @@ -28,45 +26,31 @@ class PklFormatterStep(@Transient private val configuration: Configuration) : Se } fun create(): FormatterStep { - val files = configuration.files.toList() return FormatterStep.createLazy( "pkl", - { PklFormatterState(files) }, - { PklFormatterFunc(it.files) }, + { PklFormatterStep(configuration) }, + { PklFormatterFunc(configuration) }, ) } } -data class PklFormatterState(val files: List) : Serializable { - companion object { - @Serial private const val serialVersionUID: Long = 1L - } -} - -class PklFormatterFunc(private val files: List) : FormatterFunc, Serializable { +class PklFormatterFunc(@Transient private val configuration: Configuration) : + FormatterFunc, Serializable { companion object { @Serial private const val serialVersionUID: Long = 1L } - @delegate:Transient - private val classLoader: URLClassLoader by lazy { - val urls = files.map { it.toURI().toURL() } + private val classLoader by lazy { + val urls = configuration.files.map { it.toURI().toURL() } // Use the platform classloader as parent to isolate from Gradle's classloader URLClassLoader(urls.toTypedArray(), ClassLoader.getPlatformClassLoader()) } - @delegate:Transient - private val formatterClass: Class<*> by lazy { - classLoader.loadClass("org.pkl.formatter.Formatter") - } + private val formatterClass by lazy { classLoader.loadClass("org.pkl.formatter.Formatter") } - @delegate:Transient - private val formatMethod: Method by lazy { - formatterClass.getMethod("format", String::class.java) - } + private val formatMethod by lazy { formatterClass.getMethod("format", String::class.java) } - @delegate:Transient - private val formatterInstance: Any by lazy { formatterClass.getConstructor().newInstance() } + private val formatterInstance by lazy { formatterClass.getConstructor().newInstance() } override fun apply(input: String): String { return formatMethod(formatterInstance, input) as String diff --git a/build-logic/src/main/kotlin/PklPublishing.kt b/build-logic/src/main/kotlin/PklPublishing.kt index 8f9181001..07ea6f32a 100644 --- a/build-logic/src/main/kotlin/PklPublishing.kt +++ b/build-logic/src/main/kotlin/PklPublishing.kt @@ -46,7 +46,7 @@ fun Project.configurePklPomMetadata() { connection.set("scm:git:git://github.com/apple/pkl.git") developerConnection.set("scm:git:ssh://github.com/apple/pkl.git") val buildInfo = extensions.getByType() - url.set(buildInfo.commitish.map { "https://github.com/apple/pkl/tree/$it" }) + url.set("https://github.com/apple/pkl/tree/${buildInfo.commitish}") } issueManagement { system.set("GitHub Issues") diff --git a/build-logic/src/main/kotlin/pklFatJar.gradle.kts b/build-logic/src/main/kotlin/pklFatJar.gradle.kts index acb56c769..907349188 100644 --- a/build-logic/src/main/kotlin/pklFatJar.gradle.kts +++ b/build-logic/src/main/kotlin/pklFatJar.gradle.kts @@ -15,12 +15,10 @@ */ import org.gradle.api.GradleException import org.gradle.api.artifacts.Configuration -import org.gradle.api.file.ArchiveOperations import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.* -import org.gradle.kotlin.dsl.support.serviceOf plugins { `java-library` @@ -152,19 +150,17 @@ tasks.check { dependsOn(testFatJar) } val validateFatJar by tasks.registering { val outputFile = layout.buildDirectory.file("validateFatJar/result.txt") - val shadowJarFile = tasks.shadowJar.flatMap { it.archiveFile } - val archiveOps = serviceOf() - inputs.file(shadowJarFile) + inputs.files(tasks.shadowJar) inputs.property("nonRelocations", nonRelocations) outputs.file(outputFile) - val nonRelocations = nonRelocations doLast { val unshadowedFiles = mutableListOf() - archiveOps.zipTree(shadowJarFile.get().asFile).visit { - val path = relativePath.pathString + zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit { + val fileDetails = this + val path = fileDetails.relativePath.pathString if ( - !(isDirectory || + !(fileDetails.isDirectory || path.startsWith("org/pkl/") || path.startsWith("META-INF/") || nonRelocations.any { path.startsWith(it) }) diff --git a/build-logic/src/main/kotlin/pklGraalVm.gradle.kts b/build-logic/src/main/kotlin/pklGraalVm.gradle.kts index eb68c3889..95351bebc 100644 --- a/build-logic/src/main/kotlin/pklGraalVm.gradle.kts +++ b/build-logic/src/main/kotlin/pklGraalVm.gradle.kts @@ -28,13 +28,11 @@ val downloadGraalVmAmd64 by tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAmd64) } fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) { - val installDir = graalvm.installDir - val downloadFile = graalvm.downloadFile - onlyIf { !installDir.exists() } - doLast { println("Downloaded GraalVm to $downloadFile") } + onlyIf { !graalvm.installDir.exists() } + doLast { println("Downloaded GraalVm to ${graalvm.downloadFile}") } src(graalvm.downloadUrl) - dest(downloadFile) + dest(graalvm.downloadFile) overwrite(false) tempAndMove(true) } @@ -52,8 +50,7 @@ val verifyGraalVmAmd64 by } fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) { - val installDir = graalvm.installDir - onlyIf { !installDir.exists() } + onlyIf { !graalvm.installDir.exists() } src(graalvm.downloadFile) checksum( @@ -62,26 +59,16 @@ fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) { algorithm("SHA-256") } -// incorrect diagnostic -@Suppress("UnusedReceiverParameter") -fun InstallGraalVm.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) { - homeDir = graalVm.homeDir - downloadFile = graalVm.downloadFile - version = graalVm.version - graalVmJdkVersion = graalVm.graalVmJdkVersion - installDir = graalVm.installDir -} - @Suppress("unused") val installGraalVmAarch64 by tasks.registering(InstallGraalVm::class) { dependsOn(verifyGraalVmAarch64) - configureInstallGraalVm(buildInfo.graalVmAarch64) + graalVm = buildInfo.graalVmAarch64 } @Suppress("unused") val installGraalVmAmd64 by tasks.registering(InstallGraalVm::class) { dependsOn(verifyGraalVmAmd64) - configureInstallGraalVm(buildInfo.graalVmAmd64) + graalVm = buildInfo.graalVmAmd64 } diff --git a/build-logic/src/main/kotlin/pklGradlePluginTest.gradle.kts b/build-logic/src/main/kotlin/pklGradlePluginTest.gradle.kts index 730e76ad7..5de3573c3 100644 --- a/build-logic/src/main/kotlin/pklGradlePluginTest.gradle.kts +++ b/build-logic/src/main/kotlin/pklGradlePluginTest.gradle.kts @@ -93,29 +93,21 @@ fun createCompatibilityTestTask(versionInfo: GradleVersionInfo): TaskProvider { - val currentGradleVersion = gradle.gradleVersion return tasks.register("compatibilityTest$version", Test::class.java) { mustRunAfter(tasks.test) - val myMaxHeapSize = tasks.test.map { it.maxHeapSize } - val myJvmArgs = tasks.test.map { it.jvmArgs } - val myClasspath = tasks.test.map { it.classpath } - + maxHeapSize = tasks.test.get().maxHeapSize + jvmArgs = tasks.test.get().jvmArgs + classpath = tasks.test.get().classpath systemProperty("testGradleVersion", version) systemProperty("testGradleDistributionUrl", downloadUrl) doFirst { - if ( - version == currentGradleVersion && - project.gradle.taskGraph.hasTask(project.tasks.getByName("test")) - ) { + if (version == gradle.gradleVersion && gradle.taskGraph.hasTask(tasks.test.get())) { // don't test same version twice println("This version has already been tested by the `test` task.") throw StopExecutionException() } - maxHeapSize = myMaxHeapSize.get() - jvmArgs = myJvmArgs.get() - classpath = myClasspath.get() } } } diff --git a/build-logic/src/main/kotlin/pklHtmlValidator.gradle.kts b/build-logic/src/main/kotlin/pklHtmlValidator.gradle.kts index 59014de3c..69c9ed800 100644 --- a/build-logic/src/main/kotlin/pklHtmlValidator.gradle.kts +++ b/build-logic/src/main/kotlin/pklHtmlValidator.gradle.kts @@ -68,7 +68,7 @@ val validateHtml by // write a basic result file s.t. gradle can consider task up-to-date // writing a result file in case validation fails is not easily possible with JavaExec, but also // not strictly necessary - doFirst { resultFile.get().asFile.delete() } + doFirst { project.delete(resultFile) } doLast { resultFile.get().asFile.writeText("Success.") } } diff --git a/build-logic/src/main/kotlin/pklJavaExecutable.gradle.kts b/build-logic/src/main/kotlin/pklJavaExecutable.gradle.kts index 729bd6442..d05bb1850 100644 --- a/build-logic/src/main/kotlin/pklJavaExecutable.gradle.kts +++ b/build-logic/src/main/kotlin/pklJavaExecutable.gradle.kts @@ -47,10 +47,8 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider? = null) val outputFile = layout.buildDirectory.file("testStartJavaExecutable/$name") outputs.file(outputFile) - val executableFile = javaExecutable.flatMap { it.outJar } - val pklVersion = buildInfo.pklVersionNonUnique val execOutput = providers.exec { - val executablePath = executableFile.get().asFile + val executablePath = javaExecutable.get().outputs.files.singleFile if (launcher?.isPresent == true) { commandLine( launcher.get().executablePath.asFile.absolutePath, @@ -65,9 +63,9 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider? = null) doLast { val outputText = execOutput.standardOutput.asText.get() - if (!outputText.contains(pklVersion)) { + if (!outputText.contains(buildInfo.pklVersionNonUnique)) { throw GradleException( - "Expected version output to contain current version ($pklVersion), but got '$outputText'" + "Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'" ) } outputFile.get().asFile.toPath().apply { diff --git a/build-logic/src/main/kotlin/pklNativeExecutable.gradle.kts b/build-logic/src/main/kotlin/pklNativeExecutable.gradle.kts index 3d8c08e59..563c89640 100644 --- a/build-logic/src/main/kotlin/pklNativeExecutable.gradle.kts +++ b/build-logic/src/main/kotlin/pklNativeExecutable.gradle.kts @@ -67,35 +67,13 @@ dependencies { stagedWindowsAmd64Executable(executableFile("windows-amd64.exe")) } -val nativeImageBuildService = - gradle.sharedServices.registerIfAbsent( - "nativeImageBuildService", - NativeImageBuildService::class, - ) { - maxParallelUsages = 1 - } - -private val nativeImageCommandName = - if (buildInfo.os.isWindows) "native-image.cmd" else "native-image" - -private fun NativeImageBuild.configure() { - graalSdkLibraryName = buildInfo.libs.findLibrary("graalSdk").get().map { it.module.name } - releaseBuild = buildInfo.isReleaseBuild - nativeArch = buildInfo.isNativeArch - processorDivisor = if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1 - buildService = nativeImageBuildService - usesService(nativeImageBuildService) -} - private fun NativeImageBuild.amd64() { arch = Architecture.AMD64 - nativeImageExecutable = "${buildInfo.graalVmAmd64.baseDir}/bin/$nativeImageCommandName" dependsOn(":installGraalVmAmd64") } private fun NativeImageBuild.aarch64() { arch = Architecture.AARCH64 - nativeImageExecutable = "${buildInfo.graalVmAarch64.baseDir}/bin/$nativeImageCommandName" dependsOn(":installGraalVmAarch64") } @@ -113,7 +91,6 @@ val macExecutableAmd64 by mainClass = executableSpec.mainClass amd64() setClasspath() - configure() } val macExecutableAarch64 by @@ -122,7 +99,6 @@ val macExecutableAarch64 by mainClass = executableSpec.mainClass aarch64() setClasspath() - configure() } val linuxExecutableAmd64 by @@ -131,7 +107,6 @@ val linuxExecutableAmd64 by mainClass = executableSpec.mainClass amd64() setClasspath() - configure() } val linuxExecutableAarch64 by @@ -140,7 +115,6 @@ val linuxExecutableAarch64 by mainClass = executableSpec.mainClass aarch64() setClasspath() - configure() // Ensure compatibility for kernels with page size set to 4k, 16k and 64k // (e.g. Raspberry Pi 5, Asahi Linux) extraNativeImageArgs.add("-H:PageSize=65536") @@ -152,7 +126,6 @@ val alpineExecutableAmd64 by mainClass = executableSpec.mainClass amd64() setClasspath() - configure() extraNativeImageArgs.addAll(listOf("--static", "--libc=musl")) } @@ -162,7 +135,6 @@ val windowsExecutableAmd64 by mainClass = executableSpec.mainClass amd64() setClasspath() - configure() } val assembleNative by tasks.existing @@ -171,18 +143,18 @@ val testStartNativeExecutable by tasks.registering { dependsOn(assembleNative) // dummy file for up-to-date checking - val outputFile = layout.buildDirectory.file("testStartNativeExecutable/output.txt") + val outputFile = project.layout.buildDirectory.file("testStartNativeExecutable/output.txt") outputs.file(outputFile) - val nativeExecutableFile = assembleNative.map { it.outputs.files.singleFile } - val execOutput = providers.exec { commandLine(nativeExecutableFile.get(), "--version") } - val pklVersion = buildInfo.pklVersionNonUnique + val execOutput = providers.exec { + commandLine(assembleNative.get().outputs.files.singleFile, "--version") + } doLast { val outputText = execOutput.standardOutput.asText.get() - if (!outputText.contains(pklVersion)) { + if (!outputText.contains(buildInfo.pklVersionNonUnique)) { throw GradleException( - "Expected version output to contain current version ($pklVersion), but got '$outputText'" + "Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'" ) } outputFile.get().asFile.toPath().apply { @@ -199,10 +171,10 @@ val requiredGlibcVersion: Version = Version.parse("2.17") val checkGlibc by tasks.registering { enabled = buildInfo.os.isLinux && !buildInfo.musl dependsOn(assembleNative) - val nativeExecutableFile = assembleNative.map { it.outputs.files.singleFile } - val requiredGlibcVersion = requiredGlibcVersion - val exec = providers.exec { commandLine("objdump", "-T", nativeExecutableFile.get()) } doLast { + val exec = providers.exec { + commandLine("objdump", "-T", assembleNative.get().outputs.files.singleFile) + } val output = exec.standardOutput.asText.get() val minimumGlibcVersion = output diff --git a/build.gradle.kts b/build.gradle.kts index e60d6c757..1270141a3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -53,47 +53,27 @@ idea { } } -val clean by tasks.existing { - val buildDirectory = layout.buildDirectory.map { it.asFile } - doLast { buildDirectory.get().delete() } -} +val clean by tasks.existing { delete(layout.buildDirectory) } -val printVersion by tasks.registering { - val pklVersion = buildInfo.pklVersion - doFirst { println(pklVersion.get()) } -} +val printVersion by tasks.registering { doFirst { println(buildInfo.pklVersion) } } -val printInfo by tasks.registering { - val arch = buildInfo.arch - val pklVersion = buildInfo.pklVersion - val pklVersionNonUnique = buildInfo.pklVersionNonUnique - val commitId = buildInfo.commitId - val gradleVerison = gradle.gradleVersion - val javaVersion = System.getProperty("java.version") - val isParallel = gradle.startParameter.isParallelProjectExecutionEnabled - val maxWorkerCount = gradle.startParameter.maxWorkerCount - val projectVersion = project.version - doFirst { - val message = - """ - ==== - Gradle version : $gradleVerison - Java version : $javaVersion - isParallel : $isParallel - maxWorkerCount : $maxWorkerCount - Architecture : $arch +val message = + """ +==== +Gradle version : ${gradle.gradleVersion} +Java version : ${System.getProperty("java.version")} +isParallel : ${gradle.startParameter.isParallelProjectExecutionEnabled} +maxWorkerCount : ${gradle.startParameter.maxWorkerCount} +Architecture : ${buildInfo.arch} - Project Version : $projectVersion - Pkl Version : ${pklVersion.get()} - Pkl Non-Unique Version : $pklVersionNonUnique - Git Commit ID : ${commitId.get()} - ==== - """ - .trimIndent() +Project Version : ${project.version} +Pkl Version : ${buildInfo.pklVersion} +Pkl Non-Unique Version : ${buildInfo.pklVersionNonUnique} +Git Commit ID : ${buildInfo.commitId} +==== +""" - val formattedMessage = - message.replace("====", "=".repeat(message.lines().maxByOrNull { it.length }!!.length)) +val formattedMessage = + message.replace("\n====", "\n" + "=".repeat(message.lines().maxByOrNull { it.length }!!.length)) - println(formattedMessage) - } -} +logger.info(formattedMessage) diff --git a/gradle.properties b/gradle.properties index d7bde8e88..08b8cd48a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,8 +15,6 @@ org.gradle.jvmargs= \ org.gradle.parallel=true org.gradle.caching=true -org.gradle.configuration-cache=true - kotlin.stdlib.default.dependency=false kotlin.daemon.jvmargs=-XX:+UseParallelGC #org.gradle.workers.max=1 diff --git a/pkl-cli/pkl-cli.gradle.kts b/pkl-cli/pkl-cli.gradle.kts index 707ce1c27..f25a54f8d 100644 --- a/pkl-cli/pkl-cli.gradle.kts +++ b/pkl-cli/pkl-cli.gradle.kts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.io.ByteArrayOutputStream import java.io.OutputStream import org.gradle.kotlin.dsl.support.serviceOf @@ -117,15 +118,9 @@ private fun setupJavaExecutableRun( null -> "java" else -> launcher.get().executablePath.asFile.absolutePath } + standardOutput = OutputStream.nullOutputStream() - doFirst { standardOutput = OutputStream.nullOutputStream() } - - val javaExecutableFile = tasks.javaExecutable.map { it.outputs.files.singleFile } - argumentProviders.add( - CommandLineArgumentProvider { - listOf("-jar", javaExecutableFile.get().absolutePath) + args.toList() - } - ) + args("-jar", tasks.javaExecutable.get().outputs.files.singleFile.toString(), *args) doFirst { outputFile.get().asFile.delete() } @@ -138,10 +133,7 @@ val evalTestFlags = arrayOf("eval", "-x", "1 + 1", "pkl:base") fun Exec.useRootDirAndSuppressOutput() { workingDir = rootProject.layout.projectDirectory.asFile - doFirst { - // we only care that this exec doesn't fail - standardOutput = OutputStream.nullOutputStream() - } + standardOutput = ByteArrayOutputStream() // we only care that this exec doesn't fail } // 0.28 Preparing for JDK21 toolchains revealed that `testStartJavaExecutable` may pass, even though diff --git a/pkl-commons-test/pkl-commons-test.gradle.kts b/pkl-commons-test/pkl-commons-test.gradle.kts index 3601a7281..f6f1dda9d 100644 --- a/pkl-commons-test/pkl-commons-test.gradle.kts +++ b/pkl-commons-test/pkl-commons-test.gradle.kts @@ -70,26 +70,17 @@ for (packageDir in file("src/main/files/packages").listFiles()!!) { into(destinationDir) val shasumFile = destinationDir.map { it.file("${packageDir.name}.json.sha256") } outputs.file(shasumFile) - val isWindows = buildInfo.os.isWindows + filter { line -> + line.replaceFirst("\$computedChecksum", archiveFile.get().asFile.computeChecksum()) + } doLast { val outputFile = destinationDir.get().asFile.resolve("${packageDir.name}.json") - fun sha256Hex(file: File): String { - val hash = MessageDigest.getInstance("SHA-256").digest(file.readBytes()) - return buildString(hash.size * 2) { - for (b in hash) { - append("0123456789abcdef"[b.toInt() shr 4 and 0xF]) - append("0123456789abcdef"[b.toInt() and 0xF]) - } - } - } - var contents = - outputFile.readText().replace("\$computedChecksum", sha256Hex(archiveFile.get().asFile)) - if (isWindows) { + if (buildInfo.os.isWindows) { + val contents = outputFile.readText() // workaround for https://github.com/gradle/gradle/issues/1151 - contents = contents.replace("\r\n", "\n") + outputFile.writeText(contents.replace("\r\n", "\n")) } - outputFile.writeText(contents) - shasumFile.get().asFile.writeText(sha256Hex(outputFile)) + shasumFile.get().asFile.writeText(outputFile.computeChecksum()) } } @@ -120,9 +111,6 @@ val generateKeys by "CN=localhost", ) workingDir(keystoreDir) - // capture keystoreFile inside closure so we don't reference `this$0`; which breaks Gradle - // configuration cache - val keystoreFile = keystoreFile doFirst { workingDir.mkdirs() keystoreFile.get().asFile.delete() @@ -155,3 +143,20 @@ val exportCerts by outputFile.get().asFile.delete() } } + +fun toHex(hash: ByteArray): String { + val hexDigitTable = + charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') + return buildString(hash.size * 2) { + for (b in hash) { + append(hexDigitTable[b.toInt() shr 4 and 0xF]) + append(hexDigitTable[b.toInt() and 0xF]) + } + } +} + +fun File.computeChecksum(): String { + val md = MessageDigest.getInstance("SHA-256") + val hash = md.digest(readBytes()) + return toHex(hash) +} diff --git a/pkl-config-java/pkl-config-java.gradle.kts b/pkl-config-java/pkl-config-java.gradle.kts index 7cf0caf51..24c2eaef7 100644 --- a/pkl-config-java/pkl-config-java.gradle.kts +++ b/pkl-config-java/pkl-config-java.gradle.kts @@ -33,11 +33,10 @@ val generateTestConfigClasses by classpath = pklCodegenJava mainClass.set("org.pkl.codegen.java.Main") - val codegenSources = fileTree("src/test/resources/codegenPkl") argumentProviders.add( CommandLineArgumentProvider { listOf("--output-dir", outputDir.get().asFile.path, "--generate-javadoc") + - codegenSources.map { it.path } + fileTree("src/test/resources/codegenPkl").map { it.path } } ) } diff --git a/pkl-config-kotlin/pkl-config-kotlin.gradle.kts b/pkl-config-kotlin/pkl-config-kotlin.gradle.kts index 07f0c7809..b9c4c8762 100644 --- a/pkl-config-kotlin/pkl-config-kotlin.gradle.kts +++ b/pkl-config-kotlin/pkl-config-kotlin.gradle.kts @@ -57,11 +57,10 @@ val generateTestConfigClasses by classpath = pklCodegenKotlin mainClass.set("org.pkl.codegen.kotlin.Main") - val codegenSources = fileTree("src/test/resources/codegenPkl") argumentProviders.add( CommandLineArgumentProvider { listOf("--output-dir", outputDir.get().asFile.absolutePath) + - codegenSources.map { it.absolutePath } + fileTree("src/test/resources/codegenPkl").map { it.absolutePath } } ) } diff --git a/pkl-core/pkl-core.gradle.kts b/pkl-core/pkl-core.gradle.kts index ee360c870..d715386dd 100644 --- a/pkl-core/pkl-core.gradle.kts +++ b/pkl-core/pkl-core.gradle.kts @@ -98,22 +98,20 @@ tasks.processResources { inputs.property("version", buildInfo.pklVersion) inputs.property("commitId", buildInfo.commitId) - val pklVersion = buildInfo.pklVersion - val commitId = buildInfo.commitId - val stdlibModules = - fileTree("$rootDir/stdlib") { - include("*.pkl") - exclude("doc-package-info.pkl") - } - .map { "pkl:" + it.nameWithoutExtension } - .sortedBy { it.lowercase() } - filesMatching("org/pkl/core/Release.properties") { + val stdlibModules = + fileTree("$rootDir/stdlib") { + include("*.pkl") + exclude("doc-package-info.pkl") + } + .map { "pkl:" + it.nameWithoutExtension } + .sortedBy { it.lowercase() } + filter( "tokens" to mapOf( - "version" to pklVersion.get(), - "commitId" to commitId.get(), + "version" to buildInfo.pklVersion, + "commitId" to buildInfo.commitId, "stdlibModules" to stdlibModules.joinToString(","), ) ) @@ -142,15 +140,13 @@ val externalReaderFixture by tasks.registering { group = "build" dependsOn(tasks.named("compileExternalReaderFixtureJava")) inputs.files(externalReaderFixtureSourceSet.map { it.output }) - val isWindows = buildInfo.os.isWindows - val fileName = if (isWindows) "externalreader.bat" else "externalreader" + val fileName = if (buildInfo.os.isWindows) "externalreader.bat" else "externalreader" val outputFile = layout.buildDirectory.file("fixtures/$fileName") outputs.file(outputFile) - val runtimeClasspath = externalReaderFixtureSourceSet.map { it.runtimeClasspath } doLast { - val classpath = runtimeClasspath.get().asPath + val classpath = externalReaderFixtureSourceSet.get().runtimeClasspath.asPath val scriptContent = - if (isWindows) { + if (buildInfo.os.isWindows) { """ @echo off java -cp $classpath org.pkl.core.externalreaderfixture.Main diff --git a/pkl-executor/pkl-executor.gradle.kts b/pkl-executor/pkl-executor.gradle.kts index 618956e89..573112699 100644 --- a/pkl-executor/pkl-executor.gradle.kts +++ b/pkl-executor/pkl-executor.gradle.kts @@ -68,16 +68,15 @@ sourceSets { main { java { srcDir("src/main/java") } } } val prepareHistoricalDistributions by tasks.registering { val outputDir = layout.buildDirectory.dir("pklHistoricalDistributions") - inputs.files(pklHistoricalDistributions) + inputs.files(pklHistoricalDistributions.files) outputs.dir(outputDir) - val isWindows = buildInfo.os.isWindows doLast { val distributionDir = outputDir.get().asFile.toPath().also(Files::createDirectories) - for (file in inputs.files) { + for (file in pklHistoricalDistributions.files) { val target = distributionDir.resolve(file.name) // Create normal files on Windows, symlink on macOS/linux (need admin privileges to create // symlinks on Windows) - if (isWindows) { + if (buildInfo.os.isWindows) { if (!Files.isRegularFile(target, LinkOption.NOFOLLOW_LINKS)) { if (Files.exists(target)) { Files.delete(target) diff --git a/pkl-gradle/pkl-gradle.gradle.kts b/pkl-gradle/pkl-gradle.gradle.kts index f6d660b9b..449d85a78 100644 --- a/pkl-gradle/pkl-gradle.gradle.kts +++ b/pkl-gradle/pkl-gradle.gradle.kts @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import org.gradle.api.file.ArchiveOperations -import org.gradle.kotlin.dsl.support.serviceOf - plugins { id("pklAllProjects") id("pklJavaLibrary") @@ -83,14 +80,13 @@ val externalReaderJar by archiveVersion = "" // Package all dependencies into the jar (shadow plugin lite). - val archiveOps = serviceOf() from( externalReader.runtimeClasspath.elements.map { locations -> locations.mapNotNull { location -> val f = location.asFile when { f.isDirectory -> f - f.isFile -> archiveOps.zipTree(f) + f.isFile -> zipTree(f) else -> null } } @@ -100,32 +96,18 @@ val externalReaderJar by manifest { attributes("Main-Class" to "org.pkl.gradle.test.extreader.Main") } } -// Named class avoids the anonymous inner-class `this$0` field that Gradle's configuration -// cache cannot serialize when a SAM lambda is created inside a lambda-with-receiver. -class ExternalReaderArgProvider( - private val jarFile: Provider, - private val javaExecutable: Provider, -) : CommandLineArgumentProvider { - override fun asArguments() = - listOf( - "-DpklGradle.externalReaderJar=${jarFile.get().asFile.absolutePath}", - "-DpklGradle.javaExecutable=${javaExecutable.get()}", - ) -} - -val externalReaderJarFile = externalReaderJar.flatMap { it.archiveFile } - -val javaExecutablePath = - javaToolchains.launcherFor(java.toolchain).map { it.executablePath.asFile.absolutePath } - -// Apply to all Test tasks (not just `test`) so that testJdk* tasks also receive the -// external-reader system properties without relying on jvmArgumentProviders being copied -// across tasks (which breaks the configuration cache via stale task references). -tasks.withType().configureEach { +tasks.test { dependsOn(externalReaderJar) // Currently the only way to inject system properties from lazy values in Gradle // is via `jvmArgumentProviders`. - jvmArgumentProviders += ExternalReaderArgProvider(externalReaderJarFile, javaExecutablePath) + jvmArgumentProviders += CommandLineArgumentProvider { + listOf( + "-DpklGradle.externalReaderJar=" + + externalReaderJar.get().archiveFile.get().asFile.absolutePath, + "-DpklGradle.javaExecutable=" + + javaToolchains.launcherFor(java.toolchain).get().executablePath.asFile.absolutePath, + ) + } } publishing { diff --git a/pkl-tools/pkl-tools.gradle.kts b/pkl-tools/pkl-tools.gradle.kts index bcfbc028f..61fd0c0b0 100644 --- a/pkl-tools/pkl-tools.gradle.kts +++ b/pkl-tools/pkl-tools.gradle.kts @@ -81,14 +81,13 @@ private fun Exec.configureTestStartFatJar(launcher: Provider) { inputs.files(tasks.shadowJar) executable = launcher.get().executablePath.asFile.absolutePath - doFirst { standardOutput = OutputStream.nullOutputStream() } + standardOutput = OutputStream.nullOutputStream() - val shadowJarFile = tasks.shadowJar.flatMap { it.archiveFile } argumentProviders.add( CommandLineArgumentProvider { buildList { add("-cp") - add(shadowJarFile.get().asFile.absolutePath) + add(tasks.shadowJar.get().outputs.files.singleFile.absolutePath) add("org.pkl.cli.Main") add("eval") add("-x") @@ -164,7 +163,7 @@ publishing { connection.set("scm:git:git://github.com/apple/pkl.git") developerConnection.set("scm:git:ssh://github.com/apple/pkl.git") val buildInfo = project.extensions.getByType() - url.set(buildInfo.commitish.map { "https://github.com/apple/pkl/tree/$it" }) + url.set("https://github.com/apple/pkl/tree/${buildInfo.commitish}") } issueManagement { system.set("GitHub Issues")