Skip to content

Commit e7225e4

Browse files
committed
added support for .kt files via //ENTRY directive (fixes #31)
1 parent 827e101 commit e7225e4

File tree

10 files changed

+250
-24
lines changed

10 files changed

+250
-24
lines changed

kscript

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

33
## note: always use a patch release even if it's 0
4-
KSCRIPT_VERSION=1.5.3
4+
KSCRIPT_VERSION=1.5.4-SNAPSHOT
55

66
## make sure that all requirements (java, kotlinc, maven) to run kscript are met
77
assertInPath(){ if [ -z "$(which $1)" ]; then echo "[ERROR] Could not locate '$1'" 1>&2; exit 1; fi }
@@ -65,6 +65,7 @@ if [ $# == 0 ] || [ "$1" == "-v" ] || [ "$1" == "--version" ] || [ "$1" == "-h"
6565
exit 0;
6666
fi
6767

68+
## later" custom cache directories? see https://github.com/holgerbrandl/kscript/issues/5
6869
#KSCRIPT_CACHE_DIR=${TMPDIR=/tmp}
6970
KSCRIPT_CACHE_DIR=${HOME}/.kscript
7071

@@ -160,7 +161,7 @@ if [ "$scriptFile" == "-" ]; then scriptFile="/dev/stdin"; fi
160161
## Support for support process substitution and stdin.
161162
# http://serverfault.com/questions/52034/what-is-the-difference-between-double-and-single-square-brackets-in-bash
162163
# https://viewsby.wordpress.com/2013/09/06/bash-string-ends-with/
163-
if [[ "$scriptFile" != *kts ]] && [[ -e ${scriptFile} ]] ; then
164+
if [[ "$scriptFile" != *kts ]] && [[ "$scriptFile" != *kt ]] && [[ -e ${scriptFile} ]] ; then
164165
stdinCache=${KSCRIPT_CACHE_DIR}/stdinbuffer_${RANDOM}${RANDOM}.kts # odd but works on macos as well
165166
cat "${scriptFile}" > ${stdinCache}
166167

@@ -203,9 +204,8 @@ if [[ ! -f ${scriptFile} ]]; then
203204
fi
204205

205206

206-
207207
## Extract dependencies from script
208-
#scriptFile=/Users/brandl/projects/kotlin/kscript/test/resources/multi_line_deps.kts
208+
# kscript /Users/brandl/projects/kotlin/kotlin_playground/src/test_hello.kt
209209
if [ $(grep "^// DEPS" ${scriptFile} | wc -l) -gt 0 ]; then
210210
echo "[ERROR] Dependencies must be declared by using the line prefix //DEPS" 1>&2; exit 1;
211211
fi
@@ -236,6 +236,8 @@ if [ "$is_interactive" = true ]; then
236236
exec kotlinc ${kotlin_opts} -classpath "${classpath}"
237237
fi
238238

239+
#echo "script file is $scriptFile"
240+
scriptFileExt=".${scriptFile##*.}"
239241
scriptCheckSum=$(kscript_md5 ${scriptFile})
240242

241243
## we can use readlink here which is not functional on macos, thus:
@@ -246,50 +248,90 @@ scriptCheckSum=$(kscript_md5 ${scriptFile})
246248
# echo "$(readlink ${minusFarg} $1)";
247249
#}
248250

249-
# see https://github.com/holgerbrandl/kscript/issues/5
250-
#jarDir=$(dirname $(abspath ${scriptFile}))
251+
252+
## Even if we just need and support the//ENTRY directive in case of kt-class files, we extract
253+
## it here to fail if it was used in kts files.
254+
entryDirective=$(grep "^//ENTRY" ${scriptFile} | cut -f2- -d' ' | trim)
255+
if [[ -n "${entryDirective}" ]] && [[ ${scriptFileExt} == ".kts" ]] ; then
256+
echo "ERROR: //ENTRY directive is just supported for kt class files" >&2
257+
exit 1;
258+
fi
259+
260+
261+
#https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash
251262
jarFile=${KSCRIPT_CACHE_DIR}/$(basename ${scriptFile} .kts).${scriptCheckSum}.jar
252263

253264

254265
## capitalize first letter and get rid of dashes (since this is what kotlin compiler is doing for the wrapper to create a valid java class name)
255-
className=$(basename ${scriptFile} .kts | tr '-' '_')
266+
className=$(basename ${scriptFile} ${scriptFileExt} | tr '-' '_' | tr '.' '_')
256267
#className="${className^}" ## disabled because requires bash4 and thus not on macos
257268
className=$(echo ${className:0:1} | tr '[a-z]' '[A-Z]')${className:1}
258269

259270

260-
#ls -la ${jarFile}
271+
272+
if [[ $scriptFileExt == ".kts" ]]; then
273+
execClassName=Main_${className}
274+
275+
elif [[ $scriptFileExt == ".kt" ]]; then
276+
# entryDirective=$(grep "^//ENTRY" ${scriptFile} | cut -f2- -d' ' | trim)
277+
278+
# use default entry directive if not defined
279+
if [[ -z "$entryDirective" ]]; then
280+
entryDirective="${className}Kt"
281+
else
282+
entryDirective=${entryDirective}
283+
fi
284+
285+
package=$(grep "^package" ${scriptFile} | cut -f2- -d' ')"."
286+
package=$(echo ${package} | sed 's/^.$//g')
287+
288+
execClassName="${package}${entryDirective}"
289+
fi
290+
291+
292+
echo "classpath is '${classpath}'"
293+
294+
261295
# build cache-jar if it does not yet exist
262296
if [ ! -f "${jarFile}" ]; then
263297
## remove previous (now outdated) cache jars
264298
rm -f .$(basename ${scriptFile} .kts).*.jar
265299

300+
301+
echo "kotlinc -classpath "${classpath}" -d ${jarFile} ${scriptFile}"
266302
kotlinc -classpath "${classpath}" -d ${jarFile} ${scriptFile}
267303

268304
if [ $? -eq 1 ]; then
269-
echo "ERROR: compilation of '$scriptFile' failed" >&2
305+
echo "[ERROR] compilation of '$scriptFile' failed" >&2
270306
exit 1;
271307
fi
272308

309+
if [[ $scriptFileExt == ".kts" ]]; then
310+
execClassName=Main_${className}
273311

274-
# note xxxx placeholder does not work on macos but mktemp creates unique path alreade
275-
mainJava=$(mktemp -dt kscript.XXXXXX)/Main_${className}.java
312+
# note xxxx placeholder does not work on macos but mktemp creates unique path alreade
313+
mainJava=$(mktemp -dt kscript.XXXXXX)/${execClassName}.java
276314

277-
echo '
278-
public class Main_'${className}' {
279-
public static void main(String... args) throws Exception {
280-
Class script = Main_'${className}'.class.getClassLoader().loadClass("'${className}'");
281-
script.getDeclaredConstructor(String[].class).newInstance((Object)args);
315+
echo '
316+
public class Main_'${className}' {
317+
public static void main(String... args) throws Exception {
318+
Class script = Main_'${className}'.class.getClassLoader().loadClass("'${className}'");
319+
script.getDeclaredConstructor(String[].class).newInstance((Object)args);
320+
}
282321
}
283-
}
284-
'> ${mainJava}
285-
322+
'> ${mainJava}
286323

287-
${JAVAC_CMD:=javac} ${mainJava} 2> /dev/null
324+
${JAVAC_CMD:=javac} ${mainJava} 2> /dev/null
288325

289-
## update the jar to include main-wrapper
290-
(cd $(dirname ${mainJava}) && ${JAR_CMD:=jar} uf ${jarFile} $(basename ${mainJava%%.java}.class))
326+
## update the jar to include main-wrapper
327+
(cd $(dirname ${mainJava}) && ${JAR_CMD:=jar} uf ${jarFile} $(basename ${mainJava%%.java}.class))
328+
fi
291329
fi
292330

331+
#echo "genClassName is ${className}"
332+
#echo "execClassName is ${execClassName}"
333+
#echo "jar file is ${jarFile}"
334+
293335

294336
## disabled since kotlin binary fails to add kotlin-stdb when using jar as sole argument
295337
## update manifest to specify main class
@@ -302,7 +344,6 @@ fi
302344
if [ -z "$KOTLIN_HOME" ]; then
303345
## see discussion on https://github.com/holgerbrandl/kscript/issues/15
304346
KOTLIN_HOME=$(KOTLIN_RUNNER=1 JAVACMD=echo kotlinc | awk 'NR>1' RS=' ' | grep -F kotlin.home= | cut -d= -f2)
305-
# ls -la ${KOTLIN_HOME}/lib/kotlin-script-runtime.jar
306347
fi
307348

308349
## fail if KOTLIN_HOME is still unknown or does not exist
@@ -314,4 +355,4 @@ fi
314355

315356
# http://stackoverflow.com/questions/3706689/java-class-files-in-current-directory
316357
# also see https://youtrack.jetbrains.com/issue/KT-17100
317-
exec kotlin ${kotlin_opts} -classpath ${jarFile}:${KOTLIN_HOME}/lib/kotlin-script-runtime.jar:"$classpath" Main_${className} "$@"
358+
exec kotlin ${kotlin_opts} -classpath ${jarFile}:${KOTLIN_HOME}/lib/kotlin-script-runtime.jar:"${classpath}" ${execClassName} "$@"

misc/playground.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ scriptFile=/Users/brandl/projects/kotlin/kotlin_playground/src/test.kt
5454

5555
kotlinc -d foo.jar /Users/brandl/projects/kotlin/kotlin_playground/src/test/test_1.kt
5656
unzip -l foo.jar
57+
java -classpath foo.jar 'test.Foo$Companion'
5758

5859
## does not owrk
5960
javac /Users/brandl/projects/kotlin/kotlin_playground/src/burrows/Huff.java

test/TestsReadme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ${KSCRIPT_HOME}/test/test_suite.sh
3838

3939
# run again with kotlin 1.0.X
4040
sdk use kotlin 1.0.6
41+
kscript --clear-cache
4142
./test/test_suite.sh
4243

4344
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
/**
3+
* @author Holger Brandl
4+
*/
5+
6+
class User(val age: Int)
7+
8+
fun findUserByName(name: String): User = null!!
9+
10+
fun example(names: List<String>) {
11+
val usersByAge = names
12+
.mapNotNull { findUserByName(it) }
13+
.groupBy { it.age }
14+
15+
// usersByAge.
16+
}
17+
18+
class Foo{
19+
companion object {
20+
@JvmStatic fun main(args: Array<String>) {
21+
println("foo companion was called")
22+
}
23+
}
24+
}
25+
26+
fun main(args: Array<String>) {
27+
println("main was called")
28+
29+
args.forEach { println(it) }
30+
}
31+
32+
33+
34+
//ENTRY Foo
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package test
2+
3+
/**
4+
* @author Holger Brandl
5+
*/
6+
7+
class User(val age: Int)
8+
9+
fun findUserByName(name: String): User = null!!
10+
11+
fun example(names: List<String>) {
12+
val usersByAge = names
13+
.mapNotNull { findUserByName(it) }
14+
.groupBy { it.age }
15+
16+
// usersByAge.
17+
}
18+
19+
class Foo{
20+
companion object {
21+
@JvmStatic fun main(args: Array<String>) {
22+
println("foo companion was called")
23+
}
24+
}
25+
}
26+
27+
fun main(args: Array<String>) {
28+
println("main was called")
29+
30+
args.forEach { println(it) }
31+
}
32+
33+
34+
//ENTRY Foo
35+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
/**
3+
* @author Holger Brandl
4+
*/
5+
6+
class User(val age: Int)
7+
8+
fun findUserByName(name: String): User = null!!
9+
10+
fun example(names: List<String>) {
11+
val usersByAge = names
12+
.mapNotNull { findUserByName(it) }
13+
.groupBy { it.age }
14+
15+
// usersByAge.
16+
}
17+
18+
class Foo{
19+
companion object {
20+
@JvmStatic fun main(args: Array<String>) {
21+
println("foo companion was called")
22+
}
23+
}
24+
}
25+
26+
fun main(args: Array<String>) {
27+
println("main was called")
28+
29+
args.forEach { println(it) }
30+
}
31+
32+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package test
2+
3+
/**
4+
* @author Holger Brandl
5+
*/
6+
7+
class User(val age: Int)
8+
9+
fun findUserByName(name: String): User = null!!
10+
11+
fun example(names: List<String>) {
12+
val usersByAge = names
13+
.mapNotNull { findUserByName(it) }
14+
.groupBy { it.age }
15+
16+
// usersByAge.
17+
}
18+
19+
class Foo{
20+
companion object {
21+
@JvmStatic fun main(args: Array<String>) {
22+
println("foo companion was called")
23+
}
24+
}
25+
}
26+
27+
fun main(args: Array<String>) {
28+
println("main was called")
29+
30+
args.forEach { println(it) }
31+
}
32+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env kscript
2+
3+
//DEPS log4j:log4j:1.2.14
4+
//ENTRY Foo
5+
6+
package test
7+
8+
class Foo{
9+
companion object {
10+
@JvmStatic fun main(args: Array<String>) {
11+
println("made it!")
12+
org.apache.log4j.Logger.getRootLogger()
13+
}
14+
}
15+
}
16+
17+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env kscript
2+
3+
//DEPS log4j:log4j:1.2.14
4+
5+
package test
6+
7+
fun main(args: Array<String>) {
8+
println("main was called")
9+
}
10+

test/test_suite.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,26 @@ assert 'kscript "println(1+1)"' '2'
133133
assert_statement 'echo "foo${NL}bar" | kscript "stdin.split().select(1, 2, -3)"' "" "[ERROR] Can not mix positive and negative selections" 1
134134

135135
assert_end support_api
136+
137+
138+
########################################################################################################################
139+
## kt support
140+
141+
## run kt via interpreter mode
142+
assert "${KSCRIPT_HOME}/test/resources/kt_tests/simple_app.kt" "main was called"
143+
144+
## run kt via interpreter mode with dependencies
145+
assert "kscript ${KSCRIPT_HOME}/test/resources/kt_tests/main_with_deps.kt" "made it!"
146+
147+
## test misc entry point with or without package configurations
148+
149+
assert "kscript ${KSCRIPT_HOME}/test/resources/kt_tests/custom_entry_nopckg.kt" "foo companion was called"
150+
151+
assert "kscript ${KSCRIPT_HOME}/test/resources/kt_tests/custom_entry_withpckg.kt" "foo companion was called"
152+
153+
assert "kscript ${KSCRIPT_HOME}/test/resources/kt_tests/default_entry_nopckg.kt" "main was called"
154+
155+
assert "kscript ${KSCRIPT_HOME}/test/resources/kt_tests/default_entry_withpckg.kt" "main was called"
156+
157+
assert_end kt_support
158+

0 commit comments

Comments
 (0)