Support backward compatibility with gradle-script-kotlin 0.2.0, some refactoring

This commit is contained in:
Ilya Chernikov
2016-06-27 09:17:05 -07:00
parent 1690dcd8df
commit 2ca4d635ea
3 changed files with 77 additions and 42 deletions
@@ -37,10 +37,20 @@ import kotlin.reflect.*
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class ScriptTemplateDefinition(val resolver: KClass<out ScriptDependenciesResolver>, annotation class ScriptTemplateDefinition(val resolver: KClass<out AnnotationBasedScriptDependenciesResolver>,
val scriptFilePattern: String) val scriptFilePattern: String)
interface ScriptDependenciesResolver { @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 AnnotationBasedScriptDependenciesResolver {
fun resolve(scriptFile: File?, fun resolve(scriptFile: File?,
annotations: Iterable<Annotation>, annotations: Iterable<Annotation>,
environment: Map<String, Any?>?, environment: Map<String, Any?>?,
@@ -48,13 +58,66 @@ interface ScriptDependenciesResolver {
): KotlinScriptExternalDependencies? = null ): KotlinScriptExternalDependencies? = null
} }
@Deprecated("Use new resolver Ex")
interface ScriptDependenciesResolver {
fun resolve(projectRoot: File?,
scriptFile: File?,
annotations: Iterable<KtAnnotationEntry>,
context: Any?
): KotlinScriptExternalDependencies? = null
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class AcceptedAnnotations(vararg val supportedAnnotationClasses: KClass<out Annotation>) annotation class AcceptedAnnotations(vararg val supportedAnnotationClasses: KClass<out Annotation>)
data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val environment: Map<String, Any?>?) : KotlinScriptDefinition { data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val environment: Map<String, Any?>?) : KotlinScriptDefinition {
private val definitionAnnotation by lazy { template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>() } // TODO: remove this and simplify definitionAnnotation as soon as deprecated annotations will be removed
internal class ScriptTemplateDefinitionData(val resolverClass: KClass<out AnnotationBasedScriptDependenciesResolver>,
val resolver: AnnotationBasedScriptDependenciesResolver?,
val scriptFilePattern: String?) {
val acceptedAnnotations by lazy {
val resolveMethod = AnnotationBasedScriptDependenciesResolver::resolve
val resolverMethodAnnotations =
resolverClass.memberFunctions.find {
it.name == resolveMethod.name &&
sameSignature(it, resolveMethod)
}
?.annotations
?.filterIsInstance<AcceptedAnnotations>()
resolverMethodAnnotations?.flatMap {
val v = it.supportedAnnotationClasses
v.toList() // TODO: inline after KT-9453 is resolved (now it fails with "java.lang.Class cannot be cast to kotlin.reflect.KClass")
}
?: emptyList()
}
}
internal class ObsoleteResolverProxy(val resolverAnn: ScriptDependencyResolver?) : AnnotationBasedScriptDependenciesResolver {
private val resolver by lazy { resolverAnn?.resolver?.primaryConstructor?.call() }
override fun resolve(scriptFile: File?,
annotations: Iterable<Annotation>,
environment: Map<String, Any?>?,
previousDependencies: KotlinScriptExternalDependencies?
): KotlinScriptExternalDependencies? =
resolver?.resolve(
environment?.get("projectRoot") as? File?,
scriptFile,
emptyList(),
environment as Any?
)
}
private val definitionData by lazy {
val defAnn = template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>()
if (defAnn == null) {
val resolverAnn = template.annotations.firstIsInstanceOrNull<ScriptDependencyResolver>()
val filePatternAnn = template.annotations.firstIsInstanceOrNull<ScriptFilePattern>()
ScriptTemplateDefinitionData(ObsoleteResolverProxy::class, ObsoleteResolverProxy(resolverAnn), filePatternAnn?.pattern)
}
else ScriptTemplateDefinitionData(defAnn.resolver, defAnn.resolver.primaryConstructor?.call(), defAnn.scriptFilePattern)
}
override val name = template.simpleName!! override val name = template.simpleName!!
@@ -68,45 +131,17 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val
getScriptParameters(scriptDescriptor).map { it.name } getScriptParameters(scriptDescriptor).map { it.name }
override fun <TF> isScript(file: TF): Boolean = override fun <TF> isScript(file: TF): Boolean =
definitionAnnotation?.let { Regex(it.scriptFilePattern).matches(getFileName(file)) } ?: false definitionData.scriptFilePattern?.let { Regex(it).matches(getFileName(file)) } ?: false
// 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)
private class ResolverWithAcceptedAnnotations(val resolverClass: KClass<out ScriptDependenciesResolver>) {
val resolver by lazy { resolverClass.primaryConstructor!!.call() }
val acceptedAnnotations by lazy {
val resolveMethod = ScriptDependenciesResolver::resolve
val resolverMethodAnnotations =
resolverClass.memberFunctions.find {
it.name == resolveMethod.name &&
sameSignature(it, resolveMethod)
}
?.annotations
?.filterIsInstance<AcceptedAnnotations>()
resolverMethodAnnotations?.flatMap {
val v = it.supportedAnnotationClasses
v.toList() // TODO: inline after KT-9453 is resolved (now it fails with "java.lang.Class cannot be cast to kotlin.reflect.KClass")
}
?: emptyList()
}
}
private val dependenciesResolvers by lazy {
definitionAnnotation?.resolver?.let { listOf(ResolverWithAcceptedAnnotations(it)) } ?: emptyList()
}
private val supportedAnnotationClasses: List<KClass<out Any>> by lazy {
dependenciesResolvers.flatMap { it.acceptedAnnotations }
.distinctBy { it.qualifiedName }
}
override fun <TF> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? { override fun <TF> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? {
val fileAnnotations = getAnnotationEntries(file, project) val fileAnnotations = getAnnotationEntries(file, project)
.map { KtAnnotationWrapper(it) } .map { KtAnnotationWrapper(it) }
.mapNotNull { wrappedAnn -> .mapNotNull { wrappedAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving // TODO: consider advanced matching using semantic similar to actual resolving
supportedAnnotationClasses.find { definitionData.acceptedAnnotations.find {
wrappedAnn.name == it.simpleName || wrappedAnn.name == it.qualifiedName wrappedAnn.name == it.simpleName || wrappedAnn.name == it.qualifiedName
}?.let { it to wrappedAnn } }?.let { it to wrappedAnn }
} }
@@ -120,14 +155,12 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val
annClassToWrapper.first to InvalidScriptResolverAnnotation(annClassToWrapper.second.name, annClassToWrapper.second.valueArguments, ex) annClassToWrapper.first to InvalidScriptResolverAnnotation(annClassToWrapper.second.name, annClassToWrapper.second.valueArguments, ex)
} }
} }
val fileDeps = dependenciesResolvers.mapNotNull { resolverWrapper -> val supportedAnnotations = annotations.mapNotNull { annClassToWrapper ->
val supportedAnnotations = annotations.mapNotNull { annClassToWrapper -> val annFQN = annClassToWrapper.first.qualifiedName
val annFQN = annClassToWrapper.first.qualifiedName if (definitionData.acceptedAnnotations.any { it.qualifiedName == annFQN }) annClassToWrapper.second else null
if (resolverWrapper.acceptedAnnotations.any { it.qualifiedName == annFQN }) annClassToWrapper.second else null
}
resolverWrapper.resolver.resolve(getFile(file), supportedAnnotations, environment, previousDependencies)
} }
return KotlinScriptExternalDependenciesUnion(fileDeps) val fileDeps = definitionData.resolver?.resolve(getFile(file), supportedAnnotations, environment, previousDependencies)
return fileDeps
} }
private fun <TF> getAnnotationEntries(file: TF, project: Project): Iterable<KtAnnotationEntry> = when (file) { private fun <TF> getAnnotationEntries(file: TF, project: Project): Iterable<KtAnnotationEntry> = when (file) {
@@ -118,7 +118,7 @@ class ScriptTest2 {
} }
} }
class TestKotlinScriptDependenciesResolver : ScriptDependenciesResolver { class TestKotlinScriptDependenciesResolver : AnnotationBasedScriptDependenciesResolver {
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() } private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
@@ -35,7 +35,9 @@ class GradleScriptTemplateProvider(project: Project, gim: GradleInstallationMana
gradleLibsPath?.listFiles { file -> file.extension == "jar" && depLibsPrefixes.any { file.name.startsWith(it) } } gradleLibsPath?.listFiles { file -> file.extension == "jar" && depLibsPrefixes.any { file.name.startsWith(it) } }
?.map { it.canonicalPath } ?.map { it.canonicalPath }
?: emptyList() ?: emptyList()
override val environment: Map<String, Any?>? = mapOf("gradleHome" to gradleHome) override val environment: Map<String, Any?>? = mapOf(
"gradleHome" to gradleHome,
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath )?.let { File(it) })
companion object { companion object {
private val depLibsPrefixes = listOf("gradle-script-kotlin", "gradle-core") private val depLibsPrefixes = listOf("gradle-script-kotlin", "gradle-core")