Preface
A static code detection tool, including Ali java protocol detection and link detection, supports custom pmd and link configurations, and integrates git with incremental detection plug-ins when submitting code. Hector Chris, also known as the Hundred-Armed Giant, comes from Greek mythology and is the son of Uranus and Gaia, the God of the sky, with 50 heads and 100 heads. After helping Zeus win the throne, he became the gatekeeper of Tartarus, which is the hell of Greek mythology.
Why Write Giant Hundred Arms
Many times, some bug s are caused by code irregularity. This kind of low-level error often causes great losses. We have encountered the situation of loading pictures in the main thread before. Before, because the pictures of operation configuration are smaller, so there is nothing wrong. Until one day, operation configuration has a big picture, which directly leads to big picture. Area ANR, which is a low-level error, later I wrote a custom link detection tool to detect this use of BitmapFactory in the main thread. Then I wrote some other lint detection rules, and then I met two other problems. Firstly, my lint was detected and marked, but the developer would still ignore him. It's no use to detect it without modifying it. How to force developers to test is a problem. Second, the project is huge, the overall testing time is long, and the problems detected are tens of thousands. Who will change these problems? Chen Sesame Rotten Millet Code, Who would like to change it? For this reason, I need an incremental detection scheme, combined with git, which only detects the files to be submitted for modification, so that those who modify who are unlucky, resign to fate, and avoid complaints.
Schematic diagram
Core code
Get the git modification file
Incremental detection requires the corresponding file, which uses git's command
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 }
Except deleted files, other files are extracted as incremental files.
Access of Ali Protocol Detection
Ali gives a detailed description of the Java code specification. Here we use the detection tools in the detection plug-in of the Ali specification to detect the Java files.
First add p3c dependencies to the project
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)) }
In this way, the PMD rule of Ali protocol is quoted, and the corresponding configuration is supported by introducing pmd.
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 detection
lint detection flow chart
Because of the lack of text, the detection function of lint can only be obtained by reading the source code. There are mainly two tasks, one is IncrementLint Global task, which can detect all variants of the project, and the other is IncrementLint PerVariant task, which can generate different detection according to different variants of the project. task, the detection range is also limited to the corresponding variants.
The default linttash is to detect all files. In order to detect incremental files, we need to override LintGradleClient's createLintRequest method. So I wrote an IncrementLintGradleClient.
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's API often changes, and the scope of modification is still very large. In order to shield the corresponding lint api, gradle differences and kotlin compiler differences, we use Increment Reflective Lint Runner to invoke link detection. This is no different from Reflective Lint Runner in the original project plug-in, but using our own Increment. EntLintGradleExecution is used to analyze incremental files, while allowing current projects to reference plug-ins using lintclasspath in plug-ins
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 ) }
This way the jar package path of the plug-in can be obtained by IncrementReflectiveLintRunner, and you can use your own classloader to load IncrementLintGradleExecution.
Last
This project feels like a relatively perfect static detection method. See the link below for demo.