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)
|
||||
}
|
||||
|
||||
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>) {
|
||||
val classpath = arrayListOf<URL>()
|
||||
val compilerClasspath = arrayListOf<URL>()
|
||||
var runner: Runner? = null
|
||||
var collectingArguments = false
|
||||
var collectingExpressions = false
|
||||
var needsCompiler = false
|
||||
val arguments = arrayListOf<String>()
|
||||
val compilerArguments = arrayListOf<String>()
|
||||
var expression: String? = null
|
||||
var noStdLib = false
|
||||
var noReflect = false
|
||||
var howtorun = HowToRun.GUESS
|
||||
|
||||
fun setExpression(expr: String) {
|
||||
if (expression == null) {
|
||||
expression = expr
|
||||
fun setRunner(newRunner: Runner) {
|
||||
if (runner == null) {
|
||||
runner = newRunner
|
||||
} else {
|
||||
throw RunnerException("Only single -e/-expression argument supported")
|
||||
throw AssertionError("conflicting runner settings")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,20 +78,8 @@ object Main {
|
||||
return args[i]
|
||||
}
|
||||
|
||||
if (collectingExpressions) {
|
||||
if ("-expression" == arg || "-e" == arg) {
|
||||
setExpression(next())
|
||||
i++
|
||||
continue
|
||||
} else {
|
||||
collectingArguments = true
|
||||
}
|
||||
}
|
||||
|
||||
if (collectingArguments) {
|
||||
arguments.add(arg)
|
||||
i++
|
||||
continue
|
||||
fun restAsArguments() {
|
||||
arguments.addAll(args.copyOfRange(i+1, args.size))
|
||||
}
|
||||
|
||||
if ("-help" == arg || "-h" == arg) {
|
||||
@@ -98,10 +98,26 @@ object Main {
|
||||
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) {
|
||||
setExpression(next())
|
||||
collectingExpressions = true
|
||||
needsCompiler = true
|
||||
if (howtorun != HowToRun.GUESS && howtorun != HowToRun.SCRIPT) {
|
||||
throw RunnerException("expression evaluation is not compatible with -howtorun argument ${howtorun.argName}")
|
||||
}
|
||||
setRunner(ExpressionRunner(next()))
|
||||
restAsArguments()
|
||||
break
|
||||
}
|
||||
else if ("-no-stdlib" == arg) {
|
||||
noStdLib = true
|
||||
@@ -115,20 +131,22 @@ object Main {
|
||||
compilerArguments.add(arg)
|
||||
}
|
||||
else if (arg.startsWith("-")) {
|
||||
throw RunnerException("unsupported argument: $arg")
|
||||
throw RunnerException("unknown option: $arg")
|
||||
}
|
||||
else if (arg.endsWith(".jar")) {
|
||||
runner = JarRunner(arg)
|
||||
collectingArguments = true
|
||||
else if (howtorun == HowToRun.JAR || (howtorun == HowToRun.GUESS && arg.endsWith(".jar"))) {
|
||||
setRunner(JarRunner(arg))
|
||||
restAsArguments()
|
||||
break
|
||||
}
|
||||
else if (arg.endsWith(".kts")) {
|
||||
runner = ScriptRunner(arg)
|
||||
collectingArguments = true
|
||||
needsCompiler = true
|
||||
else if (howtorun == HowToRun.SCRIPT || (howtorun == HowToRun.GUESS && arg.endsWith(".kts"))) {
|
||||
setRunner(ScriptRunner(arg))
|
||||
restAsArguments()
|
||||
break
|
||||
}
|
||||
else {
|
||||
runner = MainClassRunner(arg)
|
||||
collectingArguments = true
|
||||
setRunner(MainClassRunner(arg))
|
||||
restAsArguments()
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
@@ -145,20 +163,17 @@ object Main {
|
||||
classpath.addPath("$KOTLIN_HOME/lib/kotlin-reflect.jar")
|
||||
}
|
||||
|
||||
if (expression != null) {
|
||||
runner = ExpressionRunner(expression!!)
|
||||
} else if (runner == null) {
|
||||
runner = ReplRunner()
|
||||
needsCompiler = true
|
||||
if (runner == null) {
|
||||
setRunner(ReplRunner())
|
||||
}
|
||||
|
||||
if (needsCompiler && compilerClasspath.isEmpty()) {
|
||||
if (runner is RunnerWithCompiler && compilerClasspath.isEmpty()) {
|
||||
findCompilerJar(this::class.java, KOTLIN_HOME.resolve("lib")).forEach {
|
||||
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) {
|
||||
@@ -180,15 +195,9 @@ object Main {
|
||||
println("""kotlin: run Kotlin programs, scripts or REPL.
|
||||
|
||||
Usage: kotlin <options> <command> [<arguments>]
|
||||
where command may be one of:
|
||||
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, passing <arguments> to it
|
||||
and possible options include:
|
||||
where possible options include:
|
||||
-howtorun <value> How to run the supplied command with arguments,
|
||||
valid values: ${HowToRun.validValues}
|
||||
-classpath (-cp) <path> Paths where to find user class files
|
||||
-Dname=value Set a system JVM property
|
||||
-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
|
||||
-version Display Kotlin version
|
||||
-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)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
override fun run(classpath: List<URL>, compilerArguments: List<String>, arguments: List<String>, compilerClasspath: List<URL>) {
|
||||
val compilerArgs = ArrayList<String>()
|
||||
compilerArgs.addClasspathArgIfNeeded(classpath)
|
||||
compilerArgs.addAll(compilerArguments)
|
||||
val compilerArgs = ArrayList<String>().apply {
|
||||
addClasspathArgIfNeeded(classpath)
|
||||
addAll(compilerArguments)
|
||||
addScriptArguments(arguments)
|
||||
}
|
||||
runCompiler(compilerClasspath, compilerArgs)
|
||||
}
|
||||
}
|
||||
@@ -143,10 +152,7 @@ class ScriptRunner(private val path: String) : RunnerWithCompiler() {
|
||||
addAll(compilerArguments)
|
||||
add("-script")
|
||||
add(path)
|
||||
if (arguments.isNotEmpty() && arguments.first() != "--") {
|
||||
add("--")
|
||||
}
|
||||
addAll(arguments)
|
||||
addScriptArguments(arguments)
|
||||
}
|
||||
runCompiler(compilerClasspath, compilerArgs)
|
||||
}
|
||||
@@ -159,10 +165,7 @@ class ExpressionRunner(private val code: String) : RunnerWithCompiler() {
|
||||
addAll(compilerArguments)
|
||||
add("-expression")
|
||||
add(code)
|
||||
if (arguments.isNotEmpty() && arguments.first() != "--") {
|
||||
add("--")
|
||||
}
|
||||
addAll(arguments)
|
||||
addScriptArguments(arguments)
|
||||
}
|
||||
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() }),
|
||||
null, testDataDirectory
|
||||
).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)
|
||||
val exitCode = process.exitValue()
|
||||
try {
|
||||
@@ -198,9 +199,9 @@ class LauncherScriptTest : TestCaseWithTmpdir() {
|
||||
"-e",
|
||||
"println(args.joinToString())",
|
||||
"--",
|
||||
"-a",
|
||||
"-e",
|
||||
"b",
|
||||
expectedStdout = "-a, b\n"
|
||||
expectedStdout = "-e, b\n"
|
||||
)
|
||||
runProcess(
|
||||
"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
|
||||
fun f() : Result<Int> = Result.success(42)
|
||||
^
|
||||
"""
|
||||
"""
|
||||
)
|
||||
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", "-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"
|
||||
)
|
||||
}
|
||||
|
||||
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