6

I've been working on adding SpotBugs to the android project I'm currently working on. I managed to get it working but I'm not overly thrilled of the way it's set up. For now the configuration resides inside my app/build.gradle file, which makes the file less manageable.

I was wondering if there's an expert on SpotBugs/Gradle who knows a way to pull the configuration out into a separate file.

Here's my app/build.gradle (boilerplate removed):

buildscript {
    repositories {
        ...
    }

    dependencies {
        classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2'
        classpath 'io.fabric.tools:gradle:1.25.4'
        classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
    }
}

plugins {
    id 'com.gladed.androidgitversion' version '0.4.3'
    id "com.github.spotbugs" version "1.6.2"
}

...
apply plugin: 'com.github.spotbugs'
apply from: '../config/quality/quality.gradle'
apply from: '../app/jacoco.gradle'
apply from: '../app/ktlint.gradle'
apply from: '../app/androidgit.gradle'

...

spotbugs {
    toolVersion = '3.1.3'
    ignoreFailures = false

    effort = "min"
    // This selects what level of bugs to report: low means low priority issues will be reported
    // (in addition to medium+high), which corresponds to warning about everything.
    // TODO: boost this to low once low priority issues are fixed.
    reportLevel = "medium"

    excludeFilter = new File("$project.rootDir/config/quality/spotbugs/android-exclude-filter.xml")
}

task spotbugs(type: com.github.spotbugs.SpotBugsTask, dependsOn: 'assemble', group: 'verification') {
    classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")

    source = fileTree('src/main/java')


    // Only one report format is supported. Html is easier to read, so let's use that
    // (xml is the one that's enabled by default).
    reports {
        xml.enabled = false
        html.enabled = true
    }

    classpath = files()
}

EDIT

Whenever I'm trying to separate SpotBugs from my app/build.gradle I run into the following error:

Could not get unknown property 'SpotBugsTask' for project ':app' of type org.gradle.api.Project.

Here's my gradle file:

apply plugin: 'com.github.spotbugs'

dependencies {
    checkstyle 'com.puppycrawl.tools:checkstyle:8.11'
    spotbugs "gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.2"
//    spotbugs configurations.spotbugsPlugins.dependencies
//    spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0'
}

def qualityConfigDir = "$project.rootDir/config/quality";
def reportsDir = "$project.buildDir/reports"

check.dependsOn 'checkstyle'

task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code style checks') {
    configFile file("$qualityConfigDir/checkstyle/checkstyle-config.xml")
    source 'src/main/java'
    include '**/*.java'
    exclude '**/model/**'
    exclude '**/AppLogger.java'
    reports {
        xml.enabled = true
        xml {
            destination file("$reportsDir/checkstyle/checkstyle.xml")
        }
    }

    classpath = files()
}

spotbugs {
    toolVersion = '3.1.3'
    ignoreFailures = false

    effort = "min"
    // This selects what level of bugs to report: low means low priority issues will be reported
    // (in addition to medium+high), which corresponds to warning about everything.
    // TODO: boost this to low once low priority issues are fixed.
    reportLevel = "medium"

    excludeFilter = new File("$project.rootDir/config/quality/spotbugs/android-exclude-filter.xml")
}

task spotbugs(type: SpotBugsTask, dependsOn: 'assemble', group: 'verification') {
    classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")

    source = fileTree('src/main/java')


    // Only one report format is supported. Html is easier to read, so let's use that
    // (xml is the one that's enabled by default).
    reports {
        xml.enabled = false
        html.enabled = true
    }

    classpath = files()
}
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Bohsen
  • 4,242
  • 4
  • 32
  • 58

2 Answers2

9

Finally managed to find a solution.

I had to add the following to the section where I apply all the plugins in my app/build.gradle file:

project.extensions.extraProperties.set('SpotBugsTask', com.github.spotbugs.SpotBugsTask)

So it ended up looking like this:

buildscript {
    repositories {
        mavenCentral()
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }

    dependencies {
        classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2'
        classpath 'io.fabric.tools:gradle:1.25.4'
        classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
    }
}

plugins {
    id 'com.gladed.androidgitversion' version '0.4.3'
    id "com.github.spotbugs" version "1.6.2"
}

// Workaround to be able to access SpotBugsTask from external gradle script.
// More info: https://discuss.gradle.org/t/buildscript-dependencies-in-external-script/23243
project.extensions.extraProperties.set('SpotBugsTask', com.github.spotbugs.SpotBugsTask)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.dokka-android'
apply plugin: 'io.fabric'
apply plugin: 'spoon'
apply from: '../app/checkstyle.gradle'
apply from: '../app/jacoco.gradle'
apply from: '../app/ktlint.gradle'
apply from: '../app/androidgit.gradle'
apply from: '../app/spotbugs.gradle'

android {
...

My spotbugs.gradle file:

dependencies {
    spotbugs configurations.spotbugsPlugins.dependencies
    spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0'
}

def qualityConfigDir = "$project.rootDir/config/quality"
def reportsDir = "$project.buildDir/reports"

spotbugs {
    toolVersion = "$spotbugs_version"
    ignoreFailures = false

    effort = "min"
    // This selects what level of bugs to report: low means low priority issues will be reported
    // (in addition to medium+high), which corresponds to warning about everything.
    // TODO: boost this to low once low priority issues are fixed.
    reportLevel = "medium"

    excludeFilter = new File("$qualityConfigDir/config/quality/spotbugs/android-exclude-filter.xml")
}

tasks.register("spotbugs", SpotBugsTask) {
    dependsOn 'assemble'
    group = "verification"
    classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")

    source = fileTree('src/main/java')


    // Only one report format is supported. Html is easier to read, so let's use that
    // (xml is the one that's enabled by default).
    reports {
        xml.enabled = true
        xml {
            destination file("$reportsDir/spotbugs/spotbugs.xml")
        }
        html.enabled = true
    }

    classpath = files()
}
Bohsen
  • 4,242
  • 4
  • 32
  • 58
  • Have you an idea how to specify the version of SpotBugs to use? The `toolVersion` is the version of the plugin, not version of the SpotBugs dependency. With FindBugs, it introduced a dependency `findbugs` where it was possible to specify the FindBugs dependency version. – Abhijit Sarkar Nov 16 '18 at 01:10
  • I have the toolsversion specified inside my spotbugs.gradle. I've edited the answer to include my spotbugs.gradle. The toolversion is specified in my dependencies section of my project build script. – Bohsen Nov 16 '18 at 13:53
  • what’re the dependencies `spotbugs` and `spotbugsPlugins` for? – Abhijit Sarkar Nov 16 '18 at 17:34
  • 1
    That's for loading the [findsecbugs-plugin](https://find-sec-bugs.github.io/) a spotbugs plugin. It's able to find security vulnerabilities. – Bohsen Nov 17 '18 at 12:05
  • I see; and one last thing. It'll be useful if you show the `spotbugs` dependencies. Perhaps you should edit your answer to add these info instead of putting them as comments. – Abhijit Sarkar Nov 17 '18 at 12:06
  • Sry, but not sure what you mean. Do you mean where I've set up the version of spotbugs I'm using? – Bohsen Nov 17 '18 at 12:41
  • I mean please show the `configurations.spotbugsPlugins.dependencies` – Abhijit Sarkar Nov 17 '18 at 13:16
2

For anyone stumbling across this thread, and not satisfied with the above answer (you should question anytime you see "this works" without a "because ..."), note that if you're using an external buildscript file like the OP, and trying to configure the tasks, the real problem is that the script plugin ClassLoader is isolated from the project buildscript ClassLoader, the java.lang.Class instances representing the type com.github.spotbugs.SpotBugsTask are different, thus thewithType call doesn't match anything.

See gradle-native#742 and gradle#1262 for details, and some solutions to make it work.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • 3
    you should really specify the solutions here, instead of presenting a link that might die in the future – bharal Apr 12 '19 at 15:17
  • @bharal If github dies, so can stackoverflow. Besides, the solutions depend on the use case; there’s no one-size-fits-all solution. – Abhijit Sarkar Apr 12 '19 at 15:48