Refactor script dependnecies template and resolver interfaces:

- get rid of obsolete interfaces and annotations
- rename actual resolver interface to ScriptDependenciesResolver
- make ScriptDependenciesResolverEx interface compatible with the one from 1.1-M01 to be able to support both on the provider side
- add possibility to override file patter in the script template provider
- construct resolver lazily, if possible
This commit is contained in:
Ilya Chernikov
2016-08-01 13:48:18 +02:00
parent 8024367915
commit df64736192
4 changed files with 59 additions and 80 deletions
@@ -31,24 +31,25 @@ import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File import java.io.File
import java.util.concurrent.Future
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.memberFunctions import kotlin.reflect.memberFunctions
import kotlin.reflect.primaryConstructor import kotlin.reflect.primaryConstructor
data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>,
val resolver: ScriptDependenciesResolverEx? = null, val resolver: ScriptDependenciesResolver? = null,
val scriptFilePattern: String? = null,
val environment: Map<String, Any?>? = null val environment: Map<String, Any?>? = null
) : KotlinScriptDefinition { ) : KotlinScriptDefinition {
private val log = Logger.getInstance(KotlinScriptDefinitionFromTemplate::class.java)
// TODO: remove this and simplify definitionAnnotation as soon as deprecated annotations will be removed // TODO: remove this and simplify definitionAnnotation as soon as deprecated annotations will be removed
internal class ScriptTemplateDefinitionData(val resolverClass: KClass<out ScriptDependenciesResolverEx>, internal class ScriptTemplateDefinitionData(val resolverClass: KClass<out ScriptDependenciesResolver>,
val resolver: ScriptDependenciesResolverEx?, makeResolver: () -> ScriptDependenciesResolver?,
val scriptFilePattern: String?) { val scriptFilePattern: String?) {
val resolver: ScriptDependenciesResolver? by lazy(makeResolver)
val acceptedAnnotations: List<KClass<out Annotation>> by lazy { val acceptedAnnotations: List<KClass<out Annotation>> by lazy {
val resolveMethod = ScriptDependenciesResolverEx::resolve val resolveMethod = ScriptDependenciesResolver::resolve
val resolverMethodAnnotations = val resolverMethodAnnotations =
resolverClass.memberFunctions.find { resolverClass.memberFunctions.find {
it.name == resolveMethod.name && it.name == resolveMethod.name &&
@@ -64,33 +65,14 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>,
} }
} }
internal class ObsoleteResolverProxy(val resolverAnn: ScriptDependencyResolver?) : ScriptDependenciesResolverEx {
private val resolver by lazy { resolverAnn?.resolver?.primaryConstructor?.call() }
override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?,
report: (ScriptDependenciesResolverEx.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?> =
resolver?.resolve(
environment?.get("projectRoot") as? File?,
script.file,
emptyList(),
environment as Any?
)
.asFuture()
}
private val definitionData by lazy { private val definitionData by lazy {
val defAnn = template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>() val defAnn = template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>()
val obsoleteResolverAnn = template.annotations.firstIsInstanceOrNull<ScriptDependencyResolver>() val filePattern = scriptFilePattern ?: defAnn?.scriptFilePattern ?: DEFAULT_SCRIPT_FILE_PATTERN
val filePattern = defAnn?.scriptFilePattern ?:
template.annotations.firstIsInstanceOrNull<ScriptFilePattern>()?.pattern ?:
DEFAULT_SCRIPT_FILE_PATTERN
when { when {
resolver != null -> ScriptTemplateDefinitionData(resolver.javaClass.kotlin, resolver, filePattern) resolver != null -> ScriptTemplateDefinitionData(resolver.javaClass.kotlin, { resolver }, filePattern)
defAnn != null -> ScriptTemplateDefinitionData(defAnn.resolver, defAnn.resolver.primaryConstructor?.call(), filePattern) // TODO: logScriptDefMessage missing or invalid constructor
obsoleteResolverAnn != null -> ScriptTemplateDefinitionData(ObsoleteResolverProxy::class, ObsoleteResolverProxy(obsoleteResolverAnn), filePattern) defAnn != null -> ScriptTemplateDefinitionData(defAnn.resolver, { defAnn.resolver.primaryConstructor?.call() }, filePattern)
else -> ScriptTemplateDefinitionData(BasicScriptDependenciesResolver::class, BasicScriptDependenciesResolver(), filePattern) else -> ScriptTemplateDefinitionData(BasicScriptDependenciesResolver::class, ::BasicScriptDependenciesResolver, filePattern)
} }
} }
@@ -111,30 +93,22 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>,
// TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern // TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT) override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
override fun <TF> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? { override fun <TF> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? {
val script = BasicScriptContents(file) { val script = BasicScriptContents(file, getAnnotations = {
val classLoader = (template as Any).javaClass.classLoader val classLoader = (template as Any).javaClass.classLoader
getAnnotationEntries(file, project) getAnnotationEntries(file, project)
.mapNotNull { psiAnn -> .mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving // TODO: consider advanced matching using semantic similar to actual resolving
definitionData.acceptedAnnotations.find { ann -> definitionData.acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName } psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let { KtAnnotationWrapper(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) } }?.let { KtAnnotationWrapper(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
} }
.map { it.getProxy(classLoader) } .map { it.getProxy(classLoader) }
} })
val reportFn = { reportSeverity: ScriptDependenciesResolverEx.ReportSeverity, s: String, position: ScriptContents.Position? -> val fileDeps = definitionData.resolver?.resolve(script, environment, ::logScriptDefMessage, previousDependencies)
val msg = (position?.run { "[at $line:$col]" } ?: "") + s // TODO: use it as a Future
when (reportSeverity) {
ScriptDependenciesResolverEx.ReportSeverity.ERROR -> log.error(msg)
ScriptDependenciesResolverEx.ReportSeverity.WARNING -> log.warn(msg)
ScriptDependenciesResolverEx.ReportSeverity.INFO -> log.info(msg)
ScriptDependenciesResolverEx.ReportSeverity.DEBUG -> log.debug(msg)
}
}
val fileDeps = definitionData.resolver?.resolve(script, environment, reportFn, previousDependencies)
return fileDeps?.get() return fileDeps?.get()
} }
@@ -164,4 +138,18 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>,
override val annotations: Iterable<Annotation> by lazy { getAnnotations() } override val annotations: Iterable<Annotation> by lazy { getAnnotations() }
override val text: CharSequence? by lazy { getFileContents(myFile) } override val text: CharSequence? by lazy { getFileContents(myFile) }
} }
}
companion object {
internal val log = Logger.getInstance(KotlinScriptDefinitionFromTemplate::class.java)
}
}
internal fun logScriptDefMessage(reportSeverity: ScriptDependenciesResolver.ReportSeverity, s: String, position: ScriptContents.Position?): Unit {
val msg = (position?.run { "[at $line:$col]" } ?: "") + s
when (reportSeverity) {
ScriptDependenciesResolver.ReportSeverity.ERROR -> KotlinScriptDefinitionFromTemplate.log.error(msg)
ScriptDependenciesResolver.ReportSeverity.WARNING -> KotlinScriptDefinitionFromTemplate.log.warn(msg)
ScriptDependenciesResolver.ReportSeverity.INFO -> KotlinScriptDefinitionFromTemplate.log.info(msg)
ScriptDependenciesResolver.ReportSeverity.DEBUG -> KotlinScriptDefinitionFromTemplate.log.debug(msg)
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.script package org.jetbrains.kotlin.script
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import java.io.File import java.io.File
import java.util.concurrent.Future import java.util.concurrent.Future
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -28,19 +27,9 @@ const val DEFAULT_SCRIPT_FILE_PATTERN = ".*\\.kts"
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class ScriptTemplateDefinition(val resolver: KClass<out ScriptDependenciesResolverEx> = BasicScriptDependenciesResolver::class, annotation class ScriptTemplateDefinition(val resolver: KClass<out ScriptDependenciesResolver> = BasicScriptDependenciesResolver::class,
val scriptFilePattern: String = DEFAULT_SCRIPT_FILE_PATTERN) val scriptFilePattern: String = DEFAULT_SCRIPT_FILE_PATTERN)
@Deprecated("Use ScriptTemplateDefinition")
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ScriptFilePattern(val pattern: String)
@Deprecated("Use ScriptTemplateDefinition")
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ScriptDependencyResolver(val resolver: KClass<out ScriptDependenciesResolver>)
interface ScriptContents { interface ScriptContents {
data class Position(val line: Int, val col: Int) data class Position(val line: Int, val col: Int)
@@ -60,8 +49,7 @@ class PseudoFuture<T>(private val value: T): Future<T> {
fun KotlinScriptExternalDependencies?.asFuture(): PseudoFuture<KotlinScriptExternalDependencies?> = PseudoFuture(this) fun KotlinScriptExternalDependencies?.asFuture(): PseudoFuture<KotlinScriptExternalDependencies?> = PseudoFuture(this)
// TODO: rename to just ScriptDependenciesResolver as soon as current deprecated one will be dropped interface ScriptDependenciesResolver {
interface ScriptDependenciesResolverEx {
enum class ReportSeverity { ERROR, WARNING, INFO, DEBUG } enum class ReportSeverity { ERROR, WARNING, INFO, DEBUG }
@@ -72,16 +60,17 @@ interface ScriptDependenciesResolverEx {
): Future<KotlinScriptExternalDependencies?> = PseudoFuture(null) ): Future<KotlinScriptExternalDependencies?> = PseudoFuture(null)
} }
@Deprecated("Use new ScriptDependenciesResolverEx") @Suppress("unused") // used in gradle-script-kotlin for a moment
interface ScriptDependenciesResolver { @Deprecated("Use new ScriptDependenciesResolver, this one is left for temporary compatibility of new resolvers to kotlin plugin 1.1-M01",
fun resolve(projectRoot: File?, ReplaceWith("ScriptDependenciesResolver"))
scriptFile: File?, interface ScriptDependenciesResolverEx {
annotations: Iterable<KtAnnotationEntry>, fun resolve(script: ScriptContents,
context: Any? environment: Map<String, Any?>?,
previousDependencies: KotlinScriptExternalDependencies?
): KotlinScriptExternalDependencies? = null ): KotlinScriptExternalDependencies? = null
} }
class BasicScriptDependenciesResolver : ScriptDependenciesResolverEx class BasicScriptDependenciesResolver : ScriptDependenciesResolver
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@@ -32,7 +32,9 @@ interface ScriptTemplateProvider {
val templateClassName: String val templateClassName: String
val resolver: ScriptDependenciesResolverEx? get() = null val resolver: ScriptDependenciesResolver? get() = null
val filePattern: String? get() = null
val dependenciesClasspath: Iterable<String> val dependenciesClasspath: Iterable<String>
@@ -58,7 +60,7 @@ fun makeScriptDefsFromTemplateProviders(providers: Iterable<ScriptTemplateProvid
val loader = URLClassLoader(provider.dependenciesClasspath.map { File(it).toURI().toURL() }.toTypedArray(), ScriptTemplateProvider::class.java.classLoader) val loader = URLClassLoader(provider.dependenciesClasspath.map { File(it).toURI().toURL() }.toTypedArray(), ScriptTemplateProvider::class.java.classLoader)
val cl = loader.loadClass(provider.templateClassName) val cl = loader.loadClass(provider.templateClassName)
idToVersion.put(provider.id, provider.version) idToVersion.put(provider.id, provider.version)
KotlinScriptDefinitionFromTemplate(cl.kotlin, provider.resolver, provider.environment) KotlinScriptDefinitionFromTemplate(cl.kotlin, provider.resolver, provider.filePattern, provider.environment)
} }
catch (ex: Exception) { catch (ex: Exception) {
errorsHandler(provider, ex) errorsHandler(provider, ex)
@@ -82,7 +82,7 @@ class ScriptTest2 {
environment: Map<String, Any?>? = null, environment: Map<String, Any?>? = null,
runIsolated: Boolean = true, runIsolated: Boolean = true,
suppressOutput: Boolean = false): Class<*>? = suppressOutput: Boolean = false): Class<*>? =
compileScriptImpl("compiler/testData/script/" + scriptPath, KotlinScriptDefinitionFromTemplate(scriptBase, null, environment), runIsolated, suppressOutput) compileScriptImpl("compiler/testData/script/" + scriptPath, KotlinScriptDefinitionFromTemplate(scriptBase, null, null, environment), runIsolated, suppressOutput)
private fun compileScriptImpl( private fun compileScriptImpl(
scriptPath: String, scriptPath: String,
@@ -126,14 +126,14 @@ class ScriptTest2 {
} }
} }
class TestKotlinScriptDependenciesResolver : ScriptDependenciesResolverEx { class TestKotlinScriptDependenciesResolver : ScriptDependenciesResolver {
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() } private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class) @AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(script: ScriptContents, override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?, environment: Map<String, Any?>?,
report: (ScriptDependenciesResolverEx.ReportSeverity, String, ScriptContents.Position?) -> Unit, report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies? previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?> ): Future<KotlinScriptExternalDependencies?>
{ {