一款静态代码检测工具,包含阿里java规约检测和lint检测,支持自定义pmd和lint配置,结合git在代码提交时进行增量检测插件,赫卡同克瑞斯,也就是百臂巨人,来自希腊神话,是天空神乌拉诺斯和地神盖娅的儿子,拥有50头和100个手臂,在帮助宙斯夺得神位后,成为了塔尔塔罗斯的守门人,塔尔塔罗斯就是希腊神话中的地狱。html
不少时候有些bug就是由于代码不规范形成的,这种低级错误每每会形成重大损失,以前就曾经碰到过在主线程加载图片的状况,以前由于运营配置的图片较小,因此也就没什么事,直到有一天运营配置了一个大图,直接致使大面积的ANR,这是一个低级错误,后来我写了一个自定义lint检测工具,用来检测这种在主线程使用BitmapFactory的状况。以后又写了一些其余的lint检测规则,以后遇到了另外两个问题,第一,个人lint检测出来了,标注出来了,可是开发者依旧会对他进行忽视,检测出来而不修改那不就是白搞了?如何强制开发者进行检测,这是一个问题。第二,项目庞大,总体检测时间很长,并且检测出的问题都上万了,这些问题谁来改?陈芝麻烂谷子的代码,又有谁愿意改?为此我须要一个增量检测的方案,结合git,只对要提交的修改的文件进行检测,这样谁修改谁倒霉,听天由命,避免抱怨。java
增量检测,必然要相应的文件,这里利用了git的命令git
fun getCommitFiles(): List<File> {
val command =
arrayOf("/bin/bash", "-c", "git diff --name-only --diff-filter=ACMRTUXB HEAD")
val process = Runtime.getRuntime().exec(command)
process.waitFor()
val commitFileList = mutableListOf<File>()
try {
val inputReader = BufferedReader(InputStreamReader(process.inputStream))
var fileName = inputReader.readLine()
while (fileName != null) {
commitFileList.add(File(fileName))
fileName = inputReader.readLine()
}
inputReader.close()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: Exception) {
e.printStackTrace()
}
return commitFileList
}
复制代码
除了删除的文件,其余文件都会做为增量文件提取出来github
阿里对Java代码规范作了详尽的说明,这里咱们利用阿里规约中的检测插件中的检测工具进行java文件的检测 首先为项目添加p3c依赖api
private fun configPmdDependency(project: Project) {
project.plugins.apply(PMD)
val pmdConfig = project.configurations.getByName(PMD_CONFIGURATION)
pmdConfig.dependencies.add(project.dependencies.create(P3C_PMD_DEPENDENCY))
pmdConfig.dependencies.add(project.dependencies.create(PMD_DEPENDENCY))
}
复制代码
这样就引用了阿里规约的pmd规则,同时因为引入pmd,支持相应的配置bash
private fun configPmdTask(project: Project) {
project.afterEvaluate {
val pmdExtension = project.extensions.findByName(PMD) as PmdExtension
val pmdTask = project.tasks.create(PMDTASK, Pmd::class.java)
pmdTask.targetJdk = pmdExtension.targetJdk
pmdTask.ignoreFailures = pmdExtension.isIgnoreFailures
ALIRULESETS.addAll(pmdExtension.ruleSets)
pmdTask.ruleSets = ALIRULESETS
pmdTask.ruleSetFiles = pmdExtension.ruleSetFiles
pmdTask.source(project.rootDir)
pmdTask.isConsoleOutput = pmdExtension.isConsoleOutput
pmdTask.rulePriority = pmdExtension.rulePriority
pmdTask.reports {
it.xml.isEnabled = true
it.xml.destination = File(pmdExtension.reportsDir, "report.xml")
it.html.isEnabled = true
it.html.destination = File(pmdExtension.reportsDir, "report.html")
}
pmdTask.group = GOUP_NAME
pmdTask.include(GitUtil.getCommitFilesPathForPMD())
pmdTask.exclude("**/build/**", "**/res/**", "**/*.xml", "**/*.gradle", "**/*.kt")
}
}
复制代码
lint检测流程图app
默认的linttask是对所有文件进行检测的,为了实现对增量文件的检测,须要重写LintGradleClient的createLintRequest方法,为此我写了一个IncrementLintGradleClientide
class IncrementLintGradleClient(
version: String,
issueRegistry: IssueRegistry,
lintFlags: LintCliFlags,
gradleProject: org.gradle.api.Project,
sdkHome: File?,
variant: Variant?,
variantInputs: VariantInputs?,
buildToolInfo: BuildToolInfo?,
isAndroid: Boolean
) : LintGradleClient(
version,
issueRegistry,
lintFlags,
gradleProject,
sdkHome,
variant,
variantInputs,
buildToolInfo,
isAndroid
) {
override fun createLintRequest(files: MutableList<File>?): LintRequest {
val lintRequest = super.createLintRequest(files)
val commitFiles = GitUtil.getCommitFiles()
lintRequest.getProjects()?.forEach { project ->
commitFiles.forEach {
project.addFile(it)
}
}
return lintRequest
}
}
复制代码
lint的api常常变化,并且修改幅度还很大,为了屏蔽相应的lint api,gradle差别以及kotlin compiler的差别,咱们使用IncrementReflectiveLintRunner来调用lint检测,这个和原项目插件中ReflectiveLintRunner没什么区别,只不过使用了咱们本身的IncrementLintGradleExecution来分析增量文件,同时在插件中让当前项目使用lintclasspath引用插件工具
private fun addLintClassPath(project: Project) {
project.gradle.rootProject.configurations
val classPathConfiguration = project.gradle.rootProject.buildscript.configurations.getByName("classpath")
var hecatoncheiresDependency: Dependency? = null
classPathConfiguration.dependencies.forEach {
if (it.name.contains(Constants.HECATONCHEIRESEXTENSION_NAME)) {
hecatoncheiresDependency = it
return@forEach
}
}
val lintConfiguration = project.configurations.getByName(LintBaseTask.LINT_CLASS_PATH)
project.dependencies.add(
lintConfiguration.name,
hecatoncheiresDependency
)
}
复制代码
这样插件的jar包路径就能被IncrementReflectiveLintRunner得到,而且可使用本身的classloader用来加载IncrementLintGradleExecutiongradle
这个项目感受算是一个较为完善的静态检测方法,使用方法与demo见下面连接