Skip to content

Commit 63b3f5e

Browse files
committed
Multiplatform support and fixes.
1 parent 5123af1 commit 63b3f5e

File tree

16 files changed

+201
-109
lines changed

16 files changed

+201
-109
lines changed

NEWS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changes
22

3+
## 4.1.0 - TO BE RELEASED
4+
5+
Breaking changes
6+
7+
* Renamed env variables (cleanup):
8+
CUSTOM_KSCRIPT_PREAMBLE -> KSCRIPT_PREAMBLE
9+
KSCRIPT_IDEA_COMMAND -> KSCRIPT_COMMAND_IDEA
10+
KSCRIPT_GRADLE_COMMAND -> KSCRIPT_COMMAND_GRADLE
11+
12+
Major Enhancements
13+
14+
* Initial Windows support
15+
* Fix for resolution of dependencies
16+
17+
Minor Enhancements
18+
319
## 4.0.0
420

521
Rewrite by https://github.com/aartiPl

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ val shadowJar by tasks.getting(ShadowJar::class) {
7575
doLast {
7676
copy {
7777
from(File(projectDir, "src/kscript"))
78+
from(File(projectDir, "src/kscript.bat"))
7879
into(archiveFile.get().asFile.parentFile)
7980
}
8081
}

src/kscript

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#!/usr/bin/env bash
22

3-
4-
53
absolute_path() {
64
# if the given path to the kscript launcher is absolute (i.e. it is either starting with / or a
75
# 'letter:/' when using gitbash on windows) it is returned unchanged, otherwise we construct an absolute path
@@ -19,18 +17,33 @@ resolve_symlink() (
1917
fi
2018
)
2119

20+
ABS_KSCRIPT_PATH=$(resolve_symlink $(absolute_path $0))
2221

23-
abs_kscript_path=$(resolve_symlink $(absolute_path $0))
24-
25-
## resolve application jar path from script location and convert to windows path when using cygwin
26-
jarPath=$(dirname $abs_kscript_path)/kscript.jar
27-
if [[ $(uname) == CYGWIN* ]]; then jarPath=$(cygpath -w ${jarPath}); fi
22+
JAR_PATH=$(dirname "$ABS_KSCRIPT_PATH")/kscript.jar
2823

2924
## prefer KOTLIN_HOME instead of PATH to resolve `kotlin` location (see #145)
30-
if [[ -z "$KOTLIN_HOME" ]]; then KOTLIN_EXEC="kotlin"; else KOTLIN_EXEC="$KOTLIN_HOME/bin/kotlin"; fi
25+
if [[ -z "$KOTLIN_HOME" ]]; then
26+
KOTLIN_BIN=""
27+
28+
# see discussion on https://github.com/holgerbrandl/kscript/issues/15
29+
REGEXP="-Dkotlin\.home=([^ ]*)"
30+
GUESS_KOTLIN_HOME=$(KOTLIN_RUNNER=1 JAVACMD=echo kotlinc)
31+
32+
if [[ $GUESS_KOTLIN_HOME =~ $REGEXP ]]; then
33+
KOTLIN_BIN="${BASH_REMATCH[1]}/bin/"
34+
fi
35+
else
36+
KOTLIN_BIN="$KOTLIN_HOME/bin/"
37+
fi
38+
39+
# OSTYPE can be: linux-gnu, freebsd, darwin, cygwin, msys
40+
if [[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" ]]; then
41+
JAR_PATH=$(cygpath -w "${JAR_PATH}")
42+
true
43+
fi
3144

3245
## expose the name of the script being run to the script itself
3346
export KSCRIPT_FILE="$1"
3447

3548
## run it using command substitution to have just the user process once kscript is done
36-
eval "exec $(${KOTLIN_EXEC} -classpath ${jarPath} kscript.app.KscriptKt "$@")"
49+
eval "exec $("${KOTLIN_BIN}kotlin" -classpath "${JAR_PATH}" kscript.app.KscriptKt "$OSTYPE" "$@")"

src/kscript.bat

Whitespace-only changes.

src/main/kotlin/kscript/app/Kscript.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import kscript.app.util.ShellUtils.quit
99
import kscript.app.util.VersionChecker
1010
import org.docopt.DocOptWrapper
1111

12-
1312
/**
1413
* A kscript - Scripting enhancements for Kotlin
1514
*
@@ -19,27 +18,28 @@ import org.docopt.DocOptWrapper
1918
* @author Marcin Kuszczak
2019
*/
2120

22-
const val KSCRIPT_VERSION = "4.0.1"
21+
const val KSCRIPT_VERSION = "4.0.2"
2322

2423
fun main(args: Array<String>) {
2524
try {
26-
val config = Config.builder().build()
25+
val config = Config.builder().apply { osType = args[0] }.build()
26+
val remainingArgs = args.drop(1)
2727

2828
// skip org.docopt for version and help to allow for lazy version-check
2929
val usage = Templates.usageOptions(config.selfName, KSCRIPT_VERSION)
3030

31-
if (args.size == 1 && listOf("--help", "-h", "--version", "-v").contains(args[0])) {
31+
if (remainingArgs.size == 1 && listOf("--help", "-h", "--version", "-v").contains(remainingArgs[0])) {
3232
Logger.info(usage)
3333
VersionChecker.versionCheck(KSCRIPT_VERSION)
34-
val systemInfo = evalBash("kotlin -version").stdout
34+
val systemInfo = evalBash(config.osType, "kotlin -version").stdout
3535
Logger.info("Kotlin : " + systemInfo.split('(')[0].removePrefix("Kotlin version").trim())
3636
Logger.info("Java : " + systemInfo.split('(')[1].split('-', ')')[0].trim())
3737
return
3838
}
3939

4040
// note: with current implementation we still don't support `kscript -1` where "-1" is a valid kotlin expression
41-
val userArgs = args.dropWhile { it.startsWith("-") && it != "-" }.drop(1)
42-
val kscriptArgs = args.take(args.size - userArgs.size)
41+
val userArgs = remainingArgs.dropWhile { it.startsWith("-") && it != "-" }.drop(1)
42+
val kscriptArgs = remainingArgs.take(remainingArgs.size - userArgs.size)
4343

4444
val docopt = DocOptWrapper(kscriptArgs, usage)
4545

src/main/kotlin/kscript/app/model/Config.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ package kscript.app.model
33
import java.nio.file.Path
44

55
data class Config(
6+
val osType: OsType,
67
val selfName: String,
78
val kscriptDir: Path,
89
val customPreamble: String,
910
val intellijCommand: String,
1011
val gradleCommand: String,
1112
val kotlinHome: Path?,
12-
val osName: String,
13-
val classPathSeparator: String,
14-
val separatorChar: Char,
13+
val classPathSeparator: Char,
14+
val hostPathSeparatorChar: Char,
15+
val shellPathSeparatorChar: Char,
1516
val homeDir: Path,
1617
val kotlinOptsEnvVariable: String,
1718
val repositoryUrlEnvVariable: String,

src/main/kotlin/kscript/app/model/ConfigBuilder.kt

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,59 @@
11
package kscript.app.model
22

3-
import kscript.app.util.ShellUtils.guessKotlinHome
3+
import kscript.app.util.ShellUtils
44
import java.io.File
55
import java.nio.file.Path
66
import java.nio.file.Paths
77
import kotlin.io.path.absolute
88

99
class ConfigBuilder internal constructor() {
10-
var selfName: String = System.getenv("CUSTOM_KSCRIPT_NAME") ?: "kscript"
11-
var kscriptDir: Path = Paths.get(System.getenv("KSCRIPT_DIR") ?: (System.getProperty("user.home")!! + "/.kscript"))
12-
var customPreamble: String = System.getenv("CUSTOM_KSCRIPT_PREAMBLE") ?: ""
13-
var intellijCommand: String = System.getenv("KSCRIPT_IDEA_COMMAND") ?: "idea"
14-
var gradleCommand: String = System.getenv("KSCRIPT_GRADLE_COMMAND") ?: "gradle"
15-
var kotlinHome: Path? = (System.getenv("KOTLIN_HOME") ?: guessKotlinHome())?.let { Paths.get(it).absolute() }
16-
var osName: String = System.getProperty("os.name")
17-
var homeDir: Path = Paths.get(System.getProperty("user.home")!!)
18-
var classPathSeparator: String = if (osName.lowercase().contains("windows")) ";" else ":"
19-
var separatorChar: Char = File.separatorChar
20-
var kotlinOptsEnvVariable = System.getenv("KSCRIPT_KOTLIN_OPTS") ?: ""
21-
var repositoryUrlEnvVariable = System.getenv("KSCRIPT_REPOSITORY_URL") ?: ""
22-
var repositoryUserEnvVariable = System.getenv("KSCRIPT_REPOSITORY_USER") ?: ""
23-
var repositoryPasswordEnvVariable = System.getenv("KSCRIPT_REPOSITORY_PASSWORD") ?: ""
10+
var osType: String? = null
11+
var classPathSeparator: Char? = null
12+
var hostPathSeparatorChar: Char? = null
13+
var shellPathSeparatorChar: Char? = null
14+
var selfName: String? = null
15+
var kscriptDir: Path? = null
16+
var customPreamble: String? = null
17+
var intellijCommand: String? = null
18+
var gradleCommand: String? = null
19+
var kotlinHome: Path? = null
20+
var homeDir: Path? = null
21+
var kotlinOptsEnvVariable: String? = null
22+
var repositoryUrlEnvVariable: String? = null
23+
var repositoryUserEnvVariable: String? = null
24+
var repositoryPasswordEnvVariable: String? = null
2425

2526
fun build(): Config {
27+
//Java resolved env variables paths are always in native format; All paths should be stored in Config as native.
28+
29+
//TODO: can it be read from: System.getProperty("os.name"); what about cygwin and msys?
30+
val osType = OsType.findOrThrow(requireNotNull(osType))
31+
val classPathSeparator = classPathSeparator ?: if (osType.isWindowsLike() || osType.isUnixHostedOnWindows()) ';' else ':'
32+
val hostPathSeparatorChar = hostPathSeparatorChar ?: File.separatorChar
33+
val shellPathSeparatorChar = shellPathSeparatorChar ?: if (osType.isUnixHostedOnWindows()) '/' else hostPathSeparatorChar
34+
val selfName = selfName ?: System.getenv("KSCRIPT_NAME") ?: "kscript"
35+
val kscriptDir = kscriptDir ?: Paths.get(System.getenv("KSCRIPT_DIR") ?: (System.getProperty("user.home")!! + "/.kscript"))
36+
val customPreamble = customPreamble ?: System.getenv("KSCRIPT_PREAMBLE") ?: ""
37+
val intellijCommand = intellijCommand ?: System.getenv("KSCRIPT_COMMAND_IDEA") ?: "idea"
38+
val gradleCommand = gradleCommand ?: System.getenv("KSCRIPT_COMMAND_GRADLE") ?: "gradle"
39+
val kotlinHome = kotlinHome ?: (System.getenv("KOTLIN_HOME") ?: ShellUtils.guessKotlinHome(osType))?.let { Paths.get(it).absolute() }
40+
val homeDir = homeDir ?: Paths.get(System.getProperty("user.home")!!)
41+
val kotlinOptsEnvVariable = kotlinOptsEnvVariable ?: System.getenv("KSCRIPT_KOTLIN_OPTS") ?: ""
42+
val repositoryUrlEnvVariable = repositoryUrlEnvVariable ?: System.getenv("KSCRIPT_REPOSITORY_URL") ?: ""
43+
val repositoryUserEnvVariable = repositoryUserEnvVariable ?: System.getenv("KSCRIPT_REPOSITORY_USER") ?: ""
44+
val repositoryPasswordEnvVariable = repositoryPasswordEnvVariable ?: System.getenv("KSCRIPT_REPOSITORY_PASSWORD") ?: ""
45+
2646
return Config(
47+
osType,
2748
selfName,
2849
kscriptDir,
2950
customPreamble,
3051
intellijCommand,
3152
gradleCommand,
3253
kotlinHome,
33-
osName,
3454
classPathSeparator,
35-
separatorChar,
55+
hostPathSeparatorChar,
56+
shellPathSeparatorChar,
3657
homeDir,
3758
kotlinOptsEnvVariable,
3859
repositoryUrlEnvVariable,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package kscript.app.model
2+
3+
enum class OsType(val osName: String) {
4+
LINUX("linux-gnu"), DARWIN("darwin"), WINDOWS("windows"), CYGWIN("cygwin"), MSYS("msys"), FREEBSD("freebsd");
5+
6+
fun isUnixLike() = (this == LINUX || this == DARWIN || this == FREEBSD)
7+
fun isWindowsLike() = (this == WINDOWS)
8+
fun isUnixHostedOnWindows() = (this == CYGWIN || this == MSYS)
9+
10+
companion object {
11+
fun findOrThrow(name: String) =
12+
values().find { it.osName.equals(name, true) } ?: throw IllegalArgumentException("Unsupported OS: $name")
13+
}
14+
}

src/main/kotlin/kscript/app/resolver/CommandResolver.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import kscript.app.model.CompilerOpt
55
import kscript.app.model.Config
66
import kscript.app.model.KotlinOpt
77
import kscript.app.model.Script
8+
import kscript.app.util.FileUtils.nativeToShellPath
89
import java.nio.file.Path
910
import java.nio.file.Paths
1011
import kotlin.io.path.absolutePathString
1112
import kotlin.io.path.div
1213

1314
class CommandResolver(private val config: Config, private val script: Script) {
1415
//Syntax for different OS-es:
15-
//LINUX: kotlin -classpath "/home/vagrant/workspace/Kod/Repos/kscript/test:/home/vagrant/.kscript/cache/jar_2ccd53e06b0355d3573a4ae8698398fe/scriplet.jar:/usr/local/sdkman/candidates/kotlin/1.6.21/lib/kotlin-script-runtime.jar" Main_Scriplet
16-
//GIT-BASH: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
17-
//CYGWIN: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
18-
//WINDOWS: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
16+
//LINUX: /usr/local/sdkman/..../kotlin -classpath "/home/vagrant/workspace/Kod/Repos/kscript/test:/home/vagrant/.kscript/cache/jar_2ccd53e06b0355d3573a4ae8698398fe/scriplet.jar:/usr/local/sdkman/candidates/kotlin/1.6.21/lib/kotlin-script-runtime.jar" Main_Scriplet
17+
//GIT-BASH: /c/Users/Admin/.sdkman/candidates/kotlin/current/bin/kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
18+
//CYGWIN: /home/Admin/.sdkman/candidates/kotlin/current/bin/kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
19+
//WINDOWS: C:\Users\Admin\.sdkman\candidates\kotlin\current\bin\kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet
20+
//MACOS:
1921

2022
//Path conversion (Cygwin/mingw): cygpath -u "c:\Users\Admin"; /cygdrive/c/ - Cygwin; /c/ - Mingw
2123
//uname --> CYGWIN_NT-10.0 or MINGW64_NT-10.0-19043
@@ -34,7 +36,7 @@ class CommandResolver(private val config: Config, private val script: Script) {
3436
val kotlinOptsStr = resolveKotlinOpts(script.kotlinOpts)
3537
val userArgsStr = resolveUserArgs(userArgs)
3638
val scriptRuntime =
37-
Paths.get("${config.kotlinHome}${config.separatorChar}lib${config.separatorChar}kotlin-script-runtime.jar")
39+
Paths.get("${config.kotlinHome}${config.hostPathSeparatorChar}lib${config.hostPathSeparatorChar}kotlin-script-runtime.jar")
3840

3941
val dependenciesSet = buildSet<Path> {
4042
addAll(dependencies)
@@ -72,9 +74,13 @@ class CommandResolver(private val config: Config, private val script: Script) {
7274
private fun resolveUserArgs(userArgs: List<String>) =
7375
userArgs.joinToString(" ") { "\"${it.replace("\"", "\\\"")}\"" }
7476

75-
private fun resolveClasspath(dependencies: Set<Path>) = if (dependencies.isEmpty()) ""
76-
else "-classpath \"" + dependencies.joinToString(config.classPathSeparator) { it.absolutePathString() } + "\""
77+
private fun resolveClasspath(dependencies: Set<Path>) =
78+
if (dependencies.isEmpty()) "" else "-classpath \"" + dependencies.joinToString(config.classPathSeparator.toString()) {
79+
it.absolutePathString()
80+
} + "\""
7781

78-
private fun resolveKotlinBinary(binary: String) =
79-
if (config.kotlinHome != null) (config.kotlinHome / "bin" / binary).absolutePathString() else binary
82+
private fun resolveKotlinBinary(binary: String) = if (config.kotlinHome != null) nativeToShellPath(
83+
config.osType,
84+
(config.kotlinHome / "bin" / binary)
85+
) else binary
8086
}

src/main/kotlin/kscript/app/util/Executor.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,26 @@ import kscript.app.model.Config
55
import kscript.app.resolver.CommandResolver
66
import kscript.app.util.Logger.devMsg
77
import java.nio.file.Path
8-
import kotlin.io.path.div
98

109
class Executor(private val commandResolver: CommandResolver, private val config: Config) {
1110
fun compileKotlin(jar: Path, dependencies: Set<Path>, filePaths: Set<Path>) {
12-
if (config.kotlinHome == null && !ShellUtils.isInPath("kotlinc")) {
11+
if (config.kotlinHome == null && !ShellUtils.isInPath(config.osType, "kotlinc")) {
1312
throw IllegalStateException("${"kotlinc"} is not in PATH")
1413
}
1514

1615
val command = commandResolver.compileKotlin(jar, dependencies, filePaths)
1716

1817
devMsg("JAR compile command: $command")
1918

20-
val scriptCompileResult = ShellUtils.evalBash(command)
19+
val scriptCompileResult = ShellUtils.evalBash(config.osType, command)
2120

2221
if (scriptCompileResult.exitCode != 0) {
2322
throw IllegalStateException("Compilation of scriplet failed:\n$scriptCompileResult")
2423
}
2524
}
2625

2726
fun executeKotlin(jarArtifact: JarArtifact, dependencies: Set<Path>, userArgs: List<String>) {
28-
if (config.kotlinHome == null && !ShellUtils.isInPath("kotlin") ) {
27+
if (config.kotlinHome == null && !ShellUtils.isInPath(config.osType, "kotlin") ) {
2928
throw IllegalStateException("KOTLIN_HOME is not set and could not be inferred from context, and kotlin is not in PATH")
3029
}
3130

@@ -42,11 +41,11 @@ class Executor(private val commandResolver: CommandResolver, private val config:
4241
}
4342

4443
fun runIdea(projectPath: Path) {
45-
if (!ShellUtils.isInPath(config.intellijCommand)) {
44+
if (!ShellUtils.isInPath(config.osType, config.intellijCommand)) {
4645
throw IllegalStateException("Could not find '${config.intellijCommand}' in your PATH. You must set the command used to launch your intellij as 'KSCRIPT_IDEA_COMMAND' env property")
4746
}
4847

49-
if (!ShellUtils.isInPath(config.gradleCommand)) {
48+
if (!ShellUtils.isInPath(config.osType, config.gradleCommand)) {
5049
throw IllegalStateException(
5150
"Could not find '${config.gradleCommand}' in your PATH. You must set the command used to launch your intellij as 'KSCRIPT_GRADLE_COMMAND' env property"
5251
)
@@ -61,14 +60,14 @@ class Executor(private val commandResolver: CommandResolver, private val config:
6160
}
6261

6362
fun createPackage(projectPath: Path) {
64-
if (!ShellUtils.isInPath(config.gradleCommand)) {
63+
if (!ShellUtils.isInPath(config.osType, config.gradleCommand)) {
6564
throw IllegalStateException("gradle is required to package kscripts")
6665
}
6766

6867
val command = commandResolver.createPackage(projectPath)
6968
devMsg("Create package command: $command")
7069

71-
val result = ShellUtils.evalBash(command)
70+
val result = ShellUtils.evalBash(config.osType, command)
7271
if (result.exitCode != 0) {
7372
throw IllegalStateException("Packaging for path: '$projectPath' failed:$result")
7473
}

0 commit comments

Comments
 (0)