A little while ago a colleague of mine asked me if I had done a
Sonar analysis of my current project. Well I hadn't so I started to investigate a little on what my options were given my build was setup with
Gradle. After reading up a bit on the gradle forums, it appeared that it wasn't all that trivial. In particular getting my emma coverage available for sonar seemed to be a stumbling block. The sonar version at the time also seemed to be fairly tied to maven, a build system I sort of left behind last year.
Anyways yesterday I thought I'd check up on the announced plans for providing gradle support for sonar. Not quite there yet, but there was a snapshot version with ant support. Gradle and Ant being pretty good friends, I decided to give it a go.
Short story is that it worked. However I couldn't manage to get the testcoverage from emma in sonar, so I switched my setup to use cobertura (did slow my build quite a bit actually). Another remaining issue is all the annoying error messages from the pmd plugin in sonar. Lots of exceptions related to it for some reason insisting that I'm using jdk 1.4, but the fact is that I'm using jdk 1.6.
My build.gradle setup:
apply plugin: 'java'
configurations {
sonarLibs
coberturaLibs {extendsFrom testRuntime}
}
dependencies {
//.. other deps omitted
coberturaLibs 'net.sourceforge.cobertura:cobertura:1.9.3'
sonarLibs "org.codehaus.sonar-plugins:sonar-ant-task:0.1-SNAPSHOT"
}
def cobSerFile = "${project.buildDir}/cobertura.ser"
def srcOriginal = "${sourceSets.main.classesDir}"
def srcCopy = "${srcOriginal}-copy"
test {
useTestNG()
maxParallelForks = 2
afterTest {descriptor, result ->
println "${descriptor.className}.${descriptor.name}"
}
/** Cobertura setup **/
if (project.hasProperty('coverage') && ['on', 'true'].contains(project.properties.coverage)) {
systemProperties["net.sourceforge.cobertura.datafile"] = cobSerFile
doFirst {
ant {
// delete data file for cobertura, otherwise coverage would be added
delete(file: cobSerFile, failonerror: false)
// delete copy of original classes
delete(dir: srcCopy, failonerror: false)
// import cobertura task, so it is available in the script
taskdef(resource: 'tasks.properties', classpath: configurations.coberturaLibs.asPath)
// create copy (backup) of original class files
copy(todir: srcCopy) {
fileset(dir: srcOriginal)
}
// instrument the relevant classes in-place
'cobertura-instrument'(datafile: cobSerFile) {
fileset(dir: srcOriginal,
includes: "**/*.class",
excludes: "**/*Test.class")
}
}
}
doLast {
if (new File(srcCopy).exists()) {
// replace instrumented classes with backup copy again
ant {
delete(file: srcOriginal)
move(file: srcCopy,
tofile: srcOriginal)
}
// create cobertura reports
ant.'cobertura-report'(destdir: "${project.buildDirName}/test-results",
format: 'html', srcdir: "src/main/java", datafile: cobSerFile)
ant.'cobertura-report'(destdir: "${project.buildDirName}/test-results",
format: 'xml', srcdir: "src/main/java", datafile: cobSerFile)
}
}
}
}
task runSonar << {
ant.taskdef(name: "sonar", classname: "org.sonar.ant.SonarTask", classpath: configurations.sonarLibs.asPath)
ant.sonar(workdir: ".", key: "no.sample:sampleapp", version: '1.0') {
sources {
path(location: "src/main/java")
}
tests {
path(location: "src/test/java")
}
// tell sonar to use existing cobertura reports
property(key: "sonar.dynamicAnalysis", value: "reuseReports")
property(key: "sonar.cobertura.reportPath", value: file("build/test-results/coverage.xml"))
}
}
So to run the sonar goal with coverage I basically make the following command;
$gradle build runSonar -Pcoverage=on
And voila: