mirror of
https://github.com/apple/pkl.git
synced 2026-01-11 22:30:54 +01:00
Add support for Windows (#492)
This adds support for Windows. The in-language path separator is still `/`, to ensure Pkl programs are cross-platform. Log lines are written using CRLF endings on Windows. Modules that are combined with `--module-output-separator` uses LF endings to ensure consistent rendering across platforms. `jpkl` does not work on Windows as a direct executable. However, it can work with `java -jar jpkl`. Additional details: * Adjust git settings for Windows * Add native executable for pkl cli * Add jdk17 windows Gradle check in CI * Adjust CI test reports to be staged within Gradle rather than by shell script. * Fix: encode more characters that are not safe Windows paths * Skip running tests involving symbolic links on Windows (these require administrator privileges to run). * Introduce custom implementation of `IoUtils.relativize` * Allow Gradle to initialize ExecutableJar `Property` values * Add Gradle flag to enable remote JVM debugging Co-authored-by: Philip K.F. Hölzenspies <holzensp@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ open class BuildInfo(project: Project) {
|
||||
when {
|
||||
os.isMacOsX -> "macos"
|
||||
os.isLinux -> "linux"
|
||||
os.isWindows -> "windows"
|
||||
else -> throw RuntimeException("${os.familyName} is not supported.")
|
||||
}
|
||||
}
|
||||
@@ -36,7 +37,8 @@ open class BuildInfo(project: Project) {
|
||||
|
||||
val downloadUrl: String by lazy {
|
||||
val jdkMajor = graalVmJdkVersion.takeWhile { it != '.' }
|
||||
"https://download.oracle.com/graalvm/$jdkMajor/archive/$baseName.tar.gz"
|
||||
val extension = if (os.isWindows) "zip" else "tar.gz"
|
||||
"https://download.oracle.com/graalvm/$jdkMajor/archive/$baseName.$extension"
|
||||
}
|
||||
|
||||
val installDir: File by lazy {
|
||||
@@ -85,9 +87,14 @@ open class BuildInfo(project: Project) {
|
||||
val commitId: String by lazy {
|
||||
// only run command once per build invocation
|
||||
if (project === project.rootProject) {
|
||||
Runtime.getRuntime()
|
||||
.exec(arrayOf("git", "rev-parse", "--short", "HEAD"), arrayOf(), project.rootDir)
|
||||
.inputStream.reader().readText().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
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.listProperty
|
||||
|
||||
/**
|
||||
* Builds a self-contained Pkl CLI Jar that is directly executable on *nix
|
||||
@@ -15,27 +15,25 @@ import org.gradle.kotlin.dsl.listProperty
|
||||
*
|
||||
* https://skife.org/java/unix/2011/06/20/really_executable_jars.html
|
||||
*/
|
||||
open class ExecutableJar : DefaultTask() {
|
||||
abstract class ExecutableJar : DefaultTask() {
|
||||
@get:InputFile
|
||||
val inJar: RegularFileProperty = project.objects.fileProperty()
|
||||
abstract val inJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
val outJar: RegularFileProperty = project.objects.fileProperty()
|
||||
abstract val outJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
val jvmArgs: ListProperty<String> = project.objects.listProperty()
|
||||
abstract val jvmArgs: ListProperty<String>
|
||||
|
||||
@TaskAction
|
||||
fun buildJar() {
|
||||
val inFile = inJar.get().asFile
|
||||
val outFile = outJar.get().asFile
|
||||
val escapedJvmArgs = jvmArgs.get().joinToString(separator = " ") { "\"$it\"" }
|
||||
|
||||
val startScript = """
|
||||
#!/bin/sh
|
||||
exec java $escapedJvmArgs -jar $0 "$@"
|
||||
""".trim().trimMargin() + "\n\n\n"
|
||||
|
||||
""".trimIndent() + "\n\n\n"
|
||||
outFile.outputStream().use { outStream ->
|
||||
startScript.byteInputStream().use { it.copyTo(outStream) }
|
||||
inFile.inputStream().use { it.copyTo(outStream) }
|
||||
|
||||
@@ -93,3 +93,28 @@ val updateDependencyLocks by tasks.registering {
|
||||
}
|
||||
|
||||
val allDependencies by tasks.registering(DependencyReportTask::class)
|
||||
|
||||
tasks.withType(Test::class).configureEach {
|
||||
System.getProperty("testReportsDir")?.let { reportsDir ->
|
||||
reports.junitXml.outputLocation.set(file(reportsDir).resolve(project.name).resolve(name))
|
||||
}
|
||||
debugOptions {
|
||||
enabled = System.getProperty("jvmdebug")?.toBoolean() ?: false
|
||||
@Suppress("UnstableApiUsage")
|
||||
host = "*"
|
||||
port = 5005
|
||||
suspend = true
|
||||
server = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaExec::class).configureEach {
|
||||
debugOptions {
|
||||
enabled = System.getProperty("jvmdebug")?.toBoolean() ?: false
|
||||
@Suppress("UnstableApiUsage")
|
||||
host = "*"
|
||||
port = 5005
|
||||
suspend = true
|
||||
server = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import java.nio.file.*
|
||||
import java.util.UUID
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import de.undercouch.gradle.tasks.download.Verify
|
||||
import kotlin.io.path.createDirectories
|
||||
|
||||
plugins {
|
||||
id("de.undercouch.download")
|
||||
@@ -9,7 +10,10 @@ plugins {
|
||||
|
||||
val buildInfo = project.extensions.getByType<BuildInfo>()
|
||||
|
||||
val BuildInfo.GraalVm.downloadFile get() = file(homeDir).resolve("${baseName}.tar.gz")
|
||||
val BuildInfo.GraalVm.downloadFile get(): File {
|
||||
val extension = if (buildInfo.os.isWindows) "zip" else "tar.gz"
|
||||
return file(homeDir).resolve("${baseName}.$extension")
|
||||
}
|
||||
|
||||
// tries to minimize chance of corruption by download-to-temp-file-and-move
|
||||
val downloadGraalVmAarch64 by tasks.registering(Download::class) {
|
||||
@@ -72,11 +76,10 @@ fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) {
|
||||
}
|
||||
|
||||
doLast {
|
||||
val distroDir = "${graalVm.homeDir}/${UUID.randomUUID()}"
|
||||
val distroDir = Paths.get(graalVm.homeDir, UUID.randomUUID().toString())
|
||||
|
||||
try {
|
||||
mkdir(distroDir)
|
||||
|
||||
distroDir.createDirectories()
|
||||
println("Extracting ${graalVm.downloadFile} into $distroDir")
|
||||
// faster and more reliable than Gradle's `copy { from tarTree() }`
|
||||
exec {
|
||||
@@ -85,17 +88,18 @@ fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) {
|
||||
args("--strip-components=1", "-xzf", graalVm.downloadFile)
|
||||
}
|
||||
|
||||
val distroBinDir = if (buildInfo.os.isMacOsX) "$distroDir/Contents/Home/bin" else "$distroDir/bin"
|
||||
val distroBinDir = if (buildInfo.os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin")
|
||||
|
||||
println("Installing native-image into $distroDir")
|
||||
exec {
|
||||
executable = "$distroBinDir/gu"
|
||||
val executableName = if (buildInfo.os.isWindows) "gu.cmd" else "gu"
|
||||
executable = distroBinDir.resolve(executableName).toString()
|
||||
args("install", "--no-progress", "native-image")
|
||||
}
|
||||
|
||||
println("Creating symlink ${graalVm.installDir} for $distroDir")
|
||||
val tempLink = Paths.get("${graalVm.homeDir}/${UUID.randomUUID()}")
|
||||
Files.createSymbolicLink(tempLink, Paths.get(distroDir))
|
||||
val tempLink = Paths.get(graalVm.homeDir, UUID.randomUUID().toString())
|
||||
Files.createSymbolicLink(tempLink, distroDir)
|
||||
try {
|
||||
Files.move(tempLink, graalVm.installDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
|
||||
} catch (e: Exception) {
|
||||
|
||||
Reference in New Issue
Block a user