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