name.remal.gradle_plugins.plugins.code_quality.detekt.Detekt.kt Maven / Gradle / Ivy
package name.remal.gradle_plugins.plugins.code_quality.detekt
import name.remal.*
import name.remal.gradle_plugins.dsl.BuildTask
import name.remal.gradle_plugins.dsl.extensions.*
import name.remal.gradle_plugins.dsl.utils.code_quality.FindBugsReport
import name.remal.gradle_plugins.dsl.utils.code_quality.readFindBugsReportXml
import name.remal.gradle_plugins.plugins.code_quality.BaseCodeQualityFindBugsResultsTask
import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.nio.charset.StandardCharsets.UTF_8
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.io.use
@BuildTask
@CacheableTask
class Detekt : BaseCodeQualityFindBugsResultsTask() {
companion object {
private const val MAIN_CLASS = "name.remal.gradle_plugins.plugins.code_quality.detekt.MainKt"
private const val PATHS_SEPARATOR = ";"
private val COLOR_CHARS_REGEX = Regex("\u001B\\[\\d+m")
}
override val findBugsXmlReportName: String get() = DetektReports::findBugsXml.name
override val htmlReportName: String? get() = DetektReports::html.name
@get:Classpath
var detektClasspath: FileCollection = project.emptyFileCollection()
@get:Classpath
@get:Optional
var detektPluginsClasspath: FileCollection = project.emptyFileCollection()
@get:Classpath
@get:Optional
var classpath: FileCollection = project.emptyFileCollection()
@get:Input
@get:Optional
var configFile: File? = null
@get:Input
@get:Optional
var languageVersion: String? = null
@get:Input
@get:Optional
var jvmTarget: String? = null
@get:Input
@get:Optional
var kotlinModuleName: String? = null
init {
doFirst {
tempDir.forceDeleteRecursively()
}
}
private val extensionsJarFile: File by lazy(NONE) {
tempDir.resolve("extensions.jar").also { file ->
Detekt::class.java.getRequiredResourceAsStream("/detekt/detekt-extensions.jar").use { inputStream ->
file.createParentDirectories().outputStream().use { inputStream.copyTo(it) }
}
}
}
private val parentConfigFile: File by lazy(NONE) {
tempDir.resolve("parent-config.yml").apply {
createParentDirectories()
writer(UTF_8).use { writer ->
writer.writeln("build:")
writer.writeln(" maxIssues: -1")
writer.writeln(" failThreshold: -1")
writer.writeln(" warningThreshold: -1")
writer.writeln("")
writer.writeln("output-reports:")
writer.writeln(" active: true")
writer.writeln("")
writer.writeln("processors:")
writer.writeln(" active: false")
writer.writeln("")
writer.writeln("console-reports:")
writer.writeln(" active: false")
}
}
}
private val isKotlinModuleNameSupported: Boolean get() = parsedToolVersion != null && "false".toBoolean()
override fun doAnalyze(): FindBugsReport {
val findBugsXmlFile = reports.findBugsXml.destination.absoluteFile
val args = buildList {
getSource().files.map(File::getAbsolutePath).let {
add("--input")
add(it.joinToString(PATHS_SEPARATOR))
}
detektPluginsClasspath.toList().filter(File::exists).nullIfEmpty()?.map(File::getAbsolutePath)?.let {
add("--plugins")
add(it.joinToString(PATHS_SEPARATOR))
}
add("--build-upon-default-config")
val configFiles = buildList {
add(parentConfigFile)
configFile?.absoluteFile?.let { configFile ->
if (!configFile.exists()) throw FileNotFoundException(Detekt::class.java.simpleName + " config file can't be found: " + configFile)
add(configFile)
}
}
if (configFiles.isNotEmpty()) {
add("--config")
add(configFiles.joinToString(PATHS_SEPARATOR))
}
add("--report")
add("FindBugsXmlCustomReporter:" + findBugsXmlFile.path)
classpath.toList().filter(File::exists).nullIfEmpty()?.map(File::getAbsolutePath)?.let {
add("--classpath")
add(it.joinToString(PATHS_SEPARATOR))
}
languageVersion?.let {
add("--language-version")
add(it)
}
jvmTarget.nullIfEmpty()?.let {
add("--jvm-target")
add(it)
}
if (isKotlinModuleNameSupported) {
kotlinModuleName?.let {
add("--kotlin-module")
add(it)
}
}
}
val argsFile = tempDir.resolve("args.txt").createParentDirectories().apply {
writeText(
args.joinToString("\n"),
UTF_8
)
}
val errorOutputStream = ByteArrayOutputStream()
val standardOutputStream = ByteArrayOutputStream()
val result = project.javaexec { exec ->
exec.isIgnoreExitValue = true
exec.errorOutput = errorOutputStream
exec.standardOutput = standardOutputStream
exec.classpath = detektClasspath.filter(File::exists) + project.files(extensionsJarFile)
exec.main = MAIN_CLASS
exec.args = listOf(argsFile.absolutePath)
}
if (result.exitValue != 0) {
logger.warn { errorOutputStream.toText() }
logger.warn { standardOutputStream.toText() }
throw GradleException("Detekt invocation failed")
}
logger.debug { errorOutputStream.toText() }
logger.debug { standardOutputStream.toText() }
return readFindBugsReportXml(findBugsXmlFile)
}
private fun ByteArrayOutputStream.toText() = toByteArray()
.nullIf { isEmpty() }
?.let { String(it, UTF_8) }
?.replace(COLOR_CHARS_REGEX, "")
}