main.name.remal.gradle_plugins.plugins.java.JavaApplicationSettingsPlugin.kt Maven / Gradle / Ivy
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