Static analysis is an important tool to run over any code base, it can sniff out potential bugs, bad practices, and general formatting errors. This will go over the use and setup of three such tools in the context of Android.
The three tools that are explained in this article are:
- PMD
- FindBugs
- Checkstyle
For the setup of these tasks, I find the best thing to do is to put each of
them in their own gradle file named {taskname}.gradle, so pmd.gradle or
checkstyle.gradle, this allows easier reuse and doesn’t bloat the main
projects gradle file.
A complete code example of everything explained can be found on GitHub.
PMD
PMD is a static analysis tool that runs on the source code itself.
PMD Gradle Task
apply plugin: 'pmd'
// https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.PmdExtension.html
task pmd(
type: Pmd,
dependsOn: assemble,
group: 'verification'
) {
description 'Runs the PMD task and generates reports to the build/reports directory'
ignoreFailures = false
ruleSets = [
"java-basic",
"java-braces",
"java-strings",
"java-design",
"java-unusedcode",
"java-android"
]
source 'src/main/java'
include '**/*.java'
exclude '**/gen/**'
reports {
xml {
enabled = true
destination "$project.buildDir/reports/pmd/pmd.xml"
}
html {
enabled = true
destination "$project.buildDir/reports/pmd/pmd.html"
}
}
}
PMD Config
The pmd task can be configured by changing the ruleSets used in the gradle task above.
FindBugs
FindBugs is a static analysis tool that runs on the compiled byte code.
FindBugs is now stale and no longer updated, there is an fork that carried on development called SpotBugs, but I was having trouble configuring it to work with Android. Despite this, FindBugs still provides some valuable information
FindBugs Gradle Task
apply plugin: 'findbugs'
// https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.FindBugsExtension.html
task findbugs(
type: FindBugs,
dependsOn: assemble,
group: 'verification'
) {
description 'Runs the FindBugs task and generates reports to the build/reports directory'
classes = files("$projectDir.absolutePath/build/intermediates/classes")
classpath = files()
source 'src/main/java'
include '**/*.java'
exclude '**/gen/**'
effort = "max"
reportLevel = "low"
ignoreFailures = false
excludeFilter = new File("${project.rootDir}/analysis-configs/findbugs-excludefilter.xml")
reports {
xml {
enabled = false
destination "$project.buildDir/reports/findbugs/findbugs.xml"
}
html {
enabled = true
destination "$project.buildDir/reports/findbugs/findbugs.html"
}
}
}
FindBugs Config
It should be noted, that FindBugs will not allow you to output an xml and a html report, you must choose one or the other. FindBugs will flag some default Android items up in the reports, to skip this the excludeFilter xml file is used.
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Or>
<Class name="~.*\.R\$.*"/>
<Class name="~.*\.Manifest\$.*"/>
</Or>
</Match>
</FindBugsFilter>
If you are using Databinding with the Android project, the generated code will likely also need to be added to the list of excludes so it doesn’t flag up any issues.
CheckStyle
CheckStyle is used to ensure that a project assumes a defined coding style.
CheckStyle Gradle Task
apply plugin: 'checkstyle'
// https://docs.gradle.org/current/dsl/org.gradle.api.plugins.quality.CheckstyleExtension.html
task checkstyle(
type: Checkstyle,
group: 'verification'
) {
description 'Runs the CheckStyle task and generates reports to the build/reports directory'
classpath = files()
source 'src/main/java'
include '**/*.java'
exclude '**/gen/**'
configFile = file("$project.rootDir/analysis-configs/checkstyle-rules.xml")
reports {
xml {
enabled = true
destination "$project.buildDir/reports/checkstyle/checkstyle.xml"
}
html {
enabled = true
destination "$project.buildDir/reports/checkstyle/checkstyle.html"
}
}
}
CheckStyle Config
CheckStyles configuration is all handled within the configuration xml file
configFile = file("$project.rootDir/analysis-configs/checkstyle-rules.xml").
Below is a very barebones example, to include more checkstyle settings in the
project another line just needs to be added to the TreeWalker section in the
xml.
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
<module name = "Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<module name="TreeWalker">
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
</module>
</module>
A full list of possible configurations are available at http://checkstyle.sourceforge.net/checks.html.
All together
In order to allow your gradle to use them they need to be applied in the modules build.gradle file. At the top of your modules build.gradle file, apply these gradle files and they will become available in your gradle verification tab.
apply plugin: 'com.android.application'
apply from: 'pmd.gradle'
apply from: 'findbugs.gradle'
apply from: 'checkstyle.gradle'
android { ...
