244 lines
7.1 KiB
Groovy
244 lines
7.1 KiB
Groovy
import groovy.transform.InheritConstructors
|
|
import groovy.transform.ToString
|
|
import groovy.transform.TupleConstructor
|
|
import groovy.xml.MarkupBuilder
|
|
|
|
apply plugin: 'io.gitlab.arturbosch.detekt'
|
|
|
|
task staticAnalysis {
|
|
|
|
dependsOn 'cleanAnalyzerReports'
|
|
if (static_analyzer_enable_detekt) {
|
|
dependsOn ':detekt'
|
|
}
|
|
if (static_analyzer_enable_lint) {
|
|
dependsOn ':app:lint'
|
|
}
|
|
|
|
doLast {
|
|
def reportDir = new File(static_analyzer_report_dir)
|
|
|
|
def detektErrors = collectDetektCodeAnalyzerErrors(reportDir)
|
|
def lintErrors = collectLintCodeAnalyzerErrors(reportDir)
|
|
|
|
def totalReportFile = new File(reportDir, 'total_report.html')
|
|
|
|
totalReportFile << generateReportHtml(detektErrors, lintErrors)
|
|
|
|
println("|----------------------static_analyzer--------------------------------")
|
|
println("| static_analyzer_fail_on_lint_error : $static_analyzer_fail_on_lint_error")
|
|
println("| static_analyzer_fail_on_detect_error : $static_analyzer_fail_on_detect_error")
|
|
println("|")
|
|
println("| total_detekt_errors : ${detektErrors.size()}" + (static_analyzer_enable_detekt ? "" : " [DISABLED]"))
|
|
println("| total_lint_errors : ${lintErrors.size()}" + (static_analyzer_enable_lint ? "" : " [DISABLED]"))
|
|
println("| total_errors : ${detektErrors.size() + lintErrors.size()}")
|
|
println("|")
|
|
println("| Successfully generated HtmlTotalReport at ${totalReportFile.path}")
|
|
println("|-------------------------------------------------------------------")
|
|
|
|
if (static_analyzer_fail_on_lint_error || static_analyzer_fail_on_detect_error) {
|
|
def message = ""
|
|
def hasLintError = static_analyzer_fail_on_lint_error && !lintErrors.isEmpty()
|
|
def hasDetektError = static_analyzer_fail_on_detect_error && !detektErrors.isEmpty()
|
|
|
|
if (hasLintError) {
|
|
message += "There is ${lintErrors.size()} lint errors; "
|
|
}
|
|
if (hasDetektError) {
|
|
message += "There is ${detektErrors.size()} detekt errors; "
|
|
}
|
|
|
|
if (hasLintError || hasDetektError) {
|
|
message += "\n See report at ${totalReportFile.path}"
|
|
throw new StaticAnalysisException(message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
detekt {
|
|
debug = true
|
|
//filters = ".*test.*,.*androidTest.*,.*//*resources/.*,.*//*build/.*"
|
|
input = files("$rootDir/app/src/main/kotlin", "$rootDir/data/src/main/kotlin", "$rootDir/domain/src/main/kotlin")
|
|
config = files("$static_analyzer_config_dir/detekt-config.yml")
|
|
|
|
reports {
|
|
xml {
|
|
enabled = true
|
|
destination = file("$static_analyzer_report_dir/report_detekt.xml")
|
|
}
|
|
html {
|
|
enabled = false
|
|
}
|
|
}
|
|
}
|
|
|
|
task cleanAnalyzerReports(type: Delete) {
|
|
delete static_analyzer_report_dir
|
|
}
|
|
|
|
static def collectDetektCodeAnalyzerErrors(File reportDir) {
|
|
|
|
def detektReport = new File("$reportDir/report_detekt.xml")
|
|
|
|
if (detektReport == null) {
|
|
println("can't find detekt report")
|
|
return Collections.emptyList()
|
|
}
|
|
|
|
return new XmlSlurper()
|
|
.parse(detektReport)
|
|
.children()
|
|
.inject(new ArrayList<CodeAnalyzerError>()) { acc, file ->
|
|
|
|
println(file)
|
|
|
|
def fileName = file.@name
|
|
|
|
file.children().each { error ->
|
|
acc.add(
|
|
new CodeAnalyzerError(
|
|
position: "$fileName:${error.@line ?: "-"}:${error.@column ?: "-"}",
|
|
severity: error.@severity,
|
|
message: error.@message,
|
|
source: error.@source
|
|
)
|
|
)
|
|
}
|
|
|
|
acc
|
|
}
|
|
.sort { error -> error.position }
|
|
}
|
|
|
|
static def collectLintCodeAnalyzerErrors(File reportDir) {
|
|
|
|
def lintReports = reportDir.listFiles().findAll { file -> file.name.matches("^lint.*xml\$") }
|
|
|
|
if (lintReports == null || lintReports.isEmpty()) {
|
|
println("can't find any lint report")
|
|
return Collections.emptyList()
|
|
}
|
|
|
|
|
|
return lintReports.inject(new ArrayList<CodeAnalyzerError>()) { acc, report ->
|
|
acc.addAll(parseLintReport(report))
|
|
acc
|
|
}.sort { error -> error.position }
|
|
}
|
|
|
|
static def parseLintReport(File report) {
|
|
|
|
return new XmlSlurper()
|
|
.parse(report)
|
|
.children()
|
|
.inject(new ArrayList<CodeAnalyzerError>()) { acc, issue ->
|
|
|
|
def fileName = issue.location.@file
|
|
def line = issue.location.@line ?: "-"
|
|
def column = issue.location.@column ?: "-"
|
|
|
|
acc.add(
|
|
new CodeAnalyzerError(
|
|
position: "$fileName:$line:$column",
|
|
severity: issue.@severity,
|
|
message: issue.@message,
|
|
source: issue.@id
|
|
)
|
|
)
|
|
acc
|
|
}
|
|
}
|
|
|
|
MarkupBuilder.metaClass.appendErrorsTable { errors ->
|
|
delegate.table {
|
|
tr {
|
|
th(["class": "Position"], "Position")
|
|
th(["class": "Severity"], "Severity")
|
|
th(["class": "Source"], "Source")
|
|
th(["class": "Message"], "Message")
|
|
}
|
|
|
|
errors.forEach { error ->
|
|
tr {
|
|
td(error.position)
|
|
td(error.severity.toLowerCase())
|
|
td(error.source)
|
|
td(error.message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
def generateReportHtml(List<CodeAnalyzerError> detektErrors, List<CodeAnalyzerError> lintErrors) {
|
|
def sb = new StringWriter()
|
|
|
|
def html = new MarkupBuilder(sb)
|
|
|
|
html.doubleQuotes = true
|
|
html.expandEmptyElements = true
|
|
html.omitEmptyAttributes = false
|
|
html.omitNullAttributes = false
|
|
|
|
html.html(lang: 'en') {
|
|
head {
|
|
meta('http-equiv': '"Content-Type" content="text/html; charset=utf-8"')
|
|
title('Static analyzer report')
|
|
mkp.yieldUnescaped(getErrorTableStyle())
|
|
}
|
|
body {
|
|
println("class: " + owner)
|
|
|
|
h2("Detekt errors: ${detektErrors.size()}")
|
|
appendErrorsTable(detektErrors)
|
|
|
|
h2("Lint errors: ${lintErrors.size()}")
|
|
appendErrorsTable(lintErrors)
|
|
}
|
|
}
|
|
|
|
return sb.toString()
|
|
}
|
|
|
|
static def getErrorTableStyle() {
|
|
return """
|
|
<style>
|
|
h2 { margin-top: 50px; }
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
border-spacing: 0;
|
|
width: 100%;
|
|
table-layout: fixed;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
th, td {
|
|
text-align: left;
|
|
padding: 8px;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
tr:nth-child(even) { background-color: #f2f2f2; }
|
|
|
|
.Position { width: 40%; }
|
|
|
|
.Severity { width: 10%; }
|
|
|
|
.Source { width: 15%; }
|
|
|
|
.Message { width: 35%; }
|
|
</style>
|
|
"""
|
|
}
|
|
|
|
@ToString
|
|
@TupleConstructor
|
|
class CodeAnalyzerError {
|
|
String position, severity, message, source
|
|
}
|
|
|
|
@InheritConstructors
|
|
class StaticAnalysisException extends GradleException {}
|