SpotBugsSpec generateSpec() { SpotBugsSpecBuilder specBuilder = new SpotBugsSpecBuilder(getClasses()) .withPluginsList(getPluginClasspath()) .withSources(getAllSource()) .withClasspath(getClasspath()) .withShowProgress(getShowProgress()) .withDebugging(getLogger().isDebugEnabled()) .withEffort(getEffort()) .withReportLevel(getReportLevel()) .withMaxHeapSize(getMaxHeapSize()) .withVisitors(getVisitors()) .withOmitVisitors(getOmitVisitors()) .withExcludeFilter(getExcludeFilter()) .withIncludeFilter(getIncludeFilter()) .withExcludeBugsFilter(getExcludeBugsFilter()) .withExtraArgs(getExtraArgs()) .withJvmArgs(getJvmArgs()) .configureReports(getReports()); return specBuilder.build();
@TaskAction public void run() throws IOException, InterruptedException { new SpotBugsClasspathValidator(JavaVersion.current()).validateClasspath( getSpotbugsClasspath().getFiles().stream().map(File::getName).collect(Collectors.toSet())); SpotBugsSpec spec = generateSpec(); SpotBugsWorkerManager manager = new SpotBugsWorkerManager(); getLogging().captureStandardOutput(LogLevel.DEBUG); getLogging().captureStandardError(LogLevel.DEBUG); SpotBugsResult result = manager.runWorker(getProject().getProjectDir(), getWorkerProcessBuilderFactory(), getSpotbugsClasspath(), spec); evaluateResult(result);
@Override protected void configureForSourceSet(final SourceSet sourceSet, SpotBugsTask task) { task.setDescription("Run SpotBugs analysis for " + sourceSet.getName() + " classes"); task.setSource(sourceSet.getAllJava()); ConventionMapping taskMapping = task.getConventionMapping(); taskMapping.map("classes", new Callable<FileCollection>() { @Override public FileCollection call() { ConfigurableFileTree fileTree = StreamSupport.stream(sourceSet.getOutput().getClassesDirs().spliterator(), false) .map(project::fileTree) .reduce((lhs, rhs) -> { lhs.plus(rhs); return lhs; }).orElseThrow(() -> new InvalidUserDataException("No classes dir configured for source set " + sourceSet.getName()) return fileTree.builtBy(sourceSet.getClassesTaskName()); taskMapping.map("classpath", new Callable<FileCollection>() { @Override public FileCollection call() { return sourceSet.getCompileClasspath();
SpotBugsSpec generateSpec() { SpotBugsSpecBuilder specBuilder = new SpotBugsSpecBuilder(getClasses ()) .withPluginsList(getPluginClasspath()) .withSources(getSource()) .withClasspath(getClasspath()) .withDebugging(getLogger().isDebugEnabled()) .withEffort(getEffort()) .withReportLevel(getReportLevel()) .withMaxHeapSize(getMaxHeapSize()) .withVisitors(getVisitors()) .withOmitVisitors(getOmitVisitors()) .withExcludeFilter(getExcludeFilter()) .withIncludeFilter(getIncludeFilter()) .withExcludeBugsFilter(getExcludeBugsFilter()) .withExtraArgs(getExtraArgs()) .configureReports(getReports()); return specBuilder.build();
@TaskAction public void run() { new SpotBugsClasspathValidator(JavaVersion.current()).validateClasspath( getSpotbugsClasspath().getFiles().stream().map(File::getName).collect(Collectors.toSet())); SpotBugsSpec spec = generateSpec(); //workaround for https://github.com/spotbugs/spotbugs-gradle-plugin/issues/61 if(reports.getEnabledReports().isEmpty()) { getProject().getLogger().lifecycle("WARNING: No SpotBugs report(s) were configured; aborting execution of {}", getPath()); return; workerExecutor.submit(SpotBugsRunner.class, config -> { config.params(spec, getIgnoreFailures(), reports.getFirstEnabled().getDestination()); config.setClasspath(getSpotbugsClasspath()); config.setForkMode(ForkMode.ALWAYS); config.forkOptions( options -> { options.setDebug(spec.isDebugEnabled()); options.setJvmArgs(spec.getJvmArgs()); options.setMaxHeapSize(spec.getMaxHeapSize()); config.setIsolationMode(IsolationMode.PROCESS);
@Override public void apply( Project project ) { project.getPluginManager().apply( SpotBugsPlugin.class ); ExtensionContainer extensions = project.getExtensions(); extensions.configure( SpotBugsExtension.class, ext -> { ext.setToolVersion( "3.1.8" ); ext.setEffort( "max" ); ext.setReportLevel( "low" ); ext.setSourceSets( extensions.getByType( XenoExtension.class ).getSourceSets() ); ext.setIgnoreFailures( false ); project.getTasks().withType( SpotBugsTask.class ).configureEach( task -> { task.setEnabled ( true ); SpotBugsReports reports = task.getReports(); reports.getXml().setEnabled( false );
private void verify(Project p) {
p.getTasks().withType(SpotBugsTask.class).forEach(task -> {
SpotBugsReports reports = task.getReports();
if (reports.getText() != null && reports.getText().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for TEXT report. Set reports.text.destination to this task.",
task.getName());
throw new IllegalStateException(message);
if (reports.getXml() != null && reports.getXml().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for XML report. Set reports.xml.destination to this task.",
task.getName());
throw new IllegalStateException(message);
if (reports.getHtml() != null && reports.getHtml().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for HTML report. Set reports.html.destination. to this task",
task.getName());
throw new IllegalStateException(message);
if (reports.getEmacs() != null && reports.getEmacs().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for EMACS report. Set reports.emacs.destination. to this task",
task.getName());
throw new IllegalStateException(message);
@Override protected void configureForSourceSet(final SourceSet sourceSet, SpotBugsTask task) { task.setDescription("Run SpotBugs analysis for " + sourceSet.getName() + " classes"); task.setSourceSet(sourceSet); ConventionMapping taskMapping = task.getConventionMapping(); taskMapping.map("classes", (Callable<FileCollection>) () -> { * As a result of the changes made in gradle 4.0. * See https://docs.gradle.org/4.0/release-notes.html - Location of classes in the build directory * Compile no longer bundles all classes in one directory build-gradle/classes/main * but instead separates classes into build-gradle/classes/{language}/main. * We must therefor retrieve all output directories. Filter away the once that don't exist. Add each * existing file tree dependency to specified task. And then return the complete fileCollection, contain * all .class files available for analysis. FileCollection presentClassDirs = sourceSet.getOutput().getClassesDirs().filter(File::exists); StreamSupport.stream(presentClassDirs.spliterator(), false) .map(file -> project.fileTree(file)) .forEach(tree -> tree.builtBy(sourceSet.getClassesTaskName())); return presentClassDirs.getAsFileTree(); taskMapping.map("classpath", sourceSet::getRuntimeClasspath);
private void configureReportsConventionMapping(SpotBugsTask task, final String baseName) {
task.getReports().all(new Action<SingleFileReport>() {
@Override
public void execute(final SingleFileReport report) {
ConventionMapping reportMapping = conventionMappingOf(report);
reportMapping.map("enabled", new Callable<Boolean>() {
@Override
public Boolean call() {
return report.getName().equals("xml");
reportMapping.map("destination", new Callable<File>() {
@Override
public File call() {
return new File(extension.getReportsDir(), baseName + "." + report.getName());
/**
* The filename of a filter specifying baseline bugs to exclude from being reported.
* @return filename of a filter specifying baseline bugs to exclude from being reported
@Internal
public File getExcludeBugsFilter() {
TextResource config = getExcludeBugsFilterConfig();
return config == null ? null : config.asFile();
/**
* The filename of a filter specifying bugs to exclude from being reported.
* @return filename of a filter specifying bugs to exclude from being reported
@Internal
public File getExcludeFilter() {
TextResource config = getExcludeFilterConfig();
return config == null ? null : config.asFile();
private void configureTaskConventionMapping(Configuration configuration, SpotBugsTask task) {
ConventionMapping taskMapping = task.getConventionMapping();
taskMapping.map("spotbugsClasspath", () -> configuration);
taskMapping.map("ignoreFailures", extension::isIgnoreFailures);
taskMapping.map("effort", extension::getEffort);
taskMapping.map("reportLevel", extension::getReportLevel);
taskMapping.map("visitors", extension::getVisitors);
taskMapping.map("omitVisitors", extension::getOmitVisitors);
taskMapping.map("excludeFilterConfig", extension::getExcludeFilterConfig);
taskMapping.map("includeFilterConfig", extension::getIncludeFilterConfig);
taskMapping.map("excludeBugsFilterConfig", extension::getExcludeBugsFilterConfig);
taskMapping.map("extraArgs", extension::getExtraArgs);
taskMapping.map("showProgress", extension::isShowProgress);
taskMapping.map("jvmArgs", extension::getJvmArgs);
@Override public void apply( Project project ) { project.getPluginManager().apply( SpotBugsPlugin.class ); ExtensionContainer extensions = project.getExtensions(); extensions.configure( SpotBugsExtension.class, ext -> { ext.setToolVersion( "3.1.7" ); ext.setEffort( "max" ); ext.setReportLevel( "low" ); ext.setSourceSets( extensions.getByType( XenoExtension.class ).getSourceSets() ); ext.setIgnoreFailures( false ); project.getTasks().withType( SpotBugsTask.class ).configureEach( task -> { task.setEnabled( true ); SpotBugsReports reports = task.getReports(); reports.getXml().setEnabled( false );
private void verify(Project p) {
p.getTasks().withType(SpotBugsTask.class).forEach(task -> {
SpotBugsReports reports = task.getReports();
if (reports.getText() != null && reports.getText().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for TEXT report. Set reports.text.destination to this task.",
task.getName());
throw new IllegalStateException(message);
if (reports.getXml() != null && reports.getXml().getDestination() == null) {
String message = String.format(
"Task '%s' has no destination for XML report. Set reports.xml.destination to this task.",
task.getName());
throw new IllegalStateException(message);