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

name.remal.gradle_plugins.plugins.code_quality.detekt.Detekt.kt Maven / Gradle / Ivy

There is a newer version: 1.9.2
Show newest version
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, "")

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy