All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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