Implement -howtorun option for kotlin runner
Implement -no-stdlib argument support in kotlin runner #KT-43534 fixed
This commit is contained in:
@@ -34,24 +34,36 @@ object Main {
|
|||||||
KOTLIN_HOME = File(home)
|
KOTLIN_HOME = File(home)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class HowToRun(val argName: String) {
|
||||||
|
GUESS("guess"),
|
||||||
|
CLASSFILE("classfile"),
|
||||||
|
JAR("jar"),
|
||||||
|
SCRIPT("script");
|
||||||
|
// TODO: consider implementing REPL as well
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val validValues = "${GUESS.argName} (default), ${CLASSFILE.argName}, ${JAR.argName}, ${SCRIPT.argName} (or .<script filename extension>)"
|
||||||
|
|
||||||
|
fun fromArg(name: String): HowToRun? =
|
||||||
|
HowToRun.values().find { it.argName == name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun run(args: Array<String>) {
|
private fun run(args: Array<String>) {
|
||||||
val classpath = arrayListOf<URL>()
|
val classpath = arrayListOf<URL>()
|
||||||
val compilerClasspath = arrayListOf<URL>()
|
val compilerClasspath = arrayListOf<URL>()
|
||||||
var runner: Runner? = null
|
var runner: Runner? = null
|
||||||
var collectingArguments = false
|
|
||||||
var collectingExpressions = false
|
|
||||||
var needsCompiler = false
|
|
||||||
val arguments = arrayListOf<String>()
|
val arguments = arrayListOf<String>()
|
||||||
val compilerArguments = arrayListOf<String>()
|
val compilerArguments = arrayListOf<String>()
|
||||||
var expression: String? = null
|
|
||||||
var noStdLib = false
|
var noStdLib = false
|
||||||
var noReflect = false
|
var noReflect = false
|
||||||
|
var howtorun = HowToRun.GUESS
|
||||||
|
|
||||||
fun setExpression(expr: String) {
|
fun setRunner(newRunner: Runner) {
|
||||||
if (expression == null) {
|
if (runner == null) {
|
||||||
expression = expr
|
runner = newRunner
|
||||||
} else {
|
} else {
|
||||||
throw RunnerException("Only single -e/-expression argument supported")
|
throw AssertionError("conflicting runner settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,20 +78,8 @@ object Main {
|
|||||||
return args[i]
|
return args[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectingExpressions) {
|
fun restAsArguments() {
|
||||||
if ("-expression" == arg || "-e" == arg) {
|
arguments.addAll(args.copyOfRange(i+1, args.size))
|
||||||
setExpression(next())
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
collectingArguments = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collectingArguments) {
|
|
||||||
arguments.add(arg)
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("-help" == arg || "-h" == arg) {
|
if ("-help" == arg || "-h" == arg) {
|
||||||
@@ -98,10 +98,26 @@ object Main {
|
|||||||
compilerClasspath.addPath(path)
|
compilerClasspath.addPath(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ("-howtorun" == arg) {
|
||||||
|
if (howtorun != HowToRun.GUESS) {
|
||||||
|
throw RunnerException("-howtorun is already set to ${howtorun.argName}")
|
||||||
|
}
|
||||||
|
val howToRunArg = next()
|
||||||
|
if (howToRunArg.startsWith(".")) {
|
||||||
|
howtorun = HowToRun.SCRIPT
|
||||||
|
compilerArguments.add("-Xdefault-script-extension=$howToRunArg")
|
||||||
|
} else {
|
||||||
|
howtorun = HowToRun.fromArg(howToRunArg)
|
||||||
|
?: throw RunnerException("invalid argument to the option -howtorun $howToRunArg, valid arguments are: ${HowToRun.validValues}")
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ("-expression" == arg || "-e" == arg) {
|
else if ("-expression" == arg || "-e" == arg) {
|
||||||
setExpression(next())
|
if (howtorun != HowToRun.GUESS && howtorun != HowToRun.SCRIPT) {
|
||||||
collectingExpressions = true
|
throw RunnerException("expression evaluation is not compatible with -howtorun argument ${howtorun.argName}")
|
||||||
needsCompiler = true
|
}
|
||||||
|
setRunner(ExpressionRunner(next()))
|
||||||
|
restAsArguments()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
else if ("-no-stdlib" == arg) {
|
else if ("-no-stdlib" == arg) {
|
||||||
noStdLib = true
|
noStdLib = true
|
||||||
@@ -115,20 +131,22 @@ object Main {
|
|||||||
compilerArguments.add(arg)
|
compilerArguments.add(arg)
|
||||||
}
|
}
|
||||||
else if (arg.startsWith("-")) {
|
else if (arg.startsWith("-")) {
|
||||||
throw RunnerException("unsupported argument: $arg")
|
throw RunnerException("unknown option: $arg")
|
||||||
}
|
}
|
||||||
else if (arg.endsWith(".jar")) {
|
else if (howtorun == HowToRun.JAR || (howtorun == HowToRun.GUESS && arg.endsWith(".jar"))) {
|
||||||
runner = JarRunner(arg)
|
setRunner(JarRunner(arg))
|
||||||
collectingArguments = true
|
restAsArguments()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
else if (arg.endsWith(".kts")) {
|
else if (howtorun == HowToRun.SCRIPT || (howtorun == HowToRun.GUESS && arg.endsWith(".kts"))) {
|
||||||
runner = ScriptRunner(arg)
|
setRunner(ScriptRunner(arg))
|
||||||
collectingArguments = true
|
restAsArguments()
|
||||||
needsCompiler = true
|
break
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
runner = MainClassRunner(arg)
|
setRunner(MainClassRunner(arg))
|
||||||
collectingArguments = true
|
restAsArguments()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@@ -145,20 +163,17 @@ object Main {
|
|||||||
classpath.addPath("$KOTLIN_HOME/lib/kotlin-reflect.jar")
|
classpath.addPath("$KOTLIN_HOME/lib/kotlin-reflect.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression != null) {
|
if (runner == null) {
|
||||||
runner = ExpressionRunner(expression!!)
|
setRunner(ReplRunner())
|
||||||
} else if (runner == null) {
|
|
||||||
runner = ReplRunner()
|
|
||||||
needsCompiler = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsCompiler && compilerClasspath.isEmpty()) {
|
if (runner is RunnerWithCompiler && compilerClasspath.isEmpty()) {
|
||||||
findCompilerJar(this::class.java, KOTLIN_HOME.resolve("lib")).forEach {
|
findCompilerJar(this::class.java, KOTLIN_HOME.resolve("lib")).forEach {
|
||||||
compilerClasspath.add(it.absoluteFile.toURI().toURL())
|
compilerClasspath.add(it.absoluteFile.toURI().toURL())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.run(classpath, compilerArguments, arguments, compilerClasspath)
|
runner!!.run(classpath, compilerArguments, arguments, compilerClasspath)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<URL>.addPath(path: String) {
|
private fun MutableList<URL>.addPath(path: String) {
|
||||||
@@ -180,15 +195,9 @@ object Main {
|
|||||||
println("""kotlin: run Kotlin programs, scripts or REPL.
|
println("""kotlin: run Kotlin programs, scripts or REPL.
|
||||||
|
|
||||||
Usage: kotlin <options> <command> [<arguments>]
|
Usage: kotlin <options> <command> [<arguments>]
|
||||||
where command may be one of:
|
where possible options include:
|
||||||
foo.Bar Runs the 'main' function from the class with the given qualified name
|
-howtorun <value> How to run the supplied command with arguments,
|
||||||
(compiler arguments are ignored)
|
valid values: ${HowToRun.validValues}
|
||||||
app.jar Runs the given JAR file as 'java -jar' would do
|
|
||||||
(compiler arguments are ignored and no Kotlin stdlib is added to the classpath)
|
|
||||||
script.kts Compiles and runs the given script, passing <arguments> to it
|
|
||||||
-expression (-e) '2+2' Evaluates the expression and prints the result, passing <arguments> to it
|
|
||||||
<no command> Runs Kotlin REPL, passing <arguments> to it
|
|
||||||
and possible options include:
|
|
||||||
-classpath (-cp) <path> Paths where to find user class files
|
-classpath (-cp) <path> Paths where to find user class files
|
||||||
-Dname=value Set a system JVM property
|
-Dname=value Set a system JVM property
|
||||||
-J<option> Pass an option directly to JVM
|
-J<option> Pass an option directly to JVM
|
||||||
@@ -197,6 +206,17 @@ and possible options include:
|
|||||||
-X<flag>[=value] Pass -X argument to the compiler
|
-X<flag>[=value] Pass -X argument to the compiler
|
||||||
-version Display Kotlin version
|
-version Display Kotlin version
|
||||||
-help (-h) Print a synopsis of options
|
-help (-h) Print a synopsis of options
|
||||||
|
and command is interpreted according to the -howtorun option argument
|
||||||
|
or, in case of guess, according to the following rules:
|
||||||
|
foo.Bar Runs the 'main' function from the class with the given qualified name
|
||||||
|
(compiler arguments are ignored)
|
||||||
|
app.jar Runs the given JAR file as 'java -jar' would do
|
||||||
|
(compiler arguments are ignored and no Kotlin stdlib is added to the classpath)
|
||||||
|
script.kts Compiles and runs the given script, passing <arguments> to it
|
||||||
|
-expression (-e) '2+2' Evaluates the expression and prints the result, passing <arguments> to it
|
||||||
|
<no command> Runs Kotlin REPL
|
||||||
|
arguments are passed to the main function when running class or jar file, and for standard script definitions
|
||||||
|
as the 'args' parameter when running script or expression
|
||||||
""")
|
""")
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,11 +127,20 @@ private fun MutableList<String>.addClasspathArgIfNeeded(classpath: List<URL>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ArrayList<String>.addScriptArguments(arguments: List<String>) {
|
||||||
|
if (arguments.isNotEmpty() && arguments.first() != "--") {
|
||||||
|
add("--")
|
||||||
|
}
|
||||||
|
addAll(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
class ReplRunner : RunnerWithCompiler() {
|
class ReplRunner : RunnerWithCompiler() {
|
||||||
override fun run(classpath: List<URL>, compilerArguments: List<String>, arguments: List<String>, compilerClasspath: List<URL>) {
|
override fun run(classpath: List<URL>, compilerArguments: List<String>, arguments: List<String>, compilerClasspath: List<URL>) {
|
||||||
val compilerArgs = ArrayList<String>()
|
val compilerArgs = ArrayList<String>().apply {
|
||||||
compilerArgs.addClasspathArgIfNeeded(classpath)
|
addClasspathArgIfNeeded(classpath)
|
||||||
compilerArgs.addAll(compilerArguments)
|
addAll(compilerArguments)
|
||||||
|
addScriptArguments(arguments)
|
||||||
|
}
|
||||||
runCompiler(compilerClasspath, compilerArgs)
|
runCompiler(compilerClasspath, compilerArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,10 +152,7 @@ class ScriptRunner(private val path: String) : RunnerWithCompiler() {
|
|||||||
addAll(compilerArguments)
|
addAll(compilerArguments)
|
||||||
add("-script")
|
add("-script")
|
||||||
add(path)
|
add(path)
|
||||||
if (arguments.isNotEmpty() && arguments.first() != "--") {
|
addScriptArguments(arguments)
|
||||||
add("--")
|
|
||||||
}
|
|
||||||
addAll(arguments)
|
|
||||||
}
|
}
|
||||||
runCompiler(compilerClasspath, compilerArgs)
|
runCompiler(compilerClasspath, compilerArgs)
|
||||||
}
|
}
|
||||||
@@ -159,10 +165,7 @@ class ExpressionRunner(private val code: String) : RunnerWithCompiler() {
|
|||||||
addAll(compilerArguments)
|
addAll(compilerArguments)
|
||||||
add("-expression")
|
add("-expression")
|
||||||
add(code)
|
add(code)
|
||||||
if (arguments.isNotEmpty() && arguments.first() != "--") {
|
addScriptArguments(arguments)
|
||||||
add("--")
|
|
||||||
}
|
|
||||||
addAll(arguments)
|
|
||||||
}
|
}
|
||||||
runCompiler(compilerClasspath, compilerArgs)
|
runCompiler(compilerClasspath, compilerArgs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@file:CompilerOptions("-Xallow-result-return-type")
|
||||||
|
|
||||||
|
fun f() : Result<Int> = Result.success(42)
|
||||||
|
|
||||||
|
println(f().getOrNull())
|
||||||
@@ -58,6 +58,7 @@ class LauncherScriptTest : TestCaseWithTmpdir() {
|
|||||||
StringUtil.convertLineSeparators(process.errorStream.bufferedReader().use { it.readText() }),
|
StringUtil.convertLineSeparators(process.errorStream.bufferedReader().use { it.readText() }),
|
||||||
null, testDataDirectory
|
null, testDataDirectory
|
||||||
).replace("Picked up [_A-Z]+:.*\n".toRegex(), "")
|
).replace("Picked up [_A-Z]+:.*\n".toRegex(), "")
|
||||||
|
.replace("The system cannot find the file specified", "No such file or directory") // win -> unix
|
||||||
process.waitFor(10, TimeUnit.SECONDS)
|
process.waitFor(10, TimeUnit.SECONDS)
|
||||||
val exitCode = process.exitValue()
|
val exitCode = process.exitValue()
|
||||||
try {
|
try {
|
||||||
@@ -198,9 +199,9 @@ class LauncherScriptTest : TestCaseWithTmpdir() {
|
|||||||
"-e",
|
"-e",
|
||||||
"println(args.joinToString())",
|
"println(args.joinToString())",
|
||||||
"--",
|
"--",
|
||||||
"-a",
|
"-e",
|
||||||
"b",
|
"b",
|
||||||
expectedStdout = "-a, b\n"
|
expectedStdout = "-e, b\n"
|
||||||
)
|
)
|
||||||
runProcess(
|
runProcess(
|
||||||
"kotlin",
|
"kotlin",
|
||||||
@@ -243,7 +244,7 @@ class LauncherScriptTest : TestCaseWithTmpdir() {
|
|||||||
compiler/testData/launcher/funWithResultReturn.kts:2:11: error: 'kotlin.Result' cannot be used as a return type
|
compiler/testData/launcher/funWithResultReturn.kts:2:11: error: 'kotlin.Result' cannot be used as a return type
|
||||||
fun f() : Result<Int> = Result.success(42)
|
fun f() : Result<Int> = Result.success(42)
|
||||||
^
|
^
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
runProcess("kotlin", "-Xallow-result-return-type", "$testDataDirectory/funWithResultReturn.kts", expectedStdout = "42\n")
|
runProcess("kotlin", "-Xallow-result-return-type", "$testDataDirectory/funWithResultReturn.kts", expectedStdout = "42\n")
|
||||||
}
|
}
|
||||||
@@ -252,7 +253,16 @@ fun f() : Result<Int> = Result.success(42)
|
|||||||
runProcess("kotlin", "-e", "println(42)", expectedStdout = "42\n")
|
runProcess("kotlin", "-e", "println(42)", expectedStdout = "42\n")
|
||||||
runProcess(
|
runProcess(
|
||||||
"kotlin", "-no-stdlib", "-e", "println(42)",
|
"kotlin", "-no-stdlib", "-e", "println(42)",
|
||||||
expectedExitCode = 1, expectedStderrContains = Regex("error: unresolved reference: println")
|
expectedExitCode = 1,
|
||||||
|
expectedStderr = """error: unresolved reference: println (script.kts:1:1)
|
||||||
|
error: no script runtime was found in the classpath: class 'kotlin.script.templates.standard.ScriptTemplateWithArgs' not found. Please add kotlin-script-runtime.jar to the module dependencies. (script.kts:1:1)
|
||||||
|
script.kts:1:1: error: unresolved reference: println
|
||||||
|
println(42)
|
||||||
|
^
|
||||||
|
script.kts:1:1: error: no script runtime was found in the classpath: class 'kotlin.script.templates.standard.ScriptTemplateWithArgs' not found. Please add kotlin-script-runtime.jar to the module dependencies.
|
||||||
|
println(42)
|
||||||
|
^
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,4 +274,64 @@ fun f() : Result<Int> = Result.success(42)
|
|||||||
workDirectory = tmpdir, expectedStdout = "OK\n"
|
workDirectory = tmpdir, expectedStdout = "OK\n"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testHowToRunExpression() {
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "jar", "-e", "println(args.joinToString())", "-a", "b",
|
||||||
|
expectedExitCode = 1, expectedStderr = "error: expression evaluation is not compatible with -howtorun argument jar\n"
|
||||||
|
)
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "script", "-e", "println(args.joinToString())", "-a", "b",
|
||||||
|
expectedStdout = "-a, b\n"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testHowToRunScript() {
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "classfile", "$testDataDirectory/printargs.kts", "--", "-a", "b",
|
||||||
|
expectedExitCode = 1, expectedStderr = "error: could not find or load main class \$TESTDATA_DIR\$/printargs.kts\n"
|
||||||
|
)
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "script", "$testDataDirectory/printargs.kts", "--", "-a", "b",
|
||||||
|
expectedStdout = "-a, b\n"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testHowToRunCustomScript() {
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "$testDataDirectory/funWithResultReturn.myscript",
|
||||||
|
expectedExitCode = 1, expectedStderr = "error: could not find or load main class \$TESTDATA_DIR\$/funWithResultReturn.myscript\n"
|
||||||
|
)
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "script", "$testDataDirectory/funWithResultReturn.myscript",
|
||||||
|
expectedExitCode = 1, expectedStderr = "error: unrecognized script type: funWithResultReturn.myscript; Specify path to the script file as the first argument\n"
|
||||||
|
)
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", ".kts", "$testDataDirectory/funWithResultReturn.myscript",
|
||||||
|
expectedExitCode = 1, expectedStderr = """error: unresolved reference: CompilerOptions (funWithResultReturn.myscript:1:7)
|
||||||
|
error: 'kotlin.Result' cannot be used as a return type (funWithResultReturn.myscript:3:11)
|
||||||
|
compiler/testData/launcher/funWithResultReturn.myscript:1:7: error: unresolved reference: CompilerOptions
|
||||||
|
@file:CompilerOptions("-Xallow-result-return-type")
|
||||||
|
^
|
||||||
|
compiler/testData/launcher/funWithResultReturn.myscript:3:11: error: 'kotlin.Result' cannot be used as a return type
|
||||||
|
fun f() : Result<Int> = Result.success(42)
|
||||||
|
^
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", ".main.kts", "$testDataDirectory/funWithResultReturn.myscript",
|
||||||
|
expectedStdout = "42\n"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testHowToRunClassFile() {
|
||||||
|
runProcess("kotlinc", "$testDataDirectory/helloWorld.kt", "-d", tmpdir.path)
|
||||||
|
|
||||||
|
runProcess(
|
||||||
|
"kotlin", "-howtorun", "jar", "test.HelloWorldKt", workDirectory = tmpdir,
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedStderr = "error: could not read manifest from test.HelloWorldKt: test.HelloWorldKt (No such file or directory)\n"
|
||||||
|
)
|
||||||
|
runProcess("kotlin", "-howtorun", "classfile", "test.HelloWorldKt", expectedStdout = "Hello!\n", workDirectory = tmpdir)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user