main.name.remal.gradle_plugins.plugins.code_quality.BaseCodeQualityFindBugsResultsTask.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.code_quality
import com.google.common.reflect.TypeToken
import groovy.lang.Closure
import groovy.lang.Closure.DELEGATE_FIRST
import groovy.lang.DelegatesTo
import name.remal.asClass
import name.remal.forceDeleteRecursively
import name.remal.gradle_plugins.dsl.extensions.configureWith
import name.remal.gradle_plugins.dsl.extensions.doAfter
import name.remal.gradle_plugins.dsl.extensions.getOrNull
import name.remal.gradle_plugins.dsl.extensions.include
import name.remal.gradle_plugins.dsl.extensions.setDefaultDestinationForTask
import name.remal.gradle_plugins.dsl.extensions.sourceDirs
import name.remal.gradle_plugins.dsl.extensions.taskReportContainerClass
import name.remal.gradle_plugins.dsl.extensions.unwrapGradleGenerated
import name.remal.gradle_plugins.dsl.utils.code_quality.FindBugsReport
import name.remal.gradle_plugins.dsl.utils.code_quality.createConsoleMessages
import name.remal.gradle_plugins.dsl.utils.code_quality.readFindBugsReportXml
import name.remal.gradle_plugins.dsl.utils.code_quality.writeHtmlTo
import name.remal.gradle_plugins.dsl.utils.code_quality.writeXmlTo
import name.remal.gradle_plugins.dsl.utils.retrieveClassNameFromJavaSource
import name.remal.nullIf
import name.remal.nullIfEmpty
import name.remal.uncheckedCast
import name.remal.version.Version
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.model.ObjectFactory
import org.gradle.api.reporting.Report.OutputType.DIRECTORY
import org.gradle.api.reporting.ReportContainer
import org.gradle.api.reporting.Reporting
import org.gradle.api.reporting.SingleFileReport
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.VerificationTask
import org.gradle.initialization.BuildCancellationToken
import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP
import java.io.File
import java.lang.System.currentTimeMillis
import java.lang.reflect.ParameterizedType
import javax.inject.Inject
abstract class BaseCodeQualityFindBugsResultsTask, ExtensionType : BaseCodeQualityExtension> :
SourceTask(), VerificationTask, Reporting {
companion object {
private const val doSearchSourcesInProjectFiles = false
}
@get:Input
protected abstract val findBugsXmlReportName: String
protected abstract fun doAnalyze(): FindBugsReport
@get:Input
@get:Optional
protected open val htmlReportName: String?
get() = null
@get:Internal
protected open val toolName = this.javaClass.unwrapGradleGenerated().simpleName.substringBefore("Task")
init {
group = VERIFICATION_GROUP
}
@get:Internal
protected val reportsClass: Class = run {
val type = TypeToken.of(this.javaClass).getSupertype(BaseCodeQualityFindBugsResultsTask::class.java).type
if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
return@run type.actualTypeArguments[0].asClass().uncheckedCast>()
}
@get:Internal
protected val extensionClass: Class = run {
val type = TypeToken.of(this.javaClass).getSupertype(BaseCodeQualityFindBugsResultsTask::class.java).type
if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
return@run type.actualTypeArguments[1].asClass().uncheckedCast>()
}
@get:Internal
protected val extension: ExtensionType?
get() = project.getOrNull(extensionClass)
@get:Input
@get:Optional
protected val toolVersion: String?
get() = extension?.let { it.resolvedToolVersion ?: it.toolVersion }
@get:Internal
protected val parsedToolVersion: Version?
get() = toolVersion?.let(Version::parseOrNull)
private var ignoreFailures: Boolean = false
override fun getIgnoreFailures() = ignoreFailures
override fun setIgnoreFailures(ignoreFailures: Boolean) {
this.ignoreFailures = ignoreFailures
}
private val reports: ReportsType = objectFactory.newInstance(reportsClass.taskReportContainerClass, this).apply {
getByName(findBugsXmlReportName).isEnabled = true
}
private val findBugsXmlReport get() = reports.getByName(findBugsXmlReportName)
private val htmlReport get() = htmlReportName?.let(reports::getByName)
@Nested
override fun getReports() = reports
override fun reports(@DelegatesTo(strategy = DELEGATE_FIRST) closure: Closure) = reports.configureWith(closure)
override fun reports(configureAction: Action) = reports.configureWith(configureAction)
init {
onlyIf {
reports.forEach { it.setDefaultDestinationForTask(this) }
return@onlyIf true
}
}
@TaskAction
fun analyze() {
val gradleProject = project
val findBugsReport = doAnalyze().apply {
tool = tool.nullIfEmpty() ?: toolName
version = version.nullIfEmpty()
?: extension?.resolvedToolVersion.nullIfEmpty()
?: extension?.toolVersion.nullIfEmpty()
analysisTimestamp = analysisTimestamp ?: currentTimeMillis()
project {
name(gradleProject.displayName)
sourceDirs.forEach(::srcDir)
}
fun getSourceFile(sourceFile: String): File? {
File(sourceFile).takeIf(File::isAbsolute)?.takeIf(File::isFile)?.let { return it }
source.include(sourceFile).files.let { files ->
if (files.size >= 2) {
logger.warn("Source files tree has more than one '{}' files: {}", sourceFile, files.joinToString(", "))
return null
} else {
files.singleOrNull()?.let { return it }
}
}
if (doSearchSourcesInProjectFiles) {
return [email protected](sourceFile).takeIf(File::isFile)?.let { return it }
} else {
return null
}
}
bugs.forEach forEachBug@{ bug ->
val location = bug.location ?: return@forEachBug
if (location.className != null) return@forEachBug
val sourceFilePath = location.sourceFile ?: return@forEachBug
if (sourceFilePath.endsWith(".java")) {
val sourceFile = getSourceFile(sourceFilePath)
if (sourceFile != null) {
location.className = retrieveClassNameFromJavaSource(
sourceFile = sourceFile,
line = location.startLine,
column = location.startLineOffset
)
}
}
}
}
findBugsReport.writeXmlTo(findBugsXmlReport.destination)
htmlReport.nullIf { !enabled }?.let { report ->
val destination = report.destination
destination.forceDeleteRecursively()
val htmlFile = if (DIRECTORY == report.outputType) destination.resolve("index.html") else destination
findBugsReport.writeHtmlTo(htmlFile, this)
}
didWork = true
if (!getIgnoreFailures() && findBugsReport.bugs.isNotEmpty()) {
throw GradleException(
"%d %s violations were found".format(
findBugsReport.bugs.size,
toolName
)
)
}
}
init {
doAfter {
val xmlReportFile = findBugsXmlReport.destination.nullIf { !isFile } ?: return@doAfter
readFindBugsReportXml(xmlReportFile)
.createConsoleMessages(this)
.forEach { logger.error("\n{}", it) }
}
}
@get:Inject
protected open val objectFactory: ObjectFactory
get() = TODO()
@get:Inject
protected open val buildCancellationToken: BuildCancellationToken
get() = TODO()
protected fun handleExtension(handler: (extension: ExtensionType) -> Unit) {
onlyIf {
extension?.let(handler)
return@onlyIf true
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy