Gradle Multi-Project Issue with Checkstyle

Posted by & filed under , , .

gradle_logoI have encountered this with Gradle recently and I have struggled to find right away a solution to the issue — it took a bit of reading (more) about Gradle, Checkstyle and some digging in until I found the (rather simple) solution. So I thought I’d post it here for others to hopefully find this when they’re encountering the same problem 🙂

The problem that I encountered is the following: I’ve put together a Java project which exposes an API and compiled it as a jar/library. The idea is that other projects I’m working on can use this API, as you would expect.

And since I wrote the library mainly for my own use, as soon as I was done with it I’ve started working on the project which was going to use this API. In the process of working on this, I realized that I actually needed to make a couple of small changes to the initial library — so to save me switching in between the 2 projects, compiling, updating dependencies and so on, I’ve used gradle’s multi-project facility. (I’ve actually combined this with Git feature of having sub-projects in a repo, so my main project includes in git the library as a subproject — then I’ve referenced that project as an include in the gradle build files.)

All looks pretty standard so far, no surprises there. Until I run a gradle clean build in the main project and I get an error regarding to Checkstyle not finding configuration files! WTF??? The library (sub)project compiles fine if I launch gradle in its own folder so where is the problem?

It turns out that the issue is to do with my checkstyle suppressions. My library project has the following directory structure:

Screenshot 2015-05-05 11.45.00

As you can see I have 2 configuration files for Checkstyle: there is a checkstyle.xml which contains the rules to run against my code and also a suppress.xml which contains the suppression list (I don’t care, whatever anyone says about it, about the coding style in my unit tests so I tend to exclude them from most checks for instance). The suppression file is referenced in the main checkstyle.xml as follows:

<!-- Suppressions for unit testing code -->
<module name="SuppressionFilter">
<property name="file" value="config/checkstyle/suppress.xml" />
</module>

And this is where the issue comes from!

When running gradle against this single project, all works well because the root folder for the project is the folder where the project itself is — and from there config/checkstyle/suppress.xml resolves to the suppression list file.

However, when I include this into another project, the project folder is the root folder — so in a structure like this:

Screenshot 2015-05-05 11.53.41

Gradle will build the path for suppress.xml to rootProject/config/checkstyle/suppress.xml rather than rootProject/twitterapis/config/checkstyle/suppress.xml (twitterapis is the name of the library project which is included as a sub-module/sub-project in my main project).

So how to solve something like this?

The solution requires a coupe of small changes to the gradle build files in the main project coupled with a supporting change in the build file for the sub-project as follows:

Subproject changes

While not obvious, the Gradle Checkstyle plugin allows us to pass in a properties object (a map) to the plugin via the configProperties property. (See more instructions here: Gradle Checkstyle config)

The idea is we define a property which will be set to the sub-project folder and use that in the checkstyle.xml. So we add this to build.gradle:

def checkStyleProps = ['baseDir': "$project.projectDir"]
checkstyleMain {
configProperties = checkStyleProps
}
checkstyleTest {
configProperties = checkStyleProps
}

As a side note, it’s annoying we need 2 blocks of code for Checkstyle, as it offers 2 tasks: checkstyleMain and checkstyleTest, one for the sources one for the test sources — would be nice to also have one single block of code which gets applied to both, but for now we have to use 2 as per above.

Having configured the properties, it’s now time to reference them in the checkstyle.xml file to build the correct path to the suppression list file:

<!-- Suppressions for unit testing code -->
<module name="SuppressionFilter">
<property name="file" value="${baseDir}/config/checkstyle/suppress.xml" />
</module>

So far we haven’t really achieved much: rather than using the implicit project dir in the path, we declare it explicitly in the properties object and pass it to the Checkstyle plugin. While it makes the configuration more obvious, it doesn’t solve the problem.

So for the final step we need to make changes to the gradle build in the main project:

Main project changes

The way the twitterapis library is included in the main build is via an include directive in the settings.gradle:

//...
include 'twitterapis'
//...

and then referenced in the build.gradle in the dependencies section:

dependencies {
    //compile
    compile project(':twitterapis')
    //...
 
}

We need to make one small change in the settings.gradle file to define the project dir for this module:

include 'twitterapis'
project(':twitterapis').projectDir = new File(rootProject.projectDir, 'twitterapis')

Now upon building our library project, the project directory will be set properly to the sub-module dir, and the path to the suppression list gets resolved correctly.