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")