Skip to content

Commit

Permalink
fix: support Gradle configuration cache (#663)
Browse files Browse the repository at this point in the history
* test: reproduce the reported problem as a functional test

refs #662

* fix: do not resolve task.project at execution time

* fix: do not resolve task.project at execution time

* fix: do not resolve task.project at execution time

* chore: follow the suggestion made by SQ

https://sonarcloud.io/project/issues?id=com.github.spotbugs.gradle&issues=AX5MfO0ow8sHw4bh7DpY&open=AX5MfO0ow8sHw4bh7DpY&pullRequest=663

* test: check not only failure but also success

* test: confirm that cached entry has been reused by the following build
  • Loading branch information
KengoTODA authored Jan 21, 2022
1 parent bc05505 commit ba591fb
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,49 @@ package com.github.spotbugs.snom

import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.gradle.util.GradleVersion
import spock.lang.Requires
import spock.lang.Specification

import java.nio.file.Files

class CacheabilityFunctionalTest extends Specification {
/**
* @see <a href="https://github.com/spotbugs/spotbugs-gradle-plugin/issues/662">GitHub Issues</a>
*/
def 'spotbugsMain task runs with configuration cache'() {
given:
def buildDir = Files.createTempDirectory(null).toFile()
def version = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version)

initializeBuildFile(buildDir)

when:
BuildResult result =
GradleRunner.create()
.withProjectDir(buildDir)
.withArguments(':spotbugsMain', '--configuration-cache')
.withPluginClasspath()
.forwardOutput()
.withGradleVersion(version)
.build()

then:
!result.output.contains("Configuration cache problems found in this build")
result.output.contains("Configuration cache entry stored.")

when:
BuildResult resultOfCachedBuild = GradleRunner.create()
.withProjectDir(buildDir)
.withArguments(':spotbugsMain', '--configuration-cache')
.withPluginClasspath()
.forwardOutput()
.withGradleVersion(version)
.build()
then:
resultOfCachedBuild.task(":spotbugsMain").outcome == TaskOutcome.UP_TO_DATE
resultOfCachedBuild.output.contains("Configuration cache entry reused.")
}

/**
* Verifies the cacheability of {@link SpotBugsTask} by invoking the same code
Expand All @@ -42,8 +77,8 @@ class CacheabilityFunctionalTest extends Specification {

def version = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version)

initializeBuildFile(buildDir1, !version.startsWith('5'))
initializeBuildFile(buildDir2, !version.startsWith('5'))
initializeBuildFile(buildDir1)
initializeBuildFile(buildDir2)

when:
BuildResult result1 =
Expand Down Expand Up @@ -79,7 +114,7 @@ class CacheabilityFunctionalTest extends Specification {
return result.output.find('Build cache key for task \':spotbugsMain\' is .*')
}

private static void initializeBuildFile(File buildDir, boolean runScan) {
private static void initializeBuildFile(File buildDir) {
File buildFile = new File(buildDir, 'build.gradle')
File settingsFile = new File(buildDir, 'settings.gradle')
File propertiesFile = new File(buildDir, 'gradle.properties')
Expand All @@ -102,19 +137,17 @@ class CacheabilityFunctionalTest extends Specification {
|}
|'''.stripMargin()

if (runScan) {
settingsFile << '''
|plugins {
| id "com.gradle.enterprise" version "3.6.4"
|}
|gradleEnterprise {
| buildScan {
| termsOfServiceUrl = "https://gradle.com/terms-of-service"
| termsOfServiceAgree = "yes"
| }
|}
'''.stripMargin()
}
settingsFile << '''
|plugins {
| id "com.gradle.enterprise" version "3.6.4"
|}
|gradleEnterprise {
| buildScan {
| termsOfServiceUrl = "https://gradle.com/terms-of-service"
| termsOfServiceAgree = "yes"
| }
|}
'''.stripMargin()
File sourceDir = buildDir.toPath().resolve('src').resolve('main').resolve('java').toFile()
sourceDir.mkdirs()
File sourceFile = new File(sourceDir, 'Foo.java')
Expand Down
58 changes: 46 additions & 12 deletions src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.spotbugs.snom;
package com.github.spotbugs.snom

import com.github.spotbugs.snom.internal.SemanticVersion;
import com.github.spotbugs.snom.internal.SpotBugsHtmlReport
import com.github.spotbugs.snom.internal.SpotBugsRunnerForHybrid;
import com.github.spotbugs.snom.internal.SpotBugsRunnerForJavaExec;
Expand All @@ -21,7 +22,8 @@ import com.github.spotbugs.snom.internal.SpotBugsSarifReport;
import com.github.spotbugs.snom.internal.SpotBugsTextReport;
import com.github.spotbugs.snom.internal.SpotBugsXmlReport;
import edu.umd.cs.findbugs.annotations.NonNull
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.Nullable
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.plugins.JavaPluginExtension
Expand All @@ -33,7 +35,6 @@ import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.FileCollection;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
Expand All @@ -53,7 +54,6 @@ import org.gradle.api.tasks.VerificationTask
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.util.ClosureBackedAction
import org.gradle.util.GradleVersion;
import org.gradle.workers.WorkerExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -95,9 +95,6 @@ import javax.inject.Inject

@CacheableTask
abstract class SpotBugsTask extends DefaultTask implements VerificationTask {
private static final String FEATURE_FLAG_WORKER_API = "com.github.spotbugs.snom.worker";
private static final String FEATURE_FLAG_HYBRID_WORKER = "com.github.spotbugs.snom.javaexec-in-worker";

private final Logger log = LoggerFactory.getLogger(SpotBugsTask);

private final WorkerExecutor workerExecutor;
Expand Down Expand Up @@ -269,6 +266,10 @@ abstract class SpotBugsTask extends DefaultTask implements VerificationTask {

private boolean enableWorkerApi;
private boolean enableHybridWorker;
private FileCollection pluginJarFiles
private FileCollection spotbugsClasspath

private Provider<Boolean> isSupportingMultipleReports

void setClasses(FileCollection fileCollection) {
this.classes = fileCollection
Expand Down Expand Up @@ -341,6 +342,32 @@ abstract class SpotBugsTask extends DefaultTask implements VerificationTask {
useAuxclasspathFile = objects.property(Boolean)
setDescription("Run SpotBugs analysis.")
setGroup(JavaBasePlugin.VERIFICATION_GROUP)
def pluginConfiguration = project.getConfigurations().getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME)
pluginJarFiles = project.layout.files {
pluginConfiguration.files
}

def configuration = project.getConfigurations().getByName(SpotBugsPlugin.CONFIG_NAME)
def logger = this.log
isSupportingMultipleReports = project.provider {
configuration.resolve()
java.util.Optional<Dependency> spotbugs =
configuration.getDependencies().stream()
.filter { dependency -> "com.github.spotbugs" == dependency.group && "spotbugs" == dependency.name }
.findFirst()
if (!spotbugs.isPresent()) {
logger.warn("No spotbugs found in the {} configuration", SpotBugsPlugin.CONFIG_NAME)
return false
}
SemanticVersion version = new SemanticVersion(spotbugs.get().getVersion())
logger.debug("Using SpotBugs version {}", version)
return version >= new SemanticVersion("4.5.0")
}

def spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME)
spotbugsClasspath = project.layout.files {
spotbugsSlf4j.files + configuration.files
}
}

/**
Expand Down Expand Up @@ -418,16 +445,13 @@ abstract class SpotBugsTask extends DefaultTask implements VerificationTask {
@NonNull
@Internal
Set<File> getPluginJar() {
return getProject().getConfigurations().getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME).getFiles()
return pluginJarFiles.files
}

@NonNull
@Internal
FileCollection getSpotbugsClasspath() {
Configuration config = getProject().getConfigurations().getByName(SpotBugsPlugin.CONFIG_NAME)
Configuration spotbugsSlf4j = getProject().getConfigurations().getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME)

return getProject().files(config, spotbugsSlf4j)
return spotbugsClasspath
}

@Nullable
Expand Down Expand Up @@ -481,6 +505,16 @@ abstract class SpotBugsTask extends DefaultTask implements VerificationTask {
showStackTraces.get();
}

/**
* The multiple reports feature is available from SpotBugs 4.5.0
*
* @see <a href="https://github.com/spotbugs/spotbugs/releases/tag/4.5.0">GitHub Releases</a>
*/
@Internal
boolean isSupportingMultipleReports() {
return isSupportingMultipleReports.getOrElse(Boolean.FALSE).booleanValue()
}

@Internal
String getBaseName() {
String prunedName = name.replaceFirst("spotbugs", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
package com.github.spotbugs.snom.internal;

import com.github.spotbugs.snom.SpotBugsPlugin;
import com.github.spotbugs.snom.SpotBugsReport;
import com.github.spotbugs.snom.SpotBugsTask;
import edu.umd.cs.findbugs.annotations.NonNull;
Expand All @@ -28,14 +27,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.FileCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -45,30 +40,6 @@ public abstract class SpotBugsRunner {

public abstract void run(@NonNull SpotBugsTask task);

/**
* The multiple reports feature is available from SpotBugs 4.5.0
*
* @see <a href="https://github.com/spotbugs/spotbugs/releases/tag/4.5.0">GitHub Releases</a>
*/
private boolean isSupportingMultipleReports(Project project) {
Configuration configuration = project.getConfigurations().getByName(SpotBugsPlugin.CONFIG_NAME);
configuration.resolve();
Optional<Dependency> spotbugs =
configuration.getDependencies().stream()
.filter(
dependency ->
"com.github.spotbugs".equals(dependency.getGroup())
&& "spotbugs".equals(dependency.getName()))
.findFirst();
if (!spotbugs.isPresent()) {
log.warn("No spotbugs found in the {} configuration", SpotBugsPlugin.CONFIG_NAME);
return false;
}
SemanticVersion version = new SemanticVersion(spotbugs.get().getVersion());
log.debug("Using SpotBugs version {}", version);
return version.compareTo(new SemanticVersion("4.5.0")) >= 0;
}

protected List<String> buildArguments(SpotBugsTask task) {
List<String> args = new ArrayList<>();

Expand Down Expand Up @@ -98,7 +69,7 @@ protected List<String> buildArguments(SpotBugsTask task) {
args.add("-progress");
}

if (isSupportingMultipleReports(task.getProject())) {
if (task.isSupportingMultipleReports()) {
for (SpotBugsReport report : task.getEnabledReports()) {
File reportFile = report.getOutputLocation().getAsFile().get();
File dir = reportFile.getParentFile();
Expand Down

0 comments on commit ba591fb

Please sign in to comment.