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

main.name.remal.gradle_plugins.plugins.java.JavaApplicationSettingsPlugin.kt Maven / Gradle / Ivy

There is a newer version: 1.9.2
Show newest version
package name.remal.gradle_plugins.plugins.java

import name.remal.ASM_API
import name.remal.gradle_plugins.dsl.ApplyPluginClasses
import name.remal.gradle_plugins.dsl.BaseReflectiveProjectPlugin
import name.remal.gradle_plugins.dsl.Plugin
import name.remal.gradle_plugins.dsl.PluginAction
import name.remal.gradle_plugins.dsl.WithPlugins
import name.remal.gradle_plugins.dsl.extensions.doSetupIf
import name.remal.gradle_plugins.dsl.extensions.get
import name.remal.gradle_plugins.dsl.extensions.main
import name.remal.gradle_plugins.dsl.extensions.visitFiles
import name.remal.gradle_plugins.plugins.common.CommonSettingsPlugin
import org.gradle.api.Task
import org.gradle.api.plugins.ApplicationPlugin.TASK_RUN_NAME
import org.gradle.api.plugins.ApplicationPluginConvention
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskCollection
import org.gradle.api.tasks.TaskContainer
import org.gradle.jvm.application.tasks.CreateStartScripts
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.MethodVisitor
import org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.objectweb.asm.Opcodes.ACC_STATIC
import org.objectweb.asm.Type.VOID_TYPE
import org.objectweb.asm.Type.getMethodDescriptor
import org.objectweb.asm.Type.getType

@Plugin(
    id = "name.remal.java-application-settings",
    description = "Plugin that configures 'application' plugin if it's applied",
    tags = ["java", "application"]
)
@WithPlugins(JavaApplicationPluginId::class)
@ApplyPluginClasses(CommonSettingsPlugin::class)
class JavaApplicationSettingsPlugin : BaseReflectiveProjectPlugin() {

    @PluginAction
    fun ExtensionContainer.`Setup mainClassName`(sourceSets: SourceSetContainer, tasks: TaskContainer) {
        val settings = this[ApplicationPluginConvention::class.java]
        val sourceSet = sourceSets.main

        tasks.applicationTasks.all { task ->
            tasks.findByName(sourceSet.classesTaskName)?.let { task.dependsOn(it) }
            task.doSetupIf(Int.MIN_VALUE, { settings.mainClassName.isNullOrEmpty() }) { _ ->
                val mainClasses = mutableSetOf()
                sourceSet.output.asFileTree.matching { it.include("**/*.class") }.visitFiles { details ->
                    try {
                        details.open().use { inputStream ->
                            val classReader = ClassReader(inputStream)
                            var isMainClass = false
                            classReader.accept(object : ClassVisitor(ASM_API) {
                                override fun visitMethod(access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array?): MethodVisitor? {
                                    if (name == "main"
                                        && (access and ACC_PUBLIC != 0)
                                        && (access and ACC_STATIC != 0)
                                        && descriptor == getMethodDescriptor(VOID_TYPE, getType(Array::class.java))
                                    ) {
                                        isMainClass = true
                                    }
                                    return null
                                }
                            }, SKIP_CODE or SKIP_DEBUG or SKIP_FRAMES)
                            if (isMainClass) {
                                mainClasses.add(classReader.className.replace('/', '.'))
                            }
                        }
                    } catch (e: Exception) {
                        logger.warn("Can't parse class file: ${details.path}: $e", e)
                    }
                }

                if (mainClasses.isEmpty()) {
                    // do nothing
                } else if (mainClasses.size == 1) {
                    if (settings.mainClassName.isNullOrEmpty()) {
                        settings.mainClassName = mainClasses.single()
                    }
                } else {
                    logger.warn("There are several main classes in {} source-set. You have to set mainClassName explicitly.", sourceSet.name)
                }
            }
        }
    }


    private val TaskContainer.applicationTasks: TaskCollection
        get() = matching {
            it is CreateStartScripts
                || (it.name == TASK_RUN_NAME && it is JavaExec)
        }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy