Skip to content

Commit 04b2be9

Browse files
committed
Impl //INCLUDE directive(fixes #34)
1 parent ff60fad commit 04b2be9

File tree

10 files changed

+120
-12
lines changed

10 files changed

+120
-12
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
* Reimplemented in kotlin (fixes [#36](https://github.com/holgerbrandl/kscript/issues/36))
44
* Added cygwin support (fixes [#39](https://github.com/holgerbrandl/kscript/issues/39))
5+
* Added `//INCLUDE` directive (fixes [#34](https://github.com/holgerbrandl/kscript/issues/34)
6+
57

68
## v1.5.1
79

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ kscript --self-update
4646

4747
#### Installation without `sdkman`
4848

49-
If you have Kotlin and Maven already and you would like to install the latest `kscript` release to your `~/bin` without using `sdkman` you can do so by unzipping the [latest ](https://github.com/holgerbrandl/kscript/releases/latest) binary release.
49+
If you have Kotlin and Maven already and you would like to install the latest `kscript` release without using `sdkman` you can do so by unzipping the [latest ](https://github.com/holgerbrandl/kscript/releases/latest) binary release. Don't forget to update your `$PATH` accordingly.
5050

5151

5252

@@ -237,9 +237,24 @@ println("Hello from Kotlin with 5g of heap memory running in server mode!")
237237

238238
In order to use cygwin you need to use windows paths to provide your scripts. You can map cygwin paths using `cygpath`. Example
239239
```bash
240-
kscript $(cygpath -w /cygdrive/z/some/path/my_script.kt)
240+
kscript $(cygpath -w /cygdrive/z/some/path/my_script.kts)
241241
```
242242

243+
## Can I include another script with helper functions like `source foo.sh` in bash?
244+
245+
Yes, `kscript` supports an `//INLCUDE` directive. Absolute and relative paths, as well as URLs are supported. Example
246+
247+
```kotlin
248+
#!/usr/bin/env kscript
249+
250+
//INCLUDE Utils.kt
251+
//INCLUDE ../other_utils/Utils.kt
252+
//INCLUDE http://somewhere.com/utils.kt
253+
254+
...
255+
```
256+
For an actual example see [here](test/resources/includes/include_variations.kts).
257+
243258
Support
244259
-------
245260

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,18 @@ object ShellUtils {
5555

5656
}
5757

58+
fun errorMsg(msg: String) {
59+
System.err.println("[ERROR] " + msg)
60+
}
61+
5862
fun errorIf(value: Boolean, lazyMessage: () -> Any) {
5963
if (value) {
60-
System.err.println("[ERROR] " + lazyMessage().toString())
64+
errorMsg(lazyMessage().toString())
6165
quit(1)
6266
}
6367
}
6468

65-
6669
fun quit(status: Int): Nothing {
6770
print(if (status == 0) "true" else "false")
6871
exitProcess(status)
6972
}
70-
71-
72-
internal inline fun warning(value: Boolean, lazyMessage: () -> Any): Unit {
73-
if (!value) System.err.println(lazyMessage())
74-
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Website : https://github.com/holgerbrandl/kscript
4646
""".trim()
4747

4848
val KSCRIPT_CACHE_DIR = File(System.getenv("HOME")!!, ".kscript")
49-
49+
val SCRIPT_TEMP_DIR = createTempDir()
5050

5151
fun main(args: Array<String>) {
5252
// skip org.docopt for version and help to allow for lazy version-check
@@ -307,6 +307,9 @@ fun prepareScript(scriptResource: String): File {
307307
}
308308

309309

310+
// support //INCLUDE directive (see https://github.com/holgerbrandl/kscript/issues/34)
311+
scriptFile = resolveIncludes(scriptFile)
312+
310313
// just proceed if the script file is a regular file at this point
311314
errorIf(scriptFile == null || !scriptFile.canRead()) {
312315
"Could not read script argument '$scriptResource'"
@@ -315,8 +318,9 @@ fun prepareScript(scriptResource: String): File {
315318
return scriptFile!!
316319
}
317320

318-
private fun createTmpScript(scriptText: String): File {
319-
return File(createTempDir(), "scriptlet.${md5(scriptText)}.kts").apply {
321+
322+
fun createTmpScript(scriptText: String): File {
323+
return File(SCRIPT_TEMP_DIR, "scriptlet.${md5(scriptText)}.kts").apply {
320324
writeText(scriptText)
321325
}
322326
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package kscript.app
2+
3+
import java.io.File
4+
import java.io.FileNotFoundException
5+
import java.net.URL
6+
7+
/**
8+
* @author Holger Brandl
9+
*/
10+
11+
/** Resolve include declarations in a script file. Resolved script will be put into another temporary script */
12+
fun resolveIncludes(template: File?): File? {
13+
if (template == null) return null
14+
15+
// recursively replace //INCLUDES
16+
return resolveIncludesInternal(template)?.run { resolveIncludes(this) } ?: template
17+
}
18+
19+
private fun resolveIncludesInternal(template: File): File? {
20+
val scriptLines = template.readText().lines()
21+
22+
// don't do a thing if there's not INCLUDE in the script
23+
if (scriptLines.find { it.startsWith("//INCLUDE ") } == null) return null
24+
25+
val sb = StringBuilder()
26+
27+
scriptLines.map {
28+
if (it.startsWith("//INCLUDE")) {
29+
val include = it.split("[ ]+".toRegex()).last()
30+
31+
val includeURL = when {
32+
include.startsWith("http://") -> URL(include)
33+
include.startsWith("https://") -> URL(include)
34+
include.startsWith("./") || include.startsWith("../") -> File(template.parentFile, include).toURI().toURL()
35+
include.startsWith("/") -> File(include).toURI().toURL()
36+
else -> File(template.parentFile, include).toURI().toURL()
37+
}
38+
39+
try {
40+
sb.appendln(includeURL.readText())
41+
} catch (e: FileNotFoundException) {
42+
errorMsg("Failed to resolve //INCLUDE '${include}'")
43+
System.err.println(e.message?.lines()!!.map { it.prependIndent("[ERROR] ") })
44+
45+
quit(1)
46+
}
47+
} else {
48+
// if it's not a directive we simply skip emit the line as it is
49+
sb.appendln(it)
50+
}
51+
}
52+
53+
return createTmpScript(sb.toString())
54+
}
55+
56+
57+
// basic launcher for testing
58+
fun main(args: Array<String>) {
59+
System.err.println(resolveIncludes(File(args[0])))
60+
}
61+
62+
/**
63+
# Usage Example
64+
cd $KSCRIPT_HOME
65+
gradle shadowJar
66+
resolve_inc() { kotlin -classpath build/libs/kscript-0.1-SNAPSHOT-all.jar kscript.app.ResolveIncludesKt "$@";}
67+
resolve_inc /Users/brandl/projects/kotlin/kscript/test/resources/includes/include_variations.kts
68+
cat $(resolve_inc /Users/brandl/projects/kotlin/kscript/test/resources/includes/include_variations.kts 2>&1)
69+
*/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fun include_3() = println("include_3")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fun include_4() = println("include_4")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
println("Let's resolve includes!")
2+
3+
//INCLUDE rel_includes/include_1.kt
4+
//INCLUDE ./rel_includes//include_2.kt
5+
6+
//INCLUDE ../includes/include_3.kt
7+
//INCLUDE include_4.kt
8+
9+
include_1()
10+
include_2()
11+
include_3()
12+
include_4()
13+
14+
15+
16+
println("wow, so many includes")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fun include_1() = println("include_1")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fun include_2() = println("include_2")

0 commit comments

Comments
 (0)