diff --git a/gradle/commonStaticAnalysis.gradle b/gradle/commonStaticAnalysis.gradle index f4c4a3c..b929257 100644 --- a/gradle/commonStaticAnalysis.gradle +++ b/gradle/commonStaticAnalysis.gradle @@ -1,19 +1,13 @@ -apply plugin: 'checkstyle' apply plugin: 'cpd' -apply plugin: 'pmd' apply plugin: 'io.gitlab.arturbosch.detekt' def getCpdTask -def getPmdTask -def getCheckstyleTask def getLintTask -def getKotlinDetektTask +def getKotlinDetektTasks def appendError def appendCpdErrors def appendKotlinErrors -def appendCheckstyleErrors -def appendPmdErrors def appendLintErrors repositories { @@ -28,23 +22,6 @@ cpd { skipLexicalErrors = true } -detekt { - config = files("$buildScriptsDir/kotlin/detekt-config.yml") - parallel = true - - reports { - html { - enabled = true - destination = file("${project.buildDir}/reports/kotlin-detekt.html") - } - xml { - enabled = true - destination = file("${project.buildDir}/reports/kotlin-detekt.xml") - } - } - -} - import org.apache.tools.ant.taskdefs.condition.Os ext.getIdeaFormatTask = { isAndroidProject, sources -> @@ -71,18 +48,12 @@ ext.getIdeaFormatTask = { isAndroidProject, sources -> } } -ext.getStaticAnalysisTaskNames = { isAndroidProject, sources, buildVariant, checkstyleEnabled, pmdEnabled -> +ext.getStaticAnalysisTaskNames = { isAndroidProject, sources, buildVariant -> def tasksNames = new ArrayList() try { tasksNames.add(getCpdTask(isAndroidProject, sources)) - tasksNames.add(getKotlinDetektTask()) + tasksNames.addAll(getKotlinDetektTasks()) if (isAndroidProject) { - if (checkstyleEnabled) { - tasksNames.add(getCheckstyleTask(sources)) - } - if (pmdEnabled) { - tasksNames.add(getPmdTask(sources)) - } tasksNames.add(getLintTask(buildVariant)) } } catch (Exception exception) { @@ -105,7 +76,12 @@ ext.generateReport = { isAndroidProject -> } previousCount = count - count = appendKotlinErrors(count, new File("${project.buildDir}/reports/kotlin-detekt.xml")) + subprojects.forEach { subproject -> + def reportFile = new File("${rootProject.buildDir}/reports/kotlin-detekt-${subproject.name}.xml") + if (reportFile.exists()) { + count = appendKotlinErrors(count, reportFile).toInteger() + } + } if (count - previousCount > 0) { consoleReport.append("\nKotlin-detekt: FAILED (" + (count - previousCount) + " errors)") } else { @@ -113,30 +89,8 @@ ext.generateReport = { isAndroidProject -> } if (isAndroidProject) { - def checkstyleFile = new File("${project.buildDir}/reports/checkstyle.xml") - if (checkstyleFile.exists()) { - previousCount = count - count = appendCheckstyleErrors(count, checkstyleFile) - if (count - previousCount > 0) { - consoleReport.append("\nCheckstyle: FAILED (" + (count - previousCount) + " errors)") - } else { - consoleReport.append("\nCheckstyle: PASSED") - } - } - - def pmdFile = new File("${project.buildDir}/reports/pmd.xml") - if (pmdFile.exists()) { - previousCount = count - count = appendPmdErrors(count, pmdFile) - if (count - previousCount > 0) { - consoleReport.append("\nPMD: FAILED (" + (count - previousCount) + " errors)") - } else { - consoleReport.append("\nPMD: PASSED") - } - } - previousCount = count - count = appendLintErrors(count, new File("${project.buildDir}/reports/lint_report.xml")) + count = appendLintErrors(count, new File("${rootProject.buildDir}/reports/lint_report.xml")) if (count - previousCount > 0) { consoleReport.append("\nLint: FAILED (" + (count - previousCount) + " errors)") } else { @@ -200,47 +154,6 @@ appendCpdErrors = { count, cpdFile -> return count } -appendCheckstyleErrors = { count, checkstyleFile -> - def rootNode = new XmlParser().parse(checkstyleFile) - for (def fileNode : rootNode.children()) { - if (!fileNode.name().equals("file")) { - continue - } - - for (def errorNode : fileNode.children()) { - if (!errorNode.name().equals("error")) { - continue - } - count++ - - def error = errorNode.attribute("source") - def link = "http://checkstyle.sourceforge.net/apidocs/" + error.replace('.', '/') + ".html" - appendError(count, "Checkstyle", fileNode.attribute("name"), errorNode.attribute("line"), error, link, errorNode.attribute("message")) - } - } - return count -} - -appendPmdErrors = { count, pmdFile -> - def rootNode = new XmlParser().parse(pmdFile) - for (def fileNode : rootNode.children()) { - if (!fileNode.name().equals("file")) { - continue - } - - for (def errorNode : fileNode.children()) { - if (!errorNode.name().equals("violation")) { - continue - } - count++ - - appendError(count, "PMD", fileNode.attribute("name"), errorNode.attribute("beginline"), - errorNode.attribute("rule").trim(), errorNode.attribute("externalInfoUrl").trim(), errorNode.text().trim()) - } - } - return count -} - appendLintErrors = { count, lintFile -> def rootNode = new XmlParser().parse(lintFile) for (def issueNode : rootNode.children()) { @@ -261,7 +174,7 @@ appendLintErrors = { count, lintFile -> } getCpdTask = { isAndroidProject, sources -> - def taskName = (isAndroidProject ? "android" : "server") + "cpd_${project.name}" + def taskName = (isAndroidProject ? "android" : "server") + "cpd_${rootProject.name}" def task = tasks.findByName(taskName) if (task == null) { task = tasks.create(taskName, tasks.findByName('cpdCheck').getClass().getSuperclass()) { @@ -271,87 +184,33 @@ getCpdTask = { isAndroidProject, sources -> reports { xml { enabled = true - destination = file("${project.buildDir}/reports/cpd.xml") + destination = file("${rootProject.buildDir}/reports/cpd.xml") } } } } - return task.name -} - -getPmdTask = { sources -> - def taskName = "pmd_${project.name}" - def task = tasks.findByName(taskName) - if (task == null) { - task = tasks.create(taskName, Pmd) { - pmdClasspath = configurations.pmd.asFileTree - ruleSetFiles = files "$buildScriptsDir/pmd/rulesets/java/android.xml" - ruleSets = [] - source files(sources) - ignoreFailures = true - reports { - html { - enabled = true - destination file("${project.buildDir}/reports/pmd.html") - } - xml { - enabled = true - destination file("${project.buildDir}/reports/pmd.xml") - } - } - } - } - return task.name + return task.path } getLintTask = { buildVariant -> - def lintTaskName + def appProject = subprojects.find { it.plugins.hasPlugin("com.android.application") } + def lintTaskPath if (buildVariant != null) { - lintTaskName = "lint${buildVariant.name.capitalize()}" + lintTaskPath = ":${appProject.name}:lint${buildVariant.name.capitalize()}" } else { - def lintDebugTask = tasks.matching { it.getName().contains("lint") && it.getName().contains("Debug") }.first() - lintTaskName = lintDebugTask.getName() + def lintDebugTasks = appProject.tasks.matching { it.getName().contains("lint") && it.getName().contains("Debug") } + lintTaskPath = lintDebugTasks.first().path } - android.lintOptions.abortOnError = false - android.lintOptions.checkAllWarnings = true - android.lintOptions.warningsAsErrors = false - android.lintOptions.xmlReport = true - android.lintOptions.xmlOutput = file "$project.buildDir/reports/lint_report.xml" - android.lintOptions.htmlReport = false - android.lintOptions.lintConfig = file "$buildScriptsDir/lint/lint.xml" - return lintTaskName + if (lintTaskPath == null) { + throw IllegalStateException("Unable to find lint debug task for build variant: ${buildVariant}") + } + return lintTaskPath } -getCheckstyleTask = { sources -> - def taskName = "checkstyle_$project.name" - def task = tasks.findByName(taskName) - if (task == null) { - def compileReleaseTask = tasks.matching { - it.getName().contains("compile") && it.getName().contains("Release") && it.getName().contains("Java") && !it.getName().contains("UnitTest") - }.last() - task = tasks.create(taskName, Checkstyle) { - ignoreFailures = true - showViolations = false - source files(sources) - configFile file("$buildScriptsDir/checkstyle/configuration/touchin_checkstyle.xml") - checkstyleClasspath = configurations.checkstyle.asFileTree - classpath = files(System.getenv("ANDROID_HOME") + "/platforms/" + android.compileSdkVersion + "/android.jar") + - files(System.properties.'java.home' + "/lib/rt.jar") + - compileReleaseTask.classpath - reports { - xml { - enabled = true - destination file("${project.buildDir}/reports/checkstyle.xml") - } - } - } - } - return task.name -} - -getKotlinDetektTask = { - // TODO add excludes from rootProject.extensions.findByName("staticAnalysisExcludes") - "detekt" +getKotlinDetektTasks = { + subprojects + .findResults { it.tasks.findByName("detekt")?.path } + .findAll { !it.contains(":libs") } } task optimizePng { @@ -370,10 +229,5 @@ task optimizePng { } dependencies { - pmd 'net.sourceforge.pmd:pmd-core:5.5.3' - pmd 'net.sourceforge.pmd:pmd-java:5.5.3' - - checkstyle 'ru.touchin:checkstyle:7.6.2-fork' - pngtastic 'com.github.depsypher:pngtastic:1.2' } diff --git a/gradle/staticAnalysis.gradle b/gradle/staticAnalysis.gradle index 4e08fe8..3cb2335 100644 --- a/gradle/staticAnalysis.gradle +++ b/gradle/staticAnalysis.gradle @@ -1,3 +1,12 @@ +buildscript { + repositories { + maven { url "https://plugins.gradle.org/m2" } + } + dependencies { + classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.5.1" + } +} + def getServerProjectSources def getAndroidProjectSources @@ -18,16 +27,14 @@ gradle.projectsEvaluated { } def excludes = rootProject.extensions.findByName("staticAnalysisExcludes") - def checkstyleEnabled = rootProject.extensions.findByName("checkstyleEnabled") ?: false - def pmdEnabled = rootProject.extensions.findByName("pmdEnabled") ?: false def androidSources = getAndroidProjectSources(excludes) - def androidStaticAnalysisTasks = getStaticAnalysisTaskNames(true, androidSources, null, checkstyleEnabled, pmdEnabled) + def androidStaticAnalysisTasks = getStaticAnalysisTaskNames(true, androidSources, null) def androidIdeaFormatTask = getIdeaFormatTask(true, androidSources) task staticAnalysisWithFormatting { androidStaticAnalysisTasks.each { task -> - tasks.findByName(task).mustRunAfter(androidIdeaFormatTask) + tasks.findByName(task)?.mustRunAfter(androidIdeaFormatTask) } dependsOn androidIdeaFormatTask dependsOn androidStaticAnalysisTasks @@ -43,12 +50,12 @@ gradle.projectsEvaluated { } } - def serverStaticAnalysisTasks = getStaticAnalysisTaskNames(false, getServerProjectSources(excludes), null, checkstyleEnabled, pmdEnabled) + def serverStaticAnalysisTasks = getStaticAnalysisTaskNames(false, getServerProjectSources(excludes), null) def serverIdeaFormatTask = getIdeaFormatTask(false, getServerProjectSources(excludes)) task serverStaticAnalysisWithFormatting { serverStaticAnalysisTasks.each { task -> - tasks.findByName(task).mustRunAfter(serverIdeaFormatTask) + tasks.findByName(task)?.mustRunAfter(serverIdeaFormatTask) } dependsOn serverIdeaFormatTask dependsOn serverStaticAnalysisTasks @@ -64,11 +71,49 @@ gradle.projectsEvaluated { } } - pluginManager.withPlugin('com.android.application') { - android.applicationVariants.all { variant -> - task("staticAnalysis${variant.name.capitalize()}") { - dependsOn getStaticAnalysisTaskNames(true, androidSources, variant, checkstyleEnabled, pmdEnabled) - doFirst { generateReport(true) } + subprojects { subproject -> + if (subproject.plugins.hasPlugin("com.android.application")) { + subproject.android { + lintOptions.abortOnError = false + lintOptions.checkAllWarnings = true + lintOptions.warningsAsErrors = false + lintOptions.xmlReport = true + lintOptions.xmlOutput = file "$rootProject.buildDir/reports/lint_report.xml" + lintOptions.htmlReport = false + lintOptions.lintConfig = file "$buildScriptsDir/lint/lint.xml" + lintOptions.checkDependencies true + + applicationVariants.all { variant -> + task("staticAnalysis${variant.name.capitalize()}") { + dependsOn getStaticAnalysisTaskNames(true, androidSources, variant) + doFirst { generateReport(true) } + } + } + } + } + + def regex = ~':detekt$' + tasks.forEach { task -> + if (!task.name.contains(":libs") && task.path =~ regex) { + task.exclude '**/test/**' + task.exclude 'resources/' + task.exclude 'build/' + task.exclude 'tmp/' + + task.jvmTarget = "1.8" + } + } + + detekt { + config = files("$buildScriptsDir/kotlin/detekt-config.yml") + + reports { + txt.enabled = false + html.enabled = false + xml { + enabled = true + destination = file("${rootProject.buildDir}/reports/kotlin-detekt-${subproject.name}.xml") + } } } } diff --git a/kotlin/detekt-config.yml b/kotlin/detekt-config.yml index 28cd184..3afb6fa 100644 --- a/kotlin/detekt-config.yml +++ b/kotlin/detekt-config.yml @@ -1,13 +1,10 @@ -autoCorrect: true -failFast: false - build: # maxIssues: 10 weights: - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 processors: active: true @@ -18,6 +15,13 @@ processors: # - 'PackageCountProcessor' # - 'KtFileCountProcessor' +formatting: + active: true + android: true + autoCorrect: true + MaximumLineLength: + active: true + console-reports: active: true exclude: @@ -27,12 +31,6 @@ console-reports: # - 'FindingsReport' # - 'BuildFailureReport' -output-reports: - active: true - exclude: - - 'PlainOutputReport' - - 'HtmlOutputReport' - comments: active: false CommentOverPrivateFunction: @@ -118,7 +116,7 @@ empty-blocks: active: true EmptyFunctionBlock: active: true - ignoreOverriddenFunctions: false + ignoreOverridden: false EmptyIfBlock: active: true EmptyInitBlock: @@ -161,22 +159,22 @@ exceptions: TooGenericExceptionCaught: active: true exceptionNames: - - ArrayIndexOutOfBoundsException - - Error - - Exception - - IllegalMonitorStateException - - NullPointerException - - IndexOutOfBoundsException - - RuntimeException - - Throwable + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable allowedExceptionNameRegex: "^(_|(ignore|expected).*)" TooGenericExceptionThrown: active: true exceptionNames: - - Error - - Exception - - Throwable - - RuntimeException + - Error + - Exception + - Throwable + - RuntimeException naming: active: true @@ -209,12 +207,12 @@ naming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverriddenFunctions: true + ignoreOverridden: true MatchingDeclarationName: active: true MemberNameEqualsClassName: active: false - ignoreOverriddenFunction: true + ignoreOverridden: true ObjectPropertyNaming: active: true constantPattern: '[A-Za-z][_A-Za-z0-9]*'