CLI: support -language-version option in cli runner

This commit is contained in:
Ilya Chernikov
2023-05-25 17:20:31 +02:00
committed by Space Team
parent 30131e289f
commit 6f8049ffd3
2 changed files with 88 additions and 70 deletions
@@ -82,83 +82,89 @@ object Main {
arguments.addAll(args.copyOfRange(i+1, args.size)) arguments.addAll(args.copyOfRange(i+1, args.size))
} }
if ("-help" == arg || "-h" == arg) { when {
printUsageAndExit() "-help" == arg || "-h" == arg -> {
} printUsageAndExit()
else if ("-version" == arg) {
printVersionAndExit()
}
else if ("-classpath" == arg || "-cp" == arg) {
for (path in next().split(File.pathSeparator).filter(String::isNotEmpty)) {
classpath.addPath(path)
} }
} "-version" == arg -> {
else if ("-compiler-path" == arg) { printVersionAndExit()
for (path in next().split(File.pathSeparator).filter(String::isNotEmpty)) {
compilerClasspath.addPath(path)
} }
} "-classpath" == arg || "-cp" == arg -> {
else if ("-howtorun" == arg) { for (path in next().split(File.pathSeparator).filter(String::isNotEmpty)) {
if (howtorun != HowToRun.GUESS) { classpath.addPath(path)
throw RunnerException("-howtorun is already set to ${howtorun.argName}") }
} }
val howToRunArg = next() "-compiler-path" == arg -> {
if (howToRunArg.startsWith(".")) { for (path in next().split(File.pathSeparator).filter(String::isNotEmpty)) {
howtorun = HowToRun.SCRIPT compilerClasspath.addPath(path)
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}")
} }
} "-howtorun" == arg -> {
else if ("-expression" == arg || "-e" == arg) { if (howtorun != HowToRun.GUESS) {
if (howtorun != HowToRun.GUESS && howtorun != HowToRun.SCRIPT) { throw RunnerException("-howtorun is already set to ${howtorun.argName}")
throw RunnerException("expression evaluation is not compatible with -howtorun argument ${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}")
}
} }
setRunner(ExpressionRunner(next())) "-expression" == arg || "-e" == arg -> {
restAsArguments() if (howtorun != HowToRun.GUESS && howtorun != HowToRun.SCRIPT) {
break throw RunnerException("expression evaluation is not compatible with -howtorun argument ${howtorun.argName}")
} }
else if ("-no-stdlib" == arg) { setRunner(ExpressionRunner(next()))
noStdLib = true restAsArguments()
compilerArguments.add(arg) break
} }
else if ("-no-reflect" == arg) { "-no-stdlib" == arg -> {
noReflect = true noStdLib = true
compilerArguments.add(arg) compilerArguments.add(arg)
} }
else if (arg.startsWith("-X")) { "-no-reflect" == arg -> {
compilerArguments.add(arg) noReflect = true
} compilerArguments.add(arg)
else if (arg.startsWith("-")) { }
throw RunnerException("unknown option: $arg") arg.startsWith("-X") -> {
} compilerArguments.add(arg)
else if (howtorun == HowToRun.JAR || (howtorun == HowToRun.GUESS && arg.endsWith(".jar"))) { }
setRunner(JarRunner(arg)) "-language-version" == arg -> {
restAsArguments() compilerArguments.add(arg)
break compilerArguments.add(next())
} }
else if (howtorun == HowToRun.SCRIPT || (howtorun == HowToRun.GUESS && arg.endsWith(".kts"))) { arg.startsWith("-") -> {
setRunner(ScriptRunner(arg)) throw RunnerException("unknown option: $arg")
restAsArguments() }
break howtorun == HowToRun.JAR || howtorun == HowToRun.GUESS && arg.endsWith(".jar") -> {
} setRunner(JarRunner(arg))
else { restAsArguments()
val workingDir = File(".") break
val classFile = File(arg) }
howtorun == HowToRun.SCRIPT || howtorun == HowToRun.GUESS && arg.endsWith(".kts") -> {
setRunner(ScriptRunner(arg))
restAsArguments()
break
}
else -> {
val workingDir = File(".")
val classFile = File(arg)
// Allow running class files with '.class' extension. // Allow running class files with '.class' extension.
// In order to infer its fully qualified name, it should be located in the current working directory or a subdirectory of it // In order to infer its fully qualified name, it should be located in the current working directory or a subdirectory of it
val className = val className =
if (arg.endsWith(".class") && classFile.exists() && classFile.canonicalPath.contains(workingDir.canonicalPath)) { if (arg.endsWith(".class") && classFile.exists() && classFile.canonicalPath.contains(workingDir.canonicalPath)) {
classFile.canonicalFile.toRelativeString(workingDir.canonicalFile) classFile.canonicalFile.toRelativeString(workingDir.canonicalFile)
.removeSuffix(".class") .removeSuffix(".class")
.replace(File.separatorChar, '.') .replace(File.separatorChar, '.')
} else arg } else arg
setRunner(MainClassRunner(className)) setRunner(MainClassRunner(className))
restAsArguments() restAsArguments()
break break
}
} }
i++ i++
} }
@@ -192,6 +192,18 @@ class LauncherScriptTest : TestCaseWithTmpdir() {
) )
} }
fun testRunnerExpressionLanguageVersion20() {
runProcess(
"kotlin",
"-language-version", "2.0", "-e",
"println(args.joinToString())",
"-a",
"b",
expectedStdout = "-a, b\n",
expectedStderr = "warning: language version 2.0 is experimental, there are no backwards compatibility guarantees for new language and library features\n"
)
}
fun testCommandlineProcessing() { fun testCommandlineProcessing() {
runProcess( runProcess(
"kotlin", "kotlin",