diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy index 262853f..8222e80 100644 --- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy +++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/CodeQualityConfigurator.groovy @@ -12,13 +12,13 @@ abstract class CodeQualityConfigurator filter.exclude(rule) } + ext.exclude = { Object rule -> sourceFilter.exclude(rule) } config.delegate = it config() } @@ -34,11 +34,16 @@ abstract class CodeQualityConfigurator + task.group = 'verification' + configureReportEvaluation(task) } - project.tasks.withType(taskClass) { task -> configureTask(task) } } } } @@ -59,13 +64,14 @@ abstract class CodeQualityConfigurator sourceFilter.applyTo(task) } + } protected abstract Class getTaskClass() - protected void configureTask(T task) { - task.group = 'verification' - filter.applyTo(task) - } + protected abstract void configureReportEvaluation(T task) } diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy index f26a385..41fb479 100644 --- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy +++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/checkstyle/CheckstyleConfigurator.groovy @@ -42,33 +42,29 @@ class CheckstyleConfigurator extends CodeQualityConfigurator variant.sourceSets.each { sourceSet -> def taskName = "checkstyle${sourceSet.name.capitalize()}" - Checkstyle checkstyle = tasks.findByName(taskName) - if (checkstyle == null) { - checkstyle = tasks.create(taskName, Checkstyle) - def sourceDirs = sourceSet.java.srcDirs - def notEmptyDirs = sourceDirs.findAll { it.list()?.length > 0 } - if (!notEmptyDirs.empty) { - checkstyle.with { - description = "Run Checkstyle analysis for ${sourceSet.name} classes" - source = sourceSet.java.srcDirs - classpath = files("$buildDir/intermediates/classes/") - } + Checkstyle task = tasks.findByName(taskName) + if (task == null) { + task = tasks.create(taskName, Checkstyle) + task.with { + description = "Run Checkstyle analysis for ${sourceSet.name} classes" + source = sourceSet.java.srcDirs + classpath = files("$buildDir/intermediates/classes/") } } - checkstyle.mustRunAfter variant.javaCompile + sourceFilter.applyTo(task) + task.mustRunAfter variant.javaCompile } } } } @Override - protected void configureTask(Checkstyle checkstyle) { - super.configureTask(checkstyle) + protected void configureReportEvaluation(Checkstyle checkstyle) { checkstyle.showViolations = false checkstyle.ignoreFailures = true checkstyle.metaClass.getLogger = { QuietLogger.INSTANCE } diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy index 004d8d1..4896b27 100644 --- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy +++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsConfigurator.groovy @@ -4,10 +4,15 @@ import com.novoda.staticanalysis.EvaluateViolationsTask import com.novoda.staticanalysis.internal.CodeQualityConfigurator import org.gradle.api.Action import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileTree +import org.gradle.api.file.FileCollection import org.gradle.api.plugins.quality.FindBugs import org.gradle.api.plugins.quality.FindBugsExtension +import org.gradle.api.tasks.SourceSet import org.gradle.internal.logging.ConsoleRenderer +import java.nio.file.Path + class FindbugsConfigurator extends CodeQualityConfigurator { FindbugsConfigurator(Project project, EvaluateViolationsTask evaluateViolations) { @@ -45,23 +50,68 @@ class FindbugsConfigurator extends CodeQualityConfigurator FindBugs task = project.tasks.create("findbugs${variant.name.capitalize()}", QuietFindbugsPlugin.Task) + List androidSourceDirs = variant.sourceSets.collect { it.javaDirectories }.flatten() task.with { - group = "verification" description = "Run FindBugs analysis for ${variant.name} classes" - source = variant.sourceSets.java.srcDirs.collect { it.path }.flatten() - classes = project.fileTree(variant.javaCompile.destinationDir) + source = androidSourceDirs classpath = variant.javaCompile.classpath - dependsOn variant.javaCompile } + sourceFilter.applyTo(task) + task.conventionMapping.map("classes", { + List includes = createIncludePatterns(task.source, androidSourceDirs) + project.fileTree(variant.javaCompile.destinationDir).include(includes) + }.memoize()); + task.dependsOn variant.javaCompile } } @Override - protected void configureTask(FindBugs findBugs) { - super.configureTask(findBugs) + protected void configureJavaProject() { + project.sourceSets.each { SourceSet sourceSet -> + String taskName = sourceSet.getTaskName(toolName, null) + FindBugs task = project.tasks.findByName(taskName) + if (task != null) { + sourceFilter.applyTo(task) + task.conventionMapping.map("classes", { + List sourceDirs = sourceSet.allJava.srcDirs.findAll { it.exists() }.toList() + List includes = createIncludePatterns(task.source, sourceDirs) + createClassesTreeFrom(sourceSet).include(includes) + }.memoize()); + } + } + } + + private static List createIncludePatterns(FileCollection sourceFiles, List sourceDirs) { + List includedSourceFilesPaths = sourceFiles.matching { '**/*.java' }.files.collect { it.toPath() } + List sourceDirsPaths = sourceDirs.collect { it.toPath() } + createRelativePaths(includedSourceFilesPaths, sourceDirsPaths) + .collect { Path relativePath -> (relativePath as String) - '.java' + '*' } + } + + private static List createRelativePaths(List includedSourceFiles, List sourceDirs) { + includedSourceFiles.collect { Path sourceFile -> + sourceDirs + .findAll { Path sourceDir -> sourceFile.startsWith(sourceDir) } + .collect { Path sourceDir -> sourceDir.relativize(sourceFile) } + } + .flatten() + } + + /** + * The simple "classes = sourceSet.output" may lead to non-existing resources directory + * being passed to FindBugs Ant task, resulting in an error + * */ + private ConfigurableFileTree createClassesTreeFrom(SourceSet sourceSet) { + project.fileTree(sourceSet.output.classesDir, { + it.builtBy(sourceSet.output) + }) + } + + @Override + protected void configureReportEvaluation(FindBugs findBugs) { findBugs.ignoreFailures = true findBugs.reports.xml.enabled = true findBugs.reports.html.enabled = false diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy index a5a4be0..e028e78 100644 --- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy +++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/pmd/PmdConfigurator.groovy @@ -43,32 +43,28 @@ class PmdConfigurator extends CodeQualityConfigurator { } @Override - protected void configureAndroid(Object variants) { + protected void configureAndroidProject(Object variants) { project.with { variants.all { variant -> variant.sourceSets.each { sourceSet -> def taskName = "pmd${sourceSet.name.capitalize()}" - Pmd pmd = tasks.findByName(taskName) - if (pmd == null) { - pmd = tasks.create(taskName, Pmd) - def sourceDirs = sourceSet.java.srcDirs - def notEmptyDirs = sourceDirs.findAll { it.list()?.length > 0 } - if (!notEmptyDirs.empty) { - pmd.with { - description = "Run PMD analysis for ${sourceSet.name} classes" - source = sourceSet.java.srcDirs - } + Pmd task = tasks.findByName(taskName) + if (task == null) { + task = tasks.create(taskName, Pmd) + task.with { + description = "Run PMD analysis for ${sourceSet.name} classes" + source = sourceSet.java.srcDirs } } - pmd.mustRunAfter variant.javaCompile + sourceFilter.applyTo(task) + task.mustRunAfter variant.javaCompile } } } } @Override - protected void configureTask(Pmd pmd) { - super.configureTask(pmd) + protected void configureReportEvaluation(Pmd pmd) { pmd.ignoreFailures = true pmd.metaClass.getLogger = { QuietLogger.INSTANCE } pmd.doLast { diff --git a/plugin/src/test/fixtures/findbugs/high/HighPriorityViolator.java b/plugin/src/test/fixtures/findbugs/high/HighPriorityViolator.java deleted file mode 100644 index 7c24bd7..0000000 --- a/plugin/src/test/fixtures/findbugs/high/HighPriorityViolator.java +++ /dev/null @@ -1,9 +0,0 @@ -public class HighPriorityViolator { - - public void impossibleCast() { - final Object doubleValue = Double.valueOf(1.0); - final Long value = (Long) doubleValue; - System.out.println(" - " + value); - } - -} diff --git a/plugin/src/test/fixtures/findbugs/high/com/novoda/test/HighPriorityViolator.java b/plugin/src/test/fixtures/findbugs/high/com/novoda/test/HighPriorityViolator.java new file mode 100644 index 0000000..66f6cfa --- /dev/null +++ b/plugin/src/test/fixtures/findbugs/high/com/novoda/test/HighPriorityViolator.java @@ -0,0 +1,15 @@ +package com.novoda.test; + +public class HighPriorityViolator { + + public static class Internal { + + public void impossibleCast() { + final Object doubleValue = Double.valueOf(1.0); + final Long value = (Long) doubleValue; + System.out.println(" - " + value); + } + + } + +} diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy index 6f2979a..12ba408 100644 --- a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy +++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/findbugs/FindbugsIntegrationTest.groovy @@ -134,13 +134,13 @@ class FindbugsIntegrationTest { @Test public void shouldNotFailBuildWhenFindbugsConfiguredToExcludePattern() { TestProject.Result result = projectRule.newProject() - .withSourceSet('debug', SOURCES_WITH_LOW_VIOLATION, SOURCES_WITH_MEDIUM_VIOLATION) - .withSourceSet('release', SOURCES_WITH_HIGH_VIOLATION) + .withSourceSet('debug', SOURCES_WITH_LOW_VIOLATION) + .withSourceSet('release', SOURCES_WITH_HIGH_VIOLATION, SOURCES_WITH_MEDIUM_VIOLATION) .withPenalty('''{ maxErrors = 0 maxWarnings = 10 }''') - .withFindbugs('findbugs { exclude "HighPriorityViolator.java" }') + .withFindbugs('findbugs { exclude "com/novoda/test/HighPriorityViolator.java" }') .build('check') assertThat(result.logs).doesNotContainLimitExceeded()