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

main.name.remal.gradle_plugins.plugins.code_quality.BaseCodeQualityReflectiveProjectPlugin.kt Maven / Gradle / Ivy

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

import com.google.common.reflect.TypeToken
import name.remal.KotlinAllOpen
import name.remal.asClass
import name.remal.default
import name.remal.gradle_plugins.dsl.ApplyPluginClasses
import name.remal.gradle_plugins.dsl.BaseReflectiveProjectPlugin
import name.remal.gradle_plugins.dsl.CreateConfigurationsPluginAction
import name.remal.gradle_plugins.dsl.CreateExtensionsPluginAction
import name.remal.gradle_plugins.dsl.HighestPriorityPluginAction
import name.remal.gradle_plugins.dsl.LowestPriorityPluginAction
import name.remal.gradle_plugins.dsl.WithPlugins
import name.remal.gradle_plugins.dsl.extensions.ObjectMarker
import name.remal.gradle_plugins.dsl.extensions.addObjectMarker
import name.remal.gradle_plugins.dsl.extensions.all
import name.remal.gradle_plugins.dsl.extensions.applyGradleTransitiveDependenciesExcludes
import name.remal.gradle_plugins.dsl.extensions.createExtensionWithInjectedParams
import name.remal.gradle_plugins.dsl.extensions.defaultPropertyValue
import name.remal.gradle_plugins.dsl.extensions.dependsOn
import name.remal.gradle_plugins.dsl.extensions.disableTransitiveDependencyResolutionRules
import name.remal.gradle_plugins.dsl.extensions.get
import name.remal.gradle_plugins.dsl.extensions.getOrNull
import name.remal.gradle_plugins.dsl.extensions.hasObjectMarker
import name.remal.gradle_plugins.dsl.extensions.notation
import name.remal.gradle_plugins.dsl.extensions.registerCompatible
import name.remal.gradle_plugins.dsl.extensions.reportsBaseDirName
import name.remal.gradle_plugins.plugins.java.JavaPluginId
import name.remal.gradle_plugins.test_source_sets.TestSourceSetContainer
import name.remal.nullIfEmpty
import name.remal.uncheckedCast
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.plugins.JavaBasePlugin.CHECK_TASK_NAME
import org.gradle.api.plugins.ReportingBasePlugin
import org.gradle.api.plugins.quality.CodeQualityExtension
import org.gradle.api.reporting.ReportingExtension
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME
import org.gradle.api.tasks.SourceSet.TEST_SOURCE_SET_NAME
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.VerificationTask
import java.lang.reflect.ParameterizedType

@ApplyPluginClasses(
    ReportingBasePlugin::class
)
abstract class BaseCodeQualityReflectiveProjectPlugin : BaseReflectiveProjectPlugin()
    where TaskType : Task, TaskType : VerificationTask {

    protected open val toolName: String get() = taskClass.simpleName.substringBefore("Task")

    protected open val extensionName: String get() = extensionClass.simpleName.substringBefore("Extension").toLowerCase()

    protected open val toolVersion: String get() = "+"

    protected open val reportsBaseDirName: String get() = taskClass.reportsBaseDirName

    protected open val configurationName: String get() = extensionName
    protected open val isToolPluginsConfigurationEnabled: Boolean get() = false

    protected open val taskNameVerb: String? get() = toolName.toLowerCase()
    protected open val taskNameTarget: String? get() = null


    protected open fun Configuration.configureConfigurationExcludes() {
        applyGradleTransitiveDependenciesExcludes()
    }

    protected open fun Configuration.configureConfiguration(extension: ExtensionType, dependencyHandler: DependencyHandler) {}

    protected open val SourceSet.isTest: Boolean
        get() {
            if (name == MAIN_SOURCE_SET_NAME) {
                return false
            }
            if (name == TEST_SOURCE_SET_NAME) {
                return true
            }
            project.getOrNull(TestSourceSetContainer::class.java)?.let {
                if (this in it) {
                    return true
                }
            }
            return false
        }

    protected open fun TaskType.configureTaskForSourceSet(sourceSet: SourceSet, isTest: Boolean) {}


    protected val taskClass: Class = run {
        val type = TypeToken.of(this.javaClass).getSupertype(BaseCodeQualityReflectiveProjectPlugin::class.java).type
        if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
        return@run type.actualTypeArguments[0].asClass().uncheckedCast>()
    }

    protected val extensionClass: Class = run {
        val type = TypeToken.of(this.javaClass).getSupertype(BaseCodeQualityReflectiveProjectPlugin::class.java).type
        if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
        return@run type.actualTypeArguments[1].asClass().uncheckedCast>()
    }


    protected val project: Project get() = _project
    private lateinit var _project: Project

    @HighestPriorityPluginAction(isHidden = false)
    private fun setProjectProperty(project: Project) {
        this._project = project
    }


    @CreateExtensionsPluginAction
    protected fun Project.createExtension() {
        val reporting = this[ReportingExtension::class.java]
        createExtensionWithInjectedParams(extensionName, extensionClass).also {
            it.defaultPropertyValue(CodeQualityExtension::getToolVersion) {
                toolVersion
            }
            it.defaultPropertyValue(CodeQualityExtension::getReportsDir) {
                reporting.baseDir.resolve(reportsBaseDirName)
            }
            it.defaultPropertyValue(CodeQualityExtension::getSourceSets) {
                getOrNull(SourceSetContainer::class.java) ?: mutableListOf()
            }
        }
    }


    private val pluginsConfigurationName get() = configurationName + "Plugins"

    private fun Configuration.handleToolDependency(extension: ExtensionType) {
        extension.resolvedToolVersionRetriever = version@{
            val toolDependency = allDependencies.firstOrNull { it.hasObjectMarker(ToolDependencyMarker::class.java) }
                ?: return@version null
            val toolNotation = toolDependency.notation.withOnlyGroupAndModule()

            return@version resolvedConfiguration
                .lenientConfiguration
                .firstLevelModuleDependencies
                .singleOrNull { it.notation.withOnlyGroupAndModule() == toolNotation }
                ?.moduleVersion
                ?.nullIfEmpty()
        }
    }

    @CreateConfigurationsPluginAction
    protected fun ConfigurationContainer.createConfiguration(project: Project, dependencyHandler: DependencyHandler) {
        create(configurationName) {
            it.description = "The $toolName libraries to be used for this project"
            it.configureConfigurationExcludes()
            it.disableTransitiveDependencyResolutionRules()

            val extension = project[extensionClass]
            it.configureConfiguration(extension, dependencyHandler)
            it.handleToolDependency(extension)
        }

        if (isToolPluginsConfigurationEnabled) {
            create(pluginsConfigurationName) {
                it.description = "The $toolName plugins to be used for this project"
                it.configureConfigurationExcludes()
                it.disableTransitiveDependencyResolutionRules()
            }
        }
    }


    @LowestPriorityPluginAction
    protected fun TaskContainer.setupDefaults(project: Project) {
        val extension = project[extensionClass]
        all(taskClass) { task ->
            task.defaultPropertyValue(VerificationTask::getIgnoreFailures) { extension.isIgnoreFailures }
        }
    }


    @LowestPriorityPluginAction
    @WithPlugins(JavaPluginId::class)
    protected fun setupTasksForSourceSets(sourceSets: SourceSetContainer, tasks: TaskContainer, project: Project) {
        sourceSets.all { sourceSet ->
            tasks.registerCompatible(sourceSet.toolTaskName, taskClass) { task ->
                task.dependsOn { listOf(sourceSet.classesTaskName) }
                task.configureTaskForSourceSet(sourceSet, sourceSet.isTest)
            }
        }

        tasks.all(CHECK_TASK_NAME) { checkTask ->
            checkTask.dependsOn {
                project[extensionClass].sourceSets.default().asSequence()
                    .mapNotNull { it.toolTaskName }
                    .mapNotNull(tasks::findByName)
                    .toList()
            }
        }
    }


    @KotlinAllOpen
    private class ToolDependencyMarker : ObjectMarker

    protected fun DependencyHandler.createToolDependency(notation: String): Dependency = create(notation).apply {
        addObjectMarker(ToolDependencyMarker::class.java)
    }


    protected val ConfigurationContainer.toolConfiguration: Configuration get() = getByName(configurationName)
    protected val ConfigurationContainer.toolPluginsConfiguration: Configuration get() = getByName(pluginsConfigurationName)

    protected val SourceSet.toolTaskName: String get() = getTaskName(taskNameVerb, taskNameTarget)

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy