apply plugin: 'checkstyle' apply plugin: 'pmd' apply plugin: 'cpd' def getBinaries def getSources def getCpdTask def getPmdTask def getLintTasks def getCheckstyleTask def getStaticAnalysisTasks def appendError def appendCheckstyleErrors def appendPmdErrors def appendCpdErrors def appendLintErrors def normilizeFileUrl repositories { maven { url "http://dl.bintray.com/touchin/touchin-tools" } } configurations { pngtastic } dependencies { pmd 'net.sourceforge.pmd:pmd-core:5.5.1' pmd 'net.sourceforge.pmd:pmd-java:5.5.1' checkstyle 'ru.touchin:checkstyle:7.2-fork' pngtastic 'com.github.depsypher:pngtastic:1.2' } gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:cast" << "-Xlint:divzero" << "-Xlint:empty" << "-Xlint:deprecation" << "-Xlint:finally" << "-Xlint:overrides" << "-Xlint:path" << "-Xlint:unchecked" << "-Werror" } task staticAnalysis { def excludes = rootProject.extensions.findByName("staticAnalysisExcludes"); dependsOn getStaticAnalysisTasks(getSources(excludes), getBinaries(excludes)) doFirst { 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_${project.name}.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mCPD: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normilizeFileUrl("file://${project.buildDir}/reports/cpd_${project.name}.xml")) } else { consoleReport.append("\n\u001B[32mCPD: PASSED\u001B[0m") } previousCount = count count = appendPmdErrors(fullReport, count, new File("${project.buildDir}/reports/pmd_${project.name}.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mPMD: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normilizeFileUrl("file://${project.buildDir}/reports/pmd_${project.name}.html")) } else { consoleReport.append("\n\u001B[32mPMD: PASSED\u001B[0m") } previousCount = count count = appendLintErrors(fullReport, count, new File("${project.buildDir}/reports/lint_${project.name}.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mLint: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normilizeFileUrl("file://${project.buildDir}/reports/lint_${project.name}.html")) } else { consoleReport.append("\n\u001B[32mLint: PASSED\u001B[0m") } previousCount = count count = appendCheckstyleErrors(fullReport, count, new File("${project.buildDir}/reports/checkstyle_${project.name}.xml")) if (count - previousCount > 0) { consoleReport.append("\n\u001B[31mCheckstyle: FAILED (" + (count - previousCount) + " errors)\u001B[0m " + normilizeFileUrl("file://${project.buildDir}/reports/checkstyle_${project.name}.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 " + normilizeFileUrl("file://${project.buildDir}/reports/full_report.html")) throw new Exception(consoleReport.toString()) } else { consoleReport.append("\n\u001B[32mOverall: PASSED\u001B[0m") println(consoleReport.toString()) } } } } 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}") } } } normilizeFileUrl = { 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.equals("Lint")) { 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") } 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 } 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 } 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 } getBinaries = { excludes -> def binaries = new ArrayList() for (def project : rootProject.subprojects) { if (!project.subprojects.isEmpty() || (excludes != null && excludes.contains(project.path))) { continue } binaries.add("${project.projectDir}/build/intermediates/classes") } return binaries } getSources = { excludes -> def sources = new ArrayList() for (def project : rootProject.subprojects) { if (!project.subprojects.isEmpty() || (excludes != null && excludes.contains(project.path))) { continue } def sourcesDirectory = new File(project.projectDir.path, 'src') if (!sourcesDirectory.exists() || !sourcesDirectory.isDirectory()) { continue } for (def sourceFlavorDirectory : sourcesDirectory.listFiles()) { def sourceSetDirectory = new File(sourceFlavorDirectory.path, 'java'); if (!sourceSetDirectory.exists() || !sourceSetDirectory.isDirectory()) { continue } sources.add(sourceSetDirectory.absolutePath) } } return sources } getCpdTask = { sources -> def taskName = "cpd_${project.name}" tasks.create(taskName, tasks.findByName('cpdCheck').getClass().getSuperclass()) { minimumTokenCount = 60 source = files(sources) ignoreFailures = true reports { xml { enabled = true destination "${project.buildDir}/reports/${taskName}.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 "${project.buildDir}/reports/${taskName}.html" } xml { enabled = true destination "${project.buildDir}/reports/${taskName}.xml" } } } return taskName } 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 "${project.buildDir}/reports/${taskName}.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_${project.name}.xml") android.lintOptions.htmlReport = true android.lintOptions.htmlOutput = file("${project.buildDir}/reports/lint_${project.name}.html") android.lintOptions.lintConfig = file("${rootDir}/libraries/BuildScripts/lint/lint.xml") return lintTaskNames } getStaticAnalysisTasks = { sources, binaries -> def tasksNames = new ArrayList() try { tasksNames.add(getCpdTask(sources)) tasksNames.add(getCheckstyleTask(sources)) tasksNames.add(getPmdTask(sources)) tasksNames.addAll(getLintTasks()) } catch (Exception exception) { println(exception.toString()) } return tasksNames }