This week, in Open Event Orga App project (Github Repo), we wanted to add some static code analysers that run on each build to ensure that the app code is free of potential bugs and follows a certain style. Codacy handles a few of these things, but it is quirky and sometimes produces false positives. Furthermore, it is not a required check for builds so errors can creep in gradually. We chose checkstyle, PMD and Findbugs for static analysis as they are most popular for Java. The area they work on kind of overlaps but gives security regarding code quality. Findbugs actually analyses the bytecode instead of source code to find possible JVM bugs.
Adding dependencies
The first step was to add the required dependencies. We chose the library android-check as it contained all 3 libraries and was focused on Android and easily configurable. First, we add classpath in project level build.gradle
dependencies { classpath 'com.noveogroup.android:check:1.2.4' }
Then, we apply the plugin in app level build.gradle
apply plugin: 'com.noveogroup.android.check'
This much is enough to get you started, but by default, the build will not fail if any violations are found. To change this behaviour, we add this block in app level build.gradle
check { abortOnError true }
There are many configuration options available for the library. Do check out the project github repo using the link provided above
Configuration
The default configuration is of easy level, and will be enough for most projects, but it is of course configurable. So we took the default hard configs for 3 analysers and disabled properties which we did not need. The place you need to store the config files is the config folder in either root project directory or the app directory. The name of the config file should be checkstyle.xml, pmd.xml and findbugs.xml
These are the default settings and you can obviously configure them by following the instructions on the project repo
Checkstyle
For checkstyle, you can find the easy and hard configuration here
The basic principle is that if you need to add a check, you include a module like this:
<module name="NewlineAtEndOfFile" />
If you want to modify the default value of some property, you do it like this:
<module name="RegexpSingleline"> <property name="format" value="\s+$" /> <property name="minimum" value="0" /> <property name="maximum" value="0" /> <property name="message" value="Line has trailing spaces." /> <property name="severity" value="info" /> </module>
And if you want to remove a check, you can ignore it like this:
<module name="EqualsHashCode"> <property name="severity" value="ignore" /> </module>
It’s pretty straightforward and easy to configure.
Findbugs
For findbugs, you can find the easy and hard configuration here
Findbugs configuration exists in the form of filters where we list resources it should skip analyzing, like:
<Match> <Class name="~.*\.BuildConfig" /> </Match>
If we want to ignore a particular pattern, we can do so like this:
<!-- No need to force hashCode for simple models --> <Match> <Bug pattern="HE_EQUALS_USE_HASHCODE " /> </Match>
Sometimes, you’d want to only ignore a pattern only for certain files or fields. Findbugs supports regex to match such items:
<!-- Don't complain about rules in tests. --> <Match> <Field name="~.*mockitoRule"/> <Bug pattern="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" /> </Match>
You can also annotate your code to suppress warning in the particular class, mehod or field rather than disabling it for the whole project. For that, you need to add findbugs annotations dependency in the project
compile 'com.google.code.findbugs:findbugs-annotations:3.0.1'
And then use it like this:
@SuppressFBWarnings( value = "ICAST_IDIV_CAST_TO_DOUBLE", justification = "We want granularity to be integer") public void showChart(LineChart lineChart) { ... }
It also allows setting the justification of suppressing the rule for clarity
PMD
For findbugs, you can find the easy and hard configuration here
Like checkstyle, you have to first add a rule set to tell PMD which checks to perform:
<rule ref="rulesets/java/android.xml" />
If you want to modify the default value of the rule, you can do it like this:
<rule ref="rulesets/java/codesize.xml/TooManyMethods"> <properties> <property name="maxmethods" value="15" /> </properties> </rule>
Or if you want to entirely exclude a rule, you can do it like this:
<rule ref="rulesets/java/basic.xml"> <exclude name="OverrideBothEqualsAndHashcode" /> </rule>
PMD also supports suppressing warnings in the code itself using annotations. You don’t require any external libraries for it as it supports the in built java.lang.SuppessWarnings annotations. You can use it like this:
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") // Entries cannot be created outside loop private LineDataSet setData(Map<String, Long> map, String label) throws ParseException { ... }
As you can see, we need to prepend “PMD.” to the rule name so that there are no clashes while annotation processing. Remember to comment the reason for suppressing the warning so that your co-developers know and can remove it in future if criteria does not meet anymore.
There is a lot more to learn about these static analyzers, which you can read upon in their official documentation:
- PMD
https://pmd.github.io/ - Checkstyle
http://checkstyle.sourceforge.net/ - Findbugs
http://findbugs.sourceforge.net/