Compare commits
8 Commits
master
...
feature/ne
| Author | SHA1 | Date |
|---|---|---|
|
|
85299ff3a2 | |
|
|
a3921846df | |
|
|
aadb440ed5 | |
|
|
92911d7734 | |
|
|
512fe20be8 | |
|
|
5b8fe136f3 | |
|
|
ca21fb6c88 | |
|
|
23d74e557c |
|
|
@ -1,379 +0,0 @@
|
||||||
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 appendError
|
|
||||||
def appendCpdErrors
|
|
||||||
def appendKotlinErrors
|
|
||||||
def appendCheckstyleErrors
|
|
||||||
def appendPmdErrors
|
|
||||||
def appendLintErrors
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven { url "http://dl.bintray.com/touchin/touchin-tools" }
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
pngtastic
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ->
|
|
||||||
def ideaPath = System.getenv("IDEA_HOME")
|
|
||||||
if (ideaPath == null) {
|
|
||||||
return tasks.create((isAndroidProject ? "android" : "server") + "donothing")
|
|
||||||
}
|
|
||||||
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, buildVariant, checkstyleEnabled, pmdEnabled ->
|
|
||||||
def tasksNames = new ArrayList<String>()
|
|
||||||
try {
|
|
||||||
tasksNames.add(getCpdTask(isAndroidProject, sources))
|
|
||||||
tasksNames.add(getKotlinDetektTask())
|
|
||||||
if (isAndroidProject) {
|
|
||||||
if (checkstyleEnabled) {
|
|
||||||
tasksNames.add(getCheckstyleTask(sources))
|
|
||||||
}
|
|
||||||
if (pmdEnabled) {
|
|
||||||
tasksNames.add(getPmdTask(sources))
|
|
||||||
}
|
|
||||||
tasksNames.add(getLintTask(buildVariant))
|
|
||||||
}
|
|
||||||
} catch (Exception exception) {
|
|
||||||
println(exception.toString())
|
|
||||||
}
|
|
||||||
return tasksNames
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.generateReport = { isAndroidProject ->
|
|
||||||
StringBuilder consoleReport = new StringBuilder()
|
|
||||||
consoleReport.append("STATIC ANALYSIS RESULTS:")
|
|
||||||
def count = 0
|
|
||||||
|
|
||||||
def previousCount = count
|
|
||||||
count = appendCpdErrors(count, new File("${project.buildDir}/reports/cpd.xml"))
|
|
||||||
if (count - previousCount > 0) {
|
|
||||||
consoleReport.append("\nCPD: FAILED (" + (count - previousCount) + " errors)")
|
|
||||||
} else {
|
|
||||||
consoleReport.append("\nCPD: PASSED")
|
|
||||||
}
|
|
||||||
|
|
||||||
previousCount = count
|
|
||||||
count = appendKotlinErrors(count, new File("${project.buildDir}/reports/kotlin-detekt.xml"))
|
|
||||||
if (count - previousCount > 0) {
|
|
||||||
consoleReport.append("\nKotlin-detekt: FAILED (" + (count - previousCount) + " errors)")
|
|
||||||
} else {
|
|
||||||
consoleReport.append("\nKotlin-detekt: PASSED")
|
|
||||||
}
|
|
||||||
|
|
||||||
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"))
|
|
||||||
if (count - previousCount > 0) {
|
|
||||||
consoleReport.append("\nLint: FAILED (" + (count - previousCount) + " errors)")
|
|
||||||
} else {
|
|
||||||
consoleReport.append("\nLint: PASSED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0) {
|
|
||||||
consoleReport.append("\nOverall: FAILED (" + count + " errors)")
|
|
||||||
throw new Exception(consoleReport.toString())
|
|
||||||
} else {
|
|
||||||
consoleReport.append("\nOverall: PASSED")
|
|
||||||
println(consoleReport.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appendError = { number, analyzer, file, line, errorId, errorLink, description ->
|
|
||||||
println("$number. $analyzer : $description ($errorId)\n\tat $file: $line")
|
|
||||||
}
|
|
||||||
|
|
||||||
appendKotlinErrors = { 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++
|
|
||||||
|
|
||||||
appendError(count, "Detekt", fileNode.attribute("name"), errorNode.attribute("line"), errorNode.attribute("source"), "", errorNode.attribute("message"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
appendCpdErrors = { count, cpdFile ->
|
|
||||||
def rootNode = new XmlParser().parse(cpdFile)
|
|
||||||
for (def duplicationNode : rootNode.children()) {
|
|
||||||
if (!duplicationNode.name().equals("duplication")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
|
|
||||||
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")
|
|
||||||
duplicationPoints += "\n " + file + ":" + line
|
|
||||||
duplicationIndex++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("$count CPD: code duplication $duplicationPoints")
|
|
||||||
}
|
|
||||||
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()) {
|
|
||||||
if (!issueNode.name().equals("issue")
|
|
||||||
|| !issueNode.attribute("severity").equals("Error")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for (def locationNode : issueNode.children()) {
|
|
||||||
if (!locationNode.name().equals("location")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
appendError(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}"
|
|
||||||
def task = tasks.findByName(taskName)
|
|
||||||
if (task == null) {
|
|
||||||
task = 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 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
|
|
||||||
}
|
|
||||||
|
|
||||||
getLintTask = { buildVariant ->
|
|
||||||
def lintTaskName
|
|
||||||
if (buildVariant != null) {
|
|
||||||
lintTaskName = "lint${buildVariant.name.capitalize()}"
|
|
||||||
} else {
|
|
||||||
def lintDebugTask = tasks.matching { it.getName().contains("lint") && it.getName().contains("Debug") }.first()
|
|
||||||
lintTaskName = 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 = false
|
|
||||||
android.lintOptions.lintConfig = file "$buildScriptsDir/lint/lint.xml"
|
|
||||||
return lintTaskName
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
task optimizePng {
|
|
||||||
doFirst {
|
|
||||||
def jarArgs = new ArrayList<String>()
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
import com.android.build.gradle.AppExtension
|
||||||
|
import com.android.build.gradle.api.ApplicationVariant
|
||||||
|
import groovy.lang.Closure
|
||||||
|
import groovy.util.Node
|
||||||
|
import groovy.util.XmlParser
|
||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
|
||||||
|
val buildScriptsDir: String by rootProject.extra
|
||||||
|
|
||||||
|
apply(from = "$buildScriptsDir/gradle/commonStaticAnalysisLegacy.gradle")
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
google()
|
||||||
|
maven("https://plugins.gradle.org/m2/")
|
||||||
|
maven("https://maven.fabric.io/public")
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("org.redundent:kotlin-xml-builder:1.5.1")
|
||||||
|
classpath("com.android.tools.build:gradle:3.4.2")
|
||||||
|
classpath(kotlin("gradle-plugin", version = "1.3.41"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(plugin = "checkstyle")
|
||||||
|
apply(plugin = "pmd")
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
"pngtastic"()
|
||||||
|
}
|
||||||
|
|
||||||
|
extra["getIdeaFormatTask"] = { isAndroidProject: Boolean, sources: List<String> ->
|
||||||
|
val ideaPath = System.getenv("IDEA_HOME")
|
||||||
|
if (ideaPath == null) {
|
||||||
|
tasks.create((if (isAndroidProject) "android" else "server") + "donothing")
|
||||||
|
} else {
|
||||||
|
tasks.create((if (isAndroidProject) "android" else "server") + "IdeaFormat_${project.name}", Exec::class.java) {
|
||||||
|
val params = mutableListOf("-r", "-mask", "*.java,*.kt,*.xml")
|
||||||
|
for (source in sources) {
|
||||||
|
params.add(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
val inspectionPath = if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
listOf("cmd", "/c", "\"$ideaPath\\bin\\format.bat\" ${params.joinToString(" ")}")
|
||||||
|
} else {
|
||||||
|
listOf("$ideaPath/bin/format.sh")
|
||||||
|
}
|
||||||
|
commandLine(inspectionPath)
|
||||||
|
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
setArgs(params as List<Any>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extra["getStaticAnalysisTaskNames"] = { isAndroidProject: Boolean, sources: List<String>, buildVariant: ApplicationVariant?, checkstyleEnabled: Boolean, pmdEnabled: Boolean ->
|
||||||
|
val tasksNames = ArrayList<String>()
|
||||||
|
try {
|
||||||
|
tasksNames.add(getCpdTask(isAndroidProject, sources))
|
||||||
|
tasksNames.add(getKotlinDetektTask())
|
||||||
|
if (isAndroidProject) {
|
||||||
|
if (checkstyleEnabled) {
|
||||||
|
tasksNames.add(getCheckstyleTask(sources))
|
||||||
|
}
|
||||||
|
if (pmdEnabled) {
|
||||||
|
tasksNames.add(getPmdTask(sources))
|
||||||
|
}
|
||||||
|
tasksNames.add(getLintTask(buildVariant))
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
println(exception.toString())
|
||||||
|
}
|
||||||
|
tasksNames
|
||||||
|
}
|
||||||
|
|
||||||
|
extra["generateReport"] = { isAndroidProject: Boolean ->
|
||||||
|
val consoleReport = StringBuilder()
|
||||||
|
consoleReport.append("STATIC ANALYSIS RESULTS:")
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
var previousCount = count
|
||||||
|
count = appendCpdErrors(count, File("${project.buildDir}/reports/cpd.xml"))
|
||||||
|
if (count - previousCount > 0) {
|
||||||
|
consoleReport.append("\nCPD: FAILED (" + (count - previousCount) + " errors)")
|
||||||
|
} else {
|
||||||
|
consoleReport.append("\nCPD: PASSED")
|
||||||
|
}
|
||||||
|
|
||||||
|
previousCount = count
|
||||||
|
count = appendKotlinErrors(count, File("${project.buildDir}/reports/kotlin-detekt.xml"))
|
||||||
|
if (count - previousCount > 0) {
|
||||||
|
consoleReport.append("\nKotlin-detekt: FAILED (" + (count - previousCount) + " errors)")
|
||||||
|
} else {
|
||||||
|
consoleReport.append("\nKotlin-detekt: PASSED")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAndroidProject) {
|
||||||
|
val checkstyleFile = 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val pmdFile = 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, File("${project.buildDir}/reports/lint_report.xml"))
|
||||||
|
if (count - previousCount > 0) {
|
||||||
|
consoleReport.append("\nLint: FAILED (" + (count - previousCount) + " errors)")
|
||||||
|
} else {
|
||||||
|
consoleReport.append("\nLint: PASSED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
consoleReport.append("\nOverall: FAILED (" + count + " errors)")
|
||||||
|
throw Exception(consoleReport.toString())
|
||||||
|
} else {
|
||||||
|
consoleReport.append("\nOverall: PASSED")
|
||||||
|
println(consoleReport.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendError(number: Any, analyzer: String, file: Any?, line: Any?, errorId: Any?, errorLink: Any?, description: Any?): Unit {
|
||||||
|
println("$number. $analyzer : $description ($errorId)\n\tat $file: $line")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendKotlinErrors(count: Int, checkstyleFile: File): Int {
|
||||||
|
var newNode = count
|
||||||
|
val rootNode = XmlParser().parse(checkstyleFile)
|
||||||
|
for (fileNode in (rootNode.children() as List<Node>)) {
|
||||||
|
if (fileNode.name() != "file") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (errorNode in (fileNode.children() as List<Node>)) {
|
||||||
|
if (errorNode.name() != "error") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newNode++
|
||||||
|
|
||||||
|
appendError(
|
||||||
|
newNode,
|
||||||
|
"Detekt",
|
||||||
|
fileNode.attribute("name"),
|
||||||
|
errorNode.attribute("line"),
|
||||||
|
errorNode.attribute("source"),
|
||||||
|
"",
|
||||||
|
errorNode.attribute("message")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newNode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendCpdErrors(count: Int, cpdFile: File): Int {
|
||||||
|
var newCount = count
|
||||||
|
val rootNode = XmlParser().parse(cpdFile)
|
||||||
|
for (duplicationNode in (rootNode.children() as List<Node>)) {
|
||||||
|
if (duplicationNode.name() != "duplication") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCount++
|
||||||
|
|
||||||
|
var duplicationIndex = 0
|
||||||
|
|
||||||
|
var duplicationPoints = ""
|
||||||
|
for (filePointNode in (duplicationNode.children() as List<Node>)) {
|
||||||
|
if (filePointNode.name() == "file") {
|
||||||
|
val file = filePointNode.attribute("path")
|
||||||
|
val line = filePointNode.attribute("line")
|
||||||
|
duplicationPoints += "\n $file:$line"
|
||||||
|
duplicationIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("$newCount CPD: code duplication $duplicationPoints")
|
||||||
|
}
|
||||||
|
return newCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendCheckstyleErrors(count: Int, checkstyleFile: File): Int {
|
||||||
|
var newCount = count
|
||||||
|
val rootNode = XmlParser().parse(checkstyleFile)
|
||||||
|
for (fileNode in (rootNode.children() as List<Node>)) {
|
||||||
|
if (fileNode.name() != "file") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (errorNode in (fileNode.children() as List<Node>)) {
|
||||||
|
if (!errorNode.name().equals("error")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCount++
|
||||||
|
|
||||||
|
val error = errorNode.attribute("source")?.toString().orEmpty()
|
||||||
|
val link = "http://checkstyle.sourceforge.net/apidocs/" + error.replace(".", "/") + ".html"
|
||||||
|
appendError(
|
||||||
|
newCount,
|
||||||
|
"Checkstyle",
|
||||||
|
fileNode.attribute("name"),
|
||||||
|
errorNode.attribute("line"),
|
||||||
|
error,
|
||||||
|
link,
|
||||||
|
errorNode.attribute("message")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendPmdErrors(count: Int, pmdFile: File): Int {
|
||||||
|
var newCount = count
|
||||||
|
val rootNode = XmlParser().parse(pmdFile)
|
||||||
|
for (fileNode in (rootNode.children() as List<Node>)) {
|
||||||
|
if (fileNode.name() != "file") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (errorNode in (fileNode.children() as List<Node>)) {
|
||||||
|
if (errorNode.name() != "violation") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCount++
|
||||||
|
|
||||||
|
appendError(
|
||||||
|
newCount,
|
||||||
|
"PMD",
|
||||||
|
fileNode.attribute("name"),
|
||||||
|
errorNode.attribute("beginline"),
|
||||||
|
errorNode.attribute("rule")?.toString().orEmpty().trim(),
|
||||||
|
errorNode.attribute("externalInfoUrl")?.toString().orEmpty().trim(),
|
||||||
|
errorNode.text().orEmpty().trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendLintErrors(count: Int, lintFile: File): Int {
|
||||||
|
var newCount = count
|
||||||
|
val rootNode = XmlParser().parse(lintFile)
|
||||||
|
for (issueNode in rootNode.children()) {
|
||||||
|
if ((issueNode as Node).name() != "issue"
|
||||||
|
|| issueNode.attribute("severity") != "Error") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (locationNode in (issueNode.children() as List<Node>)) {
|
||||||
|
if (locationNode.name() != "location") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newCount++
|
||||||
|
appendError(
|
||||||
|
newCount,
|
||||||
|
"Lint",
|
||||||
|
locationNode.attribute("file"),
|
||||||
|
locationNode.attribute("line"),
|
||||||
|
issueNode.attribute("id"),
|
||||||
|
issueNode.attribute("explanation"),
|
||||||
|
issueNode.attribute("message")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCpdTask(isAndroidProject: Boolean, sources: List<String>): String =
|
||||||
|
(extra["getCpdTask"] as Closure<String>)(isAndroidProject, sources)
|
||||||
|
|
||||||
|
fun getPmdTask(sources: List<String>): String {
|
||||||
|
val taskName = "pmd_${project.name}"
|
||||||
|
var task = tasks.findByName(taskName)
|
||||||
|
if (task == null) {
|
||||||
|
task = tasks.create(taskName, Pmd::class.java) {
|
||||||
|
pmdClasspath = configurations["pmd"].asFileTree
|
||||||
|
ruleSetFiles = files("$buildScriptsDir/pmd/rulesets/java/android.xml")
|
||||||
|
ruleSets = emptyList()
|
||||||
|
source = files(sources).asFileTree
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
html.isEnabled = true
|
||||||
|
html.destination = file("${project.buildDir}/reports/pmd.html")
|
||||||
|
|
||||||
|
xml.isEnabled = true
|
||||||
|
xml.destination = file("${project.buildDir}/reports/pmd.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return task!!.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLintTask(buildVariant: ApplicationVariant?): String =
|
||||||
|
(extra["getLintTask"] as Closure<String>)(buildVariant)
|
||||||
|
|
||||||
|
fun getCheckstyleTask(sources: List<String>): String {
|
||||||
|
val taskName = "checkstyle_${project.name}"
|
||||||
|
var task = tasks.findByName(taskName)
|
||||||
|
if (task == null) {
|
||||||
|
val compileReleaseTask = tasks.matching {
|
||||||
|
it.name.contains("compile")
|
||||||
|
&& it.name.contains("Release")
|
||||||
|
&& it.name.contains("Java") &&
|
||||||
|
!it.name.contains("UnitTest")
|
||||||
|
}.last()
|
||||||
|
task = tasks.create(taskName, Checkstyle::class.java) {
|
||||||
|
ignoreFailures = true
|
||||||
|
isShowViolations = false
|
||||||
|
source = files(sources).asFileTree
|
||||||
|
configFile = file("$buildScriptsDir/checkstyle/configuration/touchin_checkstyle.xml")
|
||||||
|
checkstyleClasspath = configurations["checkstyle"].asFileTree
|
||||||
|
classpath = files(System.getenv("ANDROID_HOME") + "/platforms/" + (extensions["android"] as AppExtension).compileSdkVersion + "/android.jar") +
|
||||||
|
files(System.getProperties()["java.home"].toString() + "/lib/rt.jar") +
|
||||||
|
files((compileReleaseTask.extra["classpath"] as String))
|
||||||
|
reports {
|
||||||
|
xml.isEnabled = true
|
||||||
|
xml.destination = file("${project.buildDir}/reports/checkstyle.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return task!!.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKotlinDetektTask() = "detekt"
|
||||||
|
|
||||||
|
task("optimizePng") {
|
||||||
|
doFirst {
|
||||||
|
val jarArgs = ArrayList<String>()
|
||||||
|
jarArgs.add(configurations["pngtastic"].asPath)
|
||||||
|
val relatedPathIndex = "$rootDir".length + 1
|
||||||
|
for (file in fileTree("dir" to "$rootDir", "include" to "**/src/**/res/drawable**/*.png")) {
|
||||||
|
jarArgs.add(file.absolutePath.substring(relatedPathIndex))
|
||||||
|
}
|
||||||
|
for (file in fileTree("dir" to "$rootDir", "include" to "**/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")
|
||||||
|
|
||||||
|
"checkstyle"("ru.touchin:checkstyle:7.6.2-fork")
|
||||||
|
|
||||||
|
"pngtastic"("com.github.depsypher:pngtastic:1.2")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
|
apply plugin: 'cpd'
|
||||||
|
apply plugin: 'pmd'
|
||||||
|
apply plugin: 'io.gitlab.arturbosch.detekt'
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
pngtastic
|
||||||
|
}
|
||||||
|
|
||||||
|
cpd {
|
||||||
|
skipLexicalErrors = true
|
||||||
|
}
|
||||||
|
|
||||||
|
detekt {
|
||||||
|
input = files("${rootDir}")
|
||||||
|
config = files("$buildScriptsDir/kotlin/detekt-config.yml")
|
||||||
|
|
||||||
|
// TODO add excludes from rootProject.extensions.findByName("staticAnalysisExcludes")
|
||||||
|
filters = ".*src/test.*,.*/resources/.*,.*/tmp/.*,.*/build/.*,.*\\.kts"
|
||||||
|
|
||||||
|
reports {
|
||||||
|
html {
|
||||||
|
enabled = true
|
||||||
|
destination = file("${project.buildDir}/reports/kotlin-detekt.html")
|
||||||
|
}
|
||||||
|
xml {
|
||||||
|
enabled = true
|
||||||
|
destination = file("${project.buildDir}/reports/kotlin-detekt.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.getCpdTask = { isAndroidProject, sources ->
|
||||||
|
def taskName = (isAndroidProject ? "android" : "server") + "cpd_${project.name}"
|
||||||
|
def task = tasks.findByName(taskName)
|
||||||
|
if (task == null) {
|
||||||
|
task = 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 task.name
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.getLintTask = { buildVariant ->
|
||||||
|
def lintTaskName
|
||||||
|
if (buildVariant != null) {
|
||||||
|
lintTaskName = "lint${buildVariant.name.capitalize()}"
|
||||||
|
} else {
|
||||||
|
def lintDebugTask = tasks.matching { it.getName().contains("lint") && it.getName().contains("Debug") }.first()
|
||||||
|
lintTaskName = 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 = false
|
||||||
|
android.lintOptions.lintConfig = file "$buildScriptsDir/lint/lint.xml"
|
||||||
|
return lintTaskName
|
||||||
|
}
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
def getServerProjectSources
|
|
||||||
def getAndroidProjectSources
|
|
||||||
|
|
||||||
apply from: "$buildScriptsDir/gradle/commonStaticAnalysis.gradle"
|
|
||||||
|
|
||||||
gradle.projectsEvaluated {
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.compilerArgs <<
|
|
||||||
"-Xlint:cast" <<
|
|
||||||
"-Xlint:divzero" <<
|
|
||||||
"-Xlint:empty" <<
|
|
||||||
"-Xlint:deprecation" <<
|
|
||||||
"-Xlint:finally" <<
|
|
||||||
"-Xlint:overrides" <<
|
|
||||||
"-Xlint:path" <<
|
|
||||||
"-Werror"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 androidIdeaFormatTask = getIdeaFormatTask(true, androidSources)
|
|
||||||
|
|
||||||
task staticAnalysisWithFormatting {
|
|
||||||
androidStaticAnalysisTasks.each { task ->
|
|
||||||
tasks.findByName(task).mustRunAfter(androidIdeaFormatTask)
|
|
||||||
}
|
|
||||||
dependsOn androidIdeaFormatTask
|
|
||||||
dependsOn androidStaticAnalysisTasks
|
|
||||||
doFirst {
|
|
||||||
generateReport(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task staticAnalysis {
|
|
||||||
dependsOn androidStaticAnalysisTasks
|
|
||||||
doFirst {
|
|
||||||
generateReport(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def serverStaticAnalysisTasks = getStaticAnalysisTaskNames(false, getServerProjectSources(excludes), null, checkstyleEnabled, pmdEnabled)
|
|
||||||
def serverIdeaFormatTask = getIdeaFormatTask(false, getServerProjectSources(excludes))
|
|
||||||
|
|
||||||
task serverStaticAnalysisWithFormatting {
|
|
||||||
serverStaticAnalysisTasks.each { task ->
|
|
||||||
tasks.findByName(task).mustRunAfter(serverIdeaFormatTask)
|
|
||||||
}
|
|
||||||
dependsOn serverIdeaFormatTask
|
|
||||||
dependsOn serverStaticAnalysisTasks
|
|
||||||
doFirst {
|
|
||||||
generateReport(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task serverStaticAnalysis {
|
|
||||||
dependsOn serverStaticAnalysisTasks
|
|
||||||
doFirst {
|
|
||||||
generateReport(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginManager.withPlugin('com.android.application') {
|
|
||||||
android.applicationVariants.all { variant ->
|
|
||||||
task("staticAnalysis${variant.name.capitalize()}") {
|
|
||||||
dependsOn getStaticAnalysisTaskNames(true, androidSources, variant, checkstyleEnabled, pmdEnabled)
|
|
||||||
doFirst { generateReport(true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getServerProjectSources = { excludes ->
|
|
||||||
def sources = new ArrayList<String>()
|
|
||||||
def sourcesDirectory = new File(project.projectDir.path, 'src')
|
|
||||||
|
|
||||||
for (def sourceFlavorDirectory : sourcesDirectory.listFiles()) {
|
|
||||||
def javaSourceDirectory = new File(sourceFlavorDirectory.path, 'java')
|
|
||||||
def kotlinSourceDirectory = new File(sourceFlavorDirectory.path, 'kotlin')
|
|
||||||
|
|
||||||
if (javaSourceDirectory.exists() && javaSourceDirectory.isDirectory()) {
|
|
||||||
sources.add(javaSourceDirectory.absolutePath)
|
|
||||||
}
|
|
||||||
if (kotlinSourceDirectory.exists() && kotlinSourceDirectory.isDirectory()) {
|
|
||||||
sources.add(kotlinSourceDirectory.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sources
|
|
||||||
}
|
|
||||||
|
|
||||||
getAndroidProjectSources = { excludes ->
|
|
||||||
def sources = new ArrayList<String>()
|
|
||||||
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 javaSourceDirectory = new File(sourceFlavorDirectory.path, 'java')
|
|
||||||
def kotlinSourceDirectory = new File(sourceFlavorDirectory.path, 'kotlin')
|
|
||||||
|
|
||||||
if (javaSourceDirectory.exists() && javaSourceDirectory.isDirectory()) {
|
|
||||||
sources.add(javaSourceDirectory.absolutePath)
|
|
||||||
}
|
|
||||||
if (kotlinSourceDirectory.exists() && kotlinSourceDirectory.isDirectory()) {
|
|
||||||
sources.add(kotlinSourceDirectory.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sources
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
import com.android.build.gradle.AppExtension
|
||||||
|
import com.android.build.gradle.api.ApplicationVariant
|
||||||
|
import groovy.lang.Closure
|
||||||
|
|
||||||
|
val buildScriptsDir: String by rootProject.extra
|
||||||
|
apply(from = "$buildScriptsDir/gradle/commonStaticAnalysis.gradle.kts")
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
maven("https://plugins.gradle.org/m2/")
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:3.4.2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val getStaticAnalysisTaskNames: (Boolean, List<String>, ApplicationVariant?, Boolean, Boolean) -> List<String> by extra
|
||||||
|
val getIdeaFormatTask: (isAndroidProject: Boolean, sources: List<String>) -> Task by extra
|
||||||
|
val generateReport: (isAndroidProject: Boolean) -> Unit by extra
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//val getStaticAnalysisTaskNames: Closure<List<String>> by extra
|
||||||
|
//val getIdeaFormatTask: Closure<Task> by extra
|
||||||
|
//val generateReport: Closure<Unit> by extra
|
||||||
|
|
||||||
|
gradle.projectsEvaluated {
|
||||||
|
tasks.withType(JavaCompile::class.java) {
|
||||||
|
options.compilerArgs = listOf(
|
||||||
|
"-Xlint:cast",
|
||||||
|
"-Xlint:divzero",
|
||||||
|
"-Xlint:empty",
|
||||||
|
"-Xlint:deprecation",
|
||||||
|
"-Xlint:finally",
|
||||||
|
"-Xlint:overrides",
|
||||||
|
"-Xlint:path",
|
||||||
|
"-Werror"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val excludes = rootProject.extensions.findByName("staticAnalysisExcludes") as List<String>?
|
||||||
|
val checkstyleEnabled = rootProject.extensions.findByName("checkstyleEnabled") as? Boolean ?: false
|
||||||
|
val pmdEnabled = rootProject.extensions.findByName("pmdEnabled") as? Boolean ?: false
|
||||||
|
|
||||||
|
val androidSources = getAndroidProjectSources(excludes)
|
||||||
|
val androidStaticAnalysisTasks = getStaticAnalysisTaskNames(true, androidSources, null, checkstyleEnabled, pmdEnabled) as List<String>
|
||||||
|
val androidIdeaFormatTask = getIdeaFormatTask(true, androidSources) as Task
|
||||||
|
|
||||||
|
task("staticAnalysisWithFormatting") {
|
||||||
|
androidStaticAnalysisTasks.forEach { task ->
|
||||||
|
tasks.findByName(task)?.mustRunAfter(androidIdeaFormatTask)
|
||||||
|
}
|
||||||
|
dependsOn(androidIdeaFormatTask)
|
||||||
|
dependsOn(androidStaticAnalysisTasks)
|
||||||
|
doFirst {
|
||||||
|
generateReport(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task("staticAnalysis") {
|
||||||
|
dependsOn(androidStaticAnalysisTasks)
|
||||||
|
doFirst {
|
||||||
|
generateReport(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val serverStaticAnalysisTasks = getStaticAnalysisTaskNames(false, getServerProjectSources(excludes), null, checkstyleEnabled, pmdEnabled) as List<String>
|
||||||
|
val serverIdeaFormatTask = getIdeaFormatTask(false, getServerProjectSources(excludes)) as Task
|
||||||
|
|
||||||
|
task("serverStaticAnalysisWithFormatting") {
|
||||||
|
serverStaticAnalysisTasks.forEach { task ->
|
||||||
|
tasks.findByName(task)?.mustRunAfter(serverIdeaFormatTask)
|
||||||
|
}
|
||||||
|
dependsOn(serverIdeaFormatTask)
|
||||||
|
dependsOn(serverStaticAnalysisTasks)
|
||||||
|
doFirst {
|
||||||
|
generateReport(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task("serverStaticAnalysis") {
|
||||||
|
dependsOn(serverStaticAnalysisTasks)
|
||||||
|
doFirst {
|
||||||
|
generateReport(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginManager.withPlugin("com.android.application") {
|
||||||
|
(rootProject.extensions.findByName("android") as AppExtension).applicationVariants.forEach { variant ->
|
||||||
|
task("staticAnalysis") {
|
||||||
|
val tasks = (getStaticAnalysisTaskNames(true, androidSources, variant, checkstyleEnabled, pmdEnabled) as List<String>)
|
||||||
|
dependsOn(tasks)
|
||||||
|
doFirst {
|
||||||
|
generateReport(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getServerProjectSources(excludes: List<String>?): List<String> {
|
||||||
|
val sources = ArrayList<String>()
|
||||||
|
val sourcesDirectory = File(project.projectDir.path, "src")
|
||||||
|
|
||||||
|
for (sourceFlavorDirectory in sourcesDirectory.listFiles().orEmpty()) {
|
||||||
|
val javaSourceDirectory = File(sourceFlavorDirectory.path, "java")
|
||||||
|
val kotlinSourceDirectory = File(sourceFlavorDirectory.path, "kotlin")
|
||||||
|
|
||||||
|
if (javaSourceDirectory.exists() && javaSourceDirectory.isDirectory) {
|
||||||
|
sources.add(javaSourceDirectory.absolutePath)
|
||||||
|
}
|
||||||
|
if (kotlinSourceDirectory.exists() && kotlinSourceDirectory.isDirectory) {
|
||||||
|
sources.add(kotlinSourceDirectory.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAndroidProjectSources(excludes: List<String>?): ArrayList<String> {
|
||||||
|
val sources = ArrayList<String>()
|
||||||
|
for (project in rootProject.subprojects) {
|
||||||
|
if (project.subprojects.isNotEmpty() || (excludes != null && excludes.contains(project.path))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourcesDirectory = File(project.projectDir.path, "src")
|
||||||
|
if (!sourcesDirectory.exists() || !sourcesDirectory.isDirectory) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for (sourceFlavorDirectory in sourcesDirectory.listFiles().orEmpty()) {
|
||||||
|
val javaSourceDirectory = File(sourceFlavorDirectory.path, "java")
|
||||||
|
val kotlinSourceDirectory = File(sourceFlavorDirectory.path, "kotlin")
|
||||||
|
|
||||||
|
if (javaSourceDirectory.exists() && javaSourceDirectory.isDirectory) {
|
||||||
|
sources.add(javaSourceDirectory.absolutePath)
|
||||||
|
}
|
||||||
|
if (kotlinSourceDirectory.exists() && kotlinSourceDirectory.isDirectory) {
|
||||||
|
sources.add(kotlinSourceDirectory.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources
|
||||||
|
}
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
import groovy.json.JsonSlurper
|
|
||||||
import groovy.xml.MarkupBuilder
|
|
||||||
|
|
||||||
task stringGenerator {
|
|
||||||
generate(android.languageMap)
|
|
||||||
println("Strings generated!")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def generate(Map<String, String> sources) {
|
|
||||||
if (sources == null || sources.isEmpty()) {
|
|
||||||
throw new IOException("languageMap can't be null or empty")
|
|
||||||
}
|
|
||||||
Map jsonMap = getJsonsMap(sources)
|
|
||||||
def diffs = calcDiffs(jsonMap)
|
|
||||||
if (!diffs.isEmpty()) {
|
|
||||||
printDiffs(diffs)
|
|
||||||
throw new IllegalStateException("Strings source can't be different")
|
|
||||||
}
|
|
||||||
def defaultLang = getDefaultLangKey(sources)
|
|
||||||
jsonMap.forEach { key, json ->
|
|
||||||
|
|
||||||
def sw = new StringWriter()
|
|
||||||
def xml = new MarkupBuilder(sw)
|
|
||||||
|
|
||||||
xml.setDoubleQuotes(true)
|
|
||||||
xml.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
|
|
||||||
xml.resources() {
|
|
||||||
json.each {
|
|
||||||
k, v ->
|
|
||||||
string(name: "${k}", formatted: "false", "${v}".replace('\n', '\\n'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def stringsFile = getFile(key, key == defaultLang)
|
|
||||||
stringsFile.write(sw.toString(), "UTF-8")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private printDiffs(Map<?, ?> diffs) {
|
|
||||||
def diffLog = new StringBuilder()
|
|
||||||
diffs.forEach { k, v ->
|
|
||||||
if (v.size() > 0) {
|
|
||||||
diffLog.append("For $k was missed string keys: ${v.size()}\n")
|
|
||||||
v.forEach {
|
|
||||||
diffLog.append("\tString key: $it\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println(diffLog.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
private static def calcDiffs(Map<String, Object> jsonsMap) {
|
|
||||||
if (jsonsMap.size() == 1) {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
def keys = jsonsMap.collectEntries {
|
|
||||||
[(it.key): (it.value).keySet() as List]
|
|
||||||
}
|
|
||||||
def inclusive = keys.get(keys.keySet().first())
|
|
||||||
def diffs = keys.collectEntries {
|
|
||||||
[(it.key): inclusive - it.value.intersect(inclusive)]
|
|
||||||
}.findAll { it.value.size() > 0 }
|
|
||||||
return diffs
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, Object> getJsonsMap(Map sources) {
|
|
||||||
return sources.collectEntries {
|
|
||||||
[(it.key): new JsonSlurper().parseText(new File(it.value).text)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getFile(String key, boolean defaultLang) {
|
|
||||||
if (defaultLang) {
|
|
||||||
return new File("app/src/main/res/values/strings.xml")
|
|
||||||
} else {
|
|
||||||
def directory = new File("app/src/main/res/values-$key")
|
|
||||||
if (!directory.exists()) {
|
|
||||||
directory.mkdir()
|
|
||||||
}
|
|
||||||
return new File("app/src/main/res/values-$key/strings.xml")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getDefaultLangKey(Map<String, String> sources) {
|
|
||||||
def defaultLanguage = sources.find { it.value.contains("default") }
|
|
||||||
if (defaultLanguage != null) {
|
|
||||||
return defaultLanguage.key
|
|
||||||
} else {
|
|
||||||
throw new IOException("Can't find default language")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import org.redundent.kotlin.xml.PrintOptions
|
||||||
|
import org.redundent.kotlin.xml.xml
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("org.redundent:kotlin-xml-builder:1.5.1")
|
||||||
|
classpath("com.google.code.gson:gson:2.8.5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task("stringGenerator") {
|
||||||
|
val languageMap: Map<String, String>? by rootProject.extra
|
||||||
|
generate(languageMap)
|
||||||
|
println("Strings generated!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generate(sources: Map<String, String>?) {
|
||||||
|
if (sources == null || sources.isEmpty()) {
|
||||||
|
throw java.io.IOException("languageMap can't be null or empty")
|
||||||
|
}
|
||||||
|
val jsonMap = getJsonsMap(sources)
|
||||||
|
val diffs = calcDiffs(jsonMap)
|
||||||
|
if (diffs.isNotEmpty()) {
|
||||||
|
printDiffs(diffs)
|
||||||
|
throw IllegalStateException("Strings source can't be different")
|
||||||
|
}
|
||||||
|
val defaultLang = getDefaultLangKey(sources)
|
||||||
|
jsonMap.forEach { (key, json) ->
|
||||||
|
val xmlString = xml("resources") {
|
||||||
|
includeXmlProlog = true
|
||||||
|
json.entrySet().forEach {
|
||||||
|
"string" {
|
||||||
|
attribute("name", it.key)
|
||||||
|
attribute("formatted", "false")
|
||||||
|
-it.value.asString.replace("\n", "\\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val stringsFile = getFile(key, key == defaultLang)
|
||||||
|
stringsFile.writeText(xmlString.toString(PrintOptions(singleLineTextElements = true)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun printDiffs(diffs: Map<String, Set<String>>) {
|
||||||
|
val diffLog = StringBuilder()
|
||||||
|
diffs.forEach { (key, value) ->
|
||||||
|
if (value.isNotEmpty()) {
|
||||||
|
diffLog.append("For $key was missed string keys: ${value.size}\n")
|
||||||
|
value.forEach {
|
||||||
|
diffLog.append("\tString key: $it\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println(diffLog.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calcDiffs(jsonsMap: Map<String, JsonObject>): Map<String, Set<String>> {
|
||||||
|
if (jsonsMap.size == 1) {
|
||||||
|
return emptyMap()
|
||||||
|
}
|
||||||
|
val keys = jsonsMap.mapValues {
|
||||||
|
it.value.keySet().toList()
|
||||||
|
}
|
||||||
|
val inclusive = keys[keys.keys.first()]!!.toSet()
|
||||||
|
return keys.mapValues {
|
||||||
|
inclusive - it.value.intersect(inclusive)
|
||||||
|
}.filter { it.value.isNotEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getJsonsMap(sources: Map<String, String>): Map<String, JsonObject> {
|
||||||
|
return sources.mapValues {
|
||||||
|
JsonParser().parse(File(it.value).readText()).asJsonObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFile(key: String, defaultLang: Boolean): File {
|
||||||
|
if (defaultLang) {
|
||||||
|
return File("app/src/main/res/values/strings.xml")
|
||||||
|
} else {
|
||||||
|
val directory = File("app/src/main/res/values-$key")
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdir()
|
||||||
|
}
|
||||||
|
return File("app/src/main/res/values-$key/strings.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDefaultLangKey(sources: Map<String, String>): String {
|
||||||
|
val defaultLanguage = sources
|
||||||
|
.filter { it.value.contains("default") }
|
||||||
|
.iterator().run {
|
||||||
|
if (hasNext()) next() else null
|
||||||
|
}
|
||||||
|
if (defaultLanguage != null) {
|
||||||
|
return defaultLanguage.key
|
||||||
|
} else {
|
||||||
|
throw java.io.IOException("Can't find default language")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue