Add possibility to get location of the script.main.kts file
#KT-48414 fixed
This commit is contained in:
committed by
Ilya Chernikov
parent
7ddf83f32d
commit
ca2f37f6eb
@@ -7,6 +7,7 @@
|
||||
|
||||
package kotlin.script.experimental.api
|
||||
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
@@ -106,6 +107,17 @@ val ScriptCompilationConfigurationKeys.implicitReceivers by PropertiesCollection
|
||||
*/
|
||||
val ScriptCompilationConfigurationKeys.providedProperties by PropertiesCollection.key<Map<String, KotlinType>>() // external variables
|
||||
|
||||
/**
|
||||
* Variable name that holds a {@link File} instance pointing to the location of the script file
|
||||
*/
|
||||
val ScriptCompilationConfigurationKeys.scriptFileLocationVariable by PropertiesCollection.key<String>()
|
||||
|
||||
/**
|
||||
* File pointing to the location of the script file. Note that in some cases it might not be possible
|
||||
* to determine script file location properly - in this case the file is an empty file
|
||||
*/
|
||||
val ScriptCompilationConfigurationKeys.scriptFileLocation by PropertiesCollection.key<File>()
|
||||
|
||||
/**
|
||||
* The list of import expressions that will be implicitly applied to the script body, the syntax is the same as for the "import" statement
|
||||
*/
|
||||
|
||||
+7
-5
@@ -41,12 +41,14 @@ fun configureProvidedPropertiesFromJsr223Context(context: ScriptEvaluationConfig
|
||||
val engineBindings = jsr223context.getBindings(ScriptContext.ENGINE_SCOPE)
|
||||
val globalBindings = jsr223context.getBindings(ScriptContext.GLOBAL_SCOPE)
|
||||
for (prop in knownProperties) {
|
||||
val v = when {
|
||||
engineBindings?.containsKey(prop.key) == true -> engineBindings[prop.key]
|
||||
globalBindings?.containsKey(prop.key) == true -> globalBindings[prop.key]
|
||||
else -> return ResultWithDiagnostics.Failure("Property ${prop.key} is not found in the bindings".asErrorDiagnostics())
|
||||
if (prop.key !in updatedProperties) {
|
||||
val v = when {
|
||||
engineBindings?.containsKey(prop.key) == true -> engineBindings[prop.key]
|
||||
globalBindings?.containsKey(prop.key) == true -> globalBindings[prop.key]
|
||||
else -> return ResultWithDiagnostics.Failure("Property ${prop.key} is not found in the bindings".asErrorDiagnostics())
|
||||
}
|
||||
updatedProperties[prop.key] = v
|
||||
}
|
||||
updatedProperties[prop.key] = v
|
||||
}
|
||||
ScriptEvaluationConfiguration(context.evaluationConfiguration) {
|
||||
providedProperties(updatedProperties)
|
||||
|
||||
+20
@@ -121,6 +121,26 @@ class MainKtsIT {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
@Test
|
||||
fun testCacheWithFileLocation() {
|
||||
val scriptPath = File("$TEST_DATA_ROOT/script-file-location-default.main.kts").absolutePath
|
||||
val cache = createTempDirectory("main.kts.test")
|
||||
val expectedTestOutput = listOf(Regex.escape(scriptPath))
|
||||
|
||||
try {
|
||||
Assert.assertTrue(cache.exists() && cache.listDirectoryEntries("*.jar").isEmpty())
|
||||
runWithKotlinRunner(scriptPath, expectedTestOutput, cacheDir = cache)
|
||||
val cacheFile = cache.listDirectoryEntries("*.jar").firstOrNull()
|
||||
Assert.assertTrue(cacheFile != null && cacheFile.exists())
|
||||
|
||||
// this run should use the cached script
|
||||
runWithKotlinRunner(scriptPath, expectedTestOutput, cacheDir = cache)
|
||||
} finally {
|
||||
cache.toFile().deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHelloSerialization() {
|
||||
val paths = PathUtil.kotlinPathsForDistDirectory
|
||||
|
||||
+60
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.mainKts.test
|
||||
import org.jetbrains.kotlin.mainKts.COMPILED_SCRIPTS_CACHE_DIR_PROPERTY
|
||||
import org.jetbrains.kotlin.mainKts.impl.Directories
|
||||
import org.jetbrains.kotlin.mainKts.MainKtsScript
|
||||
import org.jetbrains.kotlin.mainKts.SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.assertTrue
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -145,6 +146,65 @@ class MainKtsTest {
|
||||
Assert.assertEquals(listOf("Hi from sub", "Hi from super", "Hi from random"), out)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptFileLocationDefaultVariable() {
|
||||
val resOk = evalFile(File("$TEST_DATA_ROOT/script-file-location-default.main.kts"))
|
||||
assertSucceeded(resOk)
|
||||
val resultValue = resOk.valueOrThrow().returnValue
|
||||
assertTrue(resultValue is ResultValue.Value) { "Result value should be of type Value" }
|
||||
val value = (resultValue as ResultValue.Value).value!!
|
||||
assertEquals("String", value::class.simpleName)
|
||||
val expectedPathSuffix = "libraries/tools/kotlin-main-kts-test/testData/script-file-location-default.main.kts"
|
||||
val actualPath = (value as String).replace("\\", "/")
|
||||
assertTrue(actualPath.endsWith(expectedPathSuffix)) { "Script file path does not end with expected path" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptFileLocationCustomizedVariable() {
|
||||
val resOk = evalFile(File("$TEST_DATA_ROOT/script-file-location-customized.main.kts"))
|
||||
assertSucceeded(resOk)
|
||||
val resultValue = resOk.valueOrThrow().returnValue
|
||||
assertTrue(resultValue is ResultValue.Value) { "Result value should be of type Value" }
|
||||
val value = (resultValue as ResultValue.Value).value!!
|
||||
assertEquals("String", value::class.simpleName)
|
||||
val expectedPathSuffix = "libraries/tools/kotlin-main-kts-test/testData/script-file-location-customized.main.kts"
|
||||
val actualPath = (value as String).replace("\\", "/")
|
||||
assertTrue(actualPath.endsWith(expectedPathSuffix)) { "Script file path does not end with expected path" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptFileLocationWithImportedScript() {
|
||||
val resOk = evalFile(File("$TEST_DATA_ROOT/script-file-location-with-imported-file.main.kts"))
|
||||
assertSucceeded(resOk)
|
||||
val resultValue = resOk.valueOrThrow().returnValue
|
||||
assertTrue(resultValue is ResultValue.Value) { "Result value should be of type Value" }
|
||||
val value = (resultValue as ResultValue.Value).value!!
|
||||
assertEquals("Array", value::class.simpleName)
|
||||
val expectedSelfPathSuffix = "libraries/tools/kotlin-main-kts-test/testData/script-file-location-with-imported-file.main.kts"
|
||||
val expectedImportedPathSuffix = "libraries/tools/kotlin-main-kts-test/testData/script-file-location-helper-imported-file.main.kts"
|
||||
val actualPathSelf = (value as Array<*>)[0].toString().replace("\\", "/")
|
||||
val actualPathImported = value[1].toString().replace("\\", "/")
|
||||
assertTrue(actualPathSelf.endsWith(expectedSelfPathSuffix)) { "Script file path does not end with expected path" }
|
||||
assertTrue(actualPathImported.endsWith(expectedImportedPathSuffix)) { "Script file path does not end with expected path" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptFileLocationDefaultVariableNotAvailableIfScriptFileVariableCustomized() {
|
||||
val resFailed = evalFile(File("$TEST_DATA_ROOT/script-file-location-customized-default-not-available.main.kts"))
|
||||
assertFailed("Unresolved reference: $SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME", resFailed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptFileLocationDefaultVariableRedefinition() {
|
||||
val resOk = evalFile(File("$TEST_DATA_ROOT/script-file-location-redefine-variable.kts"))
|
||||
assertSucceeded(resOk)
|
||||
val resultValue = resOk.valueOrThrow().returnValue
|
||||
assertTrue(resultValue is ResultValue.Value) { "Result value should be of type Value" }
|
||||
val value = (resultValue as ResultValue.Value).value!!
|
||||
assertEquals("String", value::class.simpleName)
|
||||
assertEquals("success", value)
|
||||
}
|
||||
|
||||
private fun assertIsJava6Bytecode(res: ResultWithDiagnostics<EvaluationResult>) {
|
||||
val scriptClassResource = res.valueOrThrow().returnValue.scriptClass!!.java.run {
|
||||
getResource("$simpleName.class")
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
@file:ScriptFileLocation("scriptFileLocation")
|
||||
|
||||
__FILE__.absolutePath
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
@file:ScriptFileLocation("scriptFileLocation")
|
||||
|
||||
scriptFileLocation.absolutePath
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
|
||||
__FILE__.absolutePath
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import java.io.File
|
||||
|
||||
fun getDependentScriptFile(): File {
|
||||
return __FILE__
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
|
||||
val __FILE__ = "success"
|
||||
__FILE__
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
@file:Import("script-file-location-helper-imported-file.main.kts")
|
||||
|
||||
arrayOf(__FILE__.absolutePath, getDependentScriptFile().absolutePath)
|
||||
|
||||
@@ -33,3 +33,11 @@ annotation class Import(vararg val paths: String)
|
||||
@Repeatable
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class CompilerOptions(vararg val options: String)
|
||||
|
||||
/**
|
||||
* Option that configures the name of the variable that will hold a file pointing to the script location.
|
||||
* If not specified, {@link [SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME]} will be used as the variable name
|
||||
*/
|
||||
@Target(AnnotationTarget.FILE)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ScriptFileLocation(val variable: String)
|
||||
|
||||
@@ -40,15 +40,18 @@ abstract class MainKtsScript(val args: Array<String>)
|
||||
const val COMPILED_SCRIPTS_CACHE_DIR_ENV_VAR = "KOTLIN_MAIN_KTS_COMPILED_SCRIPTS_CACHE_DIR"
|
||||
const val COMPILED_SCRIPTS_CACHE_DIR_PROPERTY = "kotlin.main.kts.compiled.scripts.cache.dir"
|
||||
const val COMPILED_SCRIPTS_CACHE_VERSION = 1
|
||||
const val SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME = "__FILE__"
|
||||
|
||||
class MainKtsScriptDefinition : ScriptCompilationConfiguration(
|
||||
{
|
||||
defaultImports(DependsOn::class, Repository::class, Import::class, CompilerOptions::class)
|
||||
defaultImports(DependsOn::class, Repository::class, Import::class, CompilerOptions::class, ScriptFileLocation::class)
|
||||
jvm {
|
||||
dependenciesFromClassContext(MainKtsScriptDefinition::class, "kotlin-main-kts", "kotlin-stdlib", "kotlin-reflect")
|
||||
}
|
||||
refineConfiguration {
|
||||
onAnnotations(DependsOn::class, Repository::class, Import::class, CompilerOptions::class, handler = MainKtsConfigurator())
|
||||
onAnnotations(ScriptFileLocation::class, handler = ScriptFileLocationCustomConfigurator())
|
||||
beforeCompiling(::configureScriptFileLocationPathVariablesForCompilation)
|
||||
beforeCompiling(::configureProvidedPropertiesFromJsr223Context)
|
||||
}
|
||||
ide {
|
||||
@@ -63,6 +66,7 @@ class MainKtsScriptDefinition : ScriptCompilationConfiguration(
|
||||
object MainKtsEvaluationConfiguration : ScriptEvaluationConfiguration(
|
||||
{
|
||||
scriptsInstancesSharing(true)
|
||||
refineConfigurationBeforeEvaluate(::configureScriptFileLocationPathVariablesForEvaluation)
|
||||
refineConfigurationBeforeEvaluate(::configureProvidedPropertiesFromJsr223Context)
|
||||
refineConfigurationBeforeEvaluate(::configureConstructorArgsFromMainArgs)
|
||||
}
|
||||
@@ -90,6 +94,48 @@ class MainKtsHostConfiguration : ScriptingHostConfiguration(
|
||||
}
|
||||
)
|
||||
|
||||
fun configureScriptFileLocationPathVariablesForEvaluation(context: ScriptEvaluationConfigurationRefinementContext): ResultWithDiagnostics<ScriptEvaluationConfiguration> {
|
||||
val compilationConfiguration = context.evaluationConfiguration[ScriptEvaluationConfiguration.compilationConfiguration]
|
||||
?: throw RuntimeException()
|
||||
val scriptFileLocation = compilationConfiguration[ScriptCompilationConfiguration.scriptFileLocation]
|
||||
?: return context.evaluationConfiguration.asSuccess()
|
||||
val scriptFileLocationVariable = compilationConfiguration[ScriptCompilationConfiguration.scriptFileLocationVariable]
|
||||
?: return context.evaluationConfiguration.asSuccess()
|
||||
|
||||
val res = context.evaluationConfiguration.with {
|
||||
providedProperties.put(mapOf(scriptFileLocationVariable to scriptFileLocation))
|
||||
}
|
||||
return res.asSuccess()
|
||||
}
|
||||
|
||||
fun configureScriptFileLocationPathVariablesForCompilation(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||
val scriptFile = (context.script as? FileBasedScriptSource)?.file ?: return context.compilationConfiguration.asSuccess()
|
||||
val scriptFileLocationVariableName = context.compilationConfiguration[ScriptCompilationConfiguration.scriptFileLocationVariable]
|
||||
?: SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME
|
||||
|
||||
return ScriptCompilationConfiguration(context.compilationConfiguration) {
|
||||
providedProperties.put(mapOf(scriptFileLocationVariableName to KotlinType(File::class)))
|
||||
scriptFileLocation.put(scriptFile)
|
||||
scriptFileLocationVariable.put(scriptFileLocationVariableName)
|
||||
}.asSuccess()
|
||||
}
|
||||
|
||||
class ScriptFileLocationCustomConfigurator : RefineScriptCompilationConfigurationHandler {
|
||||
|
||||
override operator fun invoke(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||
|
||||
val scriptLocationVariable = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)
|
||||
?.filterByAnnotationType<ScriptFileLocation>()?.firstOrNull()?.annotation?.variable
|
||||
?: return context.compilationConfiguration.asSuccess()
|
||||
|
||||
val compilationConfiguration = ScriptCompilationConfiguration(context.compilationConfiguration) {
|
||||
scriptFileLocationVariable.put(scriptLocationVariable)
|
||||
}
|
||||
|
||||
return compilationConfiguration.asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
fun configureConstructorArgsFromMainArgs(context: ScriptEvaluationConfigurationRefinementContext): ResultWithDiagnostics<ScriptEvaluationConfiguration> {
|
||||
val mainArgs = context.evaluationConfiguration[ScriptEvaluationConfiguration.jvm.mainArguments]
|
||||
val res = if (context.evaluationConfiguration[ScriptEvaluationConfiguration.constructorArgs] == null && mainArgs != null) {
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ internal fun makeCompiledScript(
|
||||
sourceFile.declarations.firstIsInstanceOrNull<KtScript>()?.let { ktScript ->
|
||||
makeOtherScripts(ktScript).onSuccess { otherScripts ->
|
||||
KJvmCompiledScript(
|
||||
containingKtFile.virtualFilePath,
|
||||
sourceFile.virtualFilePath,
|
||||
getScriptConfiguration(sourceFile),
|
||||
ktScript.fqName.asString(),
|
||||
null,
|
||||
|
||||
Reference in New Issue
Block a user