Hundred-Armed Giant and Tartarus

Keywords: Programming git Gradle Java xml

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.

Project address

Posted by omarh2005 on Tue, 23 Jul 2019 07:05:15 -0700