Implement support for -Xdefault-script-extension cli option
This commit is contained in:
+7
@@ -308,6 +308,13 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
)
|
)
|
||||||
var jvmDefault: String by FreezableVar(JvmDefaultMode.DEFAULT.description)
|
var jvmDefault: String by FreezableVar(JvmDefaultMode.DEFAULT.description)
|
||||||
|
|
||||||
|
@Argument(
|
||||||
|
value = "-Xdefault-script-extension",
|
||||||
|
valueDescription = "<script filename extension>",
|
||||||
|
description = "Compile expressions and unrecognized scripts passed with the -script argument as scripts with given filename extension"
|
||||||
|
)
|
||||||
|
var defaultScriptExtension: String? by FreezableVar(null)
|
||||||
|
|
||||||
@Argument(value = "-Xdisable-standard-script", description = "Disable standard kotlin script support")
|
@Argument(value = "-Xdisable-standard-script", description = "Disable standard kotlin script support")
|
||||||
var disableStandardScript: Boolean by FreezableVar(false)
|
var disableStandardScript: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
|||||||
+2
@@ -23,6 +23,8 @@ where advanced options include:
|
|||||||
default is 'disable' in language version 1.2 and below,
|
default is 'disable' in language version 1.2 and below,
|
||||||
'enable' since language version 1.3
|
'enable' since language version 1.3
|
||||||
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
||||||
|
-Xdefault-script-extension=<script filename extension>
|
||||||
|
Compile expressions and unrecognized scripts passed with the -script argument as scripts with given filename extension
|
||||||
-Xdisable-standard-script Disable standard kotlin script support
|
-Xdisable-standard-script Disable standard kotlin script support
|
||||||
-Xir-do-not-clear-binding-context
|
-Xir-do-not-clear-binding-context
|
||||||
When using the IR backend, do not clear BindingContext between psi2ir and lowerings
|
When using the IR backend, do not clear BindingContext between psi2ir and lowerings
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
error: unrecognized script file: $TESTDATA_DIR$/simple.kt; Specify path to the script file as the first argument
|
error: unrecognized script type: simple.kt; Specify path to the script file as the first argument
|
||||||
COMPILATION_ERROR
|
COMPILATION_ERROR
|
||||||
|
|||||||
+4
-2
@@ -11,7 +11,8 @@ import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotate
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.host.*
|
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||||
|
import kotlin.script.experimental.host.createScriptDefinitionFromTemplate
|
||||||
import kotlin.script.experimental.jvm.baseClassLoader
|
import kotlin.script.experimental.jvm.baseClassLoader
|
||||||
import kotlin.script.experimental.jvm.jvm
|
import kotlin.script.experimental.jvm.jvm
|
||||||
|
|
||||||
@@ -139,8 +140,9 @@ abstract class ScriptDefinition : UserDataHolderBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isScript(script: SourceCode): Boolean {
|
override fun isScript(script: SourceCode): Boolean {
|
||||||
|
val extension = ".$fileExtension"
|
||||||
val location = script.locationId ?: return false
|
val location = script.locationId ?: return false
|
||||||
return location.endsWith(".$fileExtension") && filePathPattern?.let {
|
return (script.name?.endsWith(extension) == true || location.endsWith(extension)) && filePathPattern?.let {
|
||||||
Regex(it).matches(FileUtilRt.toSystemIndependentName(location))
|
Regex(it).matches(FileUtilRt.toSystemIndependentName(location))
|
||||||
} != false
|
} != false
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-12
@@ -19,8 +19,10 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
|
|||||||
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
|
||||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
|
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.Serializable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
|
import kotlin.script.experimental.host.FileScriptSource
|
||||||
import kotlin.script.experimental.host.StringScriptSource
|
import kotlin.script.experimental.host.StringScriptSource
|
||||||
import kotlin.script.experimental.host.toScriptSource
|
import kotlin.script.experimental.host.toScriptSource
|
||||||
import kotlin.script.experimental.jvm.util.renderError
|
import kotlin.script.experimental.jvm.util.renderError
|
||||||
@@ -53,23 +55,21 @@ abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension {
|
|||||||
|
|
||||||
setupScriptConfiguration(configuration)
|
setupScriptConfiguration(configuration)
|
||||||
|
|
||||||
|
val defaultScriptExtension =
|
||||||
|
(arguments as? K2JVMCompilerArguments)?.defaultScriptExtension?.let { if (it.startsWith('.')) it else ".$it" }
|
||||||
|
|
||||||
val script = when {
|
val script = when {
|
||||||
arguments is K2JVMCompilerArguments && arguments.expression != null -> {
|
arguments is K2JVMCompilerArguments && arguments.expression != null -> {
|
||||||
StringScriptSource(arguments.expression!!, "script.kts")
|
StringScriptSource(arguments.expression!!, "script${defaultScriptExtension ?: ".kts"}")
|
||||||
}
|
}
|
||||||
arguments.script -> {
|
arguments.script -> {
|
||||||
val scriptFile = File(arguments.freeArgs.first())
|
val scriptFile = File(arguments.freeArgs.first()).normalize()
|
||||||
val script = scriptFile.toScriptSource()
|
|
||||||
|
|
||||||
val error = when {
|
fun invalidScript(error: String): ExitCode {
|
||||||
!scriptFile.exists() -> "Script file not found: ${arguments.freeArgs.first()}"
|
|
||||||
scriptFile.isDirectory -> "Script argument points to a directory: ${arguments.freeArgs.first()}"
|
|
||||||
!scriptDefinitionProvider.isScript(script) -> "Unrecognized script file: ${arguments.freeArgs.first()}"
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (error != null) {
|
|
||||||
val extensionHint =
|
val extensionHint =
|
||||||
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)?.let { it.size == 1 && it.first().isDefault } == true) " (.kts)"
|
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
|
||||||
|
?.let { it.size == 1 && it.first().isDefault } == true
|
||||||
|
) " (.kts)"
|
||||||
else ""
|
else ""
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
CompilerMessageSeverity.ERROR,
|
CompilerMessageSeverity.ERROR,
|
||||||
@@ -77,7 +77,22 @@ abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension {
|
|||||||
)
|
)
|
||||||
return ExitCode.COMPILATION_ERROR
|
return ExitCode.COMPILATION_ERROR
|
||||||
}
|
}
|
||||||
script
|
|
||||||
|
if (!scriptFile.exists()) return invalidScript("Script file not found: $scriptFile")
|
||||||
|
|
||||||
|
if (scriptFile.isDirectory) return invalidScript("Script argument points to a directory: $scriptFile")
|
||||||
|
|
||||||
|
var script = scriptFile.toScriptSource().takeIf {
|
||||||
|
scriptDefinitionProvider.isScript(it)
|
||||||
|
}
|
||||||
|
if (script == null && defaultScriptExtension != null) {
|
||||||
|
script = ExplicitlyNamedFileScriptSource(
|
||||||
|
scriptFile.nameWithoutExtension + defaultScriptExtension, scriptFile
|
||||||
|
).takeIf {
|
||||||
|
scriptDefinitionProvider.isScript(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
script ?: return invalidScript("Unrecognized script type: ${scriptFile.name}")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
@@ -157,3 +172,12 @@ fun ScriptDiagnostic.Severity.toCompilerMessageSeverity(): CompilerMessageSeveri
|
|||||||
ScriptDiagnostic.Severity.DEBUG -> CompilerMessageSeverity.LOGGING
|
ScriptDiagnostic.Severity.DEBUG -> CompilerMessageSeverity.LOGGING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class ExplicitlyNamedFileScriptSource(
|
||||||
|
override val name: String, file: File, preloadedText: String? = null
|
||||||
|
) : FileScriptSource(file, preloadedText), Serializable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
|
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.scripting.definitions.MessageReporter
|
import org.jetbrains.kotlin.scripting.definitions.MessageReporter
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
@@ -113,6 +113,7 @@ internal fun reportArgumentsIgnoredGenerally(
|
|||||||
K2JVMCompilerArguments::scriptTemplates,
|
K2JVMCompilerArguments::scriptTemplates,
|
||||||
K2JVMCompilerArguments::scriptResolverEnvironment,
|
K2JVMCompilerArguments::scriptResolverEnvironment,
|
||||||
K2JVMCompilerArguments::disableStandardScript,
|
K2JVMCompilerArguments::disableStandardScript,
|
||||||
|
K2JVMCompilerArguments::defaultScriptExtension,
|
||||||
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
||||||
K2JVMCompilerArguments::pluginClasspaths,
|
K2JVMCompilerArguments::pluginClasspaths,
|
||||||
K2JVMCompilerArguments::useJavac,
|
K2JVMCompilerArguments::useJavac,
|
||||||
|
|||||||
+80
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.scripting.compiler.test.linesSplitTrim
|
|||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
class ScriptingWithCliCompilerTest {
|
class ScriptingWithCliCompilerTest {
|
||||||
|
|
||||||
@@ -63,6 +64,85 @@ class ScriptingWithCliCompilerTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testExpressionAsMainKts() {
|
||||||
|
// testing that without specifying default to .main.kts, the annotation is unresolved
|
||||||
|
runWithK2JVMCompiler(
|
||||||
|
arrayOf(
|
||||||
|
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator),
|
||||||
|
"-expression",
|
||||||
|
"\\@file:CompilerOptions(\"-Xunknown1\")"
|
||||||
|
),
|
||||||
|
listOf(""),
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedSomeErrPatterns = listOf(
|
||||||
|
"unresolved reference: CompilerOptions"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// it seems not possible to make a one-liner with the annotation, and
|
||||||
|
// annotation is the easiest available distinguishing factor for the .main.kts script
|
||||||
|
// so, considering "expecting an element" error as a success here
|
||||||
|
runWithK2JVMCompiler(
|
||||||
|
arrayOf(
|
||||||
|
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator),
|
||||||
|
"-Xdefault-script-extension=.main.kts",
|
||||||
|
"-expression",
|
||||||
|
"\\@file:CompilerOptions(\"-Xunknown1\")"
|
||||||
|
),
|
||||||
|
listOf(""),
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedSomeErrPatterns = listOf(
|
||||||
|
"expecting an element"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testScriptAsMainKts() {
|
||||||
|
val scriptFile = Files.createTempFile("someScript", "").toFile()
|
||||||
|
scriptFile.writeText("@file:CompilerOptions(\"-abracadabra\")\n42")
|
||||||
|
|
||||||
|
// testing that without specifying default to .main.kts the script with extension .txt is not recognized
|
||||||
|
runWithK2JVMCompiler(
|
||||||
|
arrayOf(
|
||||||
|
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator),
|
||||||
|
"-script",
|
||||||
|
scriptFile.path
|
||||||
|
),
|
||||||
|
listOf(""),
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedSomeErrPatterns = listOf(
|
||||||
|
"unrecognized script type: someScript.+"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
runWithK2JVMCompiler(
|
||||||
|
arrayOf(
|
||||||
|
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator),
|
||||||
|
"-Xdefault-script-extension=.main.kts",
|
||||||
|
"-script",
|
||||||
|
scriptFile.path
|
||||||
|
),
|
||||||
|
listOf(""),
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedSomeErrPatterns = listOf(
|
||||||
|
"error: invalid argument: -abracadabra"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
runWithK2JVMCompiler(
|
||||||
|
arrayOf(
|
||||||
|
"-cp", getMainKtsClassPath().joinToString(File.pathSeparator),
|
||||||
|
"-Xdefault-script-extension=main.kts",
|
||||||
|
"-script",
|
||||||
|
scriptFile.path
|
||||||
|
),
|
||||||
|
listOf(""),
|
||||||
|
expectedExitCode = 1,
|
||||||
|
expectedSomeErrPatterns = listOf(
|
||||||
|
"error: invalid argument: -abracadabra"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testExpressionWithComma() {
|
fun testExpressionWithComma() {
|
||||||
runWithK2JVMCompiler(
|
runWithK2JVMCompiler(
|
||||||
|
|||||||
+16
-6
@@ -157,8 +157,9 @@ fun runWithK2JVMCompiler(
|
|||||||
|
|
||||||
fun runWithK2JVMCompiler(
|
fun runWithK2JVMCompiler(
|
||||||
args: Array<String>,
|
args: Array<String>,
|
||||||
expectedOutPatterns: List<String> = emptyList(),
|
expectedAllOutPatterns: List<String> = emptyList(),
|
||||||
expectedExitCode: Int = 0
|
expectedExitCode: Int = 0,
|
||||||
|
expectedSomeErrPatterns: List<String>? = null
|
||||||
) {
|
) {
|
||||||
val (out, err, ret) = captureOutErrRet {
|
val (out, err, ret) = captureOutErrRet {
|
||||||
CLITool.doMainNoExit(
|
CLITool.doMainNoExit(
|
||||||
@@ -169,15 +170,25 @@ fun runWithK2JVMCompiler(
|
|||||||
try {
|
try {
|
||||||
val outLines = out.lines()
|
val outLines = out.lines()
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
"Expecting pattern:\n ${expectedOutPatterns.joinToString("\n ")}\nGot:\n ${outLines.joinToString("\n ")}",
|
"Expecting pattern:\n ${expectedAllOutPatterns.joinToString("\n ")}\nGot:\n ${outLines.joinToString("\n ")}",
|
||||||
expectedOutPatterns.size, outLines.size
|
expectedAllOutPatterns.size, outLines.size
|
||||||
)
|
)
|
||||||
for ((expectedPattern, actualLine) in expectedOutPatterns.zip(outLines)) {
|
for ((expectedPattern, actualLine) in expectedAllOutPatterns.zip(outLines)) {
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
"line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",
|
"line \"$actualLine\" do not match with expected pattern \"$expectedPattern\"",
|
||||||
Regex(expectedPattern).matches(actualLine)
|
Regex(expectedPattern).matches(actualLine)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (expectedSomeErrPatterns != null) {
|
||||||
|
val errLines = err.lines()
|
||||||
|
for (expectedPattern in expectedSomeErrPatterns) {
|
||||||
|
val re = Regex(expectedPattern)
|
||||||
|
Assert.assertTrue(
|
||||||
|
"Expected pattern \"$expectedPattern\" is not found in the stderr:\n${errLines.joinToString("\n")}",
|
||||||
|
errLines.any { re.find(it) != null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Assert.assertEquals(expectedExitCode, ret.code)
|
Assert.assertEquals(expectedExitCode, ret.code)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
println("OUT:\n$out")
|
println("OUT:\n$out")
|
||||||
@@ -186,7 +197,6 @@ fun runWithK2JVMCompiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun <T> captureOutErrRet(body: () -> T): Triple<String, String, T> {
|
internal fun <T> captureOutErrRet(body: () -> T): Triple<String, String, T> {
|
||||||
val outStream = ByteArrayOutputStream()
|
val outStream = ByteArrayOutputStream()
|
||||||
val errStream = ByteArrayOutputStream()
|
val errStream = ByteArrayOutputStream()
|
||||||
|
|||||||
Reference in New Issue
Block a user