apply plugin: 'checkstyle' apply plugin: 'pmd' apply plugin: 'cpd' def getCpdTask def getPmdTask def getCheckstyleTask def getLintTasks def getKotlinDetektTask def appendError def appendCpdErrors def appendKotlinErrors def appendCheckstyleErrors def appendPmdErrors def appendLintErrors def normalizeFileUrl repositories { maven { url "http://dl.bintray.com/touchin/touchin-tools" } } configurations { pngtastic detekt } cpd { skipLexicalErrors = true } import org.apache.tools.ant.taskdefs.condition.Os ext.getIdeaFormatTask = { isAndroidProject, sources -> def ideaPath = System.getenv("IDEA_HOME") if (ideaPath == null) { return tasks.create((isAndroidProject ? "android" : "server") + "do nothing") } return tasks.create((isAndroidProject ? "android" : "server") + "IdeaFormat_$project.name", Exec) { def inspectionPath def params = ["-r", "-mask", "*.java,*.kt,*.xml"] for (String source : sources) { params.add(source) } if (Os.isFamily(Os.FAMILY_WINDOWS)) { inspectionPath = ['cmd', '/c', "${ideaPath}\\bin\\format.bat ${params.join(" ")}"] } else { inspectionPath = ["$ideaPath/bin/format.sh"] } commandLine inspectionPath if (!Os.isFamily(Os.FAMILY_WINDOWS)) { args = params } } } ext.getStaticAnalysisTaskNames = { isAndroidProject, sources -> def tasksNames = new ArrayList() try { tasksNames.add(getCpdTask(isAndroidProject, sources)) tasksNames.add(getKotlinDetektTask(isAndroidProject)) if (isAndroidProject) { tasksNames.add(getCheckstyleTask(sources)) tasksNames.add(getPmdTask(sources)) tasksNames.addAll(getLintTasks()) } } catch (Exception exception) { println(exception.toString()) } return tasksNames } ext.generateHtmlReport = { isAndroidProject -> StringBuilder fullReport = new StringBuilder() fullReport.append(""); StringBuilder consoleReport = new StringBuilder() consoleReport.append("STATIC ANALYSIS RESULTS:") def count = 0 def previousCount = count count = appendCpdErrors(fullReport, count, new File("${project.buildDir}/reports/cpd.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mCPD: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/cpd.xml")) } else { consoleReport.append("\n\u001B[32mCPD: PASSED\u001B[0m") } previousCount = count count = appendKotlinErrors(fullReport, count, new File("${project.buildDir}/reports/kotlin-detekt.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mKotlin-detekt: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/kotlin-detekt.xml")) } else { consoleReport.append("\n\u001B[32mKotlin-detekt: PASSED\u001B[0m") } if (isAndroidProject) { previousCount = count count = appendPmdErrors(fullReport, count, new File("${project.buildDir}/reports/pmd.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mPMD: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/pmd.html")) } else { consoleReport.append("\n\u001B[32mPMD: PASSED\u001B[0m") } previousCount = count count = appendLintErrors(fullReport, count, new File("${project.buildDir}/reports/lint_report.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mLint: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/lint.html")) } else { consoleReport.append("\n\u001B[32mLint: PASSED\u001B[0m") } previousCount = count count = appendCheckstyleErrors(fullReport, count, new File("${project.buildDir}/reports/checkstyle.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mCheckstyle: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/checkstyle.xml")) } else { consoleReport.append("\n\u001B[32mCheckstyle: PASSED\u001B[0m") } } fullReport.append("\n
\n"); fullReport.append("") File fullReportFile = new File("${project.buildDir}/reports/full_report.html") fullReportFile.write(fullReport.toString()); if (count > 0) { consoleReport.append("\n\u001B[31mOverall: FAILED (" + count + " errors)\u001B[0m " + normalizeFileUrl("file://${project.buildDir}/reports/full_report.html")) throw new Exception(consoleReport.toString()) } else { consoleReport.append("\n\u001B[32mOverall: PASSED\u001B[0m") println(consoleReport.toString()) } } normalizeFileUrl = { url -> return url.replace("\\", "/") } appendError = { report, number, analyzer, file, line, errorId, errorLink, description -> report.append("\n\t") report.append("\n\t\t" + number + "") report.append("\n\t\t" + analyzer + "") report.append("\n\t\t") if (analyzer == "Lint") { report.append("\n\t\t\t" + description + " (" + errorId + ")") } else if (analyzer == "Detekt") { report.append("\n\t\t\t" + description + " (" + errorId + ")") } else { report.append("\n\t\t\t" + description + " (" + errorId + ")") } report.append("\n\t\t") def indexOfSrc = file.indexOf("src") def deeplink = (indexOfSrc > 0 ? file.substring(indexOfSrc) : file).replace('\\', '/') report.append("\n\t\t") report.append("\n\t\t\t" + file + ":" + line + "") report.append("\n\t\t") report.append("\n\t") println("\n\u001B[31m" + number + " " + analyzer + ": " + description + " (" + errorId + ")\n\tat " + file + ":" + line + "\u001B[0m") } appendKotlinErrors = { report, 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") appendError(report, count, "Detekt", fileNode.attribute("name"), errorNode.attribute("line"), error, "", errorNode.attribute("message")) } } return count } appendCpdErrors = { report, count, cpdFile -> def rootNode = new XmlParser().parse(cpdFile) for (def duplicationNode : rootNode.children()) { if (!duplicationNode.name().equals("duplication")) { continue } count++ report.append("\n\t") report.append("\n\t\t" + count + "") report.append("\n\t\tCPD") def fragment = "Code duplication:

" def links = "" def duplicationIndex = 0 String duplicationPoints = "" for (def filePointNode : duplicationNode.children()) { if (filePointNode.name().equals("file")) { def file = filePointNode.attribute("path") def line = filePointNode.attribute("line"); def indexOfSrc = file.indexOf("src") def deeplink = (indexOfSrc > 0 ? file.substring(indexOfSrc) : file).replace('\\', '/') if (duplicationIndex > 0) { links += "\n\t\t\t

" } links += "Code fragment " + (duplicationIndex + 1) + "
" links += "\n\t\t\t" + file + ":" + line + "" duplicationPoints += "\n\tat " + file + ":" + line duplicationIndex++ } else if (filePointNode.name().equals("codefragment")) { fragment += filePointNode.text().replace("\n", "
").replace(" ", " ") } } report.append("\n\t\t" + fragment + "\n\t\t") report.append("\n\t\t" + links + "\n\t\t") report.append("\n\t") println("\u001B[31m" + count + " CPD: code duplication" + duplicationPoints + "\u001B[0m") } return count } appendCheckstyleErrors = { report, 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(report, count, "Checkstyle", fileNode.attribute("name"), errorNode.attribute("line"), error, link, errorNode.attribute("message")) } } return count } appendPmdErrors = { report, 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(report, count, "PMD", fileNode.attribute("name"), errorNode.attribute("beginline"), errorNode.attribute("rule").trim(), errorNode.attribute("externalInfoUrl").trim(), errorNode.text().trim()) } } return count } appendLintErrors = { report, count, lintFile -> def rootNode = new XmlParser().parse(lintFile) for (def issueNode : rootNode.children()) { if (!issueNode.name().equals("issue") || !issueNode.attribute("severity").equals("Error")) { continue } for (def locationNode : issueNode.children()) { if (!locationNode.name().equals("location")) { continue } count++ appendError(report, count, "Lint", locationNode.attribute("file"), locationNode.attribute("line"), issueNode.attribute("id"), issueNode.attribute("explanation"), issueNode.attribute("message")) } } return count } getCpdTask = { isAndroidProject, sources -> def taskName = (isAndroidProject ? "android" : "server") + "cpd_${project.name}" tasks.create(taskName, tasks.findByName('cpdCheck').getClass().getSuperclass()) { minimumTokenCount = 60 source = files(sources) ignoreFailures = true reports { xml { enabled = true destination = file("${project.buildDir}/reports/cpd.xml") } } } return taskName } getPmdTask = { sources -> def taskName = "pmd_${project.name}" tasks.create(taskName, Pmd) { pmdClasspath = configurations.pmd.asFileTree ruleSetFiles = files("${rootDir}/libraries/BuildScripts/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 taskName } getLintTasks = { def lintTaskNames = new ArrayList() def lintReleaseTask = tasks.matching { it.getName().contains("lint") && it.getName().contains("Release") }.first() //TODO return on jack lintReleaseTask.dependsOn.clear() lintTaskNames.add(lintReleaseTask.getName()) def lintDebugTask = tasks.matching { it.getName().contains("lint") && it.getName().contains("Debug") }.first() //TODO return on jack lintDebugTask.dependsOn.clear() lintTaskNames.add(lintDebugTask.getName()) 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 = true android.lintOptions.htmlOutput = file("${project.buildDir}/reports/lint_report.html") android.lintOptions.lintConfig = file("${rootDir}/libraries/BuildScripts/lint/lint.xml") return lintTaskNames } getCheckstyleTask = { sources -> def taskName = "checkstyle_${project.name}" def compileReleaseTask = tasks.matching { it.getName().contains("compile") && it.getName().contains("Release") && it.getName().contains("Java") && !it.getName().contains("UnitTest") }.last() tasks.create(taskName, Checkstyle) { ignoreFailures = true showViolations = false source files(sources) configFile file("${rootDir}/libraries/BuildScripts/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 taskName } getKotlinDetektTask = { isAndroidProject -> def taskName = (isAndroidProject ? "android" : "server") + "detektCheck_${project.name}" tasks.create(taskName, JavaExec) { main = "io.gitlab.arturbosch.detekt.cli.Main" classpath = configurations.detekt def input = "${rootDir}" def output = "${project.buildDir}/reports/kotlin-detekt.xml" def config = "${rootDir}/libraries/BuildScripts/kotlin/detekt-config.yml" // TODO add excludes from rootProject.extensions.findByName("staticAnalysisExcludes") def filters = ".*src/test.*,.*/resources/.*,.*/tmp/.*" def params = ['-i', input, '-o', output, '-c', config, '-f', filters] args(params) } return taskName } task optimizePng { doFirst { def jarArgs = new ArrayList() jarArgs.add(configurations.pngtastic.asPath) def relatedPathIndex = "${rootDir}".length() + 1 for (def file : fileTree(dir: "${rootDir}", include: '**/src/**/res/drawable**/*.png')) { jarArgs.add(file.absolutePath.substring(relatedPathIndex)) } for (def file : fileTree(dir: "${rootDir}", include: '**/src/**/res/mipmap**/*.png')) { jarArgs.add(file.absolutePath.substring(relatedPathIndex)) } javaexec { main = "-jar"; args = jarArgs; workingDir = file("${rootDir}") } } } 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' detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.0.M13.2' detekt 'io.gitlab.arturbosch.detekt:detekt-formatting:1.0.0.M13.2' }