Skip to content

Commit 8f547f0

Browse files
committed
* Replaced DocOpt with Apache command line parser
* Better handling of different variants of options: - clear-cache can be used together with <script> - short versions for all commands - add-bootstrap-header -> bootstrap-header Signed-off-by: Marcin Kuszczak <1508798+aartiPl@users.noreply.github.com>
1 parent b9259f6 commit 8f547f0

10 files changed

Lines changed: 167 additions & 149 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ tasks.test {
127127

128128
dependencies {
129129
//compileOnly(fileTree("libs"))
130-
implementation("com.offbytwo:docopt:0.6.0.20150202")
131130
implementation("commons-cli:commons-cli:1.5.0")
132131

133132
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
@@ -149,7 +148,7 @@ dependencies {
149148

150149
implementation("io.github.kscripting:shell:0.5.0")
151150

152-
implementation("org.slf4j:slf4j-nop:2.0.4")
151+
implementation("org.slf4j:slf4j-nop:2.0.5")
153152

154153
implementation("org.semver4j:semver4j:3.0.0")
155154

src/integration/kotlin/io/github/kscripting/kscript/integration/AnnotationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class AnnotationTest : TestBase {
5353
"kscript ${resolvePath("$projectDir/test/resources/illegal_depends_on_arg.kts")}",
5454
1,
5555
"",
56-
"[kscript] [ERROR] Artifact locators must be provided as separate annotation arguments and not as comma-separated list: [com.squareup.moshi:moshi:1.5.0,com.squareup.moshi:moshi-adapters:1.5.0]\n"
56+
"[kscript] [ERROR] Artifact locators must be provided as separate annotation arguments and not as comma-separated list: [com.squareup.moshi:moshi:1.5.0,com.squareup.moshi:moshi-adapters:1.5.0]\n\n"
5757
)
5858
verify("kscript $projectDir/test/resources/script_with_compile_flags.kts", 0, "hoo_ray\n", any())
5959
}

src/integration/kotlin/io/github/kscripting/kscript/integration/ScriptInputModesTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ class ScriptInputModesTest : TestBase {
121121
@Tag("windows")
122122
fun `Missing script gives always error on execution`() {
123123
verify(
124-
"kscript i_do_not_exist.kts", 1, "", "[kscript] [ERROR] Could not read script from 'i_do_not_exist.kts'\n"
124+
"kscript i_do_not_exist.kts", 1, "", "[kscript] [ERROR] Could not read script from 'i_do_not_exist.kts'\n\n"
125125
)
126126
verify(
127-
"kscript i_do_not_exist.kts", 1, "", "[kscript] [ERROR] Could not read script from 'i_do_not_exist.kts'\n"
127+
"kscript i_do_not_exist.kts", 1, "", "[kscript] [ERROR] Could not read script from 'i_do_not_exist.kts'\n\n"
128128
)
129129
}
130130
}

src/integration/kotlin/io/github/kscripting/kscript/integration/SimpleTest.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ class SimpleTest : TestBase {
2525
@Tag("posix")
2626
@Tag("windows")
2727
fun `Help is printed`() {
28-
//@formatter:off
29-
verify("kscript --help", 0, "", startsWith("kscript - Enhanced scripting support for Kotlin on *nix-based systems."))
30-
//@formatter:on
28+
verify("kscript --help", 0, "", startsWith("kscript - Enhanced scripting support for Kotlin"))
3129
}
3230
}

src/main/kotlin/io/github/kscripting/kscript/Kscript.kt

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
package io.github.kscripting.kscript
22

3-
import io.github.kscripting.kscript.code.Templates.createUsageOptions
3+
import io.github.kscripting.kscript.code.Templates
4+
import io.github.kscripting.kscript.creator.DebugInfoCreator
45
import io.github.kscripting.kscript.model.ConfigBuilder
5-
import io.github.kscripting.kscript.util.DocoptParser
6+
import io.github.kscripting.kscript.resolver.CommandResolver
7+
import io.github.kscripting.kscript.util.Executor
8+
import io.github.kscripting.kscript.util.Logger
69
import io.github.kscripting.kscript.util.Logger.errorMsg
10+
import io.github.kscripting.kscript.util.Logger.info
11+
import io.github.kscripting.kscript.util.OptionsUtils
12+
import io.github.kscripting.kscript.util.VersionChecker
713
import io.github.kscripting.shell.model.OsType
14+
import org.apache.commons.cli.CommandLineParser
15+
import org.apache.commons.cli.DefaultParser
16+
import org.apache.commons.cli.ParseException
817
import kotlin.system.exitProcess
918

19+
1020
/**
1121
* A kscript - Scripting enhancements for Kotlin
1222
*
@@ -29,14 +39,64 @@ fun main(args: Array<String>) {
2939
val userArgs = remainingArgs.dropWhile { it.startsWith("-") && it != "-" }.drop(1)
3040
val kscriptArgs = remainingArgs.take(remainingArgs.size - userArgs.size)
3141

32-
val options = DocoptParser.parse(
33-
kscriptArgs,
34-
createUsageOptions(config.osConfig.selfName, BuildConfig.APP_BUILD_TIME, BuildConfig.APP_VERSION)
35-
)
42+
val parser: CommandLineParser = DefaultParser()
43+
val options = OptionsUtils.createOptions()
44+
45+
val parsedLine = try {
46+
parser.parse(options, kscriptArgs.toTypedArray())
47+
} catch (e: ParseException) {
48+
info(OptionsUtils.createHelpText(config.osConfig.selfName, options))
49+
throw IllegalArgumentException(e.message)
50+
}
51+
52+
val parsedOptions = parsedLine.options.associate { it.longOpt to it.value }.toMutableMap()
53+
54+
if (parsedLine.argList.isNotEmpty()) {
55+
parsedOptions["script"] = parsedLine.argList[0]
56+
}
57+
58+
// Constraints
59+
if (parsedOptions.containsKey("interactive") && !parsedOptions.containsKey("script")) {
60+
info(OptionsUtils.createHelpText(config.osConfig.selfName, options))
61+
throw IllegalArgumentException("Interactive option requires Kotlin 'script' as an argument")
62+
}
63+
64+
Logger.silentMode = parsedOptions.containsKey("silent")
65+
Logger.devMode = parsedOptions.containsKey("development")
66+
67+
if (Logger.devMode) {
68+
info(DebugInfoCreator().create(config, kscriptArgs, userArgs))
69+
}
70+
71+
val executor = Executor(CommandResolver(config.osConfig))
72+
73+
if (parsedOptions.containsKey("help") || parsedOptions.containsKey("version")) {
74+
val versionChecker = VersionChecker(executor)
75+
76+
val newVersion = if (versionChecker.isThereANewKscriptVersion()) versionChecker.remoteKscriptVersion else ""
77+
78+
if (parsedOptions.containsKey("help")) {
79+
info(OptionsUtils.createHelpText(config.osConfig.selfName, options).trimEnd())
80+
}
81+
82+
info(
83+
Templates.createVersionInfo(
84+
BuildConfig.APP_BUILD_TIME,
85+
BuildConfig.APP_VERSION,
86+
newVersion,
87+
versionChecker.localKotlinVersion,
88+
versionChecker.localJreVersion
89+
)
90+
)
91+
info()
92+
93+
return
94+
}
3695

37-
KscriptHandler(config, options).handle(kscriptArgs, userArgs)
96+
KscriptHandler(executor, config, parsedOptions).handle(userArgs)
3897
} catch (e: Exception) {
3998
errorMsg(e)
99+
info()
40100
exitProcess(1)
41101
}
42102
}

src/main/kotlin/io/github/kscripting/kscript/KscriptHandler.kt

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,41 @@ import io.github.kscripting.kscript.code.Templates
55
import io.github.kscripting.kscript.creator.*
66
import io.github.kscripting.kscript.model.Config
77
import io.github.kscripting.kscript.parser.Parser
8-
import io.github.kscripting.kscript.resolver.*
8+
import io.github.kscripting.kscript.resolver.DependencyResolver
9+
import io.github.kscripting.kscript.resolver.InputOutputResolver
10+
import io.github.kscripting.kscript.resolver.ScriptResolver
11+
import io.github.kscripting.kscript.resolver.SectionResolver
912
import io.github.kscripting.kscript.util.Executor
1013
import io.github.kscripting.kscript.util.FileUtils.getArtifactsRecursively
11-
import io.github.kscripting.kscript.util.Logger
1214
import io.github.kscripting.kscript.util.Logger.info
1315
import io.github.kscripting.kscript.util.Logger.infoMsg
1416
import io.github.kscripting.kscript.util.Logger.warnMsg
15-
import io.github.kscripting.kscript.util.VersionChecker
1617
import io.github.kscripting.shell.model.ScriptType
1718
import java.net.URI
1819

19-
class KscriptHandler(private val config: Config, private val options: Map<String, String>) {
20-
fun handle(kscriptArgs: List<String>, userArgs: List<String>) {
21-
Logger.silentMode = options.getBoolean("silent")
22-
Logger.devMode = options.getBoolean("development")
23-
24-
if (Logger.devMode) {
25-
info(DebugInfoCreator().create(config, kscriptArgs, userArgs))
26-
}
27-
28-
val executor = Executor(CommandResolver(config.osConfig))
29-
30-
if (options.getBoolean("help") || options.getBoolean("version")) {
31-
val versionChecker = VersionChecker(executor)
32-
33-
val newVersion =
34-
if (versionChecker.isThereANewKscriptVersion()) versionChecker.remoteKscriptVersion else ""
35-
36-
info(
37-
Templates.createUsageOptions(
38-
config.osConfig.selfName,
39-
BuildConfig.APP_BUILD_TIME,
40-
BuildConfig.APP_VERSION,
41-
newVersion,
42-
versionChecker.localKotlinVersion,
43-
versionChecker.localJreVersion
44-
)
45-
)
46-
47-
val message = options.getOrDefault("message", "")
48-
49-
if (message.isNotBlank()) {
50-
throw IllegalArgumentException(message)
51-
}
52-
53-
return
54-
}
55-
20+
class KscriptHandler(
21+
private val executor: Executor, private val config: Config, private val options: Map<String, String>
22+
) {
23+
fun handle(userArgs: List<String>) {
5624
val cache = Cache(config.osConfig.cacheDir)
5725

5826
// optionally clear up the jar cache
59-
if (options.getBoolean("clear-cache")) {
27+
if (options.containsKey("clear-cache")) {
6028
info("Cleaning up cache...")
6129
cache.clear()
62-
return
30+
31+
if (!options.containsKey("script")) {
32+
return
33+
}
6334
}
6435

65-
val scriptSource = options.getString("script")
36+
val scriptSource = options["script"] ?: throw IllegalArgumentException("Script argument is required")
6637

6738
if (scriptSource.isBlank()) {
6839
return
6940
}
7041

71-
val enableSupportApi = options.getBoolean("text")
42+
val enableSupportApi = options.containsKey("text")
7243

7344
val preambles = buildList {
7445
if (enableSupportApi) {
@@ -82,7 +53,7 @@ class KscriptHandler(private val config: Config, private val options: Map<String
8253
val sectionResolver = SectionResolver(inputOutputResolver, Parser(), config.scriptingConfig)
8354
val scriptResolver = ScriptResolver(inputOutputResolver, sectionResolver, config.scriptingConfig)
8455

85-
if (options.getBoolean("add-bootstrap-header")) {
56+
if (options.containsKey("bootstrap-header")) {
8657
val script = scriptResolver.resolve(scriptSource, maxResolutionLevel = 0)
8758
BootstrapCreator().create(script)
8859
return
@@ -91,7 +62,7 @@ class KscriptHandler(private val config: Config, private val options: Map<String
9162
val script = scriptResolver.resolve(scriptSource, preambles)
9263

9364
if (script.deprecatedItems.isNotEmpty()) {
94-
if (options.getBoolean("report")) {
65+
if (options.containsKey("report")) {
9566
info(DeprecatedInfoCreator().create(script.deprecatedItems))
9667
return
9768
}
@@ -108,7 +79,7 @@ class KscriptHandler(private val config: Config, private val options: Map<String
10879
} + localArtifacts
10980

11081
// Create temporary dev environment
111-
if (options.getBoolean("idea")) {
82+
if (options.containsKey("idea")) {
11283
val path = cache.getOrCreateIdeaProject(script.digest) { basePath ->
11384
val uriLocalPathProvider = { uri: URI -> inputOutputResolver.resolveContent(uri).localPath }
11485
IdeaProjectCreator(executor).create(basePath, script, userArgs, uriLocalPathProvider)
@@ -130,13 +101,13 @@ class KscriptHandler(private val config: Config, private val options: Map<String
130101
}
131102

132103
// Optionally enter interactive mode
133-
if (options.getBoolean("interactive")) {
104+
if (options.containsKey("interactive")) {
134105
executor.runInteractiveRepl(jar, resolvedDependencies, script.compilerOpts, script.kotlinOpts)
135106
return
136107
}
137108

138109
//if requested try to package the into a standalone binary
139-
if (options.getBoolean("package")) {
110+
if (options.containsKey("package")) {
140111
val path =
141112
cache.getOrCreatePackage(script.digest, script.scriptLocation.scriptName) { basePath, packagePath ->
142113
PackageCreator(executor).packageKscript(basePath, packagePath, script, jar)
@@ -149,7 +120,4 @@ class KscriptHandler(private val config: Config, private val options: Map<String
149120

150121
executor.executeKotlin(jar, resolvedDependencies, userArgs, script.kotlinOpts)
151122
}
152-
153-
private fun Map<String, String>.getBoolean(key: String): Boolean = getOrDefault(key, "false").toBoolean()
154-
private fun Map<String, String>.getString(key: String): String = getValue(key)
155123
}

src/main/kotlin/io/github/kscripting/kscript/code/Templates.kt

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -96,48 +96,32 @@ object Templates {
9696
|""".trimStart().trimMargin()
9797
}
9898

99-
fun createUsageOptions(
100-
selfName: String, buildDateTime: ZonedDateTime, version: String, newVersion: String = "", kotlinVersion: String = "", jreVersion: String = ""
101-
): String {
102-
val versionLine =
103-
"Version : $version ${if (newVersion.isNotBlank()) "(new version v$newVersion is available)" else ""}"
104-
105-
val kotlinAndJreVersion = if (kotlinVersion.isNotBlank()) {
106-
"""|
107-
|Kotlin : $kotlinVersion
108-
|Java : $jreVersion
109-
|""".trimMargin()
110-
} else ""
99+
fun createTitleInfo(selfName: String) =
100+
"$selfName - Enhanced scripting support for Kotlin on *nix and Windows based systems.\n\n"
111101

112-
return """
113-
|$selfName - Enhanced scripting support for Kotlin on *nix-based systems.
114-
|
115-
|Usage:
116-
| $selfName [options] <script> [<script_args>]...
117-
| $selfName --clear-cache
118-
|
119-
|The <script> can be a script file (*kts), a script URL, - for stdin, a *.kt source file with a main method, or some kotlin code.
120-
|
121-
|Use '--clear-cache' to wipe cached script jars and urls
122-
|
123-
|Options:
124-
| -i --interactive Create interactive shell with dependencies as declared in script
125-
| -t --text Enable stdin support API for more streamlined text processing
126-
| --idea Open script in temporary Intellij session
127-
| -s --silent Suppress status logging
128-
| -d --development Enable logging of exception stack trace and additional log messages
129-
| --report Prints script's deprecated features report
130-
| --package Package script and dependencies into self-dependent binary
131-
| --add-bootstrap-header Prepend bash header that installs kscript if necessary
132-
| -h --help Prints help information
133-
| -v --version Same as '--help'
134-
|
135-
|
136-
|Copyright : 2022 Holger Brandl, Marcin Kuszczak
137-
|License : MIT
138-
|$versionLine
139-
|Build : $buildDateTime
140-
|Website : https://github.com/kscripting/kscript
141-
|""".trimMargin().trim() + kotlinAndJreVersion
142-
}
102+
fun createHeaderInfo() =
103+
"\nThe <script> can be a script file (*kts), a script URL, - for stdin, a *.kt source file with a main method, or some kotlin code.\n\n"
104+
105+
106+
fun createUsageInfo(selfName: String) =
107+
"""|Usage:
108+
| $selfName [options] <script> [<script_args>]...
109+
| $selfName --clear-cache [--development]
110+
| $selfName (--help | --version) [--development]""".trimMargin()
111+
112+
113+
fun createFooterInfo() =
114+
"""|
115+
|Copyright : 2022 Holger Brandl, Marcin Kuszczak
116+
|Website : https://github.com/kscripting/kscript
117+
|License : MIT""".trimMargin()
118+
119+
fun createVersionInfo(
120+
buildDateTime: ZonedDateTime, version: String, newVersion: String, kotlinVersion: String, jreVersion: String
121+
): String =
122+
"""|Version : $version ${if (newVersion.isNotBlank()) "(new version v$newVersion is available)" else ""}
123+
|Build : $buildDateTime
124+
|Kotlin : $kotlinVersion
125+
|Java : $jreVersion
126+
|""".trimMargin().trim()
143127
}

src/main/kotlin/io/github/kscripting/kscript/util/DocoptParser.kt

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)