Failing on Unwanted Package Imports

May 15, 2017

We can fail the build when unwanted packages (e.g., junit.framework) are imported through Gradle’s built-in checkstyle plugin. Setup is different for solo-project and multi-project builds. See the following links for a git diff of what is needed to enable this behavior:

solo-project
e4aa2758
multi-project
cd195391

Context

I never want to bring in anything from the junit.framework package when writing new code. Gradle’s checkstyle plugin and the IllegalImport rule can help me formalize this intention as part of my build.

JUnit IDE Import Choices

The build file is the best place to capture intentions like this because it is shared by everyone who touches the project. While it is possible to configure IDEs to exclude unwanted packages from import, the default behavior is that terrible choice above - where most of the options are wrong.

Solo-Project Setup

We’ll start with a simple example project that has imported a method from our unwanted package: sghill/fail-unwanted-package-imports.

Apply the plugin:

apply plugin: 'checkstyle'

The plugin is looking for configuration in ${project.projectDir}/config/checkstyle/checkstyle.xml by default.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">
    <module name="TreeWalker">
        <module name="IllegalImport">
            <property name="illegalPkgs" value="junit.framework"/>
        </module>
    </module>
</module>

Now running ./gradlew build fails our new :checkstyleTest task:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleTest'.
> Checkstyle rule violations were found. See the report at: file:///Users/sghill/code/build-well/fail-unwanted-package-imports/build/reports/checkstyle/test.html

Multi-Project Setup

We’re going to reuse the solo-project configuration in the same location for our multi-project setup. The default checkstyle file path is relative to the project instead of the rootProject, so we’ll need to do some task configuration. This is the way to share a checkstyle configuration:

allprojects {
    apply plugin: 'checkstyle'

    tasks.withType(Checkstyle) {
        def path = "${rootProject.projectDir}/config/checkstyle/checkstyle.xml"
        config resources.text.fromFile(path)
    }
}

Now running ./gradlew build --continue gives us errors from foundation and acceptance:

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':acceptance:checkstyleTest'.
> Checkstyle rule violations were found. See the report at: file:///Users/sghill/code/build-well/fail-unwanted-package-imports/acceptance/build/reports/checkstyle/test.html

==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':foundation:checkstyleTest'.
> Checkstyle rule violations were found. See the report at: file:///Users/sghill/code/build-well/fail-unwanted-package-imports/foundation/build/reports/checkstyle/test.html

Reports

By default reports are produced in xml and html formats:

  • ${project.buildDir}/reports/checkstyle/*.xml
  • ${project.buildDir}/reports/checkstyle/*.html

Example Checkstyle HTML Report

Versions

This example has been tested against the following versions:

Date Gradle Checkstyle Solo-Project Multi-Project
2017-06-20 4.0 6.19 output output
2017-06-20 3.5.1 6.19 output output
2017-06-20 3.4.1 5.9 output output
2017-06-20 3.3 5.9 output output

Profile picture

Written by @sghill, who works on build, automated change, and continuous integration systems.