Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate both XML and HTML reports from the Findbugs plugin in Gradle

Right now I have FindBugs in my build.gradle as follows

 apply plugin: 'findbugs'
        findbugs {
            ignoreFailures = true
        }
        tasks.withType(FindBugs) {
            reports {
                xml.enabled = false
                html.enabled = true
            }
        }

But if I try to enable both the HTML report (for developers to view on their machines) and the XML report (for my jenkins CI machines) I get the following

FindBugs tasks can only have one report enabled, however more than one report was enabled. You need to disable all but one of them.

is there some way / hack to enable me to generate both - even via two different tasks?

like image 945
kellyfj Avatar asked Sep 08 '16 19:09

kellyfj


3 Answers

I solved this by configuring my Gradle script so that it generates findbugs tasks for XML and HTML reports then generates a task which depends on the other two.

def findbugsTask = task('findbugs') {
    group 'Verification'
}

[ 'Html', 'Xml' ].each { reportType ->
    findbugsTask.dependsOn task("findbugs${reportType}", type: FindBugs) {
        dependsOn 'compileJavaWithJavac'
        reports {
            html.enabled = reportType == 'Html'
            xml.enabled = reportType == 'Xml'
        }
    }
}

Note that this will run the Findbugs tool twice, which generally shouldn't be an issue for continuous integration (unless your code base is huge).

like image 33
Martin Devillers Avatar answered Nov 19 '22 19:11

Martin Devillers


You probably can, but in the current state of the plugin, it looks like you have to define a separate task that extends from the FindBugs one, but has a different configuration than the standard one. The problem is that you will run FindBugs twice indeed, and that can be a performance penalty with any decently-sized codebase.

Obviously you can't use tasks.withType(FindBugs) { ... } to configure your tasks, you have to do it by task name explicitly.

Note: if you are running this on e.g. Jenkins, you would want your build.gradle to generate the xml report, and let Jenkins generate the html report from the xml one. That way it is not executed twice in your build.

like image 144
Patrice M. Avatar answered Nov 19 '22 19:11

Patrice M.


You can generate both reports without running FindBugs twice, but it isn't intuitive. If you look at how FindBugs generates its html report, you'll find it actually generates the xml first and just uses xslt to transform it into html. Knowing this, and subsequently resourcing a spotbugs issue that documented a workaround, I got the same approach working with FindBugs.

In my build.gradle file I am generating just the xml report, and running a new task afterwards that converts the xml report to html using one of the FindBugs provided stylesheets.

import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource

import static org.gradle.api.tasks.PathSensitivity.NONE

configurations {
    findbugsStylesheets { transitive false }
}

dependencies {
    findbugsStylesheets 'com.google.code.findbugs:findbugs:3.0.1'
}

tasks.withType(FindBugs) {
    maxHeapSize = "6g"

    reports {
        xml.enabled = true
        xml.withMessages true
        html.enabled = false
        html.stylesheet resources.text.fromArchiveEntry(configurations.findbugsStylesheets, 'fancy.xsl')
    }

    task "${it.name}HtmlReport" {
        def input = reports.xml.destination
        inputs.file reports.html.stylesheet.asFile() withPropertyName 'findbugsStylesheet' withPathSensitivity NONE
        inputs.files fileTree(input) withPropertyName 'input' withPathSensitivity NONE skipWhenEmpty()
        def output = file(input.absolutePath.replaceFirst(/\.xml$/, '.html'))
        outputs.file output withPropertyName 'output'
        doLast {
            def factory = TransformerFactory.newInstance('net.sf.saxon.TransformerFactoryImpl', getClass().classLoader)
            def transformer = factory.newTransformer(new StreamSource(reports.html.stylesheet.asFile()));
            transformer.transform(new StreamSource(input), new StreamResult(output))
        }
    }
    it.finalizedBy "${it.name}HtmlReport"
}
like image 1
casey Avatar answered Nov 19 '22 17:11

casey