RUNNING CUSTOM “LINT” CHECKS ON YOUR ANDROID BUILDS

by Martin Öbrink-HansenApril 13, 2015

In this post, I’ll share a very simple tip on how to add very simple custom checks of your Android source code to your Jenkins build server, but the tip should be very easily ported to other build servers too.

What?
Most developers know of Lint checks as something which perform some kind of static analysis of their code and which complain heavily about stuff if you are enabling this check for the first time on an old project. Unfortunately, chances are rather high that you choose to disable the check due to time constraints not allowing you to fix all issues right now. Or maybe you actually enable running the checks as part of your build but choose not to make lint errors break it. Those two solutions are equally bad since none of them prevent you from adding bad code to the codebase.

Why?
I won’t go to great lengths to explain why you should perform lint checks, but I’ll say that there are many, many simple checks which can be checked at compile-time and which you (or your colleagues) might not have noticed when implementing a given feature. And why not let the lint tool weed out the stupid errors since it is so much better at detecting these things than you? For example, lint checks can prevent you from publishing an app which crashes on some devices due to code calling APIs, which are not available on devices with too low versions of Android running on them. Lint will compare the minimum API version supported by your app and the API version of every call performed in your app so you can ensure that you have carefully guarded these calls correctly and therefore won’t crash your app at run-time.

How?
First of all, I’m assuming you have moved to an Android Studio/Gradle-based build of your Android project. If not, you should, since Eclipse/Ant-based builds are no longer Googles preferred way of building Android apps.

Enabling lint checks and the details of all configurable options are described in details here, so I’ll just extract the most important details here. In order to enable lint checks on your build, you just have to add the following to your build.gradle-file:

android {

    [...]

    lintOptions {
        textReport true
        ignoreWarnings true
        abortOnError true
    }
}

The […] marks other settings for your app, which have been left out of this post to simplify.

The first (optional) setting, textReport in the lintOptions section makes the output of lint visible on the command line. This is a setting I prefer since most people will have lint running when building on a build server, and if the build fails, most likely you’d like to see the output from the command line (possibly sent by email) and, therefore, it’s preferred to get the lint error output here instead of in a separate file.

The second setting, ignoreWarnings, should be pretty self-explanatory. It can be useful if you only want to focus on errors reported by lint.

The last setting, abortOnError, also doesn’t require too much explanation, I assume, but this setting should obviously be set to true, so that the build actually fails if lint errors are encountered.

A tip for custom grep-like checks
Ok, so you’ve enabled lint checks as described above. But sometimes there are other typical errors frequently introduced that regular lint checking doesn’t catch. I’ll give you an example:

If you are creating an app where security is a major concern, you don’t want to write any debug output to the console/LogCat from your release/production builds. Most likely, you’ll have some sort of mechanism in order to prevent this from happening (using a 3rd party logging framework such as Timber or possibly your own), but there are times where letting your IDE help you by generating code will introduce unwanted code.

If, for example, you write some java code which can result in an unhandled exception like this:

FileReader someFile = new FileReader(new File("some/file.name"));

Then, the IDE, Android Studio in my case, will allow you to “surround with try/catch”, and if you select this option, the following code will be generated by Android Studio:

try {
    FileReader someFile = new FileReader(new File("some/file.name"));
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

The main problem with this code, as you can probably guess, is line 4, where the stacktrace is immediately printed to the console. Your code is hopefully obfuscated by Proguard, so the stacktrace will probably be obfuscated as well, but nevertheless it still presents a problem. Since it is so easy to forget to change this auto-generated line, you’d probably want a way to automatically detect this in your code base. Luckily, this can be done very easily by doing the following:

What we want to do is search through the .java-files in our solution and look for lines containing ‘e.printStackTrace()’. This can be achieved by following these steps:

1) Log into your Jenkins build server and find your build and click on ‘Configure’ to get to the configuration settings of your build.

2) Under the ‘Build’ section, click ‘Add build step’ and select ‘Execute shell’

3) In the text field that appears, copy/paste the following code (keep the long line, line 2, as a single line):

echo CHECKING FOR OCCURENCES OF 'e.printStackTrace()'
find . -iregex '.*\(\.java\)' -print0 | xargs -0 grep -n "e.printStackTrace" | awk ' /e.printStackTrace/ { errorLine[errors] = $1; errors++ } END { if (errors > 0) { print "ERROR: FOUND OCCURENCES OF e.printStackTrace() in the following files:\n"; for (lineIdx in errorLine) { print errorLine[lineIdx]}; print "\nPlease remove the lines found above!"; exit 1 } }'
echo SUCCESS - NOTHING FOUND.

4) Place the text box (where you pasted the code above) so that it is executed before your regular build and hit ‘Save’.

Just to briefly explain the code above (in line 2 only), the pipes/chaining of commands is used heavily. First, we find all .java-files recursively within your project directory (‘.’). All these files are passed on in order to “grep for” the text ‘e.printStackTrace’. The output of the grep command is passed on to awk in order to make a decision whether to produce an error message containing the files resulting in a failure and failing the build (exit 1) or just continue if no errors are found. The decision whether to fail or not is done by saving each line containing ‘e.printStackTrace’ in the errorLine array and counting the number of (error) lines detected.

The tip above can easily be modified to any other “grep-like” check you might want to add to your build.

Implementing your own custom lint checks
If you really, really need to implement your own lint checks to enforce specific rules related to the abstract syntax tree (AST), you need to do a lot more work, which is out-of-scope for this blog post. But good starting points would be Googles own description of how to create your own lint check, and also a blog post by the team from LinkedIn describing their experiences with creating their custom lint check.

Martin Öbrink Hansen
Martin is a passionate software pilot at Trifork A/S. He is an Android and .NET journeyman who loves OSS, distributed systems, clean code, TDD/BDD and Microsoft Azure.
Besides writing for the Trifork Blog, Martin runs his own blog. And if you want more updates from him, find him on Twitter.