Skip to content

Commit

Permalink
feat: support Gradle Java Toolchains for JavaExec and WorkerAPI
Browse files Browse the repository at this point in the history
close #526

BREAKING CHANGE: Gradle v5 and v6 are now not supported
  • Loading branch information
KengoTODA committed Sep 17, 2021
1 parent b78a1b3 commit 2978bed
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 11 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
gradle: ['5.6.4', '6.9', '7.0']
gradle: ['7.0', '7.2']
steps:
- uses: actions/checkout@v2
with:
Expand All @@ -33,7 +33,7 @@ jobs:
with:
node-version: '${{ steps.nvm.outputs.NVMRC }}'
cache: npm
if: matrix.gradle == '6.9'
if: matrix.gradle == '7.0'
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
Expand All @@ -52,15 +52,15 @@ jobs:
rm -rf build/libs/*.jar
npm ci
npx semantic-release
if: matrix.gradle == '6.9'
if: matrix.gradle == '7.0'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run SonarQube Scanner
run: |
if [ "$SONAR_LOGIN" != "" ]; then
./gradlew sonarqube -Dsonar.login=$SONAR_LOGIN --no-daemon
fi
if: matrix.gradle == '6.9'
if: matrix.gradle == '7.0'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_LOGIN: ${{ secrets.SONAR_LOGIN }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2021 SpotBugs team
*
* <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.spotbugs.snom

import org.gradle.internal.impldep.com.google.common.io.Files
import org.gradle.testkit.runner.GradleRunner
import org.gradle.util.GradleVersion
import org.junit.jupiter.api.BeforeEach
import spock.lang.Specification
import spock.lang.Unroll

import static org.gradle.testkit.runner.TaskOutcome.SUCCESS

class GradleJavaToolchainsSupportFunctionalTest extends Specification {
File rootDir
File buildFile
String version = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version)

@BeforeEach
def setup() {
rootDir = Files.createTempDir()
buildFile = new File(rootDir, 'build.gradle')
buildFile << """
plugins {
id 'java'
id 'com.github.spotbugs'
}
version = 1.0
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(16)
}
}
"""
File sourceDir = rootDir.toPath().resolve("src").resolve("main").resolve("java").toFile()
sourceDir.mkdirs()
File sourceFile = new File(sourceDir, "Foo.java")
sourceFile << """
public class Foo {
public static void main(String... args) {
System.out.println("Hello, SpotBugs!");
}
}
"""
}

@Unroll
def 'Supports Gradle Java Toolchains (#processConfiguration)'() {
setup:
buildFile << """
spotbugs {
useJavaToolchains = true
}"""
when:
def arguments = [':spotbugsMain', '-is']
arguments.add(processConfigurationArgument)
def runner = GradleRunner.create()
.withProjectDir(rootDir)
.withArguments(arguments)
.withPluginClasspath()
.forwardOutput()
.withGradleVersion(version)
def result = runner.build()
then:
result.task(':spotbugsMain').outcome == SUCCESS
result.output.contains('Spotbugs will be executed using Java Toolchain configuration')
where:
processConfiguration | processConfigurationArgument
'javaexec' | '-Pcom.github.spotbugs.snom.worker=false'
'worker-api' | '-Pcom.github.spotbugs.snom.worker=true'
'javaexec-in-worker' | '-Pcom.github.spotbugs.snom.javaexec-in-worker=true'
}
@Unroll
def 'Do not use Gradle Java Toolchains if extension is not configured (#processConfiguration)'() {
when:
def arguments = [':spotbugsMain', '-is']
arguments.add(processConfigurationArgument)
def runner = GradleRunner.create()
.withProjectDir(rootDir)
.withArguments(arguments)
.withPluginClasspath()
.forwardOutput()
.withGradleVersion(version)
def result = runner.build()
then:
result.task(':spotbugsMain').outcome == SUCCESS
!result.output.contains('Spotbugs will be executed using Java Toolchain configuration')
where:
processConfiguration | processConfigurationArgument
'javaexec' | '-Pcom.github.spotbugs.snom.worker=false'
'worker-api' | '-Pcom.github.spotbugs.snom.worker=true'
'javaexec-in-worker' | '-Pcom.github.spotbugs.snom.javaexec-in-worker=true'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ class SpotBugsExtension {
@NonNull
final Property<Boolean> useAuxclasspathFile;

@NonNull
final Property<Boolean> useJavaToolchains

@Inject
SpotBugsExtension(Project project, ObjectFactory objects) {
ignoreFailures = objects.property(Boolean).convention(false);
Expand Down Expand Up @@ -188,6 +191,7 @@ class SpotBugsExtension {
maxHeapSize = objects.property(String);
toolVersion = objects.property(String)
useAuxclasspathFile = objects.property(Boolean).convention(false)
useJavaToolchains = objects.property(Boolean).convention(false)
}

void setReportLevel(@Nullable String name) {
Expand Down
36 changes: 31 additions & 5 deletions src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import edu.umd.cs.findbugs.annotations.NonNull
import edu.umd.cs.findbugs.annotations.Nullable;
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SkipWhenEmpty
Expand All @@ -48,7 +49,9 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.VerificationTask;
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;
Expand Down Expand Up @@ -91,7 +94,10 @@ import javax.inject.Inject
*/

@CacheableTask
class SpotBugsTask extends DefaultTask implements VerificationTask {
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 @@ -290,6 +296,10 @@ class SpotBugsTask extends DefaultTask implements VerificationTask {
}
}

@Nested
@Optional
abstract Property<JavaLauncher> getLauncher()

@Inject
SpotBugsTask(ObjectFactory objects, WorkerExecutor workerExecutor) {
this.workerExecutor = Objects.requireNonNull(workerExecutor);
Expand Down Expand Up @@ -360,23 +370,39 @@ class SpotBugsTask extends DefaultTask implements VerificationTask {
extraArgs.convention(extension.extraArgs)
maxHeapSize.convention(extension.maxHeapSize)
useAuxclasspathFile.convention(extension.useAuxclasspathFile)

if(extension.useJavaToolchains.isPresent() && extension.useJavaToolchains.get()) {
configureJavaLauncher()
}

this.isSpotBugsPluginApplied = isSpotBugsPluginApplied
this.enableWorkerApi = enableWorkerApi
this.enableHybridWorker = enableHybridWorker
}


/**
* Set convention for default java launcher based on Toolchain configuration
*/
private void configureJavaLauncher() {
def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain
JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class)
Provider<JavaLauncher> defaultLauncher = service.launcherFor(toolchain)
launcher.convention(defaultLauncher)
}

@TaskAction
void run() {
if (!enableWorkerApi) {
log.info("Running SpotBugs by JavaExec...");
new SpotBugsRunnerForJavaExec().run(this);
new SpotBugsRunnerForJavaExec(launcher).run(this);
} else if (enableHybridWorker && GradleVersion.current() >= GradleVersion.version("6.0")) {
// ExecOperations is supported from Gradle 6.0
log.info("Running SpotBugs by Gradle no-isolated Worker...");
new SpotBugsRunnerForHybrid(workerExecutor).run(this);
new SpotBugsRunnerForHybrid(workerExecutor, launcher).run(this);
} else {
log.info("Running SpotBugs by Gradle process-isolated Worker...");
new SpotBugsRunnerForWorker(workerExecutor).run(this);
new SpotBugsRunnerForWorker(workerExecutor, launcher).run(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.process.ExecOperations;
import org.gradle.process.JavaExecSpec;
import org.gradle.process.internal.ExecException;
Expand All @@ -50,9 +51,12 @@
*/
class SpotBugsRunnerForHybrid extends SpotBugsRunner {
private final WorkerExecutor workerExecutor;
private final Property<JavaLauncher> javaLauncher;

public SpotBugsRunnerForHybrid(@NonNull WorkerExecutor workerExecutor) {
public SpotBugsRunnerForHybrid(
@NonNull WorkerExecutor workerExecutor, Property<JavaLauncher> javaLauncher) {
this.workerExecutor = Objects.requireNonNull(workerExecutor);
this.javaLauncher = javaLauncher;
}

@Override
Expand All @@ -75,6 +79,11 @@ private Action<SpotBugsWorkParameters> configureWorkerSpec(SpotBugsTask task) {
params.getIgnoreFailures().set(task.getIgnoreFailures());
params.getShowStackTraces().set(task.getShowStackTraces());
params.getReportsDir().set(task.getReportsDir());
if (javaLauncher.isPresent()) {
params
.getJavaToolchainExecutablePath()
.set(javaLauncher.get().getExecutablePath().getAsFile().getAbsolutePath());
}
};
}

Expand All @@ -92,6 +101,8 @@ public interface SpotBugsWorkParameters extends WorkParameters {
Property<Boolean> getShowStackTraces();

DirectoryProperty getReportsDir();

Property<String> getJavaToolchainExecutablePath();
}

public abstract static class SpotBugsExecutor implements WorkAction<SpotBugsWorkParameters> {
Expand Down Expand Up @@ -140,6 +151,12 @@ private Action<? super JavaExecSpec> configureJavaExec(SpotBugsWorkParameters pa
if (maxHeapSize != null) {
spec.setMaxHeapSize(maxHeapSize);
}
if (params.getJavaToolchainExecutablePath().isPresent()) {
log.info(
"Spotbugs will be executed using Java Toolchain configuration: {}",
params.getJavaToolchainExecutablePath().get());
spec.setExecutable(params.getJavaToolchainExecutablePath().get());
}
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@
import java.util.stream.Collectors;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.provider.Property;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.process.JavaExecSpec;
import org.gradle.process.internal.ExecException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SpotBugsRunnerForJavaExec extends SpotBugsRunner {
private final Logger log = LoggerFactory.getLogger(SpotBugsRunnerForJavaExec.class);
private final Property<JavaLauncher> javaLauncher;

public SpotBugsRunnerForJavaExec(Property<JavaLauncher> javaLauncher) {
this.javaLauncher = javaLauncher;
}

@Override
public void run(@NonNull SpotBugsTask task) {
Expand Down Expand Up @@ -68,6 +75,13 @@ private Action<? super JavaExecSpec> configureJavaExec(SpotBugsTask task) {
if (maxHeapSize != null) {
spec.setMaxHeapSize(maxHeapSize);
}
if (javaLauncher.isPresent()) {
log.info(
"Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}",
javaLauncher.get().getMetadata().getVendor(),
javaLauncher.get().getMetadata().getLanguageVersion().asInt());
spec.setExecutable(javaLauncher.get().getExecutablePath().getAsFile().getAbsolutePath());
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.gradle.api.GradleException;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.workers.ProcessWorkerSpec;
import org.gradle.workers.WorkAction;
import org.gradle.workers.WorkParameters;
Expand All @@ -36,10 +37,14 @@
import org.slf4j.LoggerFactory;

public class SpotBugsRunnerForWorker extends SpotBugsRunner {
private final Logger log = LoggerFactory.getLogger(SpotBugsRunnerForWorker.class);
private final WorkerExecutor workerExecutor;
private final Property<JavaLauncher> javaLauncher;

public SpotBugsRunnerForWorker(@NonNull WorkerExecutor workerExecutor) {
public SpotBugsRunnerForWorker(
@NonNull WorkerExecutor workerExecutor, Property<JavaLauncher> javaLauncher) {
this.workerExecutor = Objects.requireNonNull(workerExecutor);
this.javaLauncher = javaLauncher;
}

@Override
Expand All @@ -60,6 +65,14 @@ private Action<ProcessWorkerSpec> configureWorkerSpec(SpotBugsTask task) {
if (maxHeapSize != null) {
option.setMaxHeapSize(maxHeapSize);
}
if (javaLauncher.isPresent()) {
log.info(
"Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}",
javaLauncher.get().getMetadata().getVendor(),
javaLauncher.get().getMetadata().getLanguageVersion().asInt());
option.setExecutable(
javaLauncher.get().getExecutablePath().getAsFile().getAbsolutePath());
}
});
};
}
Expand Down

0 comments on commit 2978bed

Please sign in to comment.