diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt index 1ee34242995..05552fec9f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt @@ -38,8 +38,7 @@ import kotlin.script.dependencies.BasicScriptDependenciesResolver import kotlin.script.dependencies.KotlinScriptExternalDependencies import kotlin.script.dependencies.ScriptContents import kotlin.script.dependencies.ScriptDependenciesResolver -import kotlin.script.extensions.SamWithReceiverAnnotations -import kotlin.script.templates.* +import kotlin.script.templates.AcceptedAnnotations open class KotlinScriptDefinitionFromAnnotatedTemplate( template: KClass, @@ -49,11 +48,15 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( ) : KotlinScriptDefinition(template) { val scriptFilePattern by lazy { - providedScriptFilePattern ?: template.annotations.firstIsInstanceOrNull()?.scriptFilePattern ?: DEFAULT_SCRIPT_FILE_PATTERN + providedScriptFilePattern + ?: template.annotations.firstIsInstanceOrNull()?.scriptFilePattern + ?: template.annotations.firstIsInstanceOrNull()?.scriptFilePattern + ?: DEFAULT_SCRIPT_FILE_PATTERN } val resolver: ScriptDependenciesResolver? by lazy { - val defAnn by lazy { template.annotations.firstIsInstanceOrNull() } + val defAnn by lazy { template.annotations.firstIsInstanceOrNull() } + val legacyDefAnn by lazy { template.annotations.firstIsInstanceOrNull() } when { providedResolver != null -> providedResolver // TODO: logScriptDefMessage missing or invalid constructor @@ -67,12 +70,27 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( log.warn("[kts] Script def error ${ex.message}") null } + legacyDefAnn != null -> + try { + log.warn("[kts] Deprecated annotations on the script template are used, please update the provider") + legacyDefAnn.resolver.primaryConstructor?.call()?.let { + LegacyScriptDependenciesResolverWrapper(it) + } + ?: null.apply { + log.warn("[kts] No default constructor found for ${legacyDefAnn.resolver.qualifiedName}") + } + } + catch (ex: ClassCastException) { + log.warn("[kts] Script def error ${ex.message}") + null + } else -> BasicScriptDependenciesResolver() } } val samWithReceiverAnnotations: List? by lazy { - template.annotations.firstIsInstanceOrNull()?.annotations?.toList() + template.annotations.firstIsInstanceOrNull()?.annotations?.toList() + ?: template.annotations.firstIsInstanceOrNull()?.annotations?.toList() } private val acceptedAnnotations: List> by lazy { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/scriptTemplate.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/scriptTemplate.kt new file mode 100644 index 00000000000..1559c04dec6 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/scriptTemplate.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.script + +import java.io.File +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit +import kotlin.reflect.KClass + +const val DEFAULT_SCRIPT_FILE_PATTERN = ".*\\.kts" + +// TODO: remove this file and all the usages after releasing GSK 1.0 + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.templatesScriptTemplateDefinition instead", + replaceWith = ReplaceWith("kotlin.script.templates.ScriptTemplateDefinition")) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class ScriptTemplateDefinition(val resolver: KClass, + val scriptFilePattern: String = DEFAULT_SCRIPT_FILE_PATTERN) + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.extensions.SamWithReceiverAnnotations instead", + replaceWith = ReplaceWith("kotlin.script.extensions.SamWithReceiverAnnotations")) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class SamWithReceiverAnnotations(vararg val annotations: String) + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.dependencies.ScriptContents instead", + replaceWith = ReplaceWith("kotlin.script.dependencies.ScriptContents")) +interface ScriptContents { + + data class Position(val line: Int, val col: Int) + + val file: File? + val annotations: Iterable + val text: CharSequence? +} + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.dependencies.PseudoFuture instead", + replaceWith = ReplaceWith("kotlin.script.dependencies.PseudoFuture")) +class PseudoFuture(private val value: T): Future { + override fun get(): T = value + override fun get(p0: Long, p1: TimeUnit): T = value + override fun cancel(p0: Boolean): Boolean = false + override fun isDone(): Boolean = true + override fun isCancelled(): Boolean = false +} + +fun KotlinScriptExternalDependencies?.asFuture(): PseudoFuture = PseudoFuture(this) + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.dependencies.ScriptDependenciesResolver instead", + replaceWith = ReplaceWith("kotlin.script.dependencies.ScriptDependenciesResolver")) +interface ScriptDependenciesResolver { + + enum class ReportSeverity { ERROR, WARNING, INFO, DEBUG } + + fun resolve(script: ScriptContents, + environment: Map?, + report: (ReportSeverity, String, ScriptContents.Position?) -> Unit, + previousDependencies: KotlinScriptExternalDependencies? + ): Future = PseudoFuture(null) +} + +@Deprecated("Used only for compatibility with legacy code, use kotlin.script.dependencies.KotlinScriptExternalDependencies instead", + replaceWith = ReplaceWith("kotlin.script.dependencies.KotlinScriptExternalDependencies")) +interface KotlinScriptExternalDependencies : Comparable { + val javaHome: String? get() = null + val classpath: Iterable get() = emptyList() + val imports: Iterable get() = emptyList() + val sources: Iterable get() = emptyList() + val scripts: Iterable get() = emptyList() + + override fun compareTo(other: KotlinScriptExternalDependencies): Int = + compareValues(javaHome, other.javaHome) + .chainCompare { compareIterables(classpath, other.classpath) } + .chainCompare { compareIterables(imports, other.imports) } + .chainCompare { compareIterables(sources, other.sources) } + .chainCompare { compareIterables(scripts, other.scripts) } +} + +private fun> compareIterables(a: Iterable, b: Iterable): Int { + val ia = a.iterator() + val ib = b.iterator() + while (true) { + if (ia.hasNext() && !ib.hasNext()) return 1 + if (!ia.hasNext() && !ib.hasNext()) return 0 + if (!ia.hasNext()) return -1 + val compRes = compareValues(ia.next(), ib.next()) + if (compRes != 0) return compRes + } +} + +private inline fun Int.chainCompare(compFn: () -> Int ): Int = if (this != 0) this else compFn() + +class LegacyScriptDependenciesResolverWrapper(val legacyResolver: ScriptDependenciesResolver) : kotlin.script.dependencies.ScriptDependenciesResolver { + + override fun resolve(script: kotlin.script.dependencies.ScriptContents, + environment: Map?, + report: (kotlin.script.dependencies.ScriptDependenciesResolver.ReportSeverity, String, kotlin.script.dependencies.ScriptContents.Position?) -> Unit, + previousDependencies: kotlin.script.dependencies.KotlinScriptExternalDependencies? + ): Future { + val legacyDeps = legacyResolver.resolve( + object : ScriptContents { + override val file: File? get() = script.file + override val annotations: Iterable get() = script.annotations + override val text: CharSequence? get() = script.text + }, + environment, + { sev, msg, pos -> report(kotlin.script.dependencies.ScriptDependenciesResolver.ReportSeverity.values()[sev.ordinal], + msg, + pos?.let { kotlin.script.dependencies.ScriptContents.Position(it.line, it.col) }) }, + previousDependencies?.let { + object : KotlinScriptExternalDependencies { + override val javaHome get() = it.javaHome + override val classpath get() = it.classpath + override val imports get() = it.imports + override val sources get() = it.sources + override val scripts get() = it.scripts + } + } + ).get() + return kotlin.script.dependencies.PseudoFuture(legacyDeps?.let { + object : kotlin.script.dependencies.KotlinScriptExternalDependencies { + override val javaHome get() = it.javaHome + override val classpath get() = it.classpath + override val imports get() = it.imports + override val sources get() = it.sources + override val scripts get() = it.scripts + } + }) + } +} \ No newline at end of file