From 02f2c941c8e1c2abd30634434e69f50f0b29a85c Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Mon, 7 Jan 2019 18:11:43 +0100 Subject: [PATCH] Add import support to main-kts, with test --- .../kotlin/mainKts/test/mainKtsTest.kt | 38 ++++++++++++-- .../testData/import-common.main.kts | 4 ++ .../testData/import-middle.main.kts | 6 +++ .../testData/import-test.main.kts | 7 +++ .../org/jetbrains/kotlin/mainKts/scriptDef.kt | 49 ++++++++++++------- .../kotlin/script/util/annotations.kt | 7 +++ 6 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 libraries/tools/kotlin-main-kts-test/testData/import-common.main.kts create mode 100644 libraries/tools/kotlin-main-kts-test/testData/import-middle.main.kts create mode 100644 libraries/tools/kotlin-main-kts-test/testData/import-test.main.kts diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt index f7a091b8e10..7fc11d4349b 100644 --- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt @@ -7,11 +7,10 @@ package org.jetbrains.kotlin.mainKts.test import org.jetbrains.kotlin.mainKts.MainKtsScript import org.junit.Assert import org.junit.Test +import java.io.ByteArrayOutputStream import java.io.File -import kotlin.script.experimental.api.EvaluationResult -import kotlin.script.experimental.api.ResultWithDiagnostics -import kotlin.script.experimental.api.ScriptEvaluationConfiguration -import kotlin.script.experimental.api.constructorArgs +import java.io.PrintStream +import kotlin.script.experimental.api.* import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate @@ -22,6 +21,7 @@ fun evalFile(scriptFile: File): ResultWithDiagnostics { val evaluationEnv = ScriptEvaluationConfiguration { constructorArgs(emptyArray()) + enableScriptsInstancesSharing() } return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), scriptDefinition, evaluationEnv) @@ -60,4 +60,32 @@ class MainKtsTest { ":\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", res is ResultWithDiagnostics.Failure && res.reports.any { it.message.contains("Unknown set of arguments to maven resolver: abracadabra") }) } -} \ No newline at end of file + + @Test + fun testImport() { + + val out = captureOut { + val res = evalFile(File("testData/import-test.main.kts")) + + Assert.assertTrue( + "test failed:\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", + res is ResultWithDiagnostics.Success + ) + }.lines() + + Assert.assertEquals(listOf("Hi from common", "Hi from middle", "sharedVar == 5"), out) + } +} + +private fun captureOut(body: () -> Unit): String { + val outStream = ByteArrayOutputStream() + val prevOut = System.out + System.setOut(PrintStream(outStream)) + try { + body() + } finally { + System.out.flush() + System.setOut(prevOut) + } + return outStream.toString().trim() +} diff --git a/libraries/tools/kotlin-main-kts-test/testData/import-common.main.kts b/libraries/tools/kotlin-main-kts-test/testData/import-common.main.kts new file mode 100644 index 00000000000..b41adeefa5e --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/import-common.main.kts @@ -0,0 +1,4 @@ + +var sharedVar = 2 + +println("Hi from common") diff --git a/libraries/tools/kotlin-main-kts-test/testData/import-middle.main.kts b/libraries/tools/kotlin-main-kts-test/testData/import-middle.main.kts new file mode 100644 index 00000000000..50d61eaa365 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/import-middle.main.kts @@ -0,0 +1,6 @@ + +@file:Import("import-common.main.kts") + +sharedVar *= 2 + +println("Hi from middle") diff --git a/libraries/tools/kotlin-main-kts-test/testData/import-test.main.kts b/libraries/tools/kotlin-main-kts-test/testData/import-test.main.kts new file mode 100644 index 00000000000..72352bcdca7 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/import-test.main.kts @@ -0,0 +1,7 @@ + +@file:Import("import-common.main.kts") +@file:Import("import-middle.main.kts") + +sharedVar = sharedVar + 1 + +println("sharedVar == $sharedVar") diff --git a/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt index 524724d7f32..55da3cb807a 100644 --- a/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt +++ b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt @@ -8,11 +8,13 @@ package org.jetbrains.kotlin.mainKts import org.jetbrains.kotlin.mainKts.impl.FilesAndIvyResolver import org.jetbrains.kotlin.script.util.DependsOn import org.jetbrains.kotlin.script.util.Repository +import org.jetbrains.kotlin.script.util.Import import java.io.File import kotlin.script.dependencies.ScriptContents import kotlin.script.dependencies.ScriptDependenciesResolver import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.api.* +import kotlin.script.experimental.host.FileScriptSource import kotlin.script.experimental.jvm.JvmDependency import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity import kotlin.script.experimental.jvm.compat.mapLegacyScriptPosition @@ -25,12 +27,12 @@ abstract class MainKtsScript(val args: Array) object MainKtsScriptDefinition : ScriptCompilationConfiguration( { - defaultImports(DependsOn::class, Repository::class) + defaultImports(DependsOn::class, Repository::class, Import::class) jvm { dependenciesFromClassContext(MainKtsScriptDefinition::class, "kotlin-main-kts") } refineConfiguration { - onAnnotations(DependsOn::class, Repository::class, handler = MainKtsConfigurator()) + onAnnotations(DependsOn::class, Repository::class, Import::class, handler = MainKtsConfigurator()) } ide { acceptedLocations(ScriptAcceptedLocation.Everywhere) @@ -41,14 +43,8 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler { private val resolver = FilesAndIvyResolver() override operator fun invoke(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics { - val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() } - ?: return context.compilationConfiguration.asSuccess() - val scriptContents = object : ScriptContents { - override val annotations: Iterable = annotations - override val file: File? = null - override val text: CharSequence? = null - } val diagnostics = arrayListOf() + fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) { diagnostics.add( ScriptDiagnostic( @@ -59,18 +55,33 @@ class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler { ) ) } - return try { - val newDepsFromResolver = resolver.resolve(scriptContents, emptyMap(), ::report, null).get() - ?: return context.compilationConfiguration.asSuccess(diagnostics) // TODO: failure - val resolvedClasspath = newDepsFromResolver.classpath.toList().takeIf { it.isNotEmpty() } - ?: return context.compilationConfiguration.asSuccess(diagnostics) // TODO: failure - ScriptCompilationConfiguration(context.compilationConfiguration) { - dependencies.append(JvmDependency(resolvedClasspath)) - }.asSuccess(diagnostics) - } catch (e: Throwable) { - ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics(path = context.script.locationId)) + val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() } + ?: return context.compilationConfiguration.asSuccess() + + val scriptBaseDir = (context.script as? FileScriptSource)?.file?.parentFile + val importedSources = annotations.flatMap { + (it as? Import)?.paths?.map { sourceName -> + FileScriptSource(scriptBaseDir?.resolve(sourceName) ?: File(sourceName)) + } ?: emptyList() } + + val resolvedClassPath = try { + val scriptContents = object : ScriptContents { + override val annotations: Iterable = annotations.filter { it is DependsOn || it is Repository } + override val file: File? = null + override val text: CharSequence? = null + } + resolver.resolve(scriptContents, emptyMap(), ::report, null).get()?.classpath?.toList() + // TODO: add diagnostics + } catch (e: Throwable) { + return ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics(path = context.script.locationId)) + } + + return ScriptCompilationConfiguration(context.compilationConfiguration) { + if (resolvedClassPath?.isNotEmpty() == true) dependencies.append(JvmDependency(resolvedClassPath)) + if (importedSources.isNotEmpty()) importScripts.append(importedSources) + }.asSuccess(diagnostics) } } diff --git a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/annotations.kt b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/annotations.kt index d15a619ddb6..c1ab85a4fcb 100644 --- a/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/annotations.kt +++ b/libraries/tools/kotlin-script-util/src/main/kotlin/org/jetbrains/kotlin/script/util/annotations.kt @@ -30,3 +30,10 @@ annotation class DependsOn(val value: String = "", val groupId: String = "", val @Retention(AnnotationRetention.SOURCE) annotation class Repository(val value: String = "", val id: String = "", val url: String = "") +/** + * Import other script(s) + */ +@Target(AnnotationTarget.FILE) +@Repeatable +@Retention(AnnotationRetention.SOURCE) +annotation class Import(vararg val paths: String)