All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
name.remal.gradle_plugins.plugins.code_quality.findbugs.BaseFindBugsSettingsPlugin.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.code_quality.findbugs
import com.google.common.reflect.TypeToken
import name.remal.*
import name.remal.gradle_plugins.dsl.BaseReflectiveProjectPlugin
import name.remal.gradle_plugins.dsl.CreateExtensionsPluginAction
import name.remal.gradle_plugins.dsl.PluginAction
import name.remal.gradle_plugins.dsl.WithPluginClasses
import name.remal.gradle_plugins.dsl.extensions.*
import name.remal.gradle_plugins.dsl.utils.ProjectAware
import name.remal.gradle_plugins.dsl.utils.XML_PRETTY_OUTPUTTER
import name.remal.gradle_plugins.plugins.classes_relocation.ClassesRelocationPlugin
import name.remal.gradle_plugins.plugins.classes_relocation.relocatedClassesJavaPackageName
import name.remal.gradle_plugins.plugins.code_quality.ExcludesExtension
import name.remal.gradle_plugins.plugins.code_quality.findbugs.extensions.FindBugsSubplugin
import name.remal.gradle_plugins.plugins.code_quality.setupQualityTaskReporters
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesExtension
import name.remal.gradle_plugins.plugins.dependencies.TransitiveDependenciesPlugin
import name.remal.version.Version
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.quality.CodeQualityExtension
import org.gradle.api.reporting.Reporting
import org.gradle.api.tasks.TaskContainer
import org.gradle.initialization.DefaultSettings.DEFAULT_BUILD_SRC_DIR
import org.jdom2.Document
import org.jdom2.Element
import org.jdom2.input.SAXBuilder
import java.io.File
import java.lang.reflect.ParameterizedType
import kotlin.collections.forEach
abstract class BaseFindBugsSettingsPlugin(
private val objectFactory: ObjectFactory
) : BaseReflectiveProjectPlugin() {
companion object {
@JvmStatic
protected val FINDBUGS_EXCLUDE_RESOURCE_URL = BaseFindBugsSettingsPlugin::class.java.getRequiredResource("exclude.xml")
private fun newSAXBuilder() = SAXBuilder().apply {
setNoValidatingXMLReaderFactory()
setNoOpEntityResolver()
}
}
protected lateinit var project: Project
@PluginAction(order = Int.MIN_VALUE, isHidden = true)
private fun populateProject(project: Project) {
this.project = project
}
protected abstract val toolName: String
protected open val extensionName: String get() = toolName.toLowerCase()
protected open val toolConfigurationName: String get() = extensionName
protected open val pluginsConfigurationName: String get() = "${extensionName}Plugins"
protected abstract val toolLatestVersion: String
protected abstract val toolArtifactGroup: String
protected abstract val toolArtifactIds: Set
protected abstract var TaskType.excludeFilterFile: File?
protected abstract val TaskType.classesDirs: FileCollection
protected val extensionType: Class = run {
val type = TypeToken.of(this.javaClass).getSupertype(BaseFindBugsSettingsPlugin::class.java).type
if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
return@run type.actualTypeArguments[0].asClass().uncheckedCast>()
}
protected val taskType: Class = run {
val type = TypeToken.of(this.javaClass).getSupertype(BaseFindBugsSettingsPlugin::class.java).type
if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
return@run type.actualTypeArguments[1].asClass().uncheckedCast>()
}
@CreateExtensionsPluginAction
fun ExtensionContainer.`Create 'excludes' extension for tool extension`() {
toolExtension.convention.add(
"excludes",
objectFactory.newInstance(ExcludesExtension::class.java)
)
}
@CreateExtensionsPluginAction
fun TaskContainer.`Create 'excludes' extension for each task`() {
all(taskType) { task ->
task.extensions.add(
"excludes",
objectFactory.newInstance(ExcludesExtension::class.java)
)
}
}
@PluginAction
@WithPluginClasses(ClassesRelocationPlugin::class)
fun ExtensionContainer.`Don't check relocated classes`(project: Project) {
globalExcludes.className(project.relocatedClassesJavaPackageName + ".*")
}
@PluginAction
fun ExtensionContainer.`Update tool version`() {
val toolExtension = this.toolExtension
val toolVersionStr = toolExtension.toolVersion
if (toolVersionStr.isNullOrEmpty()) {
toolExtension.toolVersion = toolLatestVersion
} else {
val toolVersion = Version.parseOrNull(toolVersionStr)
val buildVersion = Version.parseOrNull(toolLatestVersion)
if (toolVersion != null && buildVersion != null) {
if (toolVersion < buildVersion) {
toolExtension.toolVersion = buildVersion.toString()
}
}
}
}
@PluginAction
fun ConfigurationContainer.`Force tool dependencies to have the same version as toolVersion`(extensions: ExtensionContainer, dependencyHandler: DependencyHandler) {
val toolExtension = extensions.toolExtension
all {
it.resolutionStrategy {
it.eachDependency { dep ->
val toolVersion = toolExtension.toolVersion.nullIfEmpty() ?: return@eachDependency
with(dep.target) {
if (group == toolArtifactGroup && name in toolArtifactIds) {
if (version.isNullOrEmpty()) {
dep.useVersion(toolVersion)
}
}
}
}
}
it.beforeResolve { conf ->
val toolVersion = toolExtension.toolVersion.nullIfEmpty() ?: return@beforeResolve
val usedVersions = mutableMapOf>()
val dependenciesToForce = mutableListOf()
val forcedDependencies = mutableListOf()
conf.dependencies.forEach { dep ->
if (dep !is ExternalModuleDependency) return@forEach
if (dep.group != toolArtifactGroup || dep.name !in toolArtifactIds) return@forEach
val depKey = "${dep.group}:${dep.name}"
usedVersions.computeIfAbsent(depKey, { mutableSetOf() }).add(dep.version.default())
if (dep.isForce) {
forcedDependencies.add(depKey)
} else if (!dep.versionConstraint.preferredVersion.isNullOrEmpty()) {
forcedDependencies.add(depKey)
usedVersions.computeIfAbsent(depKey, { mutableSetOf() }).add(dep.versionConstraint.preferredVersion)
} else {
dependenciesToForce.add(depKey)
}
}
dependenciesToForce.removeAll(forcedDependencies)
dependenciesToForce.forEach { depKey ->
if (usedVersions[depKey]?.all { it == toolVersion } == true) return@forEach
conf.dependencies.add(dependencyHandler.create("$depKey:$toolVersion") {
it.isForce = true
})
}
}
}
}
@PluginAction
fun TaskContainer.`No-source if no classes exist`() {
all(taskType) {
it.noSourceIf { it.classesDirs.isEmpty }
}
}
@PluginAction
fun TaskContainer.`Setup task default filters`(project: Project) {
all(taskType) {
it.doSetup { task ->
if (task.excludeFilterFile == null) {
var rootDir: File = project.rootDir
if (rootDir.name == DEFAULT_BUILD_SRC_DIR) {
rootDir = rootDir.parentFile
}
val configFile = File(rootDir, "gradle/$extensionName/exclude.xml")
if (!configFile.isFile) {
synchronized(BaseFindBugsSettingsPlugin::class.java) {
if (!configFile.isFile) {
configFile.forceDeleteRecursively().createParentDirectories()
FINDBUGS_EXCLUDE_RESOURCE_URL.openStream().use { inputStream ->
configFile.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
}
}
}
task.excludeFilterFile = configFile
}
}
}
}
@PluginAction
@Suppress("ComplexMethod", "LongMethod")
fun TaskContainer.`Apply exclude rules`(extensions: ExtensionContainer) {
val globalExcludes = extensions.globalExcludes
all(taskType) {
it.doSetup(Int.MAX_VALUE) { task ->
val taskExcludes = task.taskExcludes
val classNames = sequenceOf(globalExcludes.classNames, taskExcludes.classNames).flatten().toSortedSet()
val sourcePaths = sequenceOf(globalExcludes.sources, taskExcludes.sources).flatten().toSortedSet()
val messages = sequenceOf(globalExcludes.messages, taskExcludes.messages).flatten().toSortedSet()
if (classNames.isEmpty() && sourcePaths.isEmpty() && messages.isEmpty()) return@doSetup
val newExcludeXmlFile = newTempFile("$extensionName-exclude-", ".xml")
task.doFirst {
val document = task.excludeFilterFile?.let { newSAXBuilder().build(it).clearNamespaces() }
?: Document().setRootElement(Element("FindBugsFilter"))
if (classNames.isNotEmpty()) {
document.rootElement.addContent(
Element("Match").addContent(
Element("Or").also { element ->
classNames.forEach { className ->
element.addContent(
Element("Class").setAttribute(
"name",
if (className.contains('*')) {
"~" + className.splitToSequence('*')
.map(::escapeRegex)
.joinToString(".*")
} else {
className
}
)
)
}
}
)
)
}
if (sourcePaths.isNotEmpty()) {
document.rootElement.addContent(
Element("Match").addContent(
Element("Or").also { element ->
sourcePaths.forEach { sourcePath ->
element.addContent(
Element("Source").setAttribute(
"name",
if (sourcePath.contains('*')) {
"~" + sourcePath.splitToSequence('*')
.map(::escapeRegex)
.joinToString(".*")
} else {
sourcePath
}
)
)
}
}
)
)
}
if (messages.isNotEmpty()) {
document.rootElement.addContent(
Element("Match").addContent(
Element("Or").also { element ->
messages.forEach { message ->
element.addContent(
Element("Bug").setAttribute(
"name",
if (message.contains('*')) {
"~" + message.splitToSequence('*')
.map(::escapeRegex)
.joinToString(".*")
} else {
message
}
)
)
}
}
)
)
}
newExcludeXmlFile.outputStream().use { outputStream ->
XML_PRETTY_OUTPUTTER.output(document, outputStream)
}
task.excludeFilterFile = newExcludeXmlFile
}
task.doLast {
newExcludeXmlFile.delete()
}
}
}
}
@PluginAction
fun TaskContainer.`Setup task reports`() {
all(taskType) {
setupQualityTaskReporters(it)
it.doSetup { task ->
task as Reporting<*>
task.reports.forEach { report ->
if ("xml" == report.name) {
report.isEnabled = true
try {
report.javaClass.findMethod("setWithMessages", Boolean::class.java)
?.apply { isAccessible = true }
?.invoke(report, true)
} catch (e: Exception) {
logger.debug(e)
}
} else {
report.isEnabled = false
}
}
}
}
}
@PluginAction
fun ExtensionContainer.`Add plugin extensions to the plugin extension`(dependencyHandler: DependencyHandler, project: Project) {
val toolExtension = this.toolExtension
loadServices(FindBugsSubplugin::class.java).toSortedSet().forEach { findBugsSubplugin ->
if (findBugsSubplugin is ProjectAware) {
findBugsSubplugin.project = project
}
toolExtension.conventionWithSelf.addExtensionMethod(findBugsSubplugin.extensionName) {
dependencyHandler.add(pluginsConfigurationName, findBugsSubplugin.dependencyNotation)
}
}
}
@PluginAction
fun TaskContainer.`Create plugins help task`() {
create("${extensionName}PluginsHelp", DisplayFindBugsPluginsHelp::class.java) {
it.toolName = [email protected]
}
}
@PluginAction
fun ConfigurationContainer.`Exclude tool from plugins configuration`() {
this[pluginsConfigurationName].apply {
exclude("com.google.code.findbugs", "findbugs")
exclude("com.google.code.findbugs", "annotations")
exclude("com.google.code.findbugs", "jsr305")
exclude("com.google.spotbugs", "spotbugs")
exclude("com.google.spotbugs", "spotbugs-annotations")
exclude("com.google.spotbugs", "annotations")
}
}
@PluginAction
@WithPluginClasses(TransitiveDependenciesPlugin::class)
fun ExtensionContainer.`Register tool configurations in 'transitiveDependencies' extension`() {
this[TransitiveDependenciesExtension::class.java].addConfigurationToProcess(
toolConfigurationName,
pluginsConfigurationName
)
}
protected val ExtensionContainer.toolExtension: ExtensionType get() = this[extensionType]
protected val ExtensionContainer.globalExcludes: ExcludesExtension get() = toolExtension.convention[ExcludesExtension::class.java]
protected val TaskType.taskExcludes: ExcludesExtension get() = this[ExcludesExtension::class.java]
}