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:
Daniel Chao
2024-05-28 15:56:20 -07:00
committed by GitHub
parent 5e4ccfd4e8
commit 8ec06e631f
76 changed files with 905 additions and 402 deletions

View File

@@ -17,11 +17,6 @@ package org.pkl.commons.cli.commands
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.groups.provideDelegate
import java.net.URI
import java.net.URISyntaxException
import org.pkl.commons.cli.CliException
import org.pkl.core.runtime.VmUtils
import org.pkl.core.util.IoUtils
abstract class BaseCommand(name: String, helpLink: String, help: String = "") :
CliktCommand(
@@ -30,30 +25,4 @@ abstract class BaseCommand(name: String, helpLink: String, help: String = "") :
epilog = "For more information, visit $helpLink",
) {
val baseOptions by BaseOptions()
/**
* Parses [moduleName] into a URI. If scheme is not present, we expect that this is a file path
* and encode any possibly invalid characters. If a scheme is present, we expect that this is a
* valid URI.
*/
protected fun parseModuleName(moduleName: String): URI =
when (moduleName) {
"-" -> VmUtils.REPL_TEXT_URI
else ->
try {
IoUtils.toUri(moduleName)
} catch (e: URISyntaxException) {
val message = buildString {
append("Module URI `$moduleName` has invalid syntax (${e.reason}).")
if (e.index > -1) {
append("\n\n")
append(moduleName)
append("\n")
append(" ".repeat(e.index))
append("^")
}
}
throw CliException(message)
}
}
}

View File

@@ -22,14 +22,50 @@ import com.github.ajalt.clikt.parameters.types.long
import com.github.ajalt.clikt.parameters.types.path
import java.io.File
import java.net.URI
import java.net.URISyntaxException
import java.nio.file.Path
import java.time.Duration
import java.util.regex.Pattern
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.core.runtime.VmUtils
import org.pkl.core.util.IoUtils
@Suppress("MemberVisibilityCanBePrivate")
class BaseOptions : OptionGroup() {
companion object {
/**
* Parses [moduleName] into a URI. If scheme is not present, we expect that this is a file path
* and encode any possibly invalid characters, and also normalize directory separators. If a
* scheme is present, we expect that this is a valid URI.
*/
fun parseModuleName(moduleName: String): URI =
when (moduleName) {
"-" -> VmUtils.REPL_TEXT_URI
else ->
// Don't use `IoUtils.toUri` here becaus we need to normalize `\` paths to `/` on Windows.
try {
if (IoUtils.isUriLike(moduleName)) URI(moduleName)
// Can't just use URI constructor, because URI(null, null, "C:/foo/bar", null) turns
// into `URI("C", null, "/foo/bar", null)`.
else if (IoUtils.isWindowsAbsolutePath(moduleName)) Path.of(moduleName).toUri()
else URI(null, null, IoUtils.toNormalizedPathString(Path.of(moduleName)), null)
} catch (e: URISyntaxException) {
val message = buildString {
append("Module URI `$moduleName` has invalid syntax (${e.reason}).")
if (e.index > -1) {
append("\n\n")
append(moduleName)
append("\n")
append(" ".repeat(e.index))
append("^")
}
}
throw CliException(message)
}
}
}
private val defaults = CliBaseOptions()
private val output =
@@ -114,7 +150,7 @@ class BaseOptions : OptionGroup() {
val settings: URI? by
option(names = arrayOf("--settings"), help = "Pkl settings module to use.").single().convert {
IoUtils.toUri(it)
parseModuleName(it)
}
val timeout: Duration? by

View File

@@ -29,7 +29,7 @@ abstract class ModulesCommand(name: String, helpLink: String, help: String = "")
) {
open val modules: List<URI> by
argument(name = "<modules>", help = "Module paths or URIs to evaluate.")
.convert { parseModuleName(it) }
.convert { BaseOptions.parseModuleName(it) }
.multiple(required = true)
protected val projectOptions by ProjectOptions()