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

There is a newer version: 1.9.2
Show newest version
package name.remal.gradle_plugins.dsl.internal

import name.remal.ASM_API
import name.remal.get
import name.remal.gradle_plugins.api.AutoService
import name.remal.gradle_plugins.api.classes_processing.BytecodeModifier
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessor
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessor.COLLECTION_STAGE
import name.remal.gradle_plugins.api.classes_processing.ClassesProcessorsGradleTaskFactory
import name.remal.gradle_plugins.api.classes_processing.ProcessContext
import name.remal.gradle_plugins.dsl.DESCRIPTION_PLUGIN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.IMPLEMENTATION_CLASS_PLUGIN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.IS_HIDDEN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.MAX_GRADLE_VERSION_PLUGIN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.MIN_GRADLE_VERSION_PLUGIN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.MaxGradleVersion
import name.remal.gradle_plugins.dsl.MinGradleVersion
import name.remal.gradle_plugins.dsl.Plugin
import name.remal.gradle_plugins.dsl.TAGS_PLUGIN_DESCRIPTOR_KEY
import name.remal.gradle_plugins.dsl.extensions.toHasEntries
import name.remal.gradle_plugins.dsl.extensions.unwrapProviders
import name.remal.nullIfEmpty
import name.remal.storeAsString
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.util.GradleVersion
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassReader.SKIP_CODE
import org.objectweb.asm.ClassReader.SKIP_DEBUG
import org.objectweb.asm.ClassReader.SKIP_FRAMES
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.Type.getDescriptor
import org.objectweb.asm.tree.AnnotationNode
import java.util.Properties

class PluginClassesProcessor(minGradleVersionDefault: String?) : ClassesProcessor {

    private val minGradleVersionDefault: GradleVersion? = minGradleVersionDefault?.let(GradleVersion::version)

    companion object {
        private val pluginDesc: String = getDescriptor(Plugin::class.java)
        private val minGradleVersionDesc: String = getDescriptor(MinGradleVersion::class.java)
        private val maxGradleVersionDesc: String = getDescriptor(MaxGradleVersion::class.java)
    }

    @Suppress("ComplexMethod")
    override fun process(bytecode: ByteArray, bytecodeModifier: BytecodeModifier, className: String, resourceName: String, context: ProcessContext) {
        val pluginAnnotationNodes = mutableListOf()
        val minGradleVersionNodes = mutableListOf()
        val maxGradleVersionNodes = mutableListOf()
        ClassReader(bytecode).accept(
            object : ClassVisitor(ASM_API) {
                override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? {
                    if (pluginDesc == desc) return AnnotationNode(desc).also { pluginAnnotationNodes.add(it) }
                    if (minGradleVersionDesc == desc) return AnnotationNode(desc).also { minGradleVersionNodes.add(it) }
                    if (maxGradleVersionDesc == desc) return AnnotationNode(desc).also { maxGradleVersionNodes.add(it) }
                    return null
                }
            },
            SKIP_DEBUG or SKIP_FRAMES or SKIP_CODE
        )

        val pluginAnnotationNode = pluginAnnotationNodes.singleOrNull() ?: return
        val pluginId = pluginAnnotationNode[Plugin::id] ?: return
        context.writeTextResource(
            "META-INF/gradle-plugins/$pluginId.properties",
            Properties().apply {
                setProperty(IMPLEMENTATION_CLASS_PLUGIN_DESCRIPTOR_KEY, className)
                pluginAnnotationNode[Plugin::description].canonize()?.let { setProperty(DESCRIPTION_PLUGIN_DESCRIPTOR_KEY, it) }
                pluginAnnotationNode[Plugin::tags]?.joinToString(";").canonize()?.let { setProperty(TAGS_PLUGIN_DESCRIPTOR_KEY, it) }
                pluginAnnotationNode[Plugin::isHidden]?.let { setProperty(IS_HIDDEN_DESCRIPTOR_KEY, it.toString()) }

                minGradleVersionDefault?.let { setProperty(MIN_GRADLE_VERSION_PLUGIN_DESCRIPTOR_KEY, it.version) }
                minGradleVersionNodes.singleOrNull()
                    ?.let { it[MinGradleVersion::value] }
                    ?.let { extractGradleVersion(it[1]) }
                    ?.let { minGradleVersion ->
                        if (minGradleVersionDefault == null || minGradleVersion > minGradleVersionDefault) {
                            setProperty(MIN_GRADLE_VERSION_PLUGIN_DESCRIPTOR_KEY, minGradleVersion.version)
                        }
                    }
                maxGradleVersionNodes.singleOrNull()
                    ?.let { it[MaxGradleVersion::value] }
                    ?.let { extractGradleVersion(it[1]) }
                    ?.let { maxGradleVersion ->
                        setProperty(MAX_GRADLE_VERSION_PLUGIN_DESCRIPTOR_KEY, maxGradleVersion.version)
                    }
            }.storeAsString()
        )
    }

    override fun getStage() = COLLECTION_STAGE


    private fun String?.canonize() = this?.trim()?.replace("\r", "").nullIfEmpty()

    private fun extractGradleVersion(enumName: String): GradleVersion {
        var firstDigitPos = -1
        for ((i, ch) in enumName.toCharArray().withIndex()) {
            if (ch.isDigit()) {
                firstDigitPos = i
                break
            }
        }

        if (firstDigitPos < 0) throw IllegalArgumentException("Gradle version enum name doesn't have digits")

        return GradleVersion.version(enumName.substring(firstDigitPos).replace('_', '.'))
    }

}

@AutoService
class PluginClassesProcessorFactory : ClassesProcessorsGradleTaskFactory {

    override fun createClassesProcessors(compileTask: AbstractCompile): List {
        if (!compileTask.classpath.toHasEntries().containsClass(Plugin::class.java)) return emptyList()
        return listOf(PluginClassesProcessor(
            compileTask.project.findProperty("gradle.min-version").unwrapProviders()?.toString()
        ))
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy