name.remal.gradle_plugins.dsl.internal.PluginClassesProcessor.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.dsl.internal
import name.remal.ASM_API
import name.remal.gradle_plugins.api.AutoService
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessor
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessor.*
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessorsGradleTaskFactory
import name.remal.gradle_plugins.dsl.Plugin
import name.remal.gradle_plugins.dsl.extensions.toHasEntries
import name.remal.mapValues
import name.remal.storeAsString
import name.remal.uncheckedCast
import org.gradle.api.tasks.compile.AbstractCompile
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassReader.*
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.Type
import org.objectweb.asm.tree.AnnotationNode
import java.util.*
val IMPLEMENTATION_CLASS_PLUGIN_DESCRIPTOR_KEY = "implementation-class"
val DISPLAY_NAME_PLUGIN_DESCRIPTOR_KEY = "x-display-name"
val DESCRIPTION_PLUGIN_DESCRIPTOR_KEY = "x-description"
val TAGS_PLUGIN_DESCRIPTOR_KEY = "x-tags"
val IS_HIDDEN_DESCRIPTOR_KEY = "x-is-hidden"
class PluginClassesProcessor : ClassesProcessor {
companion object {
private val pluginDesc: String = Type.getDescriptor(Plugin::class.java)
}
override fun process(bytecode: ByteArray, bytecodeModifier: BytecodeModifier, context: ProcessContext) {
var className: String? = null
val annotationNodes = mutableListOf()
ClassReader(bytecode).accept(
object : ClassVisitor(ASM_API) {
override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) {
className = name?.replace('/', '.')
}
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? {
if (pluginDesc == desc) {
return AnnotationNode(desc).also { annotationNodes.add(it) }
}
return null
}
},
SKIP_DEBUG or SKIP_FRAMES or SKIP_CODE
)
if (null == className) return
val pluginAnnotationValues: Map> = mutableMapOf>().apply {
annotationNodes.filter { pluginDesc == it.desc }.forEach {
it.mapValues.forEach { prop, value ->
val list = computeIfAbsent(prop, { mutableListOf() })
if (value is List<*>) {
list.addAll(value.uncheckedCast())
} else {
list.add(value)
}
}
}
}
pluginAnnotationValues[Plugin::id.name]?.forEach { pluginId ->
context.writeTextResource(
"META-INF/gradle-plugins/$pluginId.properties",
Properties().apply {
put(IMPLEMENTATION_CLASS_PLUGIN_DESCRIPTOR_KEY, className)
pluginAnnotationValues[Plugin::description.name]?.firstOrNull()?.let { put(DESCRIPTION_PLUGIN_DESCRIPTOR_KEY, it.toString()) }
pluginAnnotationValues[Plugin::tags.name]?.toSet()?.let { put(TAGS_PLUGIN_DESCRIPTOR_KEY, it.joinToString(";")) }
pluginAnnotationValues[Plugin::isHidden.name]?.firstOrNull()?.let { put(IS_HIDDEN_DESCRIPTOR_KEY, it.toString()) }
}.storeAsString()
)
}
}
override fun getStage() = COLLECTION_STAGE
}
@AutoService
class PluginClassesProcessorFactory : ClassesProcessorsGradleTaskFactory {
override fun createClassesProcessors(compileTask: AbstractCompile): List {
if (!compileTask.classpath.toHasEntries().containsClass(Plugin::class.java)) return emptyList()
return listOf(PluginClassesProcessor())
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy