mirror of
https://github.com/apple/pkl.git
synced 2026-06-10 07:42:58 +02:00
Enable Gradle configuration cache (#1646)
Enable the configuration cache feature in Gradle, and adjust various pieces of build logic that aren't configuration cache compatible.
This commit is contained in:
@@ -288,7 +288,12 @@ open class BuildInfo(private val project: Project) {
|
||||
classpath = template.classpath
|
||||
testClassesDirs = template.testClassesDirs
|
||||
jvmArgs.addAll(template.jvmArgs)
|
||||
jvmArgumentProviders.addAll(template.jvmArgumentProviders)
|
||||
// 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<Test>().configureEach in the subproject.
|
||||
forkEvery = template.forkEvery
|
||||
maxParallelForks = template.maxParallelForks
|
||||
minHeapSize = template.minHeapSize
|
||||
@@ -372,35 +377,33 @@ open class BuildInfo(private val project: Project) {
|
||||
org.gradle.internal.os.OperatingSystem.current()
|
||||
}
|
||||
|
||||
// could be `commitId: Provider<String> = 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 }
|
||||
private val computedCommitId: Provider<String> =
|
||||
// only run command once per build invocation
|
||||
if (project.path == project.rootProject.path) {
|
||||
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()
|
||||
project.providers
|
||||
.exec { commandLine("git", "rev-parse", "--short", "HEAD") }
|
||||
.standardOutput
|
||||
.asText
|
||||
.map { it.trim() }
|
||||
} else {
|
||||
project.rootProject.extensions.getByType(BuildInfo::class.java).commitId
|
||||
}
|
||||
}
|
||||
|
||||
val commitish: String by lazy { if (isReleaseBuild) project.version.toString() else commitId }
|
||||
val commitId: Provider<String> =
|
||||
// allow -DcommitId=abc123 for build environments that don't have git.
|
||||
System.getProperty("commitId")?.let { project.providers.provider { it } } ?: computedCommitId
|
||||
|
||||
val pklVersion: String by lazy {
|
||||
val commitish: Provider<String> =
|
||||
if (isReleaseBuild) project.providers.provider { project.version.toString() } else commitId
|
||||
|
||||
val pklVersion: Provider<String> =
|
||||
if (isReleaseBuild) {
|
||||
project.version.toString()
|
||||
project.providers.provider { project.version.toString() }
|
||||
} else {
|
||||
project.version.toString().replace("-SNAPSHOT", "-dev+$commitId")
|
||||
project.providers
|
||||
.provider { project.version.toString() }
|
||||
.zip(commitId) { version, id -> version.replace("-SNAPSHOT", "-dev+$id") }
|
||||
}
|
||||
}
|
||||
|
||||
val pklVersionNonUnique: String by lazy {
|
||||
if (isReleaseBuild) {
|
||||
|
||||
@@ -20,9 +20,13 @@ 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
|
||||
|
||||
@@ -32,25 +36,32 @@ constructor(
|
||||
private val fileOperations: FileOperations,
|
||||
private val execOperations: ExecOperations,
|
||||
) : DefaultTask() {
|
||||
@get:Input abstract val graalVm: Property<BuildInfo.GraalVm>
|
||||
@get:Input abstract val homeDir: Property<String>
|
||||
|
||||
@get:InputFile abstract val downloadFile: RegularFileProperty
|
||||
|
||||
@get:Input abstract val version: Property<String>
|
||||
|
||||
@get:Input abstract val graalVmJdkVersion: Property<String>
|
||||
|
||||
@get:Internal abstract val installDir: DirectoryProperty
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis") onlyIf("GraalVM not installed") { !graalVm.get().installDir.exists() }
|
||||
@Suppress("LeakingThis") onlyIf("GraalVM not installed") { !installDir.get().asFile.exists() }
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
@Suppress("unused")
|
||||
fun run() {
|
||||
// minimize chance of corruption by extract-to-random-dir-and-flip-symlink
|
||||
val distroDir = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
|
||||
val distroDir = Paths.get(homeDir.get(), UUID.randomUUID().toString())
|
||||
try {
|
||||
distroDir.createDirectories()
|
||||
println("Extracting ${graalVm.get().downloadFile} into $distroDir")
|
||||
println("Extracting ${downloadFile.get().asFile} into $distroDir")
|
||||
// faster and more reliable than Gradle's `copy { from tarTree() }`
|
||||
execOperations.exec {
|
||||
workingDir = distroDir.toFile()
|
||||
executable = "tar"
|
||||
args("--strip-components=1", "-xzf", graalVm.get().downloadFile)
|
||||
args("--strip-components=1", "-xzf", downloadFile.get().asFile)
|
||||
}
|
||||
|
||||
val os = org.gradle.internal.os.OperatingSystem.current()
|
||||
@@ -59,8 +70,8 @@ constructor(
|
||||
|
||||
println("Installing native-image into $distroDir")
|
||||
val gvmVersionMajor =
|
||||
requireNotNull(graalVm.get().version.split(".").first().toIntOrNull()) {
|
||||
"Invalid GraalVM JDK version: ${graalVm.get().graalVmJdkVersion}"
|
||||
requireNotNull(version.get().split(".").first().toIntOrNull()) {
|
||||
"Invalid GraalVM JDK version: ${graalVmJdkVersion.get()}"
|
||||
}
|
||||
if (gvmVersionMajor < 24) {
|
||||
execOperations.exec {
|
||||
@@ -70,11 +81,11 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
println("Creating symlink ${graalVm.get().installDir} for $distroDir")
|
||||
val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
|
||||
println("Creating symlink ${installDir.get().asFile} for $distroDir")
|
||||
val tempLink = Paths.get(homeDir.get(), UUID.randomUUID().toString())
|
||||
Files.createSymbolicLink(tempLink, distroDir)
|
||||
try {
|
||||
Files.move(tempLink, graalVm.get().installDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
Files.move(tempLink, installDir.get().asFile.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
fileOperations.delete(tempLink.toFile())
|
||||
|
||||
@@ -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,25 +49,31 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
|
||||
@get:InputFiles abstract val classpath: ConfigurableFileCollection
|
||||
|
||||
private val outputDir = project.layout.buildDirectory.dir("executable")
|
||||
/** Path to the `native-image` binary (e.g. `<graalVmBaseDir>/bin/native-image`). */
|
||||
@get:Input abstract val nativeImageExecutable: Property<String>
|
||||
|
||||
@get:OutputFile val outputFile = outputDir.flatMap { it.file(imageName) }
|
||||
@get:Input abstract val graalSdkLibraryName: Property<String>
|
||||
|
||||
@get:Input abstract val releaseBuild: Property<Boolean>
|
||||
|
||||
@get:Input abstract val nativeArch: Property<Boolean>
|
||||
|
||||
/** Divisor applied to `availableProcessors` to throttle native-image CPU usage. */
|
||||
@get:Input abstract val processorDivisor: Property<Int>
|
||||
|
||||
@get:Inject protected abstract val execOperations: ExecOperations
|
||||
|
||||
private val graalVm: Provider<BuildInfo.GraalVm> = arch.map { a ->
|
||||
when (a) {
|
||||
Architecture.AMD64 -> buildInfo.graalVmAmd64
|
||||
Architecture.AARCH64 -> buildInfo.graalVmAarch64
|
||||
}
|
||||
}
|
||||
@get:Inject protected abstract val layout: ProjectLayout
|
||||
|
||||
private val buildInfo: BuildInfo = project.extensions.getByType(BuildInfo::class.java)
|
||||
private val outputDir
|
||||
get() = layout.buildDirectory.dir("executable")
|
||||
|
||||
private val nativeImageCommandName =
|
||||
if (buildInfo.os.isWindows) "native-image.cmd" else "native-image"
|
||||
@get:OutputFile
|
||||
val outputFile
|
||||
get() = outputDir.flatMap { it.file(imageName) }
|
||||
|
||||
private val nativeImageExecutable = graalVm.map { "${it.baseDir}/bin/$nativeImageCommandName" }
|
||||
@get:ServiceReference("nativeImageBuildService")
|
||||
abstract val buildService: Property<NativeImageBuildService>
|
||||
|
||||
private val extraArgsFromProperties by lazy {
|
||||
System.getProperties()
|
||||
@@ -75,19 +81,7 @@ 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
|
||||
@@ -104,8 +98,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
@Suppress("unused")
|
||||
protected fun run() {
|
||||
execOperations.exec {
|
||||
val exclusions =
|
||||
listOf(buildInfo.libs.findLibrary("graalSdk").get()).map { it.get().module.name }
|
||||
val exclusions = listOf(graalSdkLibraryName.get())
|
||||
|
||||
executable = nativeImageExecutable.get()
|
||||
workingDir(outputDir)
|
||||
@@ -140,10 +133,10 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
add("-H:-ParseRuntimeOptions")
|
||||
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower)
|
||||
// executable
|
||||
if (!buildInfo.isReleaseBuild) {
|
||||
if (!releaseBuild.get()) {
|
||||
add("-Ob")
|
||||
}
|
||||
if (buildInfo.isNativeArch) {
|
||||
if (nativeArch.get()) {
|
||||
add("-march=native")
|
||||
} else {
|
||||
add("-march=compatibility")
|
||||
@@ -155,9 +148,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
}
|
||||
add(pathInput.asPath)
|
||||
// make sure dev machine stays responsive (15% slowdown on my laptop)
|
||||
val processors =
|
||||
Runtime.getRuntime().availableProcessors() /
|
||||
if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1
|
||||
val processors = Runtime.getRuntime().availableProcessors() / processorDivisor.get()
|
||||
add("-J-XX:ActiveProcessorCount=${processors}")
|
||||
// Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed
|
||||
// tools.
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -26,31 +28,45 @@ class PklFormatterStep(@Transient private val configuration: Configuration) : Se
|
||||
}
|
||||
|
||||
fun create(): FormatterStep {
|
||||
val files = configuration.files.toList()
|
||||
return FormatterStep.createLazy(
|
||||
"pkl",
|
||||
{ PklFormatterStep(configuration) },
|
||||
{ PklFormatterFunc(configuration) },
|
||||
{ PklFormatterState(files) },
|
||||
{ PklFormatterFunc(it.files) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class PklFormatterFunc(@Transient private val configuration: Configuration) :
|
||||
FormatterFunc, Serializable {
|
||||
data class PklFormatterState(val files: List<File>) : Serializable {
|
||||
companion object {
|
||||
@Serial private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
}
|
||||
|
||||
class PklFormatterFunc(private val files: List<File>) : FormatterFunc, Serializable {
|
||||
companion object {
|
||||
@Serial private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
|
||||
private val classLoader by lazy {
|
||||
val urls = configuration.files.map { it.toURI().toURL() }
|
||||
@delegate:Transient
|
||||
private val classLoader: URLClassLoader by lazy {
|
||||
val urls = files.map { it.toURI().toURL() }
|
||||
// Use the platform classloader as parent to isolate from Gradle's classloader
|
||||
URLClassLoader(urls.toTypedArray(), ClassLoader.getPlatformClassLoader())
|
||||
}
|
||||
|
||||
private val formatterClass by lazy { classLoader.loadClass("org.pkl.formatter.Formatter") }
|
||||
@delegate:Transient
|
||||
private val formatterClass: Class<*> by lazy {
|
||||
classLoader.loadClass("org.pkl.formatter.Formatter")
|
||||
}
|
||||
|
||||
private val formatMethod by lazy { formatterClass.getMethod("format", String::class.java) }
|
||||
@delegate:Transient
|
||||
private val formatMethod: Method by lazy {
|
||||
formatterClass.getMethod("format", String::class.java)
|
||||
}
|
||||
|
||||
private val formatterInstance by lazy { formatterClass.getConstructor().newInstance() }
|
||||
@delegate:Transient
|
||||
private val formatterInstance: Any by lazy { formatterClass.getConstructor().newInstance() }
|
||||
|
||||
override fun apply(input: String): String {
|
||||
return formatMethod(formatterInstance, input) as String
|
||||
|
||||
@@ -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<BuildInfo>()
|
||||
url.set("https://github.com/apple/pkl/tree/${buildInfo.commitish}")
|
||||
url.set(buildInfo.commitish.map { "https://github.com/apple/pkl/tree/$it" })
|
||||
}
|
||||
issueManagement {
|
||||
system.set("GitHub Issues")
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
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`
|
||||
@@ -150,17 +152,19 @@ tasks.check { dependsOn(testFatJar) }
|
||||
|
||||
val validateFatJar by tasks.registering {
|
||||
val outputFile = layout.buildDirectory.file("validateFatJar/result.txt")
|
||||
inputs.files(tasks.shadowJar)
|
||||
val shadowJarFile = tasks.shadowJar.flatMap { it.archiveFile }
|
||||
val archiveOps = serviceOf<ArchiveOperations>()
|
||||
inputs.file(shadowJarFile)
|
||||
inputs.property("nonRelocations", nonRelocations)
|
||||
outputs.file(outputFile)
|
||||
val nonRelocations = nonRelocations
|
||||
|
||||
doLast {
|
||||
val unshadowedFiles = mutableListOf<String>()
|
||||
zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit {
|
||||
val fileDetails = this
|
||||
val path = fileDetails.relativePath.pathString
|
||||
archiveOps.zipTree(shadowJarFile.get().asFile).visit {
|
||||
val path = relativePath.pathString
|
||||
if (
|
||||
!(fileDetails.isDirectory ||
|
||||
!(isDirectory ||
|
||||
path.startsWith("org/pkl/") ||
|
||||
path.startsWith("META-INF/") ||
|
||||
nonRelocations.any { path.startsWith(it) })
|
||||
|
||||
@@ -28,11 +28,13 @@ val downloadGraalVmAmd64 by
|
||||
tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAmd64) }
|
||||
|
||||
fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
|
||||
onlyIf { !graalvm.installDir.exists() }
|
||||
doLast { println("Downloaded GraalVm to ${graalvm.downloadFile}") }
|
||||
val installDir = graalvm.installDir
|
||||
val downloadFile = graalvm.downloadFile
|
||||
onlyIf { !installDir.exists() }
|
||||
doLast { println("Downloaded GraalVm to $downloadFile") }
|
||||
|
||||
src(graalvm.downloadUrl)
|
||||
dest(graalvm.downloadFile)
|
||||
dest(downloadFile)
|
||||
overwrite(false)
|
||||
tempAndMove(true)
|
||||
}
|
||||
@@ -50,7 +52,8 @@ val verifyGraalVmAmd64 by
|
||||
}
|
||||
|
||||
fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) {
|
||||
onlyIf { !graalvm.installDir.exists() }
|
||||
val installDir = graalvm.installDir
|
||||
onlyIf { !installDir.exists() }
|
||||
|
||||
src(graalvm.downloadFile)
|
||||
checksum(
|
||||
@@ -59,16 +62,26 @@ 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)
|
||||
graalVm = buildInfo.graalVmAarch64
|
||||
configureInstallGraalVm(buildInfo.graalVmAarch64)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val installGraalVmAmd64 by
|
||||
tasks.registering(InstallGraalVm::class) {
|
||||
dependsOn(verifyGraalVmAmd64)
|
||||
graalVm = buildInfo.graalVmAmd64
|
||||
configureInstallGraalVm(buildInfo.graalVmAmd64)
|
||||
}
|
||||
|
||||
@@ -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 { project.delete(resultFile) }
|
||||
doFirst { resultFile.get().asFile.delete() }
|
||||
doLast { resultFile.get().asFile.writeText("Success.") }
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,10 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider<JavaLauncher>? = 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 = javaExecutable.get().outputs.files.singleFile
|
||||
val executablePath = executableFile.get().asFile
|
||||
if (launcher?.isPresent == true) {
|
||||
commandLine(
|
||||
launcher.get().executablePath.asFile.absolutePath,
|
||||
@@ -63,9 +65,9 @@ fun Task.setupTestStartJavaExecutable(launcher: Provider<JavaLauncher>? = null)
|
||||
|
||||
doLast {
|
||||
val outputText = execOutput.standardOutput.asText.get()
|
||||
if (!outputText.contains(buildInfo.pklVersionNonUnique)) {
|
||||
if (!outputText.contains(pklVersion)) {
|
||||
throw GradleException(
|
||||
"Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'"
|
||||
"Expected version output to contain current version ($pklVersion), but got '$outputText'"
|
||||
)
|
||||
}
|
||||
outputFile.get().asFile.toPath().apply {
|
||||
|
||||
@@ -67,13 +67,35 @@ 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")
|
||||
}
|
||||
|
||||
@@ -91,6 +113,7 @@ val macExecutableAmd64 by
|
||||
mainClass = executableSpec.mainClass
|
||||
amd64()
|
||||
setClasspath()
|
||||
configure()
|
||||
}
|
||||
|
||||
val macExecutableAarch64 by
|
||||
@@ -99,6 +122,7 @@ val macExecutableAarch64 by
|
||||
mainClass = executableSpec.mainClass
|
||||
aarch64()
|
||||
setClasspath()
|
||||
configure()
|
||||
}
|
||||
|
||||
val linuxExecutableAmd64 by
|
||||
@@ -107,6 +131,7 @@ val linuxExecutableAmd64 by
|
||||
mainClass = executableSpec.mainClass
|
||||
amd64()
|
||||
setClasspath()
|
||||
configure()
|
||||
}
|
||||
|
||||
val linuxExecutableAarch64 by
|
||||
@@ -115,6 +140,7 @@ 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")
|
||||
@@ -126,6 +152,7 @@ val alpineExecutableAmd64 by
|
||||
mainClass = executableSpec.mainClass
|
||||
amd64()
|
||||
setClasspath()
|
||||
configure()
|
||||
extraNativeImageArgs.addAll(listOf("--static", "--libc=musl"))
|
||||
}
|
||||
|
||||
@@ -135,6 +162,7 @@ val windowsExecutableAmd64 by
|
||||
mainClass = executableSpec.mainClass
|
||||
amd64()
|
||||
setClasspath()
|
||||
configure()
|
||||
}
|
||||
|
||||
val assembleNative by tasks.existing
|
||||
@@ -143,18 +171,18 @@ val testStartNativeExecutable by tasks.registering {
|
||||
dependsOn(assembleNative)
|
||||
|
||||
// dummy file for up-to-date checking
|
||||
val outputFile = project.layout.buildDirectory.file("testStartNativeExecutable/output.txt")
|
||||
val outputFile = layout.buildDirectory.file("testStartNativeExecutable/output.txt")
|
||||
outputs.file(outputFile)
|
||||
|
||||
val execOutput = providers.exec {
|
||||
commandLine(assembleNative.get().outputs.files.singleFile, "--version")
|
||||
}
|
||||
val nativeExecutableFile = assembleNative.map { it.outputs.files.singleFile }
|
||||
val execOutput = providers.exec { commandLine(nativeExecutableFile.get(), "--version") }
|
||||
val pklVersion = buildInfo.pklVersionNonUnique
|
||||
|
||||
doLast {
|
||||
val outputText = execOutput.standardOutput.asText.get()
|
||||
if (!outputText.contains(buildInfo.pklVersionNonUnique)) {
|
||||
if (!outputText.contains(pklVersion)) {
|
||||
throw GradleException(
|
||||
"Expected version output to contain current version (${buildInfo.pklVersionNonUnique}), but got '$outputText'"
|
||||
"Expected version output to contain current version ($pklVersion), but got '$outputText'"
|
||||
)
|
||||
}
|
||||
outputFile.get().asFile.toPath().apply {
|
||||
@@ -171,10 +199,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
|
||||
|
||||
+39
-19
@@ -53,27 +53,47 @@ idea {
|
||||
}
|
||||
}
|
||||
|
||||
val clean by tasks.existing { delete(layout.buildDirectory) }
|
||||
val clean by tasks.existing {
|
||||
val buildDirectory = layout.buildDirectory.map { it.asFile }
|
||||
doLast { buildDirectory.get().delete() }
|
||||
}
|
||||
|
||||
val printVersion by tasks.registering { doFirst { println(buildInfo.pklVersion) } }
|
||||
val printVersion by tasks.registering {
|
||||
val pklVersion = buildInfo.pklVersion
|
||||
doFirst { println(pklVersion.get()) }
|
||||
}
|
||||
|
||||
val message =
|
||||
"""
|
||||
====
|
||||
Gradle version : ${gradle.gradleVersion}
|
||||
Java version : ${System.getProperty("java.version")}
|
||||
isParallel : ${gradle.startParameter.isParallelProjectExecutionEnabled}
|
||||
maxWorkerCount : ${gradle.startParameter.maxWorkerCount}
|
||||
Architecture : ${buildInfo.arch}
|
||||
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
|
||||
|
||||
Project Version : ${project.version}
|
||||
Pkl Version : ${buildInfo.pklVersion}
|
||||
Pkl Non-Unique Version : ${buildInfo.pklVersionNonUnique}
|
||||
Git Commit ID : ${buildInfo.commitId}
|
||||
====
|
||||
"""
|
||||
Project Version : $projectVersion
|
||||
Pkl Version : ${pklVersion.get()}
|
||||
Pkl Non-Unique Version : $pklVersionNonUnique
|
||||
Git Commit ID : ${commitId.get()}
|
||||
====
|
||||
"""
|
||||
.trimIndent()
|
||||
|
||||
val formattedMessage =
|
||||
message.replace("\n====", "\n" + "=".repeat(message.lines().maxByOrNull { it.length }!!.length))
|
||||
val formattedMessage =
|
||||
message.replace("====", "=".repeat(message.lines().maxByOrNull { it.length }!!.length))
|
||||
|
||||
logger.info(formattedMessage)
|
||||
println(formattedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ 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
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* 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
|
||||
|
||||
@@ -118,9 +117,15 @@ private fun setupJavaExecutableRun(
|
||||
null -> "java"
|
||||
else -> launcher.get().executablePath.asFile.absolutePath
|
||||
}
|
||||
standardOutput = OutputStream.nullOutputStream()
|
||||
|
||||
args("-jar", tasks.javaExecutable.get().outputs.files.singleFile.toString(), *args)
|
||||
doFirst { standardOutput = OutputStream.nullOutputStream() }
|
||||
|
||||
val javaExecutableFile = tasks.javaExecutable.map { it.outputs.files.singleFile }
|
||||
argumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
listOf("-jar", javaExecutableFile.get().absolutePath) + args.toList()
|
||||
}
|
||||
)
|
||||
|
||||
doFirst { outputFile.get().asFile.delete() }
|
||||
|
||||
@@ -133,7 +138,10 @@ val evalTestFlags = arrayOf("eval", "-x", "1 + 1", "pkl:base")
|
||||
|
||||
fun Exec.useRootDirAndSuppressOutput() {
|
||||
workingDir = rootProject.layout.projectDirectory.asFile
|
||||
standardOutput = ByteArrayOutputStream() // we only care that this exec doesn't fail
|
||||
doFirst {
|
||||
// we only care that this exec doesn't fail
|
||||
standardOutput = OutputStream.nullOutputStream()
|
||||
}
|
||||
}
|
||||
|
||||
// 0.28 Preparing for JDK21 toolchains revealed that `testStartJavaExecutable` may pass, even though
|
||||
|
||||
@@ -70,17 +70,26 @@ for (packageDir in file("src/main/files/packages").listFiles()!!) {
|
||||
into(destinationDir)
|
||||
val shasumFile = destinationDir.map { it.file("${packageDir.name}.json.sha256") }
|
||||
outputs.file(shasumFile)
|
||||
filter { line ->
|
||||
line.replaceFirst("\$computedChecksum", archiveFile.get().asFile.computeChecksum())
|
||||
}
|
||||
val isWindows = buildInfo.os.isWindows
|
||||
doLast {
|
||||
val outputFile = destinationDir.get().asFile.resolve("${packageDir.name}.json")
|
||||
if (buildInfo.os.isWindows) {
|
||||
val contents = outputFile.readText()
|
||||
// workaround for https://github.com/gradle/gradle/issues/1151
|
||||
outputFile.writeText(contents.replace("\r\n", "\n"))
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
shasumFile.get().asFile.writeText(outputFile.computeChecksum())
|
||||
var contents =
|
||||
outputFile.readText().replace("\$computedChecksum", sha256Hex(archiveFile.get().asFile))
|
||||
if (isWindows) {
|
||||
// workaround for https://github.com/gradle/gradle/issues/1151
|
||||
contents = contents.replace("\r\n", "\n")
|
||||
}
|
||||
outputFile.writeText(contents)
|
||||
shasumFile.get().asFile.writeText(sha256Hex(outputFile))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +120,9 @@ 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()
|
||||
@@ -143,20 +155,3 @@ 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)
|
||||
}
|
||||
|
||||
@@ -33,10 +33,11 @@ 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") +
|
||||
fileTree("src/test/resources/codegenPkl").map { it.path }
|
||||
codegenSources.map { it.path }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,10 +57,11 @@ 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) +
|
||||
fileTree("src/test/resources/codegenPkl").map { it.absolutePath }
|
||||
codegenSources.map { it.absolutePath }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -98,20 +98,22 @@ tasks.processResources {
|
||||
inputs.property("version", buildInfo.pklVersion)
|
||||
inputs.property("commitId", buildInfo.commitId)
|
||||
|
||||
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() }
|
||||
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") {
|
||||
filter<ReplaceTokens>(
|
||||
"tokens" to
|
||||
mapOf(
|
||||
"version" to buildInfo.pklVersion,
|
||||
"commitId" to buildInfo.commitId,
|
||||
"version" to pklVersion.get(),
|
||||
"commitId" to commitId.get(),
|
||||
"stdlibModules" to stdlibModules.joinToString(","),
|
||||
)
|
||||
)
|
||||
@@ -140,13 +142,15 @@ val externalReaderFixture by tasks.registering {
|
||||
group = "build"
|
||||
dependsOn(tasks.named("compileExternalReaderFixtureJava"))
|
||||
inputs.files(externalReaderFixtureSourceSet.map { it.output })
|
||||
val fileName = if (buildInfo.os.isWindows) "externalreader.bat" else "externalreader"
|
||||
val isWindows = buildInfo.os.isWindows
|
||||
val fileName = if (isWindows) "externalreader.bat" else "externalreader"
|
||||
val outputFile = layout.buildDirectory.file("fixtures/$fileName")
|
||||
outputs.file(outputFile)
|
||||
val runtimeClasspath = externalReaderFixtureSourceSet.map { it.runtimeClasspath }
|
||||
doLast {
|
||||
val classpath = externalReaderFixtureSourceSet.get().runtimeClasspath.asPath
|
||||
val classpath = runtimeClasspath.get().asPath
|
||||
val scriptContent =
|
||||
if (buildInfo.os.isWindows) {
|
||||
if (isWindows) {
|
||||
"""
|
||||
@echo off
|
||||
java -cp $classpath org.pkl.core.externalreaderfixture.Main
|
||||
|
||||
@@ -68,15 +68,16 @@ sourceSets { main { java { srcDir("src/main/java") } } }
|
||||
|
||||
val prepareHistoricalDistributions by tasks.registering {
|
||||
val outputDir = layout.buildDirectory.dir("pklHistoricalDistributions")
|
||||
inputs.files(pklHistoricalDistributions.files)
|
||||
inputs.files(pklHistoricalDistributions)
|
||||
outputs.dir(outputDir)
|
||||
val isWindows = buildInfo.os.isWindows
|
||||
doLast {
|
||||
val distributionDir = outputDir.get().asFile.toPath().also(Files::createDirectories)
|
||||
for (file in pklHistoricalDistributions.files) {
|
||||
for (file in inputs.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 (buildInfo.os.isWindows) {
|
||||
if (isWindows) {
|
||||
if (!Files.isRegularFile(target, LinkOption.NOFOLLOW_LINKS)) {
|
||||
if (Files.exists(target)) {
|
||||
Files.delete(target)
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
* 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")
|
||||
@@ -80,13 +83,14 @@ val externalReaderJar by
|
||||
archiveVersion = ""
|
||||
|
||||
// Package all dependencies into the jar (shadow plugin lite).
|
||||
val archiveOps = serviceOf<ArchiveOperations>()
|
||||
from(
|
||||
externalReader.runtimeClasspath.elements.map { locations ->
|
||||
locations.mapNotNull { location ->
|
||||
val f = location.asFile
|
||||
when {
|
||||
f.isDirectory -> f
|
||||
f.isFile -> zipTree(f)
|
||||
f.isFile -> archiveOps.zipTree(f)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -96,18 +100,32 @@ val externalReaderJar by
|
||||
manifest { attributes("Main-Class" to "org.pkl.gradle.test.extreader.Main") }
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
// 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<RegularFile>,
|
||||
private val javaExecutable: Provider<String>,
|
||||
) : 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<Test>().configureEach {
|
||||
dependsOn(externalReaderJar)
|
||||
// Currently the only way to inject system properties from lazy values in Gradle
|
||||
// is via `jvmArgumentProviders`.
|
||||
jvmArgumentProviders += CommandLineArgumentProvider {
|
||||
listOf(
|
||||
"-DpklGradle.externalReaderJar=" +
|
||||
externalReaderJar.get().archiveFile.get().asFile.absolutePath,
|
||||
"-DpklGradle.javaExecutable=" +
|
||||
javaToolchains.launcherFor(java.toolchain).get().executablePath.asFile.absolutePath,
|
||||
)
|
||||
}
|
||||
jvmArgumentProviders += ExternalReaderArgProvider(externalReaderJarFile, javaExecutablePath)
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
@@ -81,13 +81,14 @@ private fun Exec.configureTestStartFatJar(launcher: Provider<JavaLauncher>) {
|
||||
|
||||
inputs.files(tasks.shadowJar)
|
||||
executable = launcher.get().executablePath.asFile.absolutePath
|
||||
standardOutput = OutputStream.nullOutputStream()
|
||||
doFirst { standardOutput = OutputStream.nullOutputStream() }
|
||||
|
||||
val shadowJarFile = tasks.shadowJar.flatMap { it.archiveFile }
|
||||
argumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
buildList {
|
||||
add("-cp")
|
||||
add(tasks.shadowJar.get().outputs.files.singleFile.absolutePath)
|
||||
add(shadowJarFile.get().asFile.absolutePath)
|
||||
add("org.pkl.cli.Main")
|
||||
add("eval")
|
||||
add("-x")
|
||||
@@ -163,7 +164,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<BuildInfo>()
|
||||
url.set("https://github.com/apple/pkl/tree/${buildInfo.commitish}")
|
||||
url.set(buildInfo.commitish.map { "https://github.com/apple/pkl/tree/$it" })
|
||||
}
|
||||
issueManagement {
|
||||
system.set("GitHub Issues")
|
||||
|
||||
Reference in New Issue
Block a user