From 08f01867e4a209b1e8b61752e98ad6856ffe505b Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Mon, 31 Jul 2023 19:58:34 +0800 Subject: [PATCH 01/41] build: introduce Kotlin Signed-off-by: Kengo TODA --- build.gradle.kts | 7 ++++ .../com/github/spotbugs/snom/Confidence.kt} | 37 +++++++------------ 2 files changed, 21 insertions(+), 23 deletions(-) rename src/main/{groovy/com/github/spotbugs/snom/Confidence.groovy => kotlin/com/github/spotbugs/snom/Confidence.kt} (72%) diff --git a/build.gradle.kts b/build.gradle.kts index 316d4d35..ad408266 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { `java-gradle-plugin` jacoco signing + kotlin("jvm") version "1.9.0" id("com.github.spotbugs.gradle-plugin") id("com.github.spotbugs.plugin-publish") id("com.github.spotbugs.test") @@ -52,6 +53,12 @@ spotbugs { ignoreFailures.set(true) } tasks { + withType { + // Groovy code depends on class files generated by Kotlin + classpath += files(sourceSets.named("main").flatMap { + it.kotlin.destinationDirectory + }) + } named("spotbugsMain") { reports { register("sarif") { diff --git a/src/main/groovy/com/github/spotbugs/snom/Confidence.groovy b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt similarity index 72% rename from src/main/groovy/com/github/spotbugs/snom/Confidence.groovy rename to src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index ad890a9e..93ee2921 100644 --- a/src/main/groovy/com/github/spotbugs/snom/Confidence.groovy +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -11,11 +11,10 @@ * 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 java.util.Optional; -import javax.annotation.Nonnull; -import org.gradle.api.tasks.Internal; +import java.util.Optional +import org.gradle.api.tasks.Internal /** * The {@code Confidence} is used to specify the level to report bugs. Lower level contains more @@ -35,36 +34,28 @@ import org.gradle.api.tasks.Internal; * *

See also SpotBugs Manual.

*/ -enum Confidence { +enum class Confidence { /** The report level to report all detected bugs in the report. */ LOW { - @Override - Optional toCommandLineOption() { - return Optional.of("-low"); - } + override fun toCommandLineOption(): Optional = + Optional.of("-low") }, /** The report level to report medium and high priority detected bugs in the report. */ MEDIUM { - @Override - Optional toCommandLineOption() { - return Optional.of("-medium"); - } + override fun toCommandLineOption(): Optional = + Optional.of("-medium") }, /** The default level that provides the same feature with {@link #MEDIUM}. */ DEFAULT { - @Override - Optional toCommandLineOption() { - return Optional.empty(); - } + override fun toCommandLineOption(): Optional = + Optional.empty() }, /** The report level to report high priority detected bugs in the report. */ HIGH { - @Override - Optional toCommandLineOption() { - return Optional.of("-high"); - } - } + override fun toCommandLineOption(): Optional = + Optional.of("-high") + }; @Internal("This is internally used property so no need to refer to judge out-of-date or not.") - abstract @Nonnull Optional toCommandLineOption() + abstract fun toCommandLineOption(): Optional } From 107a781af5238a089c678a8d23e3ffcd75ba3664 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Mon, 31 Jul 2023 20:08:23 +0800 Subject: [PATCH 02/41] chore: convert Effort class Signed-off-by: Kengo TODA --- .../com/github/spotbugs/snom/Confidence.kt | 28 +++++++------ .../com/github/spotbugs/snom/Effort.kt} | 40 +++++++++++-------- 2 files changed, 40 insertions(+), 28 deletions(-) rename src/main/{groovy/com/github/spotbugs/snom/Effort.groovy => kotlin/com/github/spotbugs/snom/Effort.kt} (59%) diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index 93ee2921..ff74b23e 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -17,22 +17,26 @@ import java.util.Optional import org.gradle.api.tasks.Internal /** - * The {@code Confidence} is used to specify the level to report bugs. Lower level contains more - * bugs reported. To include all bugs to your report, use {@link #LOW}. + * The [Confidence] is used to specify the level to report bugs. Lower level contains more + * bugs reported. To include all bugs to your report, use [LOW]. * - *

Usage: + * ### Usage * - *

Set via the {@code spotbugs} extension to configure all tasks in your project:

- * spotbugs {
- *     reportLevel = 'low'
- *
+ * Set via the {@code spotbugs} extension to configure all tasks in your project: + * ```kotlin + * spotbugs { + * reportLevel = "low" + * } + * ``` * - *

Or via {@code SpotBugsTask} to configure the specific task in your project:

- * spotbugsMain { // or name of another task
- *     reportLevel = 'high'
- * }
+ * Or via [SpotBugsTask] to configure the specific task in your project: + * ```kotlin + * spotbugsMain { // or name of another task + * reportLevel = "high" + * } + * ``` * - *

See also SpotBugs Manual.

+ * See also [SpotBugs Manual](https://spotbugs.readthedocs.io/en/stable/running.html). */ enum class Confidence { /** The report level to report all detected bugs in the report. */ diff --git a/src/main/groovy/com/github/spotbugs/snom/Effort.groovy b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt similarity index 59% rename from src/main/groovy/com/github/spotbugs/snom/Effort.groovy rename to src/main/kotlin/com/github/spotbugs/snom/Effort.kt index 922c5636..c347e8c9 100644 --- a/src/main/groovy/com/github/spotbugs/snom/Effort.groovy +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -11,44 +11,52 @@ * 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 /** - * The {@code Effort} is configuration to adjust SpotBugs detectors. Use lower effort to reduce + * The [Effort] is configuration to adjust SpotBugs detectors. Use lower effort to reduce * computation cost. * - *

Usage: + * ### Usage * - *

Set via the {@code spotbugs} extension to configure all tasks in your project:

- * spotbugs {
- *     effort = 'less'
- * }
+ * Set via the `spotbugs` extension to configure all tasks in your project: + * ```kotlin + * spotbugs { + * effort = "less" + * } + * ``` * - *

Or via {@code SpotBugsTask} to configure the specific task in your project:

- * spotbugsMain { // or name of another task
- *     effort = 'max'
- * }
+ * Or via [SpotBugsTask] to configure the specific task in your project: + * ```kotlin + * spotbugsMain { // or name of another task + * effort = "max" + * } + * ``` * - *

See also SpotBugs Manual.

+ * See also [SpotBugs Manual](https://spotbugs.readthedocs.io/en/stable/effort.html). */ -enum Effort { +enum class Effort { /** * The effort level to minimize the computation cost. SpotBugs will try to conserve space at the * expense of precision. */ MIN, - /** The effort level to reduce the computation cost. */ + + /** The effort level to reduce the computation cost. */ LESS, - /** The default level that provides the same feature with {@link #MORE}. */ + + /** The default level that provides the same feature with [MORE]. */ DEFAULT, + /** * The effort level that uses more computation cost. SpotBugs will try to detect more problems by * Interprocedural Analysis and Null Pointer Analysis. */ MORE, + /** * The effort level that maximize the computation cost. SpotBugs will run Interprocedural Analysis * of Referenced Classes. */ MAX -} +} \ No newline at end of file From a2db47a4762f465a65640bd79069e8e5b7487850 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Mon, 31 Jul 2023 20:13:06 +0800 Subject: [PATCH 03/41] chore: convert internal.SemanticVersion Signed-off-by: Kengo TODA --- .../snom/internal/SemanticVersion.java | 70 ------------------- .../spotbugs/snom/internal/SemanticVersion.kt | 53 ++++++++++++++ 2 files changed, 53 insertions(+), 70 deletions(-) delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SemanticVersion.java create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SemanticVersion.java b/src/main/groovy/com/github/spotbugs/snom/internal/SemanticVersion.java deleted file mode 100644 index 1b814b71..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SemanticVersion.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import java.util.Comparator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @see spec - */ -public final class SemanticVersion implements Comparable { - /** - * @see regular expressions 101 - */ - private static final Pattern PATTERN = - Pattern.compile( - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"); - - private final int major; - private final int minor; - private final int patch; - - public SemanticVersion(String version) { - Matcher matcher = PATTERN.matcher(version); - if (!matcher.matches()) { - throw new IllegalArgumentException(version + " is not valid as a semantic version"); - } - - major = Integer.parseInt(matcher.group(1), 10); - minor = Integer.parseInt(matcher.group(2), 10); - patch = Integer.parseInt(matcher.group(3), 10); - } - - public int getMajor() { - return major; - } - - public int getMinor() { - return minor; - } - - public int getPatch() { - return patch; - } - - @Override - public int compareTo(SemanticVersion that) { - return Comparator.comparingInt(SemanticVersion::getMajor) - .thenComparingInt(SemanticVersion::getMinor) - .thenComparingInt(SemanticVersion::getPatch) - .compare(this, that); - } - - @Override - public String toString() { - return "SemanticVersion(" + major + '.' + minor + '.' + patch + ')'; - } -} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt new file mode 100644 index 00000000..bb43c4b6 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import java.util.regex.Pattern + +/** + * See [SemVer 2.0 specification](https://semver.org/spec/v2.0.0.html) + */ +class SemanticVersion(version: String) : Comparable { + val major: Int + val minor: Int + val patch: Int + + init { + val matcher = PATTERN.matcher(version) + require(matcher.matches()) { "$version is not valid as a semantic version" } + major = matcher.group(1).toInt(10) + minor = matcher.group(2).toInt(10) + patch = matcher.group(3).toInt(10) + } + + override operator fun compareTo(other: SemanticVersion?): Int { + return Comparator.comparingInt { obj: SemanticVersion -> obj.major } + .thenComparingInt { obj: SemanticVersion -> obj.minor } + .thenComparingInt { obj: SemanticVersion -> obj.patch } + .compare(this, other) + } + + override fun toString(): String { + return "SemanticVersion($major.$minor.$patch)" + } + + companion object { + /** + * @see [regular expressions 101](https://regex101.com/r/vkijKf/1/) + */ + private val PATTERN = Pattern.compile( + "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + ) + } +} \ No newline at end of file From 50fbd4e8a762427eb40f8729d1bea8b4efa85af8 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Mon, 31 Jul 2023 20:46:15 +0800 Subject: [PATCH 04/41] chore: convert SpotBugsExtension Signed-off-by: Kengo TODA --- build.gradle.kts | 8 +- ...m.github.spotbugs.gradle-plugin.gradle.kts | 3 + .../spotbugs/snom/SpotBugsBasePlugin.java | 31 ++- .../spotbugs/snom/SpotBugsExtension.groovy | 205 ------------------ .../com/github/spotbugs/snom/Confidence.kt | 7 +- .../kotlin/com/github/spotbugs/snom/Effort.kt | 4 +- .../github/spotbugs/snom/SpotBugsExtension.kt | 152 +++++++++++++ .../spotbugs/snom/internal/SemanticVersion.kt | 4 +- 8 files changed, 196 insertions(+), 218 deletions(-) delete mode 100644 src/main/groovy/com/github/spotbugs/snom/SpotBugsExtension.groovy create mode 100644 src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt diff --git a/build.gradle.kts b/build.gradle.kts index ad408266..5a55dd99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,9 +55,11 @@ spotbugs { tasks { withType { // Groovy code depends on class files generated by Kotlin - classpath += files(sourceSets.named("main").flatMap { - it.kotlin.destinationDirectory - }) + classpath += files( + sourceSets.named("main").flatMap { + it.kotlin.destinationDirectory + }, + ) } named("spotbugsMain") { reports { diff --git a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts index 4caed177..40890c73 100644 --- a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts +++ b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts @@ -35,6 +35,9 @@ spotless { greclipse() indentWithSpaces() } + kotlin { + ktlint() + } kotlinGradle { ktlint() } diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java b/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java index e602410c..cc99cf95 100644 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java +++ b/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java @@ -22,7 +22,9 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.plugins.ReportingBasePlugin; +import org.gradle.api.reporting.ReportingExtension; import org.gradle.util.GradleVersion; public class SpotBugsBasePlugin implements Plugin { @@ -30,6 +32,8 @@ public class SpotBugsBasePlugin implements Plugin { private static final String FEATURE_FLAG_HYBRID_WORKER = "com.github.spotbugs.snom.javaexec-in-worker"; + private static final String DEFAULT_REPORTS_DIR_NAME = "spotbugs"; + /** * Supported Gradle version described at official manual site. project.getVersion().toString())); + + // ReportingBasePlugin should be applied before we create this SpotBugsExtension instance + DirectoryProperty baseReportsDir = + project.getExtensions().getByType(ReportingExtension.class).getBaseDirectory(); + extension + .getReportsDir() + .convention(baseReportsDir.map(directory -> directory.dir(DEFAULT_REPORTS_DIR_NAME))); + extension.getUseAuxclasspathFile().convention(true); + extension.getUseJavaToolchains().convention(false); + + return extension; } private void createConfiguration(Project project, SpotBugsExtension extension) { diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsExtension.groovy b/src/main/groovy/com/github/spotbugs/snom/SpotBugsExtension.groovy deleted file mode 100644 index 07e6a8aa..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsExtension.groovy +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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 edu.umd.cs.findbugs.annotations.NonNull -import edu.umd.cs.findbugs.annotations.Nullable -import org.gradle.api.Action -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.reporting.ReportingExtension - -import javax.inject.Inject; -import org.gradle.api.Project; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; - -/** - * The extension to configure the SpotBugs Gradle plugin. Most of properties in this extension will be used as the default property of all {@link SpotBugsTask}. - * All properties are optional. - * - *

Usage: - *

After you apply the SpotBugs Gradle plugin to project, write extension like below:

- * - *

See also SpotBugs Manual about configuration.

- */ -class SpotBugsExtension { - static final String DEFAULT_REPORTS_DIR_NAME = "spotbugs"; - - @NonNull - final Property ignoreFailures; - @NonNull - final Property showStackTraces; - /** - * Property to enable progress reporting during the analysis. Default value is {@code false}. - */ - @NonNull - final Property showProgress; - /** - * Property to specify the level to report bugs. Default value is {@link Confidence#DEFAULT}. - */ - @NonNull - final Property reportLevel; - /** - * Property to adjust SpotBugs detectors. Default value is {@link Effort#DEFAULT}. - */ - @NonNull - final Property effort; - /** - * Property to enable visitors (detectors) for analysis. Default is empty that means all visitors run analysis. - */ - @NonNull - final ListProperty visitors; - /** - * Property to disable visitors (detectors) for analysis. Default is empty that means SpotBugs omits no visitor. - */ - @NonNull - final ListProperty omitVisitors; - /** - * Property to set the directory to generate report files. Default is {@code "$buildDir/reports/spotbugs"}. - * - *

Note that each {@link SpotBugsTask} creates own sub-directory in this directory.

- */ - @NonNull - final DirectoryProperty reportsDir; - /** - * Property to set the filter file to limit which bug should be reported. - * - *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. - * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

- * - *

See also SpotBugs Manual about Filter file.

- */ - @NonNull - final RegularFileProperty includeFilter; - /** - * Property to set the filter file to limit which bug should be reported. - * - *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. - * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

- * - *

See also SpotBugs Manual about Filter file.

- */ - @NonNull - final RegularFileProperty excludeFilter; - /** - * Property to set the baseline file. This file is a Spotbugs result file, and all bugs reported in this file will not be - * reported in the final output. - */ - @NonNull - final RegularFileProperty baselineFile; - /** - * Property to specify the target classes for analysis. Default value is empty that means all classes are analyzed. - */ - @NonNull - final ListProperty onlyAnalyze; - /** - * Property to specify the name of project. Some reporting formats use this property. Default value is the name of your Gradle project. - */ - @NonNull - final Property projectName; - /** - * Property to specify the release identifier of project. Some reporting formats use this property. Default value is the version of your Gradle project. - */ - @NonNull - final Property release; - /** - * Property to specify the extra arguments for SpotBugs. Default value is empty so SpotBugs will get no extra argument. - */ - @NonNull - final ListProperty extraArgs; - /** - * Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. - */ - @NonNull - final ListProperty jvmArgs; - /** - * Property to specify the max heap size ({@code -Xmx} option) of JVM process. - * Default value is empty so the default configuration made by Gradle will be used. - */ - @NonNull - final Property maxHeapSize; - - @NonNull - final Property toolVersion - - @NonNull - final Property useAuxclasspathFile; - - @NonNull - final Property useJavaToolchains - - @Inject - SpotBugsExtension(Project project, ObjectFactory objects) { - ignoreFailures = objects.property(Boolean).convention(false); - showStackTraces = objects.property(Boolean).convention(false); - showProgress = objects.property(Boolean); - reportLevel = objects.property(Confidence); - effort = objects.property(Effort); - visitors = objects.listProperty(String); - omitVisitors = objects.listProperty(String); - reportsDir = objects.directoryProperty() - includeFilter = objects.fileProperty() - excludeFilter = objects.fileProperty() - baselineFile = objects.fileProperty() - onlyAnalyze = objects.listProperty(String); - projectName = objects.property(String); - release = objects.property(String); - - projectName.convention(project.name) - release.convention(project.provider {project.version.toString()}) - - // ReportingBasePlugin should be applied before we create this SpotBugsExtension instance - DirectoryProperty baseReportsDir = project.extensions.getByType(ReportingExtension).baseDirectory - reportsDir = objects.directoryProperty().convention(baseReportsDir.map({ - it.dir(DEFAULT_REPORTS_DIR_NAME) - })) - - jvmArgs = objects.listProperty(String); - extraArgs = objects.listProperty(String); - maxHeapSize = objects.property(String); - toolVersion = objects.property(String) - useAuxclasspathFile = objects.property(Boolean).convention(true) - useJavaToolchains = objects.property(Boolean).convention(false) - } - - void setReportLevel(@Nullable String name) { - Confidence confidence = name == null ? null : Confidence.valueOf(name.toUpperCase()) - getReportLevel().set(confidence) - } - - void setEffort(@Nullable String name) { - Effort effort = name == null ? null : Effort.valueOf(name.toUpperCase()) - getEffort().set(effort) - } -} diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index ff74b23e..46148757 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -13,8 +13,8 @@ */ package com.github.spotbugs.snom -import java.util.Optional import org.gradle.api.tasks.Internal +import java.util.Optional /** * The [Confidence] is used to specify the level to report bugs. Lower level contains more @@ -44,21 +44,24 @@ enum class Confidence { override fun toCommandLineOption(): Optional = Optional.of("-low") }, + /** The report level to report medium and high priority detected bugs in the report. */ MEDIUM { override fun toCommandLineOption(): Optional = Optional.of("-medium") }, + /** The default level that provides the same feature with {@link #MEDIUM}. */ DEFAULT { override fun toCommandLineOption(): Optional = Optional.empty() }, + /** The report level to report high priority detected bugs in the report. */ HIGH { override fun toCommandLineOption(): Optional = Optional.of("-high") - }; + }, ; @Internal("This is internally used property so no need to refer to judge out-of-date or not.") abstract fun toCommandLineOption(): Optional diff --git a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt index c347e8c9..bae9a430 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -58,5 +58,5 @@ enum class Effort { * The effort level that maximize the computation cost. SpotBugs will run Interprocedural Analysis * of Referenced Classes. */ - MAX -} \ No newline at end of file + MAX, +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt new file mode 100644 index 00000000..8d9183ed --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property + +/** + * The extension to configure the SpotBugs Gradle plugin. Most of properties in this extension will be used as the default property of all {@link SpotBugsTask}. + * All properties are optional. + * + * ### Usage + * After you apply the SpotBugs Gradle plugin to project, write extension like below: + * ```kotlin + * spotbugs { + * ignoreFailures.set(false) + * showStackTraces.set(true) + * showProgress.set(false) + * reportLevel.set("default") + * effort.set("default") + * visitors.value(listOf("FindSqlInjection", "SwitchFallthrough")) + * omitVisitors.value(listOf("FindNonShortCircuit")) + * reportsDir.set("$buildDir/reports/spotbugs") + * includeFilter.set("spotbugs-include.xml") + * excludeFilter.set("spotbugs-exclude.xml") + * onlyAnalyze.value(listOf("com.foobar.MyClass", "com.foobar.mypkg.*")) + * projectName.set(name) + * release.set(version) + * extraArgs.value(listOf("-nested:false")) + * jvmArgs.value(listOf("-Duser.language=ja")) + * maxHeapSize.set("512m") + * } + * ``` + * + * See also [SpotBugs Manual about configuration](https://spotbugs.readthedocs.io/en/stable/running.html). + */ +interface SpotBugsExtension { + val ignoreFailures: Property + val showStackTraces: Property + + /** + * Property to enable progress reporting during the analysis. Default value is `false`. + */ + val showProgress: Property + + /** + * Property to specify the level to report bugs. Default value is [Confidence.DEFAULT]. + */ + val reportLevel: Property + + /** + * Property to adjust SpotBugs detectors. Default value is [Effort.DEFAULT]. + */ + val effort: Property + + /** + * Property to enable visitors (detectors) for analysis. Default is empty that means all visitors run analysis. + */ + val visitors: ListProperty + + /** + * Property to disable visitors (detectors) for analysis. Default is empty that means SpotBugs omits no visitor. + */ + val omitVisitors: ListProperty + + /** + * Property to set the directory to generate report files. Default is `"$buildDir/reports/spotbugs"`. + * + * Note that each [SpotBugsTask] creates own sub-directory in this directory. + */ + val reportsDir: DirectoryProperty + + /** + * Property to set the filter file to limit which bug should be reported. + * + * Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, + * use [#onlyAnalyze] instead. + * To limit the visitors (detectors) to run, use [visitors] and [omitVisitors] instead. + * + * See also [SpotBugs Manual about Filter file](https://spotbugs.readthedocs.io/en/stable/filter.html). + */ + val includeFilter: RegularFileProperty + + /** + * Property to set the filter file to limit which bug should be reported. + * + * Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, + * use [onlyAnalyze] instead. + * To limit the visitors (detectors) to run, use [visitors] and [omitVisitors] instead. + * + * See also [SpotBugs Manual about Filter file](https://spotbugs.readthedocs.io/en/stable/filter.html). + */ + val excludeFilter: RegularFileProperty + + /** + * Property to set the baseline file. This file is a Spotbugs result file, and all bugs reported in this file will not be + * reported in the final output. + */ + val baselineFile: RegularFileProperty + + /** + * Property to specify the target classes for analysis. Default value is empty that means all classes are analyzed. + */ + val onlyAnalyze: ListProperty + + /** + * Property to specify the name of project. Some reporting formats use this property. Default value is the name of your Gradle project. + */ + val projectName: Property + + /** + * Property to specify the release identifier of project. Some reporting formats use this property. + * Default value is the version of your Gradle project. + */ + val release: Property + + /** + * Property to specify the extra arguments for SpotBugs. + * Default value is empty so SpotBugs will get no extra argument. + */ + val extraArgs: ListProperty + + /** + * Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. + */ + val jvmArgs: ListProperty + + /** + * Property to specify the max heap size (`-Xmx` option) of JVM process. + * Default value is empty so the default configuration made by Gradle will be used. + */ + val maxHeapSize: Property + + val toolVersion: Property + + val useAuxclasspathFile: Property + + val useJavaToolchains: Property +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt index bb43c4b6..4adf38ea 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SemanticVersion.kt @@ -47,7 +47,7 @@ class SemanticVersion(version: String) : Comparable { * @see [regular expressions 101](https://regex101.com/r/vkijKf/1/) */ private val PATTERN = Pattern.compile( - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", ) } -} \ No newline at end of file +} From d247057dbb05b229bf3c33340e33d7d28e5ec424 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Wed, 2 Aug 2023 04:00:36 +0800 Subject: [PATCH 05/41] chore: keep replacing Java code with Kotlin code Signed-off-by: Kengo TODA --- .idea/kotlinc.xml | 6 + .idea/runConfigurations.xml | 10 - .../spotbugs/snom/SpotBugsBasePlugin.java | 172 ----------------- .../github/spotbugs/snom/SpotBugsPlugin.java | 61 ------ .../snom/internal/SpotBugsTaskFactory.java | 131 ------------- .../spotbugs/snom/SpotBugsBasePlugin.kt | 173 ++++++++++++++++++ .../github/spotbugs/snom/SpotBugsPlugin.kt | 67 +++++++ .../snom/internal/SpotBugsTaskFactory.kt | 122 ++++++++++++ 8 files changed, 368 insertions(+), 374 deletions(-) create mode 100644 .idea/kotlinc.xml delete mode 100644 .idea/runConfigurations.xml delete mode 100644 src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/SpotBugsPlugin.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.java create mode 100644 src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 00000000..fdf8d994 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea5..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java b/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java deleted file mode 100644 index 0a850de0..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsBasePlugin.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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 java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.net.URL; -import java.util.Properties; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.plugins.ReportingBasePlugin; -import org.gradle.api.reporting.ReportingExtension; -import org.gradle.util.GradleVersion; - -public class SpotBugsBasePlugin implements Plugin { - 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 static final String DEFAULT_REPORTS_DIR_NAME = "spotbugs"; - - /** - * Supported Gradle version described at official manual site. The Gradle Worker API needs 5.6 or - * later, so we use this value as minimal required version. - */ - private static final GradleVersion SUPPORTED_VERSION = GradleVersion.version("7.0"); - - @Override - public void apply(Project project) { - verifyGradleVersion(GradleVersion.current()); - project.getPluginManager().apply(ReportingBasePlugin.class); - - SpotBugsExtension extension = createExtension(project); - createConfiguration(project, extension); - createPluginConfiguration(project); - - String enableWorkerApi = getPropertyOrDefault(project, FEATURE_FLAG_WORKER_API, "true"); - String enableHybridWorker = getPropertyOrDefault(project, FEATURE_FLAG_HYBRID_WORKER, "true"); - project - .getTasks() - .withType(SpotBugsTask.class) - .configureEach( - task -> - task.init( - extension, - Boolean.parseBoolean(enableWorkerApi), - Boolean.parseBoolean(enableHybridWorker))); - } - - private SpotBugsExtension createExtension(Project project) { - SpotBugsExtension extension = - project - .getExtensions() - .create( - SpotBugsPlugin.EXTENSION_NAME, - SpotBugsExtension.class, - project, - project.getObjects()); - extension.getIgnoreFailures().convention(false); - extension.getShowStackTraces().convention(false); - extension.getProjectName().convention(project.provider(project::getName)); - extension.getRelease().convention(project.provider(() -> project.getVersion().toString())); - - // ReportingBasePlugin should be applied before we create this SpotBugsExtension instance - DirectoryProperty baseReportsDir = - project.getExtensions().getByType(ReportingExtension.class).getBaseDirectory(); - extension - .getReportsDir() - .convention(baseReportsDir.map(directory -> directory.dir(DEFAULT_REPORTS_DIR_NAME))); - extension.getUseAuxclasspathFile().convention(true); - extension.getUseJavaToolchains().convention(false); - - return extension; - } - - private void createConfiguration(Project project, SpotBugsExtension extension) { - Properties props = loadProperties(); - extension.getToolVersion().convention(props.getProperty("spotbugs-version")); - - Configuration configuration = - project - .getConfigurations() - .create(SpotBugsPlugin.CONFIG_NAME) - .setDescription("configuration for the SpotBugs engine") - .setVisible(false) - .setTransitive(true); - - configuration.defaultDependencies( - (DependencySet dependencies) -> - dependencies.add( - project - .getDependencies() - .create("com.github.spotbugs:spotbugs:" + extension.getToolVersion().get()))); - - Configuration spotbugsSlf4j = - project - .getConfigurations() - .create(SpotBugsPlugin.SLF4J_CONFIG_NAME) - .setDescription("configuration for the SLF4J provider to run SpotBugs") - .setVisible(false) - .setTransitive(true); - - spotbugsSlf4j.defaultDependencies( - (DependencySet dependencies) -> - dependencies.add( - project - .getDependencies() - .create("org.slf4j:slf4j-simple:" + props.getProperty("slf4j-version")))); - } - - Properties loadProperties() { - URL url = - SpotBugsPlugin.class.getClassLoader().getResource("spotbugs-gradle-plugin.properties"); - try (InputStream input = url.openStream()) { - Properties prop = new Properties(); - prop.load(input); - return prop; - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private Configuration createPluginConfiguration(Project project) { - Configuration configuration = - project - .getConfigurations() - .create(SpotBugsPlugin.PLUGINS_CONFIG_NAME) - .setDescription("configuration for the external SpotBugs plugins") - .setVisible(false) - .setTransitive(true); - project - .getConfigurations() - .create(SpotBugsPlugin.INTERNAL_CONFIG_NAME) - .setDescription( - "configuration for the external SpotBugs plugins excluding transitive dependencies") - .setTransitive(false); - return configuration; - } - - void verifyGradleVersion(GradleVersion version) { - if (version.compareTo(SUPPORTED_VERSION) < 0) { - String message = - String.format( - "Gradle version %s is unsupported. Please use %s or later.", - version, SUPPORTED_VERSION); - throw new IllegalArgumentException(message); - } - } - - private String getPropertyOrDefault(Project project, String propertyName, String defaultValue) { - return project.hasProperty(propertyName) - ? project.property(propertyName).toString() - : defaultValue; - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsPlugin.java b/src/main/groovy/com/github/spotbugs/snom/SpotBugsPlugin.java deleted file mode 100644 index bdd5b71f..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsPlugin.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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 com.github.spotbugs.snom.internal.SpotBugsTaskFactory; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaBasePlugin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SpotBugsPlugin implements Plugin { - public static final String CONFIG_NAME = "spotbugs"; - public static final String PLUGINS_CONFIG_NAME = "spotbugsPlugins"; - - /** - * The configuration contains SpotBugs plugin jar files only - * - * @see GitHub issue - */ - public static final String INTERNAL_CONFIG_NAME = "actualSpotbugsPlugins"; - - public static final String SLF4J_CONFIG_NAME = "spotbugsSlf4j"; - public static final String EXTENSION_NAME = "spotbugs"; - - private final Logger log = LoggerFactory.getLogger(getClass()); - - @Override - public void apply(Project project) { - project.getPluginManager().apply(SpotBugsBasePlugin.class); - project - .getPluginManager() - .withPlugin( - "java-base", - javaBase -> { - log.debug( - "The javaBase plugin has been applied, so making the check task depending on all of SpotBugsTask"); - project - .getTasks() - .named(JavaBasePlugin.CHECK_TASK_NAME) - .configure( - task -> task.dependsOn(project.getTasks().withType(SpotBugsTask.class))); - }); - createTasks(project); - } - - private void createTasks(Project project) { - new SpotBugsTaskFactory().generate(project); - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.java deleted file mode 100644 index 75d8272c..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.android.build.gradle.AppExtension; -import com.android.build.gradle.BaseExtension; -import com.android.build.gradle.LibraryExtension; -import com.android.build.gradle.api.BaseVariant; -import com.github.spotbugs.snom.SpotBugsTask; -import org.gradle.api.Action; -import org.gradle.api.DomainObjectSet; -import org.gradle.api.GradleException; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaBasePlugin; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.plugins.JavaPluginExtension; -import org.gradle.api.tasks.SourceSetContainer; -import org.gradle.api.tasks.compile.JavaCompile; -import org.gradle.util.GradleVersion; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SpotBugsTaskFactory { - private final Logger log = LoggerFactory.getLogger(SpotBugsTaskFactory.class); - - public void generate(Project project) { - generateForJava(project); - generateForAndroid(project); - } - - private SourceSetContainer getSourceSetContainer(Project project) { - if (GradleVersion.current().compareTo(GradleVersion.version("7.1")) < 0) { - return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets(); - } else { - return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); - } - } - - private void generateForJava(Project project) { - project - .getPlugins() - .withType(JavaBasePlugin.class) - .configureEach( - javaBasePlugin -> { - getSourceSetContainer(project) - .all( - sourceSet -> { - String name = sourceSet.getTaskName("spotbugs", null); - log.debug("Creating SpotBugsTask for {}", sourceSet); - project - .getTasks() - .register( - name, - SpotBugsTask.class, - task -> { - task.setSourceDirs( - sourceSet.getAllSource().getSourceDirectories()); - task.setClassDirs(sourceSet.getOutput()); - task.setAuxClassPaths(sourceSet.getCompileClasspath()); - String description = - String.format( - "Run SpotBugs analysis for the source set '%s'", - sourceSet.getName()); - task.setDescription(description); - }); - }); - }); - } - - static String toLowerCamelCase(String head, String tail) { - if (tail == null || tail.isEmpty()) { - return head; - } - StringBuilder builder = new StringBuilder(head.length() + tail.length()); - builder.append(head).append(Character.toUpperCase(tail.charAt(0))).append(tail.substring(1)); - return builder.toString(); - } - - private void generateForAndroid(Project project) { - - @SuppressWarnings("rawtypes") - final Action action = - (Action) - plugin -> { - final BaseExtension baseExtension = - project.getExtensions().getByType(BaseExtension.class); - DomainObjectSet variants; - if (baseExtension instanceof AppExtension) { - variants = ((AppExtension) baseExtension).getApplicationVariants(); - } else if (baseExtension instanceof LibraryExtension) { - variants = ((LibraryExtension) baseExtension).getLibraryVariants(); - } else { - throw new GradleException("Unrecognized Android extension " + baseExtension); - } - variants.all( - (BaseVariant variant) -> { - String spotbugsTaskName = toLowerCamelCase("spotbugs", variant.getName()); - log.debug("Creating SpotBugsTask for {}", variant.getName()); - project - .getTasks() - .register( - spotbugsTaskName, - SpotBugsTask.class, - spotbugsTask -> { - final JavaCompile javaCompile = - variant.getJavaCompileProvider().get(); - spotbugsTask.setSourceDirs(javaCompile.getSource()); - spotbugsTask.setClassDirs( - project.files(javaCompile.getDestinationDir())); - spotbugsTask.setAuxClassPaths(javaCompile.getClasspath()); - spotbugsTask.dependsOn(javaCompile); - }); - }); - }; - - project.getPlugins().withId("com.android.application", action); - project.getPlugins().withId("com.android.library", action); - } -} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt new file mode 100644 index 00000000..9e76d32d --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt @@ -0,0 +1,173 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.api.Action +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.DependencySet +import org.gradle.api.file.Directory +import org.gradle.api.plugins.ReportingBasePlugin +import org.gradle.api.reporting.ReportingExtension +import org.gradle.util.GradleVersion +import java.io.IOException +import java.io.UncheckedIOException +import java.util.* +import kotlin.IllegalArgumentException +import kotlin.String +import kotlin.toString + +class SpotBugsBasePlugin : Plugin { + override fun apply(project: Project) { + verifyGradleVersion(GradleVersion.current()) + project.pluginManager.apply(ReportingBasePlugin::class.java) + val extension = createExtension(project) + createConfiguration(project, extension) + createPluginConfiguration(project) + val enableWorkerApi = getPropertyOrDefault(project, FEATURE_FLAG_WORKER_API, "true") + val enableHybridWorker = getPropertyOrDefault(project, FEATURE_FLAG_HYBRID_WORKER, "true") + project + .tasks + .withType(SpotBugsTask::class.java) + .configureEach { task -> + task.init( + extension, + enableWorkerApi.toBoolean(), + enableHybridWorker.toBoolean() + ) + } + } + + private fun createExtension(project: Project): SpotBugsExtension { + val extension = project + .extensions + .create( + SpotBugsPlugin.EXTENSION_NAME, + SpotBugsExtension::class.java, + project, + project.objects + ) + extension.ignoreFailures.convention(false) + extension.showStackTraces.convention(false) + extension.projectName.convention(project.provider { project.name }) + extension.release.convention(project.provider { + project.version.toString() + }) + + // ReportingBasePlugin should be applied before we create this SpotBugsExtension instance + val baseReportsDir = project.extensions.getByType( + ReportingExtension::class.java + ).baseDirectory + extension + .reportsDir + .convention(baseReportsDir.map { directory: Directory -> + directory.dir( + DEFAULT_REPORTS_DIR_NAME + ) + }) + extension.useAuxclasspathFile.convention(true) + extension.useJavaToolchains.convention(false) + return extension + } + + private fun createConfiguration(project: Project, extension: SpotBugsExtension) { + val props = loadProperties() + extension.toolVersion.convention(props.getProperty("spotbugs-version")) + val configuration = project + .configurations + .create(SpotBugsPlugin.CONFIG_NAME) + .setDescription("configuration for the SpotBugs engine") + .setVisible(false) + .setTransitive(true) + configuration.defaultDependencies { dependencies: DependencySet -> + dependencies.add( + project + .dependencies + .create(extension.toolVersion.map { + "com.github.spotbugs:spotbugs:$it" + }) + ) + } + val spotbugsSlf4j = project + .configurations + .create(SpotBugsPlugin.SLF4J_CONFIG_NAME) + .setDescription("configuration for the SLF4J provider to run SpotBugs") + .setVisible(false) + .setTransitive(true) + spotbugsSlf4j.defaultDependencies { dependencies: DependencySet -> + dependencies.add( + project + .dependencies + .create("org.slf4j:slf4j-simple:" + props.getProperty("slf4j-version")) + ) + } + } + + fun loadProperties(): Properties { + val url = SpotBugsPlugin::class.java.classLoader.getResource("spotbugs-gradle-plugin.properties") + try { + url!!.openStream().use { input -> + val prop = Properties() + prop.load(input) + return prop + } + } catch (e: IOException) { + throw UncheckedIOException(e) + } + } + + private fun createPluginConfiguration(project: Project): Configuration { + val configuration = project + .configurations + .create(SpotBugsPlugin.PLUGINS_CONFIG_NAME) + .setDescription("configuration for the external SpotBugs plugins") + .setVisible(false) + .setTransitive(true) + project + .configurations + .create(SpotBugsPlugin.INTERNAL_CONFIG_NAME) + .setDescription( + "configuration for the external SpotBugs plugins excluding transitive dependencies" + ).isTransitive = false + return configuration + } + + fun verifyGradleVersion(version: GradleVersion) { + if (version < SUPPORTED_VERSION) { + val message = String.format( + "Gradle version %s is unsupported. Please use %s or later.", + version, SUPPORTED_VERSION + ) + throw IllegalArgumentException(message) + } + } + + private fun getPropertyOrDefault(project: Project, propertyName: String, defaultValue: String): String { + return if (project.hasProperty(propertyName)) project.property(propertyName).toString() else defaultValue + } + + companion object { + private const val FEATURE_FLAG_WORKER_API = "com.github.spotbugs.snom.worker" + private const val FEATURE_FLAG_HYBRID_WORKER = "com.github.spotbugs.snom.javaexec-in-worker" + private const val DEFAULT_REPORTS_DIR_NAME = "spotbugs" + + /** + * Supported Gradle version described at [official manual site](http://spotbugs.readthedocs.io/en/latest/gradle.html). [The Gradle Worker API](https://guides.gradle.org/using-the-worker-api/) needs 5.6 or + * later, so we use this value as minimal required version. + */ + private val SUPPORTED_VERSION = GradleVersion.version("7.0") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt new file mode 100644 index 00000000..1cc5ebc3 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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 com.github.spotbugs.snom.internal.SpotBugsTaskFactory +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.AppliedPlugin +import org.gradle.api.plugins.JavaBasePlugin +import org.slf4j.LoggerFactory + +class SpotBugsPlugin : Plugin { + private val log = LoggerFactory.getLogger(javaClass) + override fun apply(project: Project) { + project.pluginManager.apply(SpotBugsBasePlugin::class.java) + project + .pluginManager + .withPlugin( + "java-base" + ) { javaBase: AppliedPlugin -> + log.debug( + "The javaBase plugin has been applied, so making the check task depending on all of SpotBugsTask" + ) + project + .tasks + .named(JavaBasePlugin.CHECK_TASK_NAME) + .configure { task: Task -> + task.dependsOn( + project.tasks.withType( + SpotBugsTask::class.java + ) + ) + } + } + createTasks(project) + } + + private fun createTasks(project: Project) { + SpotBugsTaskFactory().generate(project) + } + + companion object { + const val CONFIG_NAME = "spotbugs" + const val PLUGINS_CONFIG_NAME = "spotbugsPlugins" + + /** + * The configuration contains SpotBugs plugin jar files only + * + * @see [GitHub issue](https://github.com/spotbugs/spotbugs-gradle-plugin/issues/910) + */ + const val INTERNAL_CONFIG_NAME = "actualSpotbugsPlugins" + const val SLF4J_CONFIG_NAME = "spotbugsSlf4j" + const val EXTENSION_NAME = "spotbugs" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt new file mode 100644 index 00000000..9e0d9bec --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.android.build.gradle.AppExtension +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.api.BaseVariant +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.* +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.util.GradleVersion +import org.slf4j.LoggerFactory + +class SpotBugsTaskFactory { + private val log = LoggerFactory.getLogger(SpotBugsTaskFactory::class.java) + fun generate(project: Project) { + generateForJava(project) + generateForAndroid(project) + } + + private fun getSourceSetContainer(project: Project): SourceSetContainer { + return if (GradleVersion.current() < GradleVersion.version("7.1")) { + project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets + } else { + project.extensions.getByType(JavaPluginExtension::class.java).sourceSets + } + } + + private fun generateForJava(project: Project) { + project + .plugins + .withType(JavaBasePlugin::class.java) + .configureEach { + getSourceSetContainer(project) + .all { sourceSet: SourceSet -> + val name = sourceSet.getTaskName("spotbugs", null) + log.debug("Creating SpotBugsTask for {}", sourceSet) + project + .tasks + .register( + name, + SpotBugsTask::class.java, + Action { task: SpotBugsTask -> + task.sourceDirs = sourceSet.allSource.sourceDirectories + task.classDirs = sourceSet.output + task.auxClassPaths = sourceSet.compileClasspath + val description = String.format( + "Run SpotBugs analysis for the source set '%s'", + sourceSet.name + ) + task.description = description + }) + } + } + } + + private fun generateForAndroid(project: Project) { + val action: Action?> = + Action { + val baseExtension = project.extensions.getByType( + BaseExtension::class.java + ) + val variants: DomainObjectSet = when (baseExtension) { + is AppExtension -> baseExtension.applicationVariants + is LibraryExtension -> baseExtension.libraryVariants + else -> throw GradleException("Unrecognized Android extension $baseExtension") + } + variants.all { variant: BaseVariant -> + val spotbugsTaskName = + toLowerCamelCase( + "spotbugs", + variant.name + ) + log.debug("Creating SpotBugsTask for {}", variant.name) + project + .tasks + .register( + spotbugsTaskName, + SpotBugsTask::class.java, + Action { spotbugsTask: SpotBugsTask -> + val javaCompile = + variant.javaCompileProvider.get() + spotbugsTask.sourceDirs = javaCompile.source + spotbugsTask.classDirs = project.files(javaCompile.destinationDir) + spotbugsTask.auxClassPaths = javaCompile.classpath + spotbugsTask.dependsOn(javaCompile) + }) + } + } + project.plugins.withId("com.android.application", action) + project.plugins.withId("com.android.library", action) + } + + companion object { + fun toLowerCamelCase(head: String, tail: String?): String { + if (tail.isNullOrEmpty()) { + return head + } + return buildString(head.length + tail.length) { + append(head) + append(tail[0].uppercaseChar()) + append(tail.substring(1)) + } + } + } +} \ No newline at end of file From 1c1c0820f70c63651504e246feb0c6fd75e69e89 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Wed, 2 Aug 2023 04:12:07 +0800 Subject: [PATCH 06/41] chore: translate report related file Signed-off-by: Kengo TODA --- .idea/codeStyles/Project.xml | 13 ++ .idea/codeStyles/codeStyleConfig.xml | 5 + .../github/spotbugs/snom/SpotBugsReport.java | 154 ------------------ .../snom/internal/SpotBugsHtmlReport.java | 97 ----------- .../snom/internal/SpotBugsSarifReport.java | 35 ---- .../snom/internal/SpotBugsTextReport.java | 41 ----- .../snom/internal/SpotBugsXmlReport.java | 41 ----- .../github/spotbugs/snom/SpotBugsReport.kt | 123 ++++++++++++++ .../snom/internal/SpotBugsHtmlReport.kt | 83 ++++++++++ .../snom/internal/SpotBugsSarifReport.kt | 31 ++++ .../snom/internal/SpotBugsTextReport.kt | 35 ++++ .../snom/internal/SpotBugsXmlReport.kt | 35 ++++ 12 files changed, 325 insertions(+), 368 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 src/main/groovy/com/github/spotbugs/snom/SpotBugsReport.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsSarifReport.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTextReport.java delete mode 100644 src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsXmlReport.java create mode 100644 src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..f5cb8715 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..a55e7a17 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsReport.java b/src/main/groovy/com/github/spotbugs/snom/SpotBugsReport.java deleted file mode 100644 index 7ffbc99c..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsReport.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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 edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import groovy.lang.Closure; -import java.io.File; -import javax.annotation.Nullable; -import javax.inject.Inject; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -import org.gradle.api.reporting.CustomizableHtmlReport; -import org.gradle.api.reporting.Report; -import org.gradle.api.reporting.SingleFileReport; -import org.gradle.api.resources.TextResource; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; -import org.gradle.util.ConfigureUtil; - -public abstract class SpotBugsReport - implements SingleFileReport, - CustomizableHtmlReport // to expose CustomizableHtmlReport#setStylesheet to build script -{ - private final RegularFileProperty destination; - private final Property isRequired; - private final SpotBugsTask task; - - @Inject - public SpotBugsReport(ObjectFactory objects, SpotBugsTask task) { - this.destination = objects.fileProperty(); - this.isRequired = objects.property(Boolean.class).convention(Boolean.TRUE); - this.task = task; - } - - @NonNull - public abstract String toCommandLineOption(); - - /** - * @deprecated use {@link #getOutputLocation()} instead. - */ - @Deprecated - @Internal - public File getDestination() { - return destination.get().getAsFile(); - } - - @Override - public RegularFileProperty getOutputLocation() { - return destination; - } - - @Override - @Internal("This property returns always same value") - public OutputType getOutputType() { - return OutputType.FILE; - } - - @Override - @Input - public Property getRequired() { - return isRequired; - } - - /** - * @deprecated use {@link #getRequired()} instead. - */ - @Deprecated - @Internal - public boolean isEnabled() { - return isRequired.get(); - } - - /** - * @deprecated use {@code getRequired().set(value)} instead. - */ - @Deprecated - public void setEnabled(boolean b) { - isRequired.set(b); - } - - /** - * @deprecated use {@code getRequired().set(provider)} instead. - */ - @Deprecated - public void setEnabled(Provider provider) { - isRequired.set(provider); - } - - /** - * @deprecated use {@code getOutputLocation().set(file)} instead. - */ - @Deprecated - public void setDestination(File file) { - destination.set(file); - } - - /** - * @deprecated use {@code getOutputLocation().set(provider)} instead. - */ - @Deprecated - public void setDestination(Provider provider) { - destination.set(this.task.getProject().getLayout().file(provider)); - } - - @Override - public Report configure(Closure closure) { - ConfigureUtil.configureSelf(closure, this); - return this; - } - - @Override - @Internal("This property provides only a human readable name.") - public String getDisplayName() { - return String.format("%s type report generated by the task %s", getName(), getTask().getPath()); - } - - @CheckForNull - @Override - // TODO adding an @Input triggers 'cannot be serialized' exception - public TextResource getStylesheet() { - return null; - } - - @Override - public void setStylesheet(@Nullable TextResource textResource) { - throw new UnsupportedOperationException( - String.format("stylesheet property is not available in the %s type report", getName())); - } - - public void setStylesheet(@Nullable String path) { - throw new UnsupportedOperationException( - String.format("stylesheet property is not available in the %s type report", getName())); - } - - @NonNull - @Internal - protected final SpotBugsTask getTask() { - return task; - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.java deleted file mode 100644 index 412dabb4..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.io.File; -import java.util.Optional; -import javax.inject.Inject; -import org.gradle.api.InvalidUserDataException; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; -import org.gradle.api.resources.TextResource; - -public abstract class SpotBugsHtmlReport extends SpotBugsReport { - private final Property stylesheet; - private final Property stylesheetPath; - - @Inject - public SpotBugsHtmlReport(ObjectFactory objects, SpotBugsTask task) { - super(objects, task); - // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.html" - getOutputLocation().convention(task.getReportsDir().file(task.getBaseName() + ".html")); - stylesheet = objects.property(TextResource.class); - stylesheetPath = objects.property(String.class); - } - - @NonNull - @Override - public String toCommandLineOption() { - @Nullable TextResource stylesheet = getStylesheet(); - - if (stylesheet == null) { - return "-html"; - } else { - return "-html:" + stylesheet.asFile().getAbsolutePath(); - } - } - - @Override - public TextResource getStylesheet() { - if (stylesheet.isPresent()) { - return stylesheet.get(); - } else if (stylesheetPath.isPresent()) { - return resolve(stylesheetPath.get()); - } - - return null; - } - - private TextResource resolve(String path) { - Optional spotbugsJar = - getTask() - .getProject() - .getConfigurations() - .getByName("spotbugs") - .files( - dependency -> - dependency.getGroup().equals("com.github.spotbugs") - && dependency.getName().equals("spotbugs")) - .stream() - .findFirst(); - if (spotbugsJar.isPresent()) { - return getTask() - .getProject() - .getResources() - .getText() - .fromArchiveEntry(spotbugsJar.get(), path); - } else { - throw new InvalidUserDataException( - "The dependency on SpotBugs not found in 'spotbugs' configuration"); - } - } - - @Override - public void setStylesheet(@Nullable TextResource textResource) { - stylesheet.set(textResource); - } - - @Override - public void setStylesheet(@Nullable String path) { - stylesheetPath.set(path); - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsSarifReport.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsSarifReport.java deleted file mode 100644 index 98955282..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsSarifReport.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import javax.inject.Inject; -import org.gradle.api.model.ObjectFactory; - -public abstract class SpotBugsSarifReport extends SpotBugsReport { - @Inject - public SpotBugsSarifReport(ObjectFactory objects, SpotBugsTask task) { - super(objects, task); - // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.sarif" - getOutputLocation().convention(task.getReportsDir().file(task.getBaseName() + ".sarif")); - } - - @NonNull - @Override - public String toCommandLineOption() { - return "-sarif"; - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTextReport.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTextReport.java deleted file mode 100644 index 409b15cf..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsTextReport.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import javax.inject.Inject; -import org.gradle.api.model.ObjectFactory; - -public abstract class SpotBugsTextReport extends SpotBugsReport { - - @Inject - public SpotBugsTextReport(ObjectFactory objects, SpotBugsTask task) { - super(objects, task); - // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.txt" - getOutputLocation().convention(task.getReportsDir().file(task.getBaseName() + ".txt")); - } - - @NonNull - @Override - public String toCommandLineOption() { - return "-sortByClass"; - } - - @Override - public String getDisplayName() { - return String.format("Text type report generated by the task %s", getTask().getPath()); - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsXmlReport.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsXmlReport.java deleted file mode 100644 index 00794466..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsXmlReport.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import javax.inject.Inject; -import org.gradle.api.model.ObjectFactory; - -public abstract class SpotBugsXmlReport extends SpotBugsReport { - - @Inject - public SpotBugsXmlReport(ObjectFactory objects, SpotBugsTask task) { - super(objects, task); - // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.xml" - getOutputLocation().convention(task.getReportsDir().file(task.getBaseName() + ".xml")); - } - - @NonNull - @Override - public String toCommandLineOption() { - return "-xml:withMessages"; - } - - @Override - public String getDisplayName() { - return String.format("XML type report generated by the task %s", getTask().getPath()); - } -} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt new file mode 100644 index 00000000..7568d09e --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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 groovy.lang.Closure +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.reporting.CustomizableHtmlReport +import org.gradle.api.reporting.Report +import org.gradle.api.reporting.SingleFileReport +import org.gradle.api.resources.TextResource +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.util.ConfigureUtil +import java.io.File +import javax.inject.Inject + +abstract class SpotBugsReport @Inject constructor( + objects: ObjectFactory, @get:Internal + protected val task: SpotBugsTask +) : + SingleFileReport, CustomizableHtmlReport // to expose CustomizableHtmlReport#setStylesheet to build script +{ + private val destination: RegularFileProperty + private val isRequired: Property + + init { + destination = objects.fileProperty() + isRequired = objects.property(Boolean::class.java).convention(true) + } + + abstract fun toCommandLineOption(): String + + @Internal + @Deprecated("use {@link #getOutputLocation()} instead.") + fun getDestination(): File { + return destination.get().asFile + } + + override fun getOutputLocation(): RegularFileProperty { + return destination + } + + @Internal("This property returns always same value") + override fun getOutputType(): Report.OutputType { + return Report.OutputType.FILE + } + + @Input + override fun getRequired(): Property { + return isRequired + } + + @get:Deprecated("use {@link #getRequired()} instead.") + @get:Internal + @set:Deprecated("use {@code getRequired().set(value)} instead.") + var isEnabled: Boolean + get() = isRequired.get() + set(b) { + isRequired.set(b) + } + + @Deprecated("use {@code getRequired().set(provider)} instead.") + fun setEnabled(provider: Provider?) { + isRequired.set(provider) + } + + @Deprecated("use {@code getOutputLocation().set(file)} instead.") + override fun setDestination(file: File) { + destination.set(file) + } + + @Deprecated("use {@code getOutputLocation().set(provider)} instead.") + fun setDestination(provider: Provider?) { + destination.set(task.project.layout.file(provider)) + } + + override fun configure(closure: Closure<*>?): Report { + ConfigureUtil.configureSelf(closure, this) + return this + } + + @Internal("This property provides only a human readable name.") + override fun getDisplayName(): String { + return String.format("%s type report generated by the task %s", name, task.path) + } + + // TODO adding an @Input triggers 'cannot be serialized' exception + override fun getStylesheet(): TextResource? { + return null + } + + override fun setStylesheet(textResource: TextResource?) { + throw UnsupportedOperationException( + String.format( + "stylesheet property is not available in the %s type report", + name + ) + ) + } + + open fun setStylesheet(path: String?) { + throw UnsupportedOperationException( + String.format( + "stylesheet property is not available in the %s type report", + name + ) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt new file mode 100644 index 00000000..07525729 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.InvalidUserDataException +import org.gradle.api.artifacts.Dependency +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.resources.TextResource +import javax.inject.Inject + +abstract class SpotBugsHtmlReport @Inject constructor(objects: ObjectFactory, task: SpotBugsTask) : + SpotBugsReport(objects, task) { + private val stylesheet: Property + private val stylesheetPath: Property + + init { + // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.html" + outputLocation.convention(task.reportsDir.file(task.baseName + ".html")) + stylesheet = objects.property(TextResource::class.java) + stylesheetPath = objects.property(String::class.java) + } + + override fun toCommandLineOption(): String { + val stylesheet = getStylesheet() + return if (stylesheet == null) { + "-html" + } else { + "-html:" + stylesheet.asFile().absolutePath + } + } + + override fun getStylesheet(): TextResource? { + if (stylesheet.isPresent) { + return stylesheet.get() + } else if (stylesheetPath.isPresent) { + return resolve(stylesheetPath.get()) + } + return null + } + + private fun resolve(path: String): TextResource { + val spotbugsJar = task + .project + .configurations + .getByName("spotbugs") + .files { dependency: Dependency -> dependency.group == "com.github.spotbugs" && dependency.name == "spotbugs" } + .stream() + .findFirst() + return if (spotbugsJar.isPresent) { + task + .project + .resources + .text + .fromArchiveEntry(spotbugsJar.get(), path) + } else { + throw InvalidUserDataException( + "The dependency on SpotBugs not found in 'spotbugs' configuration" + ) + } + } + + override fun setStylesheet(textResource: TextResource?) { + stylesheet.set(textResource) + } + + override fun setStylesheet(path: String?) { + stylesheetPath.set(path) + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt new file mode 100644 index 00000000..7b730e40 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.model.ObjectFactory +import javax.inject.Inject + +abstract class SpotBugsSarifReport @Inject constructor(objects: ObjectFactory?, task: SpotBugsTask) : + SpotBugsReport(objects, task) { + init { + // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.sarif" + outputLocation.convention(task.reportsDir.file(task.baseName + ".sarif")) + } + + override fun toCommandLineOption(): String { + return "-sarif" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt new file mode 100644 index 00000000..e9f78168 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.model.ObjectFactory +import javax.inject.Inject + +abstract class SpotBugsTextReport @Inject constructor(objects: ObjectFactory, task: SpotBugsTask) : + SpotBugsReport(objects, task) { + init { + // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.txt" + outputLocation.convention(task.reportsDir.file(task.baseName + ".txt")) + } + + override fun toCommandLineOption(): String { + return "-sortByClass" + } + + override fun getDisplayName(): String { + return String.format("Text type report generated by the task %s", task.path) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt new file mode 100644 index 00000000..bd10b118 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.model.ObjectFactory +import javax.inject.Inject + +abstract class SpotBugsXmlReport @Inject constructor(objects: ObjectFactory, task: SpotBugsTask) : + SpotBugsReport(objects, task) { + init { + // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.xml" + outputLocation.convention(task.reportsDir.file(task.baseName + ".xml")) + } + + override fun toCommandLineOption(): String { + return "-xml:withMessages" + } + + override fun getDisplayName(): String { + return String.format("XML type report generated by the task %s", task.path) + } +} \ No newline at end of file From 376bd18b048f2e9166e6f71c81c5e2c2b3ee07c8 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Thu, 3 Aug 2023 04:56:32 +0800 Subject: [PATCH 07/41] chore: add OutputScanner Signed-off-by: Kengo TODA --- .../spotbugs/snom/internal/OutputScanner.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt new file mode 100644 index 00000000..40db66ae --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.OutputStream; + +/** + * Monitors the stdout of forked process, and report when it contains some problems reported by + * SpotBugs core. + */ +internal class OutputScanner(out: OutputStream) : FilterOutputStream(out) { + private val builder = ByteArrayOutputStream() + private var isFailedToReport = false + get() = field + + override fun write(b: ByteArray, off: Int, len: Int) { + super.write(b, off, len) + builder.write(b, off, len) + } + + override fun write(b: Int) { + super.write(b) + builder.write(b) + if (b == '\n'.code) { + val line: String = builder.toString() + if (line.contains("Could not generate HTML output")) { + isFailedToReport = true + } + builder.reset() + } + } +} From 12d40358ff6ccabe832e9177370635131d003e6e Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Thu, 3 Aug 2023 04:56:51 +0800 Subject: [PATCH 08/41] build: update build settings Signed-off-by: Kengo TODA --- .idea/jarRepositories.xml | 5 +++++ build.gradle.kts | 3 +++ 2 files changed, 8 insertions(+) diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 0380d8d3..e9449b52 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -26,5 +26,10 @@

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import org.gradle.api.GradleException; -import org.gradle.api.file.FileCollection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class SpotBugsRunner { - private final Logger log = LoggerFactory.getLogger(SpotBugsRunner.class); - - public abstract void run(@NonNull SpotBugsTask task); - - protected List buildArguments(SpotBugsTask task) { - List args = new ArrayList<>(); - - Set plugins = task.getPluginJar(); - if (!plugins.isEmpty()) { - args.add("-pluginList"); - args.add(join(plugins)); - } - - args.add("-timestampNow"); - if (!task.getAuxClassPaths().isEmpty()) { - if (task.getUseAuxclasspathFile().get()) { - args.add("-auxclasspathFromFile"); - String auxClasspathFile = createFileForAuxClasspath(task); - log.debug("Using auxclasspath file: {}", auxClasspathFile); - args.add(auxClasspathFile); - } else { - args.add("-auxclasspath"); - args.add(join(task.getAuxClassPaths().getFiles())); - } - } - if (!task.getSourceDirs().isEmpty()) { - args.add("-sourcepath"); - args.add(task.getSourceDirs().getAsPath()); - } - if (task.getShowProgress().getOrElse(Boolean.FALSE).booleanValue()) { - args.add("-progress"); - } - - for (SpotBugsReport report : task.getEnabledReports()) { - File reportFile = report.getOutputLocation().getAsFile().get(); - File dir = reportFile.getParentFile(); - dir.mkdirs(); - args.add(report.toCommandLineOption() + "=" + reportFile.getAbsolutePath()); - } - - if (task.getEffort().isPresent()) { - args.add("-effort:" + task.getEffort().get().name().toLowerCase()); - } - if (task.getReportLevel().isPresent()) { - task.getReportLevel().get().toCommandLineOption().ifPresent(args::add); - } - if (task.getVisitors().isPresent() && !task.getVisitors().get().isEmpty()) { - args.add("-visitors"); - args.add(task.getVisitors().get().stream().collect(Collectors.joining(","))); - } - if (task.getOmitVisitors().isPresent() && !task.getOmitVisitors().get().isEmpty()) { - args.add("-omitVisitors"); - args.add(task.getOmitVisitors().get().stream().collect(Collectors.joining(","))); - } - if (task.getIncludeFilter().isPresent() && task.getIncludeFilter().get() != null) { - args.add("-include"); - args.add(task.getIncludeFilter().get().getAsFile().getAbsolutePath()); - } - if (task.getExcludeFilter().isPresent() && task.getExcludeFilter().get() != null) { - args.add("-exclude"); - args.add(task.getExcludeFilter().get().getAsFile().getAbsolutePath()); - } - if (task.getBaselineFile().isPresent() && task.getBaselineFile().get() != null) { - args.add("-excludeBugs"); - args.add(task.getBaselineFile().get().getAsFile().getAbsolutePath()); - } - if (task.getOnlyAnalyze().isPresent() && !task.getOnlyAnalyze().get().isEmpty()) { - args.add("-onlyAnalyze"); - args.add(task.getOnlyAnalyze().get().stream().collect(Collectors.joining(","))); - } - - args.add("-projectName"); - args.add(task.getProjectName().get()); - args.add("-release"); - args.add(task.getRelease().get()); - - File file = task.getAnalyseClassFile().getAsFile().get(); - generateFile(task.getClasses(), task.getAnalyseClassFile().getAsFile().get()); - args.add("-analyzeFromFile"); - args.add(file.getAbsolutePath()); - - args.addAll(task.getExtraArgs().getOrElse(Collections.emptyList())); - log.debug("Arguments for SpotBugs are generated: {}", args); - return args; - } - - private String createFileForAuxClasspath(SpotBugsTask task) { - String auxClasspath = - task.getAuxClassPaths().getFiles().stream() - .map(File::getAbsolutePath) - .collect(Collectors.joining("\n")); - try { - Path auxClasspathFile = task.getAuxclasspathFile(); - try { - Files.createDirectories(auxClasspathFile.getParent()); - if (!Files.exists(auxClasspathFile)) { - Files.createFile(auxClasspathFile); - } - Files.write( - auxClasspathFile, auxClasspath.getBytes(), StandardOpenOption.TRUNCATE_EXISTING); - return auxClasspathFile.normalize().toString(); - } catch (Exception e) { - throw new GradleException( - "Could not create auxiliary classpath file for SpotBugsTask at " - + auxClasspathFile.normalize().toString(), - e); - } - } catch (Exception e) { - throw new GradleException("Could not create auxiliary classpath file for SpotBugsTask", e); - } - } - - private void generateFile(FileCollection files, File file) { - try { - Iterable lines = - files.filter(File::exists).getFiles().stream().map(File::getAbsolutePath)::iterator; - Files.write(file.toPath(), lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE); - } catch (IOException e) { - throw new GradleException("Fail to generate the text file to list target .class files", e); - } - } - - protected List buildJvmArguments(SpotBugsTask task) { - List args = task.getJvmArgs().getOrElse(Collections.emptyList()); - log.debug("Arguments for JVM process are generated: {}", args); - return args; - } - - private String join(Collection files) { - return files.stream() - .map(File::getAbsolutePath) - .collect(Collectors.joining(File.pathSeparator)); - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.java deleted file mode 100644 index fb9799f4..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import groovy.lang.Closure; -import java.io.File; -import java.net.URI; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import javax.inject.Inject; -import org.gradle.api.Action; -import org.gradle.api.GradleException; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.RegularFile; -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.workers.WorkAction; -import org.gradle.workers.WorkParameters; -import org.gradle.workers.WorkerExecutor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A {@link SpotBugsRunner} implementation that runs SpotBugs process from the worker process. This - * approach enables applying benefit of both {@link org.gradle.api.Project#javaexec(Closure)} and - * Worker API: provide larger Java heap to SpotBugs process and shorten their lifecycle. - * - * @see The related GitHub - * issue - */ -class SpotBugsRunnerForHybrid extends SpotBugsRunner { - private final WorkerExecutor workerExecutor; - private final Property javaLauncher; - - public SpotBugsRunnerForHybrid( - @NonNull WorkerExecutor workerExecutor, Property javaLauncher) { - this.workerExecutor = Objects.requireNonNull(workerExecutor); - this.javaLauncher = javaLauncher; - } - - @Override - public void run(@NonNull SpotBugsTask task) { - workerExecutor.noIsolation().submit(SpotBugsExecutor.class, configureWorkerSpec(task)); - } - - private Action configureWorkerSpec(SpotBugsTask task) { - return params -> { - List args = new ArrayList<>(); - args.add("-exitcode"); - args.addAll(buildArguments(task)); - params.getClasspath().setFrom(task.getSpotbugsClasspath()); - params.getJvmArgs().set(buildJvmArguments(task)); - params.getArgs().set(args); - String maxHeapSize = task.getMaxHeapSize().getOrNull(); - if (maxHeapSize != null) { - params.getMaxHeapSize().set(maxHeapSize); - } - params.getIgnoreFailures().set(task.getIgnoreFailures()); - params.getShowStackTraces().set(task.getShowStackTraces()); - task.getEnabledReports().stream() - .map(SpotBugsReport::getOutputLocation) - .forEach(params.getReports()::add); - if (javaLauncher.isPresent()) { - params - .getJavaToolchainExecutablePath() - .set(javaLauncher.get().getExecutablePath().getAsFile().getAbsolutePath()); - } - }; - } - - public interface SpotBugsWorkParameters extends WorkParameters { - ConfigurableFileCollection getClasspath(); - - Property getMaxHeapSize(); - - ListProperty getArgs(); - - ListProperty getJvmArgs(); - - Property getIgnoreFailures(); - - Property getShowStackTraces(); - - Property getJavaToolchainExecutablePath(); - - ListProperty getReports(); - } - - /** - * Exit code which is set when classes needed for analysis were missing. - * - * @see Constant - * Field Values from javadoc of the SpotBugs - */ - private static final int MISSING_CLASS_FLAG = 2; - - public abstract static class SpotBugsExecutor implements WorkAction { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final ExecOperations execOperations; - - @Inject - public SpotBugsExecutor(ExecOperations execOperations) { - this.execOperations = Objects.requireNonNull(execOperations); - } - - @Override - public void execute() { - // TODO print version of SpotBugs and Plugins - SpotBugsWorkParameters params = getParameters(); - - final int exitValue = - execOperations.javaexec(configureJavaExec(params)).rethrowFailure().getExitValue(); - if (ignoreMissingClassFlag(exitValue) == 0) { - return; - } - - if (params.getIgnoreFailures().getOrElse(Boolean.FALSE)) { - log.warn("SpotBugs ended with exit code " + exitValue); - return; - } - - String errorMessage = "Verification failed: SpotBugs ended with exit code " + exitValue + "."; - List reportPaths = - params.getReports().get().stream() - .map(RegularFile::getAsFile) - .map(File::toPath) - .map(Path::toUri) - .map(URI::toString) - .collect(Collectors.toList()); - if (!reportPaths.isEmpty()) { - errorMessage += " See the report at: " + String.join(",", reportPaths); - } - throw new GradleException(errorMessage); - } - - private int ignoreMissingClassFlag(int exitValue) { - if ((exitValue & MISSING_CLASS_FLAG) == 0) { - return exitValue; - } - log.debug( - "MISSING_CLASS_FLAG (2) was set to the exit code, but ignore it to keep the task result stable."); - return (exitValue ^ MISSING_CLASS_FLAG); - } - - private Action configureJavaExec(SpotBugsWorkParameters params) { - return spec -> { - spec.setJvmArgs(params.getJvmArgs().get()); - spec.classpath(params.getClasspath()); - spec.setArgs(params.getArgs().get()); - spec.getMainClass().set("edu.umd.cs.findbugs.FindBugs2"); - String maxHeapSize = params.getMaxHeapSize().getOrNull(); - 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()); - } - spec.setIgnoreExitValue(true); - }; - } - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.java deleted file mode 100644 index 1bf720b2..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.File; -import java.net.URI; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import org.gradle.api.Action; -import org.gradle.api.GradleException; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -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; - - public SpotBugsRunnerForJavaExec(Property javaLauncher) { - this.javaLauncher = javaLauncher; - } - - @Override - public void run(@NonNull SpotBugsTask task) { - // TODO print version of SpotBugs and Plugins - try { - task.getProject().javaexec(configureJavaExec(task)).rethrowFailure().assertNormalExitValue(); - } catch (ExecException e) { - if (task.getIgnoreFailures()) { - log.warn("SpotBugs reported failures", task.getShowStackTraces() ? e : null); - } else { - String errorMessage = "Verification failed: SpotBugs execution thrown exception."; - List reportPaths = - task.getEnabledReports().stream() - .map(SpotBugsReport::getOutputLocation) - .map(RegularFileProperty::getAsFile) - .map(Provider::get) - .map(File::toPath) - .map(Path::toUri) - .map(URI::toString) - .collect(Collectors.toList()); - if (!reportPaths.isEmpty()) { - errorMessage += "See the report at: " + String.join(",", reportPaths); - } - throw new GradleException(errorMessage, e); - } - } - } - - private Action configureJavaExec(SpotBugsTask task) { - return spec -> { - List args = new ArrayList<>(); - args.add("-exitcode"); - args.addAll(buildArguments(task)); - spec.classpath(task.getSpotbugsClasspath()); - spec.setJvmArgs(buildJvmArguments(task)); - spec.getMainClass().set("edu.umd.cs.findbugs.FindBugs2"); - spec.setArgs(args); - String maxHeapSize = task.getMaxHeapSize().getOrNull(); - 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()); - } - }; - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.java b/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.java deleted file mode 100644 index 88fcfafb..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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.internal; - -import com.github.spotbugs.snom.SpotBugsReport; -import com.github.spotbugs.snom.SpotBugsTask; -import edu.umd.cs.findbugs.DetectorFactoryCollection; -import edu.umd.cs.findbugs.FindBugs; -import edu.umd.cs.findbugs.FindBugs2; -import edu.umd.cs.findbugs.TextUICommandLine; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.File; -import java.net.URI; -import java.nio.file.Path; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import org.gradle.api.Action; -import org.gradle.api.GradleException; -import org.gradle.api.file.RegularFile; -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; -import org.gradle.workers.WorkQueue; -import org.gradle.workers.WorkerExecutor; -import org.slf4j.Logger; -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; - - public SpotBugsRunnerForWorker( - @NonNull WorkerExecutor workerExecutor, Property javaLauncher) { - this.workerExecutor = Objects.requireNonNull(workerExecutor); - this.javaLauncher = javaLauncher; - } - - @Override - public void run(@NonNull SpotBugsTask task) { - Objects.requireNonNull(task); - - WorkQueue workerQueue = workerExecutor.processIsolation(configureWorkerSpec(task)); - workerQueue.submit(SpotBugsExecutor.class, configureWorkParameters(task)); - } - - private Action configureWorkerSpec(SpotBugsTask task) { - return spec -> { - spec.getClasspath().setFrom(task.getSpotbugsClasspath()); - spec.forkOptions( - option -> { - option.jvmArgs(buildJvmArguments(task)); - String maxHeapSize = task.getMaxHeapSize().getOrNull(); - 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()); - } - }); - }; - } - - private Action configureWorkParameters(SpotBugsTask task) { - return params -> { - params.getArguments().addAll(buildArguments(task)); - params.getIgnoreFailures().set(task.getIgnoreFailures()); - params.getShowStackTraces().set(task.getShowStackTraces()); - task.getEnabledReports().stream() - .map(SpotBugsReport::getOutputLocation) - .forEach(params.getReports()::add); - }; - } - - interface SpotBugsWorkParameters extends WorkParameters { - ListProperty getArguments(); - - Property getIgnoreFailures(); - - Property getShowStackTraces(); - - ListProperty getReports(); - } - - public abstract static class SpotBugsExecutor implements WorkAction { - private final Logger log = LoggerFactory.getLogger(SpotBugsExecutor.class); - - @Override - public void execute() { - SpotBugsWorkParameters params = getParameters(); - String[] args = params.getArguments().get().toArray(new String[0]); - DetectorFactoryCollection.resetInstance(new DetectorFactoryCollection()); - - try { - edu.umd.cs.findbugs.Version.printVersion(false); - try (FindBugs2 findBugs2 = new FindBugs2()) { - TextUICommandLine commandLine = new TextUICommandLine(); - FindBugs.processCommandLine(commandLine, args, findBugs2); - findBugs2.execute(); - - StringBuilder message = new StringBuilder(); - if (findBugs2.getErrorCount() > 0) { - message.append(findBugs2.getErrorCount()).append(" SpotBugs errors were found."); - } - if (findBugs2.getBugCount() > 0) { - if (message.length() > 0) { - message.append(' '); - } - message.append(findBugs2.getBugCount()).append(" SpotBugs violations were found."); - } - if (message.length() > 0) { - List reportPaths = - params.getReports().get().stream() - .map(RegularFile::getAsFile) - .map(File::toPath) - .map(Path::toUri) - .map(URI::toString) - .collect(Collectors.toList()); - if (!reportPaths.isEmpty()) { - message.append("See the report at: ").append(String.join(", ", reportPaths)); - } - - GradleException e = new GradleException(message.toString()); - - if (params.getIgnoreFailures().getOrElse(Boolean.FALSE).booleanValue()) { - log.warn(message.toString()); - if (params.getShowStackTraces().getOrElse(Boolean.FALSE).booleanValue()) { - log.warn("", e); - } - } else { - throw e; - } - } - } - } catch (GradleException e) { - throw e; - } catch (Exception e) { - throw new GradleException("Verification failed: SpotBugs execution thrown exception", e); - } - } - } -} diff --git a/src/main/groovy/com/github/spotbugs/snom/package-info.java b/src/main/groovy/com/github/spotbugs/snom/package-info.java deleted file mode 100644 index e0bac48c..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright 2019 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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. - */ -/** - * This package provides public classes/interfaces for users. All published - * classes/interfaces/methods in this package (not including sub-packages) are treated as public and - * keep compatible. - * - *

The {@code SNOM} stands for {@code SpotBugs Newly Organized Mechanism}. - */ -package com.github.spotbugs.snom; diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt index ee889056..58bb7655 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt @@ -41,12 +41,12 @@ class SpotBugsBasePlugin : Plugin { .tasks .withType(SpotBugsTask::class.java) .configureEach { task -> - task.init( - extension, - enableWorkerApi.toBoolean(), - enableHybridWorker.toBoolean() - ) - } + task.init( + extension, + enableWorkerApi.toBoolean(), + enableHybridWorker.toBoolean(), + ) + } } private fun createExtension(project: Project): SpotBugsExtension { @@ -56,26 +56,30 @@ class SpotBugsBasePlugin : Plugin { SpotBugsPlugin.EXTENSION_NAME, SpotBugsExtension::class.java, project, - project.objects + project.objects, ) extension.ignoreFailures.convention(false) extension.showStackTraces.convention(false) extension.projectName.convention(project.provider { project.name }) - extension.release.convention(project.provider { - project.version.toString() - }) + extension.release.convention( + project.provider { + project.version.toString() + }, + ) // ReportingBasePlugin should be applied before we create this SpotBugsExtension instance val baseReportsDir = project.extensions.getByType( - ReportingExtension::class.java + ReportingExtension::class.java, ).baseDirectory extension .reportsDir - .convention(baseReportsDir.map { directory: Directory -> - directory.dir( - DEFAULT_REPORTS_DIR_NAME - ) - }) + .convention( + baseReportsDir.map { directory: Directory -> + directory.dir( + DEFAULT_REPORTS_DIR_NAME, + ) + }, + ) extension.useAuxclasspathFile.convention(true) extension.useJavaToolchains.convention(false) return extension @@ -94,9 +98,11 @@ class SpotBugsBasePlugin : Plugin { dependencies.add( project .dependencies - .create(extension.toolVersion.map { - "com.github.spotbugs:spotbugs:$it" - }) + .create( + extension.toolVersion.map { + "com.github.spotbugs:spotbugs:$it" + }, + ), ) } val spotbugsSlf4j = project @@ -109,7 +115,7 @@ class SpotBugsBasePlugin : Plugin { dependencies.add( project .dependencies - .create("org.slf4j:slf4j-simple:" + props.getProperty("slf4j-version")) + .create("org.slf4j:slf4j-simple:" + props.getProperty("slf4j-version")), ) } } @@ -140,7 +146,8 @@ class SpotBugsBasePlugin : Plugin { if (version < SUPPORTED_VERSION) { val message = String.format( "Gradle version %s is unsupported. Please use %s or later.", - version, SUPPORTED_VERSION + version, + SUPPORTED_VERSION, ) throw IllegalArgumentException(message) } @@ -161,4 +168,4 @@ class SpotBugsBasePlugin : Plugin { */ private val SUPPORTED_VERSION = GradleVersion.version("7.0") } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt index bde6d512..a668d1e8 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt @@ -28,10 +28,10 @@ class SpotBugsPlugin : Plugin { project .pluginManager .withPlugin( - "java-base" + "java-base", ) { javaBase: AppliedPlugin -> log.debug( - "The javaBase plugin has been applied, so making the check task depending on all of SpotBugsTask" + "The javaBase plugin has been applied, so making the check task depending on all of SpotBugsTask", ) project .tasks @@ -39,8 +39,8 @@ class SpotBugsPlugin : Plugin { .configure { task: Task -> task.dependsOn( project.tasks.withType( - SpotBugsTask::class.java - ) + SpotBugsTask::class.java, + ), ) } } @@ -63,4 +63,4 @@ class SpotBugsPlugin : Plugin { const val SLF4J_CONFIG_NAME = "spotbugsSlf4j" const val EXTENSION_NAME = "spotbugs" } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt index 7568d09e..b0da14e0 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt @@ -29,11 +29,11 @@ import java.io.File import javax.inject.Inject abstract class SpotBugsReport @Inject constructor( - objects: ObjectFactory, @get:Internal - protected val task: SpotBugsTask + objects: ObjectFactory, + @get:Internal + protected val task: SpotBugsTask, ) : - SingleFileReport, CustomizableHtmlReport // to expose CustomizableHtmlReport#setStylesheet to build script -{ + SingleFileReport, CustomizableHtmlReport { // to expose CustomizableHtmlReport#setStylesheet to build script private val destination: RegularFileProperty private val isRequired: Property @@ -107,8 +107,8 @@ abstract class SpotBugsReport @Inject constructor( throw UnsupportedOperationException( String.format( "stylesheet property is not available in the %s type report", - name - ) + name, + ), ) } @@ -116,8 +116,8 @@ abstract class SpotBugsReport @Inject constructor( throw UnsupportedOperationException( String.format( "stylesheet property is not available in the %s type report", - name - ) + name, + ), ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt index 40db66ae..297396ac 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt @@ -13,9 +13,9 @@ */ package com.github.spotbugs.snom.internal -import java.io.ByteArrayOutputStream; -import java.io.FilterOutputStream; -import java.io.OutputStream; +import java.io.ByteArrayOutputStream +import java.io.FilterOutputStream +import java.io.OutputStream /** * Monitors the stdout of forked process, and report when it contains some problems reported by diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt index 07525729..08fb4249 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt @@ -68,7 +68,7 @@ abstract class SpotBugsHtmlReport @Inject constructor(objects: ObjectFactory, ta .fromArchiveEntry(spotbugsJar.get(), path) } else { throw InvalidUserDataException( - "The dependency on SpotBugs not found in 'spotbugs' configuration" + "The dependency on SpotBugs not found in 'spotbugs' configuration", ) } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt new file mode 100644 index 00000000..48424ada --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt @@ -0,0 +1,169 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.GradleException +import org.gradle.api.file.FileCollection +import org.slf4j.LoggerFactory +import java.io.File +import java.io.IOException +import java.lang.Boolean +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.StandardOpenOption +import java.util.* +import java.util.stream.Collectors +import kotlin.Exception +import kotlin.String +import kotlin.collections.ArrayList + +abstract class SpotBugsRunner { + private val log = LoggerFactory.getLogger(SpotBugsRunner::class.java) + abstract fun run(task: SpotBugsTask) + protected fun buildArguments(task: SpotBugsTask): List { + val args: MutableList = ArrayList() + val plugins = task.getPluginJar() + if (plugins.isNotEmpty()) { + args.add("-pluginList") + args.add(join(plugins)) + } + args.add("-timestampNow") + if (!task.getAuxClassPaths().isEmpty) { + if (task.getUseAuxclasspathFile().get()) { + args.add("-auxclasspathFromFile") + val auxClasspathFile = createFileForAuxClasspath(task) + log.debug("Using auxclasspath file: {}", auxClasspathFile) + args.add(auxClasspathFile) + } else { + args.add("-auxclasspath") + args.add(join(task.getAuxClassPaths().files)) + } + } + if (!task.getSourceDirs().isEmpty) { + args.add("-sourcepath") + args.add(task.getSourceDirs().asPath) + } + if (task.getShowProgress().getOrElse(Boolean.FALSE)) { + args.add("-progress") + } + for (report: SpotBugsReport in task.getEnabledReports()) { + val reportFile = report.outputLocation.asFile.get() + val dir = reportFile.parentFile + dir.mkdirs() + args.add(report.toCommandLineOption() + "=" + reportFile.absolutePath) + } + if (task.getEffort().isPresent) { + args.add("-effort:" + task.getEffort().get().name.lowercase(Locale.getDefault())) + } + if (task.getReportLevel().isPresent) { + task.getReportLevel().get().toCommandLineOption().ifPresent { e: String -> + args.add( + e, + ) + } + } + if (task.getVisitors().isPresent && task.getVisitors().get().isNotEmpty()) { + args.add("-visitors") + args.add(task.getVisitors().get().stream().collect(Collectors.joining(","))) + } + if (task.getOmitVisitors().isPresent && task.getOmitVisitors().get().isNotEmpty()) { + args.add("-omitVisitors") + args.add(task.getOmitVisitors().get().stream().collect(Collectors.joining(","))) + } + if (task.getIncludeFilter().isPresent && task.getIncludeFilter().get() != null) { + args.add("-include") + args.add(task.getIncludeFilter().get().asFile.absolutePath) + } + if (task.getExcludeFilter().isPresent && task.getExcludeFilter().get() != null) { + args.add("-exclude") + args.add(task.getExcludeFilter().get().asFile.absolutePath) + } + if (task.getBaselineFile().isPresent && task.getBaselineFile().get() != null) { + args.add("-excludeBugs") + args.add(task.getBaselineFile().get().asFile.absolutePath) + } + if (task.getOnlyAnalyze().isPresent && task.getOnlyAnalyze().get().isNotEmpty()) { + args.add("-onlyAnalyze") + args.add(task.getOnlyAnalyze().get().stream().collect(Collectors.joining(","))) + } + args.add("-projectName") + args.add(task.getProjectName().get()) + args.add("-release") + args.add(task.getRelease().get()) + val file = task.getAnalyseClassFile().asFile.get() + generateFile(task.getClasses(), task.getAnalyseClassFile().asFile.get()) + args.add("-analyzeFromFile") + args.add(file.absolutePath) + args.addAll(task.getExtraArgs().getOrElse(emptyList())) + log.debug("Arguments for SpotBugs are generated: {}", args) + return args + } + + private fun createFileForAuxClasspath(task: SpotBugsTask): String { + val auxClasspath = task.getAuxClassPaths().files.stream() + .map { obj: File -> obj.absolutePath } + .collect(Collectors.joining("\n")) + try { + val auxClasspathFile = task.getAuxclasspathFile() + try { + Files.createDirectories(auxClasspathFile.parent) + if (!Files.exists(auxClasspathFile)) { + Files.createFile(auxClasspathFile) + } + Files.write( + auxClasspathFile, + auxClasspath.toByteArray(), + StandardOpenOption.TRUNCATE_EXISTING, + ) + return auxClasspathFile.normalize().toString() + } catch (e: Exception) { + throw GradleException( + "Could not create auxiliary classpath file for SpotBugsTask at " + + auxClasspathFile.normalize().toString(), + e, + ) + } + } catch (e: Exception) { + throw GradleException("Could not create auxiliary classpath file for SpotBugsTask", e) + } + } + + private fun generateFile(files: FileCollection, file: File) { + try { + val lines = + Iterable { + files.filter { obj: File -> obj.exists() } + .files.stream().map { obj: File -> obj.absolutePath } + .iterator() + } + Files.write(file.toPath(), lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE) + } catch (e: IOException) { + throw GradleException("Fail to generate the text file to list target .class files", e) + } + } + + protected fun buildJvmArguments(task: SpotBugsTask): List { + val args = task.getJvmArgs().getOrElse(emptyList()) + log.debug("Arguments for JVM process are generated: {}", args) + return args + } + + private fun join(files: Collection): String { + return files.stream() + .map { obj: File -> obj.absolutePath } + .collect(Collectors.joining(File.pathSeparator)) + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt new file mode 100644 index 00000000..6b792667 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt @@ -0,0 +1,175 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFile +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.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.slf4j.LoggerFactory +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.util.stream.Collectors +import javax.inject.Inject + +/** + * A {@link SpotBugsRunner} implementation that runs SpotBugs process from the worker process. This + * approach enables applying benefit of both {@link org.gradle.api.Project#javaexec(Closure)} and + * Worker API: provide larger Java heap to SpotBugs process and shorten their lifecycle. + * + * @see The related GitHub + * issue + */ +class SpotBugsRunnerForHybrid( + private val workerExecutor: WorkerExecutor, + private val javaLauncher: Property, +) : SpotBugsRunner() { + + override fun run(task: SpotBugsTask) { + workerExecutor.noIsolation().submit(SpotBugsExecutor::class.java, configureWorkerSpec(task)) + } + + private fun configureWorkerSpec(task: SpotBugsTask): Action { + return Action { params: SpotBugsWorkParameters -> + val args = mutableListOf() + args.add("-exitcode") + args.addAll(buildArguments(task)) + params.getClasspath().setFrom(task.getSpotbugsClasspath()) + params.getJvmArgs().set(buildJvmArguments(task)) + params.getArgs().set(args) + val maxHeapSize = task.getMaxHeapSize().getOrNull() + if (maxHeapSize != null) { + params.getMaxHeapSize().set(maxHeapSize) + } + params.getIgnoreFailures().set(task.getIgnoreFailures()) + params.getShowStackTraces().set(task.getShowStackTraces()) + task.getEnabledReports().stream() + .map(SpotBugsReport::getOutputLocation) + .forEach(params.getReports()::add) + if (javaLauncher.isPresent) { + params + .getJavaToolchainExecutablePath() + .set(javaLauncher.get().executablePath.asFile.absolutePath) + } + } + } + + interface SpotBugsWorkParameters : WorkParameters { + fun getClasspath(): ConfigurableFileCollection + + fun getMaxHeapSize(): Property + fun getArgs(): ListProperty + + fun getJvmArgs(): ListProperty + + fun getIgnoreFailures(): Property + + fun getShowStackTraces(): Property + + fun getJavaToolchainExecutablePath(): Property + + fun getReports(): ListProperty + } + + abstract class SpotBugsExecutor @Inject constructor( + private val execOperations: ExecOperations, + ) : WorkAction { + private val log = LoggerFactory.getLogger(this.javaClass) + + override fun execute() { + // TODO print version of SpotBugs and Plugins + val params = getParameters() + + val exitValue = + execOperations.javaexec(configureJavaExec(params)).rethrowFailure().exitValue + if (ignoreMissingClassFlag(exitValue) == 0) { + return + } + + if (params.getIgnoreFailures().getOrElse(false)) { + log.warn("SpotBugs ended with exit code $exitValue") + return + } + + val errorMessage = buildString { + append("Verification failed: SpotBugs ended with exit code $exitValue.") + val reportPaths = + params.getReports().get().stream() + .map(RegularFile::getAsFile) + .map(File::toPath) + .map(Path::toUri) + .map(URI::toString) + .collect(Collectors.toList()) + if (reportPaths.isNotEmpty()) { + append(" See the report at: ") + append(reportPaths.joinToString(",")) + } + } + throw GradleException(errorMessage) + } + + private fun ignoreMissingClassFlag(exitValue: Int): Int { + if ((exitValue.and(MISSING_CLASS_FLAG)) == 0) { + return exitValue + } + log.debug( + "MISSING_CLASS_FLAG (2) was set to the exit code, but ignore it to keep the task result stable.", + ) + return (exitValue.xor(MISSING_CLASS_FLAG)) + } + + private fun configureJavaExec(params: SpotBugsWorkParameters): Action { + return Action { spec -> + spec.jvmArgs = params.getJvmArgs().get() + spec.classpath(params.getClasspath()) + spec.setArgs(params.getArgs().get()) + spec.mainClass.set("edu.umd.cs.findbugs.FindBugs2") + val maxHeapSize = params.getMaxHeapSize().getOrNull() + if (maxHeapSize != null) { + spec.maxHeapSize = maxHeapSize + } + if (params.getJavaToolchainExecutablePath().isPresent) { + log.info( + "Spotbugs will be executed using Java Toolchain configuration: {}", + params.getJavaToolchainExecutablePath().get(), + ) + spec.executable = params.getJavaToolchainExecutablePath().get() + } + spec.setIgnoreExitValue(true) + } + } + } + + companion object { + /** + * Exit code which is set when classes needed for analysis were missing. + * + * @see Constant + * Field Values from javadoc of the SpotBugs + */ + private const val MISSING_CLASS_FLAG = 2 + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt new file mode 100644 index 00000000..ab0597f6 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.file.RegularFileProperty +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.LoggerFactory +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.util.stream.Collectors +import javax.inject.Inject + +class SpotBugsRunnerForJavaExec @Inject constructor( + private val javaLauncher: Property, +) : SpotBugsRunner() { + private val log = LoggerFactory.getLogger(SpotBugsRunnerForJavaExec::class.java) + + override fun run(task: SpotBugsTask) { + // TODO print version of SpotBugs and Plugins + try { + task.project.javaexec(configureJavaExec(task)).rethrowFailure().assertNormalExitValue() + } catch (e: ExecException) { + if (task.getIgnoreFailures()) { + log.warn( + "SpotBugs reported failures", + if (task.getShowStackTraces()) { + e + } else { + null + }, + ) + } else { + val errorMessage = buildString { + append("Verification failed: SpotBugs execution thrown exception.") + val reportPaths = + task.getEnabledReports().stream() + .map(SpotBugsReport::getOutputLocation) + .map(RegularFileProperty::getAsFile) + .map { + it.get() + } + .map(File::toPath) + .map(Path::toUri) + .map(URI::toString) + .collect(Collectors.toList()) + if (reportPaths.isNotEmpty()) { + append("See the report at: ") + append(reportPaths.joinToString(",")) + } + } + throw GradleException(errorMessage, e) + } + } + } + + private fun configureJavaExec(task: SpotBugsTask): Action { + return Action { spec -> + val args = mutableListOf() + args.add("-exitcode") + args.addAll(buildArguments(task)) + spec.classpath(task.getSpotbugsClasspath()) + spec.jvmArgs = buildJvmArguments(task) + spec.mainClass.set("edu.umd.cs.findbugs.FindBugs2") + spec.setArgs(args) + val maxHeapSize = task.maxHeapSize.getOrNull() + if (maxHeapSize != null) { + spec.maxHeapSize = maxHeapSize + } + if (javaLauncher.isPresent) { + log.info( + "Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}", + javaLauncher.get().metadata.vendor, + javaLauncher.get().metadata.languageVersion.asInt(), + ) + spec.executable = javaLauncher.get().executablePath.asFile.absolutePath + } + } + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt new file mode 100644 index 00000000..51d0bd7c --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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.internal + +import com.github.spotbugs.snom.SpotBugsReport +import com.github.spotbugs.snom.SpotBugsTask +import edu.umd.cs.findbugs.DetectorFactoryCollection +import edu.umd.cs.findbugs.FindBugs +import edu.umd.cs.findbugs.FindBugs2 +import edu.umd.cs.findbugs.TextUICommandLine +import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.process.JavaForkOptions +import org.gradle.workers.ProcessWorkerSpec +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.slf4j.LoggerFactory +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.util.stream.Collectors +import javax.inject.Inject + +class SpotBugsRunnerForWorker @Inject constructor( + private val workerExecutor: WorkerExecutor, + private val javaLauncher: Property, +) : SpotBugsRunner() { + private val log = LoggerFactory.getLogger(SpotBugsRunnerForWorker::class.java) + + override fun run(task: SpotBugsTask) { + val workerQueue = workerExecutor.processIsolation(configureWorkerSpec(task)) + workerQueue.submit(SpotBugsExecutor::class.java, configureWorkParameters(task)) + } + + private fun configureWorkerSpec(task: SpotBugsTask): Action { + return Action { spec -> + spec.classpath.setFrom(task.getSpotbugsClasspath()) + spec.forkOptions { option: JavaForkOptions -> + option.jvmArgs(buildJvmArguments(task)) + val maxHeapSize = task.maxHeapSize.getOrNull() + if (maxHeapSize != null) { + option.maxHeapSize = maxHeapSize + } + if (javaLauncher.isPresent) { + log.info( + "Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}", + javaLauncher.get().metadata.vendor, + javaLauncher.get().metadata.languageVersion.asInt(), + ) + option.executable = javaLauncher.get().executablePath.asFile.absolutePath + } + } + } + } + + private fun configureWorkParameters(task: SpotBugsTask): Action { + return Action { params -> + params.getArguments().addAll(buildArguments(task)) + params.getIgnoreFailures().set(task.getIgnoreFailures()) + params.getShowStackTraces().set(task.getShowStackTraces()) + task.enabledReports.stream() + .map(SpotBugsReport::getOutputLocation) + .forEach(params.getReports()::add) + } + } + + interface SpotBugsWorkParameters : WorkParameters { + fun getArguments(): ListProperty + + fun getIgnoreFailures(): Property + + fun getShowStackTraces(): Property + + fun getReports(): ListProperty + } + + abstract class SpotBugsExecutor : WorkAction { + private val log = LoggerFactory.getLogger(SpotBugsExecutor::class.java) + + override fun execute() { + val args = parameters.getArguments().get().toTypedArray() + DetectorFactoryCollection.resetInstance(DetectorFactoryCollection()) + + try { + edu.umd.cs.findbugs.Version.printVersion(false) + FindBugs2().use { findBugs2 -> + val commandLine = TextUICommandLine() + FindBugs.processCommandLine(commandLine, args, findBugs2) + findBugs2.execute() + + val message = buildString { + if (findBugs2.errorCount > 0) { + append(findBugs2.errorCount).append(" SpotBugs errors were found.") + } + if (findBugs2.bugCount > 0) { + if (isNotEmpty()) { + append(' ') + } + append(findBugs2.bugCount) + append(" SpotBugs violations were found.") + } + } + if (message.isNotEmpty()) { + val reportLocation = buildString { + val reportPaths = + parameters.getReports().get().stream() + .map(RegularFile::getAsFile) + .map(File::toPath) + .map(Path::toUri) + .map(URI::toString) + .collect(Collectors.toList()) + if (reportPaths.isNotEmpty()) { + append("See the report at: ") + append(reportPaths.joinToString(", ")) + } + } + + val e = GradleException(message + reportLocation) + if (parameters.getIgnoreFailures().getOrElse(false)) { + log.warn(message) + log.warn(reportLocation) + if (parameters.getShowStackTraces().getOrElse(false)) { + log.warn("", e) + } + } else { + throw e + } + } + } + } catch (e: GradleException) { + throw e + } catch (e: Exception) { + throw GradleException("Verification failed: SpotBugs execution thrown exception", e) + } + } + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt index 7b730e40..2e495e7c 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt @@ -28,4 +28,4 @@ abstract class SpotBugsSarifReport @Inject constructor(objects: ObjectFactory?, override fun toCommandLineOption(): String { return "-sarif" } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt index 9e0d9bec..23df77b7 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt @@ -18,7 +18,11 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.LibraryExtension import com.android.build.gradle.api.BaseVariant import com.github.spotbugs.snom.SpotBugsTask -import org.gradle.api.* +import org.gradle.api.Action +import org.gradle.api.DomainObjectSet +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.plugins.JavaPluginExtension @@ -62,10 +66,11 @@ class SpotBugsTaskFactory { task.auxClassPaths = sourceSet.compileClasspath val description = String.format( "Run SpotBugs analysis for the source set '%s'", - sourceSet.name + sourceSet.name, ) task.description = description - }) + }, + ) } } } @@ -74,7 +79,7 @@ class SpotBugsTaskFactory { val action: Action?> = Action { val baseExtension = project.extensions.getByType( - BaseExtension::class.java + BaseExtension::class.java, ) val variants: DomainObjectSet = when (baseExtension) { is AppExtension -> baseExtension.applicationVariants @@ -85,7 +90,7 @@ class SpotBugsTaskFactory { val spotbugsTaskName = toLowerCamelCase( "spotbugs", - variant.name + variant.name, ) log.debug("Creating SpotBugsTask for {}", variant.name) project @@ -100,7 +105,8 @@ class SpotBugsTaskFactory { spotbugsTask.classDirs = project.files(javaCompile.destinationDir) spotbugsTask.auxClassPaths = javaCompile.classpath spotbugsTask.dependsOn(javaCompile) - }) + }, + ) } } project.plugins.withId("com.android.application", action) @@ -119,4 +125,4 @@ class SpotBugsTaskFactory { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt index e9f78168..c7b6e6e1 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt @@ -32,4 +32,4 @@ abstract class SpotBugsTextReport @Inject constructor(objects: ObjectFactory, ta override fun getDisplayName(): String { return String.format("Text type report generated by the task %s", task.path) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt index bd10b118..5f50aaf4 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt @@ -32,4 +32,4 @@ abstract class SpotBugsXmlReport @Inject constructor(objects: ObjectFactory, tas override fun getDisplayName(): String { return String.format("XML type report generated by the task %s", task.path) } -} \ No newline at end of file +} diff --git a/src/main/groovy/com/github/spotbugs/snom/internal/package-info.java b/src/main/kotlin/com/github/spotbugs/snom/internal/package-info.java similarity index 100% rename from src/main/groovy/com/github/spotbugs/snom/internal/package-info.java rename to src/main/kotlin/com/github/spotbugs/snom/internal/package-info.java From 55ab06a75181936b6b54950e2d950fca11051e58 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 6 Aug 2023 07:33:28 +0800 Subject: [PATCH 11/41] chore: replace all Java/Groovy with Kotlin Signed-off-by: Kengo TODA --- build.gradle.kts | 13 +- ...m.github.spotbugs.gradle-plugin.gradle.kts | 16 +- .../github/spotbugs/snom/SpotBugsTask.groovy | 522 ------------------ .../github/spotbugs/snom/SpotBugsReport.kt | 4 +- .../com/github/spotbugs/snom/SpotBugsTask.kt | 437 +++++++++++++++ .../snom/internal/SpotBugsHtmlReport.kt | 2 +- .../spotbugs/snom/internal/SpotBugsRunner.kt | 66 +-- .../snom/internal/SpotBugsRunnerForHybrid.kt | 8 +- .../internal/SpotBugsRunnerForJavaExec.kt | 4 +- .../snom/internal/SpotBugsRunnerForWorker.kt | 6 +- .../snom/internal/SpotBugsSarifReport.kt | 4 +- .../snom/internal/SpotBugsTaskFactory.kt | 12 +- .../snom/internal/SpotBugsTextReport.kt | 2 +- .../snom/internal/SpotBugsXmlReport.kt | 2 +- .../internal/SpotBugsTaskFactoryTest.java | 10 +- 15 files changed, 502 insertions(+), 606 deletions(-) delete mode 100644 src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy create mode 100644 src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt diff --git a/build.gradle.kts b/build.gradle.kts index 31c64535..66eed48e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,13 +27,11 @@ repositories { mavenCentral() } -val errorproneVersion = "2.20.0" val spotBugsVersion = "4.7.3" val slf4jVersion = "2.0.0" val androidGradlePluginVersion = "7.3.1" dependencies { - errorprone("com.google.errorprone:error_prone_core:$errorproneVersion") compileOnly(localGroovy()) compileOnly("com.github.spotbugs:spotbugs:$spotBugsVersion") compileOnly("com.android.tools.build:gradle:$androidGradlePluginVersion") @@ -56,14 +54,6 @@ spotbugs { ignoreFailures.set(true) } tasks { - withType { - // Groovy code depends on class files generated by Kotlin - classpath += files( - sourceSets.named("main").flatMap { - it.kotlin.destinationDirectory - }, - ) - } named("spotbugsMain") { reports { register("sarif") { @@ -83,6 +73,9 @@ tasks { withType { dependsOn(processResources) } + named("javadoc") { + enabled = false + } } defaultTasks("spotlessApply", "build") diff --git a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts index 40890c73..5df3c1ff 100644 --- a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts +++ b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts @@ -1,22 +1,8 @@ -import net.ltgt.gradle.errorprone.errorprone - plugins { id("com.diffplug.spotless") - id("net.ltgt.errorprone") -} - -tasks.withType().configureEach { - // disable warnings in generated code by immutables - // https://github.com/google/error-prone/issues/329 - options.errorprone.disableWarningsInGeneratedCode.set(true) } -tasks.withType().configureEach { - docTitle = "SpotBugs Gradle Plugin" - link("https://docs.gradle.org/current/javadoc/", "org.gradle.api.") - link("https://docs.oracle.com/en/java/javase/11/docs/api/", "java.") - link("https://docs.groovy-lang.org/latest/html/gapi/", "groovy.", "org.codehaus.groovy.") -} +// TODO introduce KDoc spotless { java { diff --git a/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy b/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy deleted file mode 100644 index 99b91eb1..00000000 --- a/src/main/groovy/com/github/spotbugs/snom/SpotBugsTask.groovy +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Copyright 2021 SpotBugs team - * - *

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 - * - *

http://www.apache.org/licenses/LICENSE-2.0 - * - *

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 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; -import com.github.spotbugs.snom.internal.SpotBugsRunnerForWorker; -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 org.gradle.api.artifacts.Dependency; -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.OutputFile; -import org.gradle.api.tasks.SkipWhenEmpty - -import org.gradle.api.Action; -import org.gradle.api.DefaultTask; -import org.gradle.api.InvalidUserDataException; -import org.gradle.api.NamedDomainObjectContainer; -import org.gradle.api.file.FileCollection; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Nested; -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.jvm.toolchain.JavaLauncher -import org.gradle.jvm.toolchain.JavaToolchainService; -import org.gradle.util.ClosureBackedAction -import org.gradle.workers.WorkerExecutor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory - -import javax.inject.Inject -import java.nio.file.Path - -/** - * The Gradle task to run the SpotBugs analysis. All properties are optional. - * - *

Usage for Java project: - *

After you apply the SpotBugs Gradle plugin to project, {@code SpotBugsTask} is automatically - * generated for each sourceSet. If you want to configure generated tasks, write build scripts like below:

- * spotbugsMain {
- *     sourceDirs = sourceSets.main.allSource.srcDirs
- *     classDirs = sourceSets.main.output
- *     auxClassPaths = sourceSets.main.compileClasspath
- *
- *     ignoreFailures = false
- *     showStackTraces = true
- *     showProgress = false
- *     reportLevel = 'default'
- *     effort = 'default'
- *     visitors = [ 'FindSqlInjection', 'SwitchFallthrough' ]
- *     omitVisitors = [ 'FindNonShortCircuit' ]
- *     reportsDir = file("$buildDir/reports/spotbugs")
- *     includeFilter = file('spotbugs-include.xml')
- *     excludeFilter = file('spotbugs-exclude.xml')
- *     baselineFile = file('spotbugs-baseline.xml')
- *     onlyAnalyze = ['com.foobar.MyClass', 'com.foobar.mypkg.*']
- *     projectName = name
- *     release = version
- *     extraArgs = [ '-nested:false' ]
- *     jvmArgs = [ '-Duser.language=ja' ]
- *     maxHeapSize = '512m'
- *}
- * - *

See also SpotBugs Manual about configuration.

- */ - -@CacheableTask -abstract class SpotBugsTask extends DefaultTask implements VerificationTask { - private final Logger log = LoggerFactory.getLogger(SpotBugsTask); - - private final WorkerExecutor workerExecutor; - - @NonNull final Property ignoreFailures; - @NonNull final Property showStackTraces; - /** - * Property to enable progress reporting during the analysis. Default value is {@code false}. - */ - @Input - @Optional - @NonNull - final Property showProgress; - /** - * Property to specify the level to report bugs. Default value is {@link Confidence#DEFAULT}. - */ - @Input - @Optional - @NonNull - final Property reportLevel; - /** - * Property to adjust SpotBugs detectors. Default value is {@link Effort#DEFAULT}. - */ - @Input - @Optional - @NonNull - final Property effort; - /** - * Property to enable visitors (detectors) for analysis. Default is empty that means all visitors run analysis. - */ - @Input - @NonNull - final ListProperty visitors; - /** - * Property to disable visitors (detectors) for analysis. Default is empty that means SpotBugs omits no visitor. - */ - @Input - @NonNull - final ListProperty omitVisitors; - - /** - * Property to set the directory to generate report files. Default is {@code "$buildDir/reports/spotbugs/$taskName"}. - */ - @Internal("Refer the destination of each report instead.") - @NonNull - final DirectoryProperty reportsDir; - - /** - * Property to specify which report you need. - * - * @see SpotBugsReport - */ - @Internal - @NonNull - final NamedDomainObjectContainer reports; - - /** - * Property to set the filter file to limit which bug should be reported. - * - *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. - * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

- * - *

See also SpotBugs Manual about Filter file.

- */ - @Optional - @InputFile - @PathSensitive(PathSensitivity.RELATIVE) - @NonNull - final RegularFileProperty includeFilter; - /** - * Property to set the filter file to limit which bug should be reported. - * - *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. - * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

- * - *

See also SpotBugs Manual about Filter file.

- */ - @Optional - @InputFile - @PathSensitive(PathSensitivity.RELATIVE) - @NonNull - final RegularFileProperty excludeFilter; - /** - * Property to set the baseline file. This file is a Spotbugs result file, and all bugs reported in this file will not be - * reported in the final output. - */ - @Optional - @InputFile - @PathSensitive(PathSensitivity.RELATIVE) - @NonNull - final RegularFileProperty baselineFile; - /** - * Property to specify the target classes for analysis. Default value is empty that means all classes are analyzed. - */ - @Input - @NonNull - final ListProperty onlyAnalyze; - /** - * Property to specify the name of project. Some reporting formats use this property. - * Default value is {@code "${project.name} (${task.name})"}. - *
- * Note that this property, if treated as a task input, can break cacheability.
- * As such, it has been marked {@link Internal} to exclude it from task up-to-date and - * cacheability checks. - */ - @Internal - @NonNull - final Property projectName; - /** - * Property to specify the release identifier of project. Some reporting formats use this property. Default value is the version of your Gradle project. - */ - @Input - @NonNull - final Property release; - /** - * Property to specify the extra arguments for SpotBugs. Default value is empty so SpotBugs will get no extra argument. - */ - @Optional - @Input - @NonNull - final ListProperty extraArgs; - /** - * Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. - */ - @Optional - @Input - @NonNull - final ListProperty jvmArgs; - /** - * Property to specify the max heap size ({@code -Xmx} option) of JVM process. - * Default value is empty so the default configuration made by Gradle will be used. - */ - @Optional - @Input - @NonNull - final Property maxHeapSize; - /** - * Property to specify the directories that contain the source of target classes to analyze. - * Default value is the source directory of the target sourceSet. - */ - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - FileCollection sourceDirs; - - /** - * Property to specify the directories that contains the target classes to analyze. - * Default value is the output directory of the target sourceSet. - */ - @Internal - FileCollection classDirs; - - /** - * Property to specify the aux class paths that contains the libraries to refer during analysis. - * Default value is the compile-scope dependencies of the target sourceSet. - */ - @Classpath - FileCollection auxClassPaths; - - /** - * Property to enable auxclasspathFromFile and prevent Argument List Too Long issues in java processes. - * Default value is {@code false}. - */ - @Input - @Optional - @NonNull - final Property useAuxclasspathFile - - @Internal - private Path auxclasspathFile; - private FileCollection classes; - - private boolean enableWorkerApi; - private boolean enableHybridWorker; - private FileCollection pluginJarFiles - private FileCollection spotbugsClasspath - - void setClasses(FileCollection fileCollection) { - this.classes = fileCollection - } - - /** - * Property to specify the target classes to analyse by SpotBugs. - * Default value is the all existing {@code .class} files in {@link #getClassDirs}. - */ - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - @SkipWhenEmpty - @NonNull - FileCollection getClasses() { - if (classes == null) { - if (getClassDirs() == null) { - throw new InvalidUserDataException("The classDirs property is not set") - } - return getClassDirs().asFileTree.filter({ File file -> - file.name.endsWith(".class") - }) - } else { - return classes - } - } - - @Nested - @Optional - abstract Property getLauncher() - - /** - * A file that lists class files and jar files to analyse. - */ - @OutputFile - @NonNull - abstract RegularFileProperty getAnalyseClassFile() - - @Inject - SpotBugsTask(ObjectFactory objects, WorkerExecutor workerExecutor) { - this.workerExecutor = Objects.requireNonNull(workerExecutor); - - sourceDirs = objects.fileCollection() - auxClassPaths = objects.fileCollection() - ignoreFailures = objects.property(Boolean) - showStackTraces = objects.property(Boolean) - showProgress = objects.property(Boolean); - reportLevel = objects.property(Confidence); - effort = objects.property(Effort); - visitors = objects.listProperty(String); - omitVisitors = objects.listProperty(String); - reportsDir = objects.directoryProperty() - reports = - objects.domainObjectContainer( - SpotBugsReport, {name -> - switch (name) { - case "html": - return objects.newInstance(SpotBugsHtmlReport.class, name, objects, this) - case "xml": - return objects.newInstance(SpotBugsXmlReport.class, name, objects, this) - case "text": - return objects.newInstance(SpotBugsTextReport.class, name, objects, this) - case "sarif": - return objects.newInstance(SpotBugsSarifReport.class, name, objects, this) - default: - throw new InvalidUserDataException(name + " is invalid as the report name"); - } - }); - includeFilter = objects.fileProperty() - excludeFilter = objects.fileProperty() - baselineFile = objects.fileProperty() - onlyAnalyze = objects.listProperty(String); - projectName = objects.property(String); - release = objects.property(String); - jvmArgs = objects.listProperty(String); - extraArgs = objects.listProperty(String); - maxHeapSize = objects.property(String); - useAuxclasspathFile = objects.property(Boolean) - setDescription("Run SpotBugs analysis.") - setGroup(JavaBasePlugin.VERIFICATION_GROUP) - def internalPluginConfiguration = project.configurations.getByName(SpotBugsPlugin.INTERNAL_CONFIG_NAME) - pluginJarFiles = project.layout.files { - internalPluginConfiguration.files - } - def pluginConfiguration = project.configurations.getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME) - - def configuration = project.getConfigurations().getByName(SpotBugsPlugin.CONFIG_NAME) - def logger = this.log - - def spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME) - spotbugsClasspath = project.layout.files { - spotbugsSlf4j.files + pluginConfiguration.files + configuration.files - } - } - - /** - * Set properties from extension right after the task creation. User may overwrite these - * properties by build script. - * - * @param extension the source extension to copy the properties. - */ - void init(SpotBugsExtension extension, boolean enableWorkerApi, boolean enableHybridWorker) { - def taskName = getName() - auxclasspathFile = project.layout.buildDirectory.file("spotbugs/auxclasspath/$taskName").get().getAsFile().toPath() - - ignoreFailures.convention(extension.ignoreFailures) - showStackTraces.convention(extension.showStackTraces) - showProgress.convention(extension.showProgress) - reportLevel.convention(extension.reportLevel) - effort.convention(extension.effort) - visitors.convention(extension.visitors) - omitVisitors.convention(extension.omitVisitors) - // the default reportsDir is "$buildDir/reports/spotbugs/" - reportsDir.convention(extension.reportsDir) - includeFilter.convention(extension.includeFilter) - excludeFilter.convention(extension.excludeFilter) - baselineFile.convention(extension.baselineFile) - onlyAnalyze.convention(extension.onlyAnalyze) - projectName.convention(extension.projectName.map({p -> String.format("%s (%s)", p, taskName)})) - release.convention(extension.release) - jvmArgs.convention(extension.jvmArgs) - extraArgs.convention(extension.extraArgs) - maxHeapSize.convention(extension.maxHeapSize) - useAuxclasspathFile.convention(extension.useAuxclasspathFile) - - if(extension.useJavaToolchains.isPresent() && extension.useJavaToolchains.get()) { - configureJavaLauncher() - } - - this.enableWorkerApi = enableWorkerApi - this.enableHybridWorker = enableHybridWorker - - def file = new File(project.buildDir, this.name + "-analyse-class-file.txt") - analyseClassFile.set(file) - } - - - /** - * 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 defaultLauncher = service.launcherFor(toolchain) - launcher.convention(defaultLauncher) - } - - @TaskAction - void run() { - if (!enableWorkerApi) { - log.info("Running SpotBugs by JavaExec..."); - new SpotBugsRunnerForJavaExec(launcher).run(this); - } else if (enableHybridWorker) { - log.info("Running SpotBugs by Gradle no-isolated Worker..."); - new SpotBugsRunnerForHybrid(workerExecutor, launcher).run(this); - } else { - log.info("Running SpotBugs by Gradle process-isolated Worker..."); - new SpotBugsRunnerForWorker(workerExecutor, launcher).run(this); - } - } - - final NamedDomainObjectContainer reports( - Closure> closure) { - return reports( - new ClosureBackedAction>(closure)) - } - - final NamedDomainObjectContainer reports( - Action> configureAction) { - configureAction.execute(reports) - return reports - } - - @NonNull - @Internal - Set getPluginJar() { - return pluginJarFiles.files - } - - @NonNull - @Internal - FileCollection getSpotbugsClasspath() { - return spotbugsClasspath - } - - @Nullable - @Optional - @Nested - SpotBugsReport getFirstEnabledReport() { - java.util.Optional report = reports.stream().filter({ report -> report.enabled }).findFirst() - return report.orElse(null) - } - - @NonNull - @Optional - @Nested - Set getEnabledReports() { - return reports.findAll { it.enabled } - } - - void setReportLevel(@Nullable String name) { - Confidence confidence = name == null ? null : Confidence.valueOf(name.toUpperCase()) - getReportLevel().set(confidence) - } - - void setEffort(@Nullable String name) { - Effort effort = name == null ? null : Effort.valueOf(name.toUpperCase()) - getEffort().set(effort) - } - - void setIgnoreFailures(Provider b) { - ignoreFailures.set(b); - } - - void setIgnoreFailures(boolean b) { - ignoreFailures.set(b); - } - - void setShowStackTraces(Provider b) { - showStackTraces.set(b); - } - - void setShowStackTraces(boolean b) { - showStackTraces.set(b) - } - - @Input - boolean getIgnoreFailures() { - ignoreFailures.get(); - } - - @Input - boolean getShowStackTraces() { - showStackTraces.get(); - } - - @Internal - String getBaseName() { - String prunedName = name.replaceFirst("spotbugs", "") - if (prunedName.isEmpty()) { - prunedName = task.getName() - } - return new StringBuilder().append(Character.toLowerCase(prunedName.charAt(0))).append(prunedName.substring(1)).toString() - } - - Path getAuxclasspathFile() { - return auxclasspathFile - } -} diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt index b0da14e0..b2702a84 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsReport.kt @@ -74,7 +74,7 @@ abstract class SpotBugsReport @Inject constructor( } @Deprecated("use {@code getRequired().set(provider)} instead.") - fun setEnabled(provider: Provider?) { + fun setEnabled(provider: Provider) { isRequired.set(provider) } @@ -84,7 +84,7 @@ abstract class SpotBugsReport @Inject constructor( } @Deprecated("use {@code getOutputLocation().set(provider)} instead.") - fun setDestination(provider: Provider?) { + fun setDestination(provider: Provider) { destination.set(task.project.layout.file(provider)) } diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt new file mode 100644 index 00000000..21774264 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -0,0 +1,437 @@ +/* + * Copyright 2021 SpotBugs team + * + *

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 + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

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 com.github.spotbugs.snom.internal.SpotBugsHtmlReport +import com.github.spotbugs.snom.internal.SpotBugsRunnerForHybrid +import com.github.spotbugs.snom.internal.SpotBugsRunnerForJavaExec +import com.github.spotbugs.snom.internal.SpotBugsRunnerForWorker +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 groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.DefaultTask +import org.gradle.api.InvalidUserDataException +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.api.tasks.TaskAction +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.workers.WorkerExecutor +import org.slf4j.LoggerFactory +import java.nio.file.Path +import java.util.* +import javax.inject.Inject + +/** + * The Gradle task to run the SpotBugs analysis. All properties are optional. + * + *

Usage for Java project: + *

After you apply the SpotBugs Gradle plugin to project, {@code SpotBugsTask} is automatically + * generated for each sourceSet. If you want to configure generated tasks, write build scripts like below:

+ * spotbugsMain {
+ *     sourceDirs = sourceSets.main.allSource.srcDirs
+ *     classDirs = sourceSets.main.output
+ *     auxClassPaths = sourceSets.main.compileClasspath
+ *
+ *     ignoreFailures = false
+ *     showStackTraces = true
+ *     showProgress = false
+ *     reportLevel = 'default'
+ *     effort = 'default'
+ *     visitors = [ 'FindSqlInjection', 'SwitchFallthrough' ]
+ *     omitVisitors = [ 'FindNonShortCircuit' ]
+ *     reportsDir = file("$buildDir/reports/spotbugs")
+ *     includeFilter = file('spotbugs-include.xml')
+ *     excludeFilter = file('spotbugs-exclude.xml')
+ *     baselineFile = file('spotbugs-baseline.xml')
+ *     onlyAnalyze = ['com.foobar.MyClass', 'com.foobar.mypkg.*']
+ *     projectName = name
+ *     release = version
+ *     extraArgs = [ '-nested:false' ]
+ *     jvmArgs = [ '-Duser.language=ja' ]
+ *     maxHeapSize = '512m'
+ *}
+ * + *

See also SpotBugs Manual about configuration.

+ */ + +@CacheableTask +abstract class SpotBugsTask @Inject constructor( + private val workerExecutor: WorkerExecutor, +) : DefaultTask(), VerificationTask { + private val log = LoggerFactory.getLogger(SpotBugsTask::class.java) + + @get:Input + abstract val ignoreFailures: Property + + @get:Input + abstract val showStackTraces: Property + + /** + * Property to enable progress reporting during the analysis. Default value is {@code false}. + */ + @get:Optional + @get:Input + abstract val showProgress: Property + + /** + * Property to specify the level to report bugs. Default value is {@link Confidence#DEFAULT}. + */ + @get:Input + @get:Optional + abstract val reportLevel: Property + + /** + * Property to adjust SpotBugs detectors. Default value is {@link Effort#DEFAULT}. + */ + @get:Input + @get:Optional + abstract val effort: Property + + /** + * Property to enable visitors (detectors) for analysis. Default is empty that means all visitors run analysis. + */ + @get:Input + abstract val visitors: ListProperty + + /** + * Property to disable visitors (detectors) for analysis. Default is empty that means SpotBugs omits no visitor. + */ + @get:Input + abstract val omitVisitors: ListProperty + + /** + * Property to set the directory to generate report files. Default is {@code "$buildDir/reports/spotbugs/$taskName"}. + */ + @get:Internal("Refer the destination of each report instead.") + abstract val reportsDir: DirectoryProperty + + /** + * Property to specify which report you need. + * + * @see SpotBugsReport + */ + @get:Internal + val reports: NamedDomainObjectContainer + + /** + * Property to set the filter file to limit which bug should be reported. + * + *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. + * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

+ * + *

See also SpotBugs Manual about Filter file.

+ */ + @get:Optional + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val includeFilter: RegularFileProperty + + /** + * Property to set the filter file to limit which bug should be reported. + * + *

Note that this property will NOT limit which bug should be detected. To limit the target classes to analyze, use {@link #onlyAnalyze} instead. + * To limit the visitors (detectors) to run, use {@link #visitors} and {@link #omitVisitors} instead.

+ * + *

See also SpotBugs Manual about Filter file.

+ */ + @get:Optional + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val excludeFilter: RegularFileProperty + + /** + * Property to set the baseline file. This file is a Spotbugs result file, and all bugs reported in this file will not be + * reported in the final output. + */ + @get:Optional + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val baselineFile: RegularFileProperty + + /** + * Property to specify the target classes for analysis. Default value is empty that means all classes are analyzed. + */ + @get:Input + abstract val onlyAnalyze: ListProperty + + /** + * Property to specify the name of project. Some reporting formats use this property. + * Default value is {@code "${project.name} (${task.name})"}. + *
+ * Note that this property, if treated as a task input, can break cacheability.
+ * As such, it has been marked {@link Internal} to exclude it from task up-to-date and + * cacheability checks. + */ + @get:Internal + abstract val projectName: Property + + /** + * Property to specify the release identifier of project. Some reporting formats use this property. Default value is the version of your Gradle project. + */ + @get:Input + abstract val release: Property + + /** + * Property to specify the extra arguments for SpotBugs. Default value is empty so SpotBugs will get no extra argument. + */ + @get:Optional + @get:Input + abstract val extraArgs: ListProperty + + /** + * Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. + */ + @get:Optional + @get:Input + abstract val jvmArgs: ListProperty + + /** + * Property to specify the max heap size ({@code -Xmx} option) of JVM process. + * Default value is empty so the default configuration made by Gradle will be used. + */ + @get:Optional + @get:Input + abstract val maxHeapSize: Property + + /** + * Property to specify the directories that contain the source of target classes to analyze. + * Default value is the source directory of the target sourceSet. + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val sourceDirs: ConfigurableFileCollection + + /** + * Property to specify the directories that contains the target classes to analyze. + * Default value is the output directory of the target sourceSet. + */ + @get:Internal + abstract val classDirs: ConfigurableFileCollection + + /** + * Property to specify the aux class paths that contains the libraries to refer during analysis. + * Default value is the compile-scope dependencies of the target sourceSet. + */ + @get:Classpath + abstract val auxClassPaths: ConfigurableFileCollection + + /** + * Property to enable auxclasspathFromFile and prevent Argument List Too Long issues in java processes. + * Default value is {@code false}. + */ + @get:Input + @get:Optional + abstract val useAuxclasspathFile: Property + + @get:Internal + lateinit var auxclasspathFile: Path + + /** + * Property to specify the target classes to analyse by SpotBugs. + * Default value is the all existing {@code .class} files in {@link #getClassDirs}. + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:SkipWhenEmpty + var classes: FileCollection? = null + get() { + return field + ?: ( + classDirs.asFileTree.filter { + it.name.endsWith(".class") + }) + } + + private var enableWorkerApi: Boolean = true + private var enableHybridWorker: Boolean = true + + @get:Internal + abstract val pluginJarFiles: ListProperty + + @get:Internal + abstract val spotbugsClasspath: ListProperty + + @get:Nested + abstract val launcher: Property + + /** + * A file that lists class files and jar files to analyse. + */ + @get:OutputFile + abstract val analyseClassFile: RegularFileProperty + + init { + val objects = project.objects + this.reports = objects.domainObjectContainer( + SpotBugsReport::class.java, + ) { name: String -> + when (name) { + "html" -> objects.newInstance(SpotBugsHtmlReport::class.java, name, objects, this) + "xml" -> objects.newInstance(SpotBugsXmlReport::class.java, name, objects, this) + "text" -> objects.newInstance(SpotBugsTextReport::class.java, name, objects, this) + "sarif" -> objects.newInstance(SpotBugsSarifReport::class.java, name, objects, this) + else -> throw InvalidUserDataException("$name is invalid as the report name") + } + } + setDescription("Run SpotBugs analysis.") + setGroup(JavaBasePlugin.VERIFICATION_GROUP) + } + + /** + * Set properties from extension right after the task creation. User may overwrite these + * properties by build script. + * + * @param extension the source extension to copy the properties. + */ + fun init(extension: SpotBugsExtension, enableWorkerApi: Boolean, enableHybridWorker: Boolean) { + // TODO use property + this.auxclasspathFile = project.layout.buildDirectory.file("spotbugs/auxclasspath/$name").get().asFile.toPath() + + ignoreFailures.convention(extension.ignoreFailures) + showStackTraces.convention(extension.showStackTraces) + showProgress.convention(extension.showProgress) + reportLevel.convention(extension.reportLevel) + effort.convention(extension.effort) + visitors.convention(extension.visitors) + omitVisitors.convention(extension.omitVisitors) + // the default reportsDir is "$buildDir/reports/spotbugs/" + reportsDir.convention(extension.reportsDir) + includeFilter.convention(extension.includeFilter) + excludeFilter.convention(extension.excludeFilter) + baselineFile.convention(extension.baselineFile) + onlyAnalyze.convention(extension.onlyAnalyze) + projectName.convention(extension.projectName.map { p -> String.format("%s (%s)", p, name) }) + release.convention(extension.release) + jvmArgs.convention(extension.jvmArgs) + extraArgs.convention(extension.extraArgs) + maxHeapSize.convention(extension.maxHeapSize) + useAuxclasspathFile.convention(extension.useAuxclasspathFile) + + if (extension.useJavaToolchains.isPresent && extension.useJavaToolchains.get()) { + configureJavaLauncher() + } + + this.enableWorkerApi = enableWorkerApi + this.enableHybridWorker = enableHybridWorker + + analyseClassFile.set(project.buildDir.resolve(this.name + "-analyse-class-file.txt")) + + val pluginConfiguration = project.configurations.getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME) + pluginJarFiles.convention( + pluginConfiguration.files.map { file -> + project.objects.fileProperty().apply { + set(file) + } + }, + ) + val configuration = project.configurations.getByName(SpotBugsPlugin.CONFIG_NAME) + val spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME) + spotbugsClasspath.convention( + (spotbugsSlf4j.files + configuration.files).map { file -> + project.objects.fileProperty().apply { + set(file) + } + }, + ) + } + + /** + * Set convention for default java launcher based on Toolchain configuration + */ + private fun configureJavaLauncher() { + val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain + val service = project.extensions.getByType(JavaToolchainService::class.java) + val defaultLauncher = service.launcherFor(toolchain) + launcher.convention(defaultLauncher) + } + + @TaskAction + fun run() { + if (!enableWorkerApi) { + log.info("Running SpotBugs by JavaExec...") + SpotBugsRunnerForJavaExec(launcher).run(this) + } else if (enableHybridWorker) { + log.info("Running SpotBugs by Gradle no-isolated Worker...") + SpotBugsRunnerForHybrid(workerExecutor, launcher).run(this) + } else { + log.info("Running SpotBugs by Gradle process-isolated Worker...") + SpotBugsRunnerForWorker(workerExecutor, launcher).run(this) + } + } + + fun reports(closure: Closure>): NamedDomainObjectContainer { + return reports(ClosureBackedAction(closure)) + } + + fun reports( + configureAction: Action>, + ): NamedDomainObjectContainer { + configureAction.execute(reports) + return reports + } + + @Nullable + @Optional + @Nested + fun getFirstEnabledReport(): SpotBugsReport { + val report = reports.stream().filter { report -> report.isEnabled }.findFirst() + return report.orElse(null) + } + + @NonNull + @Optional + @Nested + fun getEnabledReports(): Set { + return reports.matching { report -> report.isEnabled } + } + + @Internal + fun getBaseName(): String { + var prunedName = name.replaceFirst("spotbugs", "") + if (prunedName.isEmpty()) { + prunedName = this.name + } + + return buildString { + append(Character.toLowerCase(prunedName[0])) + append(prunedName.substring(1)) + } + } +} diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt index 08fb4249..c54f5192 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt @@ -29,7 +29,7 @@ abstract class SpotBugsHtmlReport @Inject constructor(objects: ObjectFactory, ta init { // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.html" - outputLocation.convention(task.reportsDir.file(task.baseName + ".html")) + outputLocation.convention(task.reportsDir.file(task.getBaseName() + ".html")) stylesheet = objects.property(TextResource::class.java) stylesheetPath = objects.property(String::class.java) } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt index 48424ada..df7688b2 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt @@ -35,28 +35,28 @@ abstract class SpotBugsRunner { abstract fun run(task: SpotBugsTask) protected fun buildArguments(task: SpotBugsTask): List { val args: MutableList = ArrayList() - val plugins = task.getPluginJar() - if (plugins.isNotEmpty()) { + val plugins = task.pluginJarFiles + if (plugins.isPresent) { args.add("-pluginList") - args.add(join(plugins)) + args.add(join(plugins.get().map { it.get().asFile })) } args.add("-timestampNow") - if (!task.getAuxClassPaths().isEmpty) { - if (task.getUseAuxclasspathFile().get()) { + if (!task.auxClassPaths.isEmpty) { + if (task.useAuxclasspathFile.get()) { args.add("-auxclasspathFromFile") val auxClasspathFile = createFileForAuxClasspath(task) log.debug("Using auxclasspath file: {}", auxClasspathFile) args.add(auxClasspathFile) } else { args.add("-auxclasspath") - args.add(join(task.getAuxClassPaths().files)) + args.add(join(task.auxClassPaths.files)) } } - if (!task.getSourceDirs().isEmpty) { + if (!task.sourceDirs.isEmpty) { args.add("-sourcepath") - args.add(task.getSourceDirs().asPath) + args.add(task.sourceDirs.asPath) } - if (task.getShowProgress().getOrElse(Boolean.FALSE)) { + if (task.showProgress.getOrElse(Boolean.FALSE)) { args.add("-progress") } for (report: SpotBugsReport in task.getEnabledReports()) { @@ -65,59 +65,59 @@ abstract class SpotBugsRunner { dir.mkdirs() args.add(report.toCommandLineOption() + "=" + reportFile.absolutePath) } - if (task.getEffort().isPresent) { - args.add("-effort:" + task.getEffort().get().name.lowercase(Locale.getDefault())) + if (task.effort.isPresent) { + args.add("-effort:" + task.effort.get().name.lowercase(Locale.getDefault())) } - if (task.getReportLevel().isPresent) { - task.getReportLevel().get().toCommandLineOption().ifPresent { e: String -> + if (task.reportLevel.isPresent) { + task.reportLevel.get().toCommandLineOption().ifPresent { e: String -> args.add( e, ) } } - if (task.getVisitors().isPresent && task.getVisitors().get().isNotEmpty()) { + if (task.visitors.isPresent && task.visitors.get().isNotEmpty()) { args.add("-visitors") - args.add(task.getVisitors().get().stream().collect(Collectors.joining(","))) + args.add(task.visitors.get().stream().collect(Collectors.joining(","))) } - if (task.getOmitVisitors().isPresent && task.getOmitVisitors().get().isNotEmpty()) { + if (task.omitVisitors.isPresent && task.omitVisitors.get().isNotEmpty()) { args.add("-omitVisitors") - args.add(task.getOmitVisitors().get().stream().collect(Collectors.joining(","))) + args.add(task.omitVisitors.get().stream().collect(Collectors.joining(","))) } - if (task.getIncludeFilter().isPresent && task.getIncludeFilter().get() != null) { + if (task.includeFilter.isPresent && task.includeFilter.get() != null) { args.add("-include") - args.add(task.getIncludeFilter().get().asFile.absolutePath) + args.add(task.includeFilter.get().asFile.absolutePath) } - if (task.getExcludeFilter().isPresent && task.getExcludeFilter().get() != null) { + if (task.excludeFilter.isPresent && task.excludeFilter.get() != null) { args.add("-exclude") - args.add(task.getExcludeFilter().get().asFile.absolutePath) + args.add(task.excludeFilter.get().asFile.absolutePath) } - if (task.getBaselineFile().isPresent && task.getBaselineFile().get() != null) { + if (task.baselineFile.isPresent && task.baselineFile.get() != null) { args.add("-excludeBugs") - args.add(task.getBaselineFile().get().asFile.absolutePath) + args.add(task.baselineFile.get().asFile.absolutePath) } - if (task.getOnlyAnalyze().isPresent && task.getOnlyAnalyze().get().isNotEmpty()) { + if (task.onlyAnalyze.isPresent && task.onlyAnalyze.get().isNotEmpty()) { args.add("-onlyAnalyze") - args.add(task.getOnlyAnalyze().get().stream().collect(Collectors.joining(","))) + args.add(task.onlyAnalyze.get().stream().collect(Collectors.joining(","))) } args.add("-projectName") - args.add(task.getProjectName().get()) + args.add(task.projectName.get()) args.add("-release") - args.add(task.getRelease().get()) - val file = task.getAnalyseClassFile().asFile.get() - generateFile(task.getClasses(), task.getAnalyseClassFile().asFile.get()) + args.add(task.release.get()) + val file = task.analyseClassFile.asFile.get() + generateFile(task.classes ?: task.project.layout.files(), task.analyseClassFile.asFile.get()) args.add("-analyzeFromFile") args.add(file.absolutePath) - args.addAll(task.getExtraArgs().getOrElse(emptyList())) + args.addAll(task.extraArgs.getOrElse(emptyList())) log.debug("Arguments for SpotBugs are generated: {}", args) return args } private fun createFileForAuxClasspath(task: SpotBugsTask): String { - val auxClasspath = task.getAuxClassPaths().files.stream() + val auxClasspath = task.auxClassPaths.files.stream() .map { obj: File -> obj.absolutePath } .collect(Collectors.joining("\n")) try { - val auxClasspathFile = task.getAuxclasspathFile() + val auxClasspathFile = task.auxclasspathFile try { Files.createDirectories(auxClasspathFile.parent) if (!Files.exists(auxClasspathFile)) { @@ -156,7 +156,7 @@ abstract class SpotBugsRunner { } protected fun buildJvmArguments(task: SpotBugsTask): List { - val args = task.getJvmArgs().getOrElse(emptyList()) + val args = task.jvmArgs.getOrElse(emptyList()) log.debug("Arguments for JVM process are generated: {}", args) return args } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt index 6b792667..1bd6fb75 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt @@ -56,15 +56,15 @@ class SpotBugsRunnerForHybrid( val args = mutableListOf() args.add("-exitcode") args.addAll(buildArguments(task)) - params.getClasspath().setFrom(task.getSpotbugsClasspath()) + params.getClasspath().setFrom(task.spotbugsClasspath) params.getJvmArgs().set(buildJvmArguments(task)) params.getArgs().set(args) - val maxHeapSize = task.getMaxHeapSize().getOrNull() + val maxHeapSize = task.maxHeapSize.getOrNull() if (maxHeapSize != null) { params.getMaxHeapSize().set(maxHeapSize) } - params.getIgnoreFailures().set(task.getIgnoreFailures()) - params.getShowStackTraces().set(task.getShowStackTraces()) + params.getIgnoreFailures().set(task.ignoreFailures) + params.getShowStackTraces().set(task.showStackTraces) task.getEnabledReports().stream() .map(SpotBugsReport::getOutputLocation) .forEach(params.getReports()::add) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt index ab0597f6..f286fffb 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt @@ -42,7 +42,7 @@ class SpotBugsRunnerForJavaExec @Inject constructor( if (task.getIgnoreFailures()) { log.warn( "SpotBugs reported failures", - if (task.getShowStackTraces()) { + if (task.showStackTraces.get()) { e } else { null @@ -77,7 +77,7 @@ class SpotBugsRunnerForJavaExec @Inject constructor( val args = mutableListOf() args.add("-exitcode") args.addAll(buildArguments(task)) - spec.classpath(task.getSpotbugsClasspath()) + spec.classpath(task.spotbugsClasspath) spec.jvmArgs = buildJvmArguments(task) spec.mainClass.set("edu.umd.cs.findbugs.FindBugs2") spec.setArgs(args) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt index 51d0bd7c..bb5b0922 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt @@ -50,7 +50,7 @@ class SpotBugsRunnerForWorker @Inject constructor( private fun configureWorkerSpec(task: SpotBugsTask): Action { return Action { spec -> - spec.classpath.setFrom(task.getSpotbugsClasspath()) + spec.classpath.setFrom(task.spotbugsClasspath) spec.forkOptions { option: JavaForkOptions -> option.jvmArgs(buildJvmArguments(task)) val maxHeapSize = task.maxHeapSize.getOrNull() @@ -73,8 +73,8 @@ class SpotBugsRunnerForWorker @Inject constructor( return Action { params -> params.getArguments().addAll(buildArguments(task)) params.getIgnoreFailures().set(task.getIgnoreFailures()) - params.getShowStackTraces().set(task.getShowStackTraces()) - task.enabledReports.stream() + params.getShowStackTraces().set(task.showStackTraces) + task.getEnabledReports().stream() .map(SpotBugsReport::getOutputLocation) .forEach(params.getReports()::add) } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt index 2e495e7c..03254764 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsSarifReport.kt @@ -18,11 +18,11 @@ import com.github.spotbugs.snom.SpotBugsTask import org.gradle.api.model.ObjectFactory import javax.inject.Inject -abstract class SpotBugsSarifReport @Inject constructor(objects: ObjectFactory?, task: SpotBugsTask) : +abstract class SpotBugsSarifReport @Inject constructor(objects: ObjectFactory, task: SpotBugsTask) : SpotBugsReport(objects, task) { init { // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.sarif" - outputLocation.convention(task.reportsDir.file(task.baseName + ".sarif")) + outputLocation.convention(task.reportsDir.file(task.getBaseName() + ".sarif")) } override fun toCommandLineOption(): String { diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt index 23df77b7..cdc50baf 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt @@ -61,9 +61,9 @@ class SpotBugsTaskFactory { name, SpotBugsTask::class.java, Action { task: SpotBugsTask -> - task.sourceDirs = sourceSet.allSource.sourceDirectories - task.classDirs = sourceSet.output - task.auxClassPaths = sourceSet.compileClasspath + task.sourceDirs.setFrom(sourceSet.allSource.sourceDirectories) + task.classDirs.setFrom(sourceSet.output) + task.auxClassPaths.setFrom(sourceSet.compileClasspath) val description = String.format( "Run SpotBugs analysis for the source set '%s'", sourceSet.name, @@ -101,9 +101,9 @@ class SpotBugsTaskFactory { Action { spotbugsTask: SpotBugsTask -> val javaCompile = variant.javaCompileProvider.get() - spotbugsTask.sourceDirs = javaCompile.source - spotbugsTask.classDirs = project.files(javaCompile.destinationDir) - spotbugsTask.auxClassPaths = javaCompile.classpath + spotbugsTask.sourceDirs.setFrom(javaCompile.source) + spotbugsTask.classDirs.setFrom(javaCompile.destinationDirectory) + spotbugsTask.auxClassPaths.setFrom(javaCompile.classpath) spotbugsTask.dependsOn(javaCompile) }, ) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt index c7b6e6e1..d6eb0714 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTextReport.kt @@ -22,7 +22,7 @@ abstract class SpotBugsTextReport @Inject constructor(objects: ObjectFactory, ta SpotBugsReport(objects, task) { init { // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.txt" - outputLocation.convention(task.reportsDir.file(task.baseName + ".txt")) + outputLocation.convention(task.reportsDir.file(task.getBaseName() + ".txt")) } override fun toCommandLineOption(): String { diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt index 5f50aaf4..05870ebd 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsXmlReport.kt @@ -22,7 +22,7 @@ abstract class SpotBugsXmlReport @Inject constructor(objects: ObjectFactory, tas SpotBugsReport(objects, task) { init { // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.xml" - outputLocation.convention(task.reportsDir.file(task.baseName + ".xml")) + outputLocation.convention(task.reportsDir.file(task.getBaseName() + ".xml")) } override fun toCommandLineOption(): String { diff --git a/src/test/java/com/github/spotbugs/snom/internal/SpotBugsTaskFactoryTest.java b/src/test/java/com/github/spotbugs/snom/internal/SpotBugsTaskFactoryTest.java index b9009d8c..68075442 100644 --- a/src/test/java/com/github/spotbugs/snom/internal/SpotBugsTaskFactoryTest.java +++ b/src/test/java/com/github/spotbugs/snom/internal/SpotBugsTaskFactoryTest.java @@ -20,9 +20,11 @@ class SpotBugsTaskFactoryTest { @Test void toLowerCamelCase() { - assertEquals("spotbugs", SpotBugsTaskFactory.toLowerCamelCase("spotbugs", null)); - assertEquals("spotbugs", SpotBugsTaskFactory.toLowerCamelCase("spotbugs", "")); - assertEquals("spotbugsMain", SpotBugsTaskFactory.toLowerCamelCase("spotbugs", "main")); - assertEquals("spotbugsMainCode", SpotBugsTaskFactory.toLowerCamelCase("spotbugs", "mainCode")); + assertEquals("spotbugs", SpotBugsTaskFactory.Companion.toLowerCamelCase("spotbugs", null)); + assertEquals("spotbugs", SpotBugsTaskFactory.Companion.toLowerCamelCase("spotbugs", "")); + assertEquals( + "spotbugsMain", SpotBugsTaskFactory.Companion.toLowerCamelCase("spotbugs", "main")); + assertEquals( + "spotbugsMainCode", SpotBugsTaskFactory.Companion.toLowerCamelCase("spotbugs", "mainCode")); } } From bfb6218645f12b8bd3dcb32a2ae44067c5255b38 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 6 Aug 2023 07:35:34 +0800 Subject: [PATCH 12/41] ci: remove javadoc workflow Signed-off-by: Kengo TODA --- .github/workflows/javadoc.yml | 36 ----------------------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/javadoc.yml diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml deleted file mode 100644 index 25dc2834..00000000 --- a/.github/workflows/javadoc.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Javadoc -on: - push: - branches: - - master -jobs: - javadoc: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - persist-credentials: false - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: 11 - cache: gradle - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - cache: npm - - name: Generate Groovydoc - run: ./gradlew groovydoc - - name: Prepare to Deploy - run: | - npm ci - rm -f .git/hooks/commit-msg - - name: Deploy - uses: JamesIves/github-pages-deploy-action@releases/v3 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages - FOLDER: build/docs/groovydoc/ From d73b96354fba27e5c3fa78f4f73ac87b4e1e1190 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 6 Aug 2023 07:37:39 +0800 Subject: [PATCH 13/41] ci: run workflow even on release branch Signed-off-by: Kengo TODA --- .github/workflows/gradle.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e18d066d..8e12b60c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -4,9 +4,11 @@ on: push: branches: - master + - v6 pull_request: branches: - master + - v6 jobs: build: From 8ec3bf395ec1a4207b06c2e1ea706a97e04c400b Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 6 Aug 2023 09:10:20 +0800 Subject: [PATCH 14/41] test: resolve some known issues Signed-off-by: Kengo TODA --- .../spotbugs/snom/SpotBugsBasePlugin.kt | 2 -- .../github/spotbugs/snom/SpotBugsPlugin.kt | 3 +-- .../com/github/spotbugs/snom/SpotBugsTask.kt | 16 ++++++++------ .../snom/internal/SpotBugsTaskFactory.kt | 22 +++++-------------- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt index 58bb7655..f1f6b97c 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt @@ -55,8 +55,6 @@ class SpotBugsBasePlugin : Plugin { .create( SpotBugsPlugin.EXTENSION_NAME, SpotBugsExtension::class.java, - project, - project.objects, ) extension.ignoreFailures.convention(false) extension.showStackTraces.convention(false) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt index a668d1e8..40ef0c79 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt @@ -17,7 +17,6 @@ import com.github.spotbugs.snom.internal.SpotBugsTaskFactory import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.plugins.AppliedPlugin import org.gradle.api.plugins.JavaBasePlugin import org.slf4j.LoggerFactory @@ -29,7 +28,7 @@ class SpotBugsPlugin : Plugin { .pluginManager .withPlugin( "java-base", - ) { javaBase: AppliedPlugin -> + ) { log.debug( "The javaBase plugin has been applied, so making the check task depending on all of SpotBugsTask", ) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 21774264..154355ef 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -92,11 +92,12 @@ import javax.inject.Inject */ @CacheableTask -abstract class SpotBugsTask @Inject constructor( - private val workerExecutor: WorkerExecutor, -) : DefaultTask(), VerificationTask { +abstract class SpotBugsTask : DefaultTask(), VerificationTask { private val log = LoggerFactory.getLogger(SpotBugsTask::class.java) + @get:Inject + abstract val workerExecutor: WorkerExecutor + @get:Input abstract val ignoreFailures: Property @@ -274,9 +275,10 @@ abstract class SpotBugsTask @Inject constructor( get() { return field ?: ( - classDirs.asFileTree.filter { - it.name.endsWith(".class") - }) + classDirs.asFileTree.filter { + it.name.endsWith(".class") + } + ) } private var enableWorkerApi: Boolean = true @@ -321,7 +323,7 @@ abstract class SpotBugsTask @Inject constructor( * @param extension the source extension to copy the properties. */ fun init(extension: SpotBugsExtension, enableWorkerApi: Boolean, enableHybridWorker: Boolean) { - // TODO use property + // TODO use Property this.auxclasspathFile = project.layout.buildDirectory.file("spotbugs/auxclasspath/$name").get().asFile.toPath() ignoreFailures.convention(extension.ignoreFailures) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt index cdc50baf..a0e809f9 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsTaskFactory.kt @@ -55,22 +55,12 @@ class SpotBugsTaskFactory { .all { sourceSet: SourceSet -> val name = sourceSet.getTaskName("spotbugs", null) log.debug("Creating SpotBugsTask for {}", sourceSet) - project - .tasks - .register( - name, - SpotBugsTask::class.java, - Action { task: SpotBugsTask -> - task.sourceDirs.setFrom(sourceSet.allSource.sourceDirectories) - task.classDirs.setFrom(sourceSet.output) - task.auxClassPaths.setFrom(sourceSet.compileClasspath) - val description = String.format( - "Run SpotBugs analysis for the source set '%s'", - sourceSet.name, - ) - task.description = description - }, - ) + project.tasks.register(name, SpotBugsTask::class.java) { + it.sourceDirs.setFrom(sourceSet.allSource.sourceDirectories) + it.classDirs.setFrom(sourceSet.output) + it.auxClassPaths.setFrom(sourceSet.compileClasspath) + it.description = "Run SpotBugs analysis for the source set '${sourceSet.name}'" + } } } } From 9e6b483707505fb10568c0df599807237357f074 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 6 Aug 2023 09:57:40 +0800 Subject: [PATCH 15/41] test: resolve some known issues Signed-off-by: Kengo TODA --- .../github/spotbugs/snom/SpotBugsBasePlugin.kt | 2 +- .../com/github/spotbugs/snom/SpotBugsTask.kt | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt index f1f6b97c..8a56bef5 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsBasePlugin.kt @@ -99,7 +99,7 @@ class SpotBugsBasePlugin : Plugin { .create( extension.toolVersion.map { "com.github.spotbugs:spotbugs:$it" - }, + }.get(), ), ) } diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 154355ef..2b8910a3 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -55,7 +55,6 @@ import org.gradle.util.ClosureBackedAction import org.gradle.workers.WorkerExecutor import org.slf4j.LoggerFactory import java.nio.file.Path -import java.util.* import javax.inject.Inject /** @@ -98,8 +97,15 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { @get:Inject abstract val workerExecutor: WorkerExecutor - @get:Input - abstract val ignoreFailures: Property + @Input + override fun getIgnoreFailures(): Boolean = + this.ignoreFailures.get() + + override fun setIgnoreFailures(ignoreFailures: Boolean) { + this.ignoreFailures.set(ignoreFailures) + } + + private val ignoreFailures = project.objects.property(Boolean::class.java) @get:Input abstract val showStackTraces: Property @@ -291,6 +297,7 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { abstract val spotbugsClasspath: ListProperty @get:Nested + @get:Optional abstract val launcher: Property /** @@ -412,7 +419,7 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { @Nullable @Optional @Nested - fun getFirstEnabledReport(): SpotBugsReport { + fun getFirstEnabledReport(): SpotBugsReport? { val report = reports.stream().filter { report -> report.isEnabled }.findFirst() return report.orElse(null) } From 8173a0ab9fd8645c93d0ee36ca8d2e9fcebbb842 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Mon, 7 Aug 2023 06:21:10 +0800 Subject: [PATCH 16/41] test: fix known issues Signed-off-by: Kengo TODA --- .../github/spotbugs/snom/BasePluginFunctionalTest.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy index 1b6b9b54..1acc5811 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy @@ -76,7 +76,7 @@ public class Foo { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs = sourceSets.main.output + classDirs.set(sourceSets.main.output) } """ when: @@ -99,7 +99,7 @@ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs = sourceSets.main.output + classDirs.set(sourceSets.main.output) } """ when: @@ -130,7 +130,7 @@ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs = sourceSets.main.output + classDirs.set(sourceSets.main.output) } """ when: From 5db6b1ed244e270f7a0f4d6080fcc48d0b626cea Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Thu, 10 Aug 2023 18:09:26 +0800 Subject: [PATCH 17/41] build: remove needless spotless rules Signed-off-by: Kengo TODA --- .../com.github.spotbugs.gradle-plugin.gradle.kts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts index 5df3c1ff..9c192be9 100644 --- a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts +++ b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts @@ -5,22 +5,6 @@ plugins { // TODO introduce KDoc spotless { - java { - licenseHeaderFile("gradle/HEADER.txt") - removeUnusedImports() - googleJavaFormat() - } - groovy { - licenseHeaderFile("gradle/HEADER.txt") - target("**/*.groovy") - greclipse() - indentWithSpaces() - } - groovyGradle { - target("**/*.gradle") - greclipse() - indentWithSpaces() - } kotlin { ktlint() } From 78cdc3acfb96339c88d0bb1aefcab0c434052cf6 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Thu, 10 Aug 2023 18:12:05 +0800 Subject: [PATCH 18/41] test: use canonicalPath to support Windows Signed-off-by: Kengo TODA --- .../com/github/spotbugs/snom/AndroidFunctionalTest.groovy | 4 ++-- .../com/github/spotbugs/snom/ExtensionFunctionalTest.groovy | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/AndroidFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/AndroidFunctionalTest.groovy index caa817c2..e0886a7c 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/AndroidFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/AndroidFunctionalTest.groovy @@ -107,7 +107,7 @@ buildscript { """ runner.pluginClasspath.forEach({ file -> buildFile << """ - classpath files('${file.absolutePath}') + classpath files('${file.canonicalPath}') """ }) buildFile << """ @@ -148,7 +148,7 @@ buildscript { """ runner.pluginClasspath.forEach({ file -> buildFile << """ - classpath files('${file.absolutePath}') + classpath files('${file.canonicalPath}') """ }) buildFile << """ diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy index 6a35e470..57927092 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy @@ -75,7 +75,7 @@ spotbugs { then: SUCCESS == result.task(":spotbugsMain").outcome result.getOutput().contains("-include") - result.getOutput().contains(filter.getAbsolutePath()) + result.getOutput().contains(filter.canonicalPath) } def "can use excludeFilter"() { @@ -99,7 +99,7 @@ spotbugs { then: SUCCESS == result.task(":spotbugsMain").outcome result.getOutput().contains("-exclude") - result.getOutput().contains(filter.getAbsolutePath()) + result.getOutput().contains(filter.canonicalPath) } def "can use baselineFile"() { @@ -123,7 +123,7 @@ spotbugs { then: SUCCESS == result.task(":spotbugsMain").outcome result.getOutput().contains("-excludeBugs") - result.getOutput().contains(baseline.getAbsolutePath()) + result.getOutput().contains(baseline.canonicalPath) } def "can use visitors"() { From b597026d54041db1cd929e194a22ac1488e561ac Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Thu, 10 Aug 2023 20:36:56 +0800 Subject: [PATCH 19/41] chore: reflect changes from #929 Signed-off-by: Kengo TODA --- .../com/github/spotbugs/snom/internal/OutputScanner.kt | 2 +- .../spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt | 9 ++++++++- .../spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt | 6 ++++++ .../spotbugs/snom/internal/SpotBugsRunnerForWorker.kt | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt index 297396ac..62f1e34c 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/OutputScanner.kt @@ -23,7 +23,7 @@ import java.io.OutputStream */ internal class OutputScanner(out: OutputStream) : FilterOutputStream(out) { private val builder = ByteArrayOutputStream() - private var isFailedToReport = false + var isFailedToReport = false get() = field override fun write(b: ByteArray, off: Int, len: Int) { diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt index 1bd6fb75..dda5984a 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt @@ -97,6 +97,7 @@ class SpotBugsRunnerForHybrid( private val execOperations: ExecOperations, ) : WorkAction { private val log = LoggerFactory.getLogger(this.javaClass) + private lateinit var stderrOutputScanner: OutputScanner; override fun execute() { // TODO print version of SpotBugs and Plugins @@ -104,11 +105,15 @@ class SpotBugsRunnerForHybrid( val exitValue = execOperations.javaexec(configureJavaExec(params)).rethrowFailure().exitValue + val ignoreFailures = params.getIgnoreFailures().getOrElse(false); if (ignoreMissingClassFlag(exitValue) == 0) { + if (stderrOutputScanner.isFailedToReport && !ignoreFailures) { + throw GradleException("SpotBugs analysis succeeded but report generation failed"); + } return } - if (params.getIgnoreFailures().getOrElse(false)) { + if (ignoreFailures) { log.warn("SpotBugs ended with exit code $exitValue") return } @@ -158,6 +163,8 @@ class SpotBugsRunnerForHybrid( spec.executable = params.getJavaToolchainExecutablePath().get() } spec.setIgnoreExitValue(true) + stderrOutputScanner = OutputScanner(System.err); + spec.setErrorOutput(stderrOutputScanner); } } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt index f286fffb..401a0a0c 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt @@ -33,11 +33,15 @@ class SpotBugsRunnerForJavaExec @Inject constructor( private val javaLauncher: Property, ) : SpotBugsRunner() { private val log = LoggerFactory.getLogger(SpotBugsRunnerForJavaExec::class.java) + private lateinit var stderrOutputScanner: OutputScanner; override fun run(task: SpotBugsTask) { // TODO print version of SpotBugs and Plugins try { task.project.javaexec(configureJavaExec(task)).rethrowFailure().assertNormalExitValue() + if (stderrOutputScanner.isFailedToReport && !task.getIgnoreFailures()) { + throw GradleException("SpotBugs analysis succeeded but report generation failed"); + } } catch (e: ExecException) { if (task.getIgnoreFailures()) { log.warn( @@ -85,6 +89,8 @@ class SpotBugsRunnerForJavaExec @Inject constructor( if (maxHeapSize != null) { spec.maxHeapSize = maxHeapSize } + stderrOutputScanner = OutputScanner(System.err); + spec.setErrorOutput(stderrOutputScanner); if (javaLauncher.isPresent) { log.info( "Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}", diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt index bb5b0922..a8b6cc23 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt @@ -37,6 +37,7 @@ import java.nio.file.Path import java.util.stream.Collectors import javax.inject.Inject +@Deprecated("Will be removed in v6 release") class SpotBugsRunnerForWorker @Inject constructor( private val workerExecutor: WorkerExecutor, private val javaLauncher: Property, From d43acd32bedd48a7414c6d8c53ce64aa0f77a9b2 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 05:39:10 +0800 Subject: [PATCH 20/41] chore: apply more kotlin-like coding style Signed-off-by: Kengo TODA --- .../snom/internal/SpotBugsRunnerForHybrid.kt | 14 ++++---------- .../snom/internal/SpotBugsRunnerForWorker.kt | 12 ++---------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt index dda5984a..bda40774 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt @@ -48,11 +48,7 @@ class SpotBugsRunnerForHybrid( ) : SpotBugsRunner() { override fun run(task: SpotBugsTask) { - workerExecutor.noIsolation().submit(SpotBugsExecutor::class.java, configureWorkerSpec(task)) - } - - private fun configureWorkerSpec(task: SpotBugsTask): Action { - return Action { params: SpotBugsWorkParameters -> + workerExecutor.noIsolation().submit(SpotBugsExecutor::class.java) { params: SpotBugsWorkParameters -> val args = mutableListOf() args.add("-exitcode") args.addAll(buildArguments(task)) @@ -101,11 +97,9 @@ class SpotBugsRunnerForHybrid( override fun execute() { // TODO print version of SpotBugs and Plugins - val params = getParameters() - val exitValue = - execOperations.javaexec(configureJavaExec(params)).rethrowFailure().exitValue - val ignoreFailures = params.getIgnoreFailures().getOrElse(false); + execOperations.javaexec(configureJavaExec(parameters)).rethrowFailure().exitValue + val ignoreFailures = parameters.getIgnoreFailures().getOrElse(false); if (ignoreMissingClassFlag(exitValue) == 0) { if (stderrOutputScanner.isFailedToReport && !ignoreFailures) { throw GradleException("SpotBugs analysis succeeded but report generation failed"); @@ -121,7 +115,7 @@ class SpotBugsRunnerForHybrid( val errorMessage = buildString { append("Verification failed: SpotBugs ended with exit code $exitValue.") val reportPaths = - params.getReports().get().stream() + parameters.getReports().get().stream() .map(RegularFile::getAsFile) .map(File::toPath) .map(Path::toUri) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt index a8b6cc23..a3de4a53 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt @@ -45,12 +45,7 @@ class SpotBugsRunnerForWorker @Inject constructor( private val log = LoggerFactory.getLogger(SpotBugsRunnerForWorker::class.java) override fun run(task: SpotBugsTask) { - val workerQueue = workerExecutor.processIsolation(configureWorkerSpec(task)) - workerQueue.submit(SpotBugsExecutor::class.java, configureWorkParameters(task)) - } - - private fun configureWorkerSpec(task: SpotBugsTask): Action { - return Action { spec -> + val workerQueue = workerExecutor.processIsolation { spec -> spec.classpath.setFrom(task.spotbugsClasspath) spec.forkOptions { option: JavaForkOptions -> option.jvmArgs(buildJvmArguments(task)) @@ -68,10 +63,7 @@ class SpotBugsRunnerForWorker @Inject constructor( } } } - } - - private fun configureWorkParameters(task: SpotBugsTask): Action { - return Action { params -> + workerQueue.submit(SpotBugsExecutor::class.java) { params -> params.getArguments().addAll(buildArguments(task)) params.getIgnoreFailures().set(task.getIgnoreFailures()) params.getShowStackTraces().set(task.showStackTraces) From 52eac5114a786ba1eec7af70783d03717c2278a5 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 06:32:16 +0800 Subject: [PATCH 21/41] test: pass several tests Signed-off-by: Kengo TODA --- .../snom/BasePluginFunctionalTest.groovy | 6 ++-- .../KotlinBuildScriptFunctionalTest.groovy | 32 ++++++++++--------- .../com/github/spotbugs/snom/Confidence.kt | 7 ++++ .../kotlin/com/github/spotbugs/snom/Effort.kt | 9 ++++++ .../snom/internal/SpotBugsRunnerForHybrid.kt | 10 +++--- .../internal/SpotBugsRunnerForJavaExec.kt | 8 ++--- .../snom/internal/SpotBugsRunnerForWorker.kt | 2 -- 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy index 1acc5811..1b6b9b54 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy @@ -76,7 +76,7 @@ public class Foo { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs.set(sourceSets.main.output) + classDirs = sourceSets.main.output } """ when: @@ -99,7 +99,7 @@ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs.set(sourceSets.main.output) + classDirs = sourceSets.main.output } """ when: @@ -130,7 +130,7 @@ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { buildFile << """ task spotbugsMain(type: com.github.spotbugs.snom.SpotBugsTask) { dependsOn 'classes' - classDirs.set(sourceSets.main.output) + classDirs = sourceSets.main.output } """ when: diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index b3adc086..f6d0ba29 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -31,6 +31,8 @@ class KotlinBuildScriptFunctionalTest extends Specification { rootDir = Files.createTempDirectory("KotlinBuildScriptFunctionalTest").toFile() buildFile = new File(rootDir, 'build.gradle.kts') buildFile << """ +import com.github.spotbugs.snom.Confidence.Companion.assign +import com.github.spotbugs.snom.Effort.Companion.assign plugins { `java` id("com.github.spotbugs") @@ -58,21 +60,21 @@ public class Foo { setup: buildFile << """ spotbugs { - ignoreFailures.set(false) - showStackTraces.set(true) - showProgress.set(true) - effort.set(com.github.spotbugs.snom.Effort.DEFAULT) - reportLevel.set(com.github.spotbugs.snom.Confidence.DEFAULT) - visitors.set(listOf("FindSqlInjection", "SwitchFallthrough")) - omitVisitors.set(listOf("FindNonShortCircuit")) - reportsDir.set(file("\$buildDir/spotbugs")) - includeFilter.set(file("include.xml")) - excludeFilter.set(file("exclude.xml")) - baselineFile.set(file("baseline.xml")) - onlyAnalyze.set(listOf("com.foobar.MyClass", "com.foobar.mypkg.*")) - maxHeapSize.set("1g") - extraArgs.set(listOf("-nested:false")) - jvmArgs.set(listOf("-Duser.language=ja")) + ignoreFailures = false + showStackTraces = true + showProgress = true + effort = "DEFAULT" + reportLevel = "DEFAULT" + visitors = listOf("FindSqlInjection", "SwitchFallthrough") + omitVisitors = listOf("FindNonShortCircuit") + reportsDir = file("\$buildDir/spotbugs") + includeFilter = file("include.xml") + excludeFilter = file("exclude.xml") + baselineFile = file("baseline.xml") + onlyAnalyze = listOf("com.foobar.MyClass", "com.foobar.mypkg.*") + maxHeapSize = "1g" + extraArgs = listOf("-nested:false") + jvmArgs = listOf("-Duser.language=ja") } """ new File(rootDir, "include.xml") << "" diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index 46148757..69c560ec 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -13,6 +13,7 @@ */ package com.github.spotbugs.snom +import org.gradle.api.provider.Property import org.gradle.api.tasks.Internal import java.util.Optional @@ -65,4 +66,10 @@ enum class Confidence { @Internal("This is internally used property so no need to refer to judge out-of-date or not.") abstract fun toCommandLineOption(): Optional + + companion object { + fun Property.assign(string: String) { + set(Confidence.valueOf(string)) + } + } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt index bae9a430..1aa508a8 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -13,6 +13,8 @@ */ package com.github.spotbugs.snom +import org.gradle.api.provider.Property + /** * The [Effort] is configuration to adjust SpotBugs detectors. Use lower effort to reduce * computation cost. @@ -59,4 +61,11 @@ enum class Effort { * of Referenced Classes. */ MAX, + + ; + companion object { + fun Property.assign(string: String) { + set(Effort.valueOf(string)) + } + } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt index bda40774..dc5acfcc 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForHybrid.kt @@ -93,16 +93,16 @@ class SpotBugsRunnerForHybrid( private val execOperations: ExecOperations, ) : WorkAction { private val log = LoggerFactory.getLogger(this.javaClass) - private lateinit var stderrOutputScanner: OutputScanner; + private lateinit var stderrOutputScanner: OutputScanner override fun execute() { // TODO print version of SpotBugs and Plugins val exitValue = execOperations.javaexec(configureJavaExec(parameters)).rethrowFailure().exitValue - val ignoreFailures = parameters.getIgnoreFailures().getOrElse(false); + val ignoreFailures = parameters.getIgnoreFailures().getOrElse(false) if (ignoreMissingClassFlag(exitValue) == 0) { if (stderrOutputScanner.isFailedToReport && !ignoreFailures) { - throw GradleException("SpotBugs analysis succeeded but report generation failed"); + throw GradleException("SpotBugs analysis succeeded but report generation failed") } return } @@ -157,8 +157,8 @@ class SpotBugsRunnerForHybrid( spec.executable = params.getJavaToolchainExecutablePath().get() } spec.setIgnoreExitValue(true) - stderrOutputScanner = OutputScanner(System.err); - spec.setErrorOutput(stderrOutputScanner); + stderrOutputScanner = OutputScanner(System.err) + spec.setErrorOutput(stderrOutputScanner) } } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt index 401a0a0c..9a904e66 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForJavaExec.kt @@ -33,14 +33,14 @@ class SpotBugsRunnerForJavaExec @Inject constructor( private val javaLauncher: Property, ) : SpotBugsRunner() { private val log = LoggerFactory.getLogger(SpotBugsRunnerForJavaExec::class.java) - private lateinit var stderrOutputScanner: OutputScanner; + private lateinit var stderrOutputScanner: OutputScanner override fun run(task: SpotBugsTask) { // TODO print version of SpotBugs and Plugins try { task.project.javaexec(configureJavaExec(task)).rethrowFailure().assertNormalExitValue() if (stderrOutputScanner.isFailedToReport && !task.getIgnoreFailures()) { - throw GradleException("SpotBugs analysis succeeded but report generation failed"); + throw GradleException("SpotBugs analysis succeeded but report generation failed") } } catch (e: ExecException) { if (task.getIgnoreFailures()) { @@ -89,8 +89,8 @@ class SpotBugsRunnerForJavaExec @Inject constructor( if (maxHeapSize != null) { spec.maxHeapSize = maxHeapSize } - stderrOutputScanner = OutputScanner(System.err); - spec.setErrorOutput(stderrOutputScanner); + stderrOutputScanner = OutputScanner(System.err) + spec.setErrorOutput(stderrOutputScanner) if (javaLauncher.isPresent) { log.info( "Spotbugs will be executed using Java Toolchain configuration: Vendor: {} | Version: {}", diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt index a3de4a53..4a34b3f3 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunnerForWorker.kt @@ -19,14 +19,12 @@ import edu.umd.cs.findbugs.DetectorFactoryCollection import edu.umd.cs.findbugs.FindBugs import edu.umd.cs.findbugs.FindBugs2 import edu.umd.cs.findbugs.TextUICommandLine -import org.gradle.api.Action import org.gradle.api.GradleException import org.gradle.api.file.RegularFile import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.process.JavaForkOptions -import org.gradle.workers.ProcessWorkerSpec import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters import org.gradle.workers.WorkerExecutor From f961e41883bb7b6c27d1109cbc48a01b7feda59f Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 09:19:29 +0800 Subject: [PATCH 22/41] test: fix all broken test cases Signed-off-by: Kengo TODA --- .../snom/ExtensionFunctionalTest.groovy | 9 ++- .../snom/StandardFunctionalTest.groovy | 12 +++- .../com/github/spotbugs/snom/SpotBugsTask.kt | 8 ++- .../snom/internal/SpotBugsHtmlReport.kt | 64 +++++++++---------- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy index 57927092..ffe7ef56 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/ExtensionFunctionalTest.groovy @@ -18,6 +18,7 @@ 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.Ignore import spock.lang.Specification import static org.gradle.testkit.runner.TaskOutcome.SUCCESS @@ -221,9 +222,13 @@ spotbugs { def "can use effort and reportLevel"() { buildFile << """ +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort spotbugs { - effort = 'less' - reportLevel = 'high' + // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 + // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ + effort = Effort.valueOf('LESS') + reportLevel = Confidence.valueOf('HIGH') }""" when: def result = GradleRunner.create() diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy index 05fa2592..ace9ed6c 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy @@ -133,9 +133,13 @@ dependencies { def "can use effort and reportLevel"() { buildFile << """ +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort spotbugsMain { - effort = 'min' - reportLevel = 'high' + // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 + // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ + effort = Effort.valueOf('MIN') + reportLevel = Confidence.valueOf('HIGH') }""" when: def result = GradleRunner.create() @@ -775,7 +779,9 @@ spotbugs { given: buildFile << """ spotbugs { - reportLevel = com.github.spotbugs.snom.Confidence.DEFAULT + // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 + // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ + reportLevel = com.github.spotbugs.snom.Confidence.valueOf('DEFAULT') } """ diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 2b8910a3..34c31023 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -373,9 +373,11 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { val configuration = project.configurations.getByName(SpotBugsPlugin.CONFIG_NAME) val spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME) spotbugsClasspath.convention( - (spotbugsSlf4j.files + configuration.files).map { file -> - project.objects.fileProperty().apply { - set(file) + project.provider { + (spotbugsSlf4j.files + configuration.files).map { file -> + project.objects.fileProperty().apply { + set(file) + } } }, ) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt index c54f5192..37c8a7e8 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsHtmlReport.kt @@ -13,59 +13,43 @@ */ 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 org.gradle.api.InvalidUserDataException +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.resources.TextResource +import org.gradle.api.resources.TextResourceFactory import javax.inject.Inject abstract class SpotBugsHtmlReport @Inject constructor(objects: ObjectFactory, task: SpotBugsTask) : SpotBugsReport(objects, task) { private val stylesheet: Property - private val stylesheetPath: Property init { // the default reportsDir is "$buildDir/reports/spotbugs/${baseName}.html" outputLocation.convention(task.reportsDir.file(task.getBaseName() + ".html")) - stylesheet = objects.property(TextResource::class.java) - stylesheetPath = objects.property(String::class.java) + stylesheet = task.project.objects.property(TextResource::class.java) } override fun toCommandLineOption(): String { - val stylesheet = getStylesheet() - return if (stylesheet == null) { - "-html" - } else { - "-html:" + stylesheet.asFile().absolutePath - } + return stylesheet.map { + "-html:" + it.asFile().absolutePath + }.getOrElse("-html") } - override fun getStylesheet(): TextResource? { - if (stylesheet.isPresent) { - return stylesheet.get() - } else if (stylesheetPath.isPresent) { - return resolve(stylesheetPath.get()) - } - return null - } + override fun getStylesheet(): TextResource? = + stylesheet.orNull - private fun resolve(path: String): TextResource { - val spotbugsJar = task - .project - .configurations - .getByName("spotbugs") - .files { dependency: Dependency -> dependency.group == "com.github.spotbugs" && dependency.name == "spotbugs" } - .stream() - .findFirst() - return if (spotbugsJar.isPresent) { - task - .project - .resources - .text - .fromArchiveEntry(spotbugsJar.get(), path) + private fun resolve(path: String, configuration: Configuration, textResourceFactory: TextResourceFactory): TextResource { + val spotbugsJar = configuration.files { dependency: Dependency -> dependency.group == "com.github.spotbugs" && dependency.name == "spotbugs" } + .find { it.isFile } + return if (spotbugsJar != null) { + textResourceFactory + .fromArchiveEntry(spotbugsJar, path) } else { throw InvalidUserDataException( "The dependency on SpotBugs not found in 'spotbugs' configuration", @@ -78,6 +62,22 @@ abstract class SpotBugsHtmlReport @Inject constructor(objects: ObjectFactory, ta } override fun setStylesheet(path: String?) { - stylesheetPath.set(path) + if (path == null) { + stylesheet.set(null as TextResource) + } else { + val configuration = task + .project + .configurations + .getByName(SpotBugsPlugin.CONFIG_NAME) + val textResourceFactory = task + .project + .resources + .text + stylesheet.set( + task.project.provider { + resolve(path, configuration, textResourceFactory) + }, + ) + } } } From 9de73d7e1b36870c745baad6567438d93a59f0b7 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 13:51:43 +0800 Subject: [PATCH 23/41] test: kotlin lazy assignment prop is enable by default from 8.2 https://docs.gradle.org/8.2/release-notes.html#simple-property-assignment-in-kotlin-dsl-enabled-by-default Signed-off-by: Kengo TODA --- .../github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index f6d0ba29..4835083a 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -16,6 +16,7 @@ package com.github.spotbugs.snom import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.util.GradleVersion +import spock.lang.IgnoreIf import spock.lang.Specification import java.nio.file.Files @@ -56,6 +57,7 @@ public class Foo { """ } + @IgnoreIf({ GradleVersion.version(version) < GradleVersion.version("8.2") }) def "can set params to SpotBugsExtension"() { setup: buildFile << """ From a39266be2d7e4d8c3f47f337957e08ed007b1566 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 13:54:11 +0800 Subject: [PATCH 24/41] test: remove needless imports Signed-off-by: Kengo TODA --- .../com/github/spotbugs/snom/StandardFunctionalTest.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy index ace9ed6c..7e263dda 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy @@ -133,8 +133,6 @@ dependencies { def "can use effort and reportLevel"() { buildFile << """ -import com.github.spotbugs.snom.Confidence -import com.github.spotbugs.snom.Effort spotbugsMain { // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ From 45204956cd1d9a17d6537443b90547305133828b Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 14:05:44 +0800 Subject: [PATCH 25/41] test: skip tests if Gradle version is too old Signed-off-by: Kengo TODA --- .github/workflows/gradle.yml | 2 +- .../github/spotbugs/snom/BasePluginFunctionalTest.groovy | 7 +++++-- .../spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 8e12b60c..208d76e0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - gradle: ['7.6.2', '8.0'] + gradle: ['7.6.2', '8.1', '8.2'] steps: - uses: actions/checkout@v3 with: diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy index 1b6b9b54..771a2ec5 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/BasePluginFunctionalTest.groovy @@ -18,11 +18,14 @@ 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.IgnoreIf import spock.lang.Specification import spock.lang.Unroll -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS - +@IgnoreIf({ + def current = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version) + return GradleVersion.version(current) < GradleVersion.version("8.1") +}) class BasePluginFunctionalTest extends Specification { File rootDir File buildFile diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index 4835083a..2b565862 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -57,7 +57,9 @@ public class Foo { """ } - @IgnoreIf({ GradleVersion.version(version) < GradleVersion.version("8.2") }) + @IgnoreIf({ + def current = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version) + return GradleVersion.version(current) < GradleVersion.version("8.2") }) def "can set params to SpotBugsExtension"() { setup: buildFile << """ From f7a1d2c2515b283b2423ce6db860aa67e62d509f Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 14:21:11 +0800 Subject: [PATCH 26/41] docs: sync changes of interface to documentations Signed-off-by: Kengo TODA --- README.md | 121 +++++++++--------- .../KotlinBuildScriptFunctionalTest.groovy | 4 +- .../com/github/spotbugs/snom/Confidence.kt | 8 +- .../kotlin/com/github/spotbugs/snom/Effort.kt | 8 +- .../github/spotbugs/snom/SpotBugsExtension.kt | 34 ++--- .../github/spotbugs/snom/SpotBugsPlugin.kt | 2 +- 6 files changed, 94 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 6d5249c8..ff7af430 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,45 @@ Refer [the Gradle Plugin portal](https://plugins.gradle.org/plugin/com.github.sp Configure `spotbugs` extension to configure the behaviour of tasks: +```kotlin +// require Gradle 8.2+ +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort +spotbugs { + ignoreFailures = false + showStackTraces = true + showProgress = true + effort = Effort.DEFAULT + reportLevel = Confidence.DEFAULT + visitors = listOf("FindSqlInjection", "SwitchFallthrough") + omitVisitors = listOf("FindNonShortCircuit") + reportsDir = file("$buildDir/spotbugs") + includeFilter = file("include.xml") + excludeFilter = file("exclude.xml") + baselineFile = file("baseline.xml") + onlyAnalyze = listOf("com.foobar.MyClass", "com.foobar.mypkg.*") + maxHeapSize = "1g" + extraArgs = listOf("-nested:false") + jvmArgs = listOf("-Duser.language=ja") +} +``` + +
+with Groovy DSL + ```groovy +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort spotbugs { ignoreFailures = false showStackTraces = true showProgress = true - effort = 'default' - reportLevel = 'default' + + // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 + // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ + effort = Effort.valueOf('DEFAULT') + reportLevel = Confidence.valueOf('DEFAULT') + visitors = [ 'FindSqlInjection', 'SwitchFallthrough' ] omitVisitors = [ 'FindNonShortCircuit' ] reportsDir = file("$buildDir/spotbugs") @@ -48,63 +80,41 @@ spotbugs { jvmArgs = [ '-Duser.language=ja' ] } ``` - -
-with Kotlin DSL - -```kotlin -spotbugs { - ignoreFailures.set(false) - showStackTraces.set(true) - showProgress.set(true) - effort.set(com.github.spotbugs.snom.Effort.DEFAULT) - reportLevel.set(com.github.spotbugs.snom.Confidence.DEFAULT) - visitors.set(listOf("FindSqlInjection", "SwitchFallthrough")) - omitVisitors.set(listOf("FindNonShortCircuit")) - reportsDir.set(file("$buildDir/spotbugs")) - includeFilter.set(file("include.xml")) - excludeFilter.set(file("exclude.xml")) - baselineFile.set(file("baseline.xml")) - onlyAnalyze.set(listOf("com.foobar.MyClass", "com.foobar.mypkg.*")) - maxHeapSize.set("1g") - extraArgs.set(listOf("-nested:false")) - jvmArgs.set(listOf("-Duser.language=ja")) -} -```
Configure `spotbugsPlugin` to apply any SpotBugs plugin: -```groovy +```kotlin dependencies { - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0' + spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0") } ```
-with Kotlin DSL +with Groovy DSL -```kotlin +```groovy dependencies { - spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0") + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0' } ```
Configure `spotbugs` to choose your favorite SpotBugs version: -```groovy +```kotlin dependencies { - spotbugs 'com.github.spotbugs:spotbugs:4.7.1' + spotbugs("com.github.spotbugs:spotbugs:4.7.1") } ``` +
-with Kotlin DSL +with Groovy DSL -```kotlin +```groovy dependencies { - spotbugs("com.github.spotbugs:spotbugs:4.7.1") + spotbugs 'com.github.spotbugs:spotbugs:4.7.1' } ```
@@ -125,6 +135,19 @@ TBU Configure [`SpotBugsTask`](https://spotbugs-gradle-plugin.netlify.com/com/github/spotbugs/snom/spotbugstask) directly, to set task-specific properties. +```kotlin +// require Gradle 8.2+ +tasks.spotbugsMain { + reports.create("html") { + required = true + outputLocation = file("$buildDir/reports/spotbugs.html") + setStylesheet("fancy-hist.xsl") + } +} +``` + +
+with Groovy DSL ```groovy // Example to configure HTML report spotbugsMain { @@ -137,29 +160,18 @@ spotbugsMain { } } ``` - -
-with Kotlin DSL - -```kotlin -tasks.spotbugsMain { - reports.create("html") { - required.set(true) - outputLocation.set(file("$buildDir/reports/spotbugs.html")) - setStylesheet("fancy-hist.xsl") - } -} -```
## SpotBugs version mapping -By default, this Gradle Plugin uses the SpotBugs version listed in this table. +By default, this Gradle Plugin uses the SpotBugs version listed in the following table. You can change SpotBugs version by [the `toolVersion` property of the spotbugs extension](https://spotbugs-gradle-plugin.netlify.com/com/github/spotbugs/snom/spotbugsextension#toolVersion) or the `spotbugs` configuration. | Gradle Plugin | SpotBugs | |--------------:|---------:| +| 6.0.0 | 4.7.3 | +| 5.1.x | 4.7.3 | | 5.0.13 | 4.7.3 | | 5.0.12 | 4.7.2 | | 5.0.9 | 4.7.1 | @@ -167,17 +179,6 @@ You can change SpotBugs version by [the `toolVersion` property of the spotbugs e | 5.0.4 | 4.5.3 | | 5.0.3 | 4.5.2 | | 5.0.2 | 4.5.1 | -| 4.7.10 | 4.5.0 | -| 4.7.8 | 4.4.2 | -| 4.7.5 | 4.4.1 | -| 4.7.3 | 4.4.0 | -| 4.7.2 | 4.3.0 | -| 4.6.1 | 4.2.1 | -| 4.5.0 | 4.1.1 | -| 4.4.4 | 4.0.6 | -| 4.4.2 | 4.0.5 | -| 4.0.7 | 4.0.2 | -| 4.0.0 | 4.0.0 | ### Refer the version in the build script diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index 2b565862..6c092d65 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -144,8 +144,8 @@ dependencies { buildFile << """ tasks.spotbugsMain { reports.create("html") { - required.set(true) - outputLocation.set(file("\$buildDir/reports/spotbugs.html")) + required = true + outputLocation = file("\$buildDir/reports/spotbugs.html") setStylesheet("fancy-hist.xsl") } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index 69c560ec..976e607d 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -25,15 +25,19 @@ import java.util.Optional * * Set via the {@code spotbugs} extension to configure all tasks in your project: * ```kotlin + * // require Gradle 8.2+ + * import com.github.spotbugs.snom.Confidence * spotbugs { - * reportLevel = "low" + * reportLevel = Confidence.LOW * } * ``` * * Or via [SpotBugsTask] to configure the specific task in your project: * ```kotlin + * // require Gradle 8.2+ + * import com.github.spotbugs.snom.Confidence * spotbugsMain { // or name of another task - * reportLevel = "high" + * reportLevel = Confidence.LOW * } * ``` * diff --git a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt index 1aa508a8..95f82675 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -23,15 +23,19 @@ import org.gradle.api.provider.Property * * Set via the `spotbugs` extension to configure all tasks in your project: * ```kotlin + * // require Gradle 8.2+ + * import com.github.spotbugs.snom.Effort * spotbugs { - * effort = "less" + * effort = Effort.LESS * } * ``` * * Or via [SpotBugsTask] to configure the specific task in your project: * ```kotlin + * // require Gradle 8.2+ + * import com.github.spotbugs.snom.Effort * spotbugsMain { // or name of another task - * effort = "max" + * effort = Effort.MAX * } * ``` * diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt index 8d9183ed..206440ad 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsExtension.kt @@ -25,23 +25,25 @@ import org.gradle.api.provider.Property * ### Usage * After you apply the SpotBugs Gradle plugin to project, write extension like below: * ```kotlin + * // require Gradle 8.2+ + * import com.github.spotbugs.snom.Confidence + * import com.github.spotbugs.snom.Effort * spotbugs { - * ignoreFailures.set(false) - * showStackTraces.set(true) - * showProgress.set(false) - * reportLevel.set("default") - * effort.set("default") - * visitors.value(listOf("FindSqlInjection", "SwitchFallthrough")) - * omitVisitors.value(listOf("FindNonShortCircuit")) - * reportsDir.set("$buildDir/reports/spotbugs") - * includeFilter.set("spotbugs-include.xml") - * excludeFilter.set("spotbugs-exclude.xml") - * onlyAnalyze.value(listOf("com.foobar.MyClass", "com.foobar.mypkg.*")) - * projectName.set(name) - * release.set(version) - * extraArgs.value(listOf("-nested:false")) - * jvmArgs.value(listOf("-Duser.language=ja")) - * maxHeapSize.set("512m") + * ignoreFailures = false + * showStackTraces = true + * showProgress = true + * effort = Effort.DEFAULT + * reportLevel = Confidence.DEFAULT + * visitors = listOf("FindSqlInjection", "SwitchFallthrough") + * omitVisitors = listOf("FindNonShortCircuit") + * reportsDir = file("$buildDir/spotbugs") + * includeFilter = file("include.xml") + * excludeFilter = file("exclude.xml") + * baselineFile = file("baseline.xml") + * onlyAnalyze = listOf("com.foobar.MyClass", "com.foobar.mypkg.*") + * maxHeapSize = "1g" + * extraArgs = listOf("-nested:false") + * jvmArgs = listOf("-Duser.language=ja") * } * ``` * diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt index 40ef0c79..f1ce903d 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsPlugin.kt @@ -56,7 +56,7 @@ class SpotBugsPlugin : Plugin { /** * The configuration contains SpotBugs plugin jar files only * - * @see [GitHub issue](https://github.com/spotbugs/spotbugs-gradle-plugin/issues/910) + * @see GitHub issue */ const val PLUGINS_CONFIG_NAME = "spotbugsPlugins" const val SLF4J_CONFIG_NAME = "spotbugsSlf4j" From 8c96471303bcb3316bd6f0546875de4142ea92cd Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 14:33:59 +0800 Subject: [PATCH 27/41] test: add missing imports Signed-off-by: Kengo TODA --- .../com/github/spotbugs/snom/StandardFunctionalTest.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy index 7e263dda..c5fb26ed 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy @@ -133,6 +133,8 @@ dependencies { def "can use effort and reportLevel"() { buildFile << """ +import import com.github.spotbugs.snom.Confidence +import import com.github.spotbugs.snom.Effort spotbugsMain { // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ From 0091772fe80f455930bdd6e4596342cb1255aef2 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 14:57:08 +0800 Subject: [PATCH 28/41] test: fix test cases Signed-off-by: Kengo TODA --- .../spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy | 3 +++ .../com/github/spotbugs/snom/StandardFunctionalTest.groovy | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index 6c092d65..e6ade5b4 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -140,6 +140,9 @@ dependencies { result.output.contains("SpotBugs 4.0.0-beta4") || result.output.contains("spotbugs-4.0.0-beta4.jar") } + @IgnoreIf({ + def current = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version) + return GradleVersion.version(current) < GradleVersion.version("8.2") }) def "can generate spotbugs.html in configured outputLocation"() { buildFile << """ tasks.spotbugsMain { diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy index c5fb26ed..ace9ed6c 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/StandardFunctionalTest.groovy @@ -133,8 +133,8 @@ dependencies { def "can use effort and reportLevel"() { buildFile << """ -import import com.github.spotbugs.snom.Confidence -import import com.github.spotbugs.snom.Effort +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.Effort spotbugsMain { // https://discuss.kotlinlang.org/t/bug-cannot-use-kotlin-enum-from-groovy/1521 // https://touk.pl/blog/2018/05/28/testing-kotlin-with-spock-part-2-enum-with-instance-method/ From 8b7218b09cea89749768865b4c9d51f365d9665d Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 15:17:31 +0800 Subject: [PATCH 29/41] chore: make SpotBugsTask configuration-cache safe Signed-off-by: Kengo TODA --- .../spotbugs/snom/internal/SpotBugsRunner.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt index df7688b2..d8bc4ac4 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt @@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory import java.io.File import java.io.IOException import java.lang.Boolean -import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.StandardOpenOption import java.util.* @@ -104,7 +103,7 @@ abstract class SpotBugsRunner { args.add("-release") args.add(task.release.get()) val file = task.analyseClassFile.asFile.get() - generateFile(task.classes ?: task.project.layout.files(), task.analyseClassFile.asFile.get()) + task.classes?.let { generateFile(it, file) } args.add("-analyzeFromFile") args.add(file.absolutePath) args.addAll(task.extraArgs.getOrElse(emptyList())) @@ -143,13 +142,13 @@ abstract class SpotBugsRunner { private fun generateFile(files: FileCollection, file: File) { try { - val lines = - Iterable { - files.filter { obj: File -> obj.exists() } - .files.stream().map { obj: File -> obj.absolutePath } - .iterator() - } - Files.write(file.toPath(), lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE) + file.bufferedWriter().use { writer -> + files.filter(File::exists) + .forEach { + writer.write(it.absolutePath) + writer.newLine() + } + } } catch (e: IOException) { throw GradleException("Fail to generate the text file to list target .class files", e) } From c378771188a025bb89e1c31a05172901b358491d Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 15:25:28 +0800 Subject: [PATCH 30/41] build: introduce Dokka Signed-off-by: Kengo TODA --- .github/workflows/dokka.yml | 38 +++++++++++++++++++++++++++++++++++++ build.gradle.kts | 1 + 2 files changed, 39 insertions(+) create mode 100644 .github/workflows/dokka.yml diff --git a/.github/workflows/dokka.yml b/.github/workflows/dokka.yml new file mode 100644 index 00000000..9deba03d --- /dev/null +++ b/.github/workflows/dokka.yml @@ -0,0 +1,38 @@ +name: Javadoc +on: + push: + branches: + - master + +jobs: + javadoc: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + persist-credentials: false + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 11 + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + cache: npm + - name: Generate Groovydoc + uses: gradle/gradle-build-action@v2 + with: + arguments: dokkaHtml + - name: Prepare to Deploy + run: | + npm ci + rm -f .git/hooks/commit-msg + - name: Deploy + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: build/dokka/html/ \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 4b7b0a44..d0185d9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ plugins { jacoco signing kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.8.20" id("com.github.spotbugs.gradle-plugin") id("com.github.spotbugs.plugin-publish") id("com.github.spotbugs.test") From 0a4db3f07eda27ffbb643c795def1f19f9833931 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 15:25:52 +0800 Subject: [PATCH 31/41] ci: deploy prerelease from the v6 branch Signed-off-by: Kengo TODA --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index f57c2781..7fe11477 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,10 @@ "branches": [ { "name": "master" + }, + { + "name": "v6", + "prerelease": true } ], "plugins": [ From 71777befd4ccb6efb8331d7cedf9bb9d84c8464d Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 17:18:35 +0800 Subject: [PATCH 32/41] test: skip cachability check if NamedDomainObjectContainer is not cache-able Signed-off-by: Kengo TODA --- .../snom/CacheabilityFunctionalTest.groovy | 4 +++ .../com/github/spotbugs/snom/SpotBugsTask.kt | 28 +++++++------------ .../spotbugs/snom/internal/SpotBugsRunner.kt | 4 +-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy index 10fdaf2d..6c8678e0 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/CacheabilityFunctionalTest.groovy @@ -17,6 +17,7 @@ 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.IgnoreIf import spock.lang.Specification import java.nio.file.Files @@ -25,6 +26,9 @@ class CacheabilityFunctionalTest extends Specification { /** * @see GitHub Issues */ + @IgnoreIf({ + def current = System.getProperty('snom.test.functional.gradle', GradleVersion.current().version) + return GradleVersion.version(current) < GradleVersion.version("8.1") }) def 'spotbugsMain task runs with configuration cache'() { given: def buildDir = Files.createTempDirectory(null).toFile() diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 34c31023..13b75abb 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -154,8 +154,7 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { * * @see SpotBugsReport */ - @get:Internal - val reports: NamedDomainObjectContainer + private val reports: NamedDomainObjectContainer /** * Property to set the filter file to limit which bug should be reported. @@ -291,10 +290,10 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { private var enableHybridWorker: Boolean = true @get:Internal - abstract val pluginJarFiles: ListProperty + abstract val pluginJarFiles: ConfigurableFileCollection @get:Internal - abstract val spotbugsClasspath: ListProperty + abstract val spotbugsClasspath: ConfigurableFileCollection @get:Nested @get:Optional @@ -363,23 +362,16 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { analyseClassFile.set(project.buildDir.resolve(this.name + "-analyse-class-file.txt")) val pluginConfiguration = project.configurations.getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME) - pluginJarFiles.convention( - pluginConfiguration.files.map { file -> - project.objects.fileProperty().apply { - set(file) - } - }, + pluginJarFiles.from( + project.provider { pluginConfiguration.files } ) val configuration = project.configurations.getByName(SpotBugsPlugin.CONFIG_NAME) val spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME) - spotbugsClasspath.convention( - project.provider { - (spotbugsSlf4j.files + configuration.files).map { file -> - project.objects.fileProperty().apply { - set(file) - } - } - }, + spotbugsClasspath.from( + project.layout.files( + project.provider { spotbugsSlf4j.files }, + project.provider { configuration.files } + ) ) } diff --git a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt index d8bc4ac4..eb726f5e 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/internal/SpotBugsRunner.kt @@ -35,9 +35,9 @@ abstract class SpotBugsRunner { protected fun buildArguments(task: SpotBugsTask): List { val args: MutableList = ArrayList() val plugins = task.pluginJarFiles - if (plugins.isPresent) { + if (!plugins.isEmpty) { args.add("-pluginList") - args.add(join(plugins.get().map { it.get().asFile })) + args.add(join(plugins.files)) } args.add("-timestampNow") if (!task.auxClassPaths.isEmpty) { From 72f44640fb2a237919357b94e808d7e5a8d682c8 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 17:43:50 +0800 Subject: [PATCH 33/41] chore: make the reports field public Signed-off-by: Kengo TODA --- src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 13b75abb..14e3338e 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -154,7 +154,8 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { * * @see SpotBugsReport */ - private val reports: NamedDomainObjectContainer + @get:Internal + val reports: NamedDomainObjectContainer /** * Property to set the filter file to limit which bug should be reported. From 3221337e2df673aa0723c0c6413a3db7c0286af2 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 17:54:44 +0800 Subject: [PATCH 34/41] chore: apply spotless Signed-off-by: Kengo TODA --- src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 14e3338e..1f79b68a 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -364,15 +364,15 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { val pluginConfiguration = project.configurations.getByName(SpotBugsPlugin.PLUGINS_CONFIG_NAME) pluginJarFiles.from( - project.provider { pluginConfiguration.files } + project.provider { pluginConfiguration.files }, ) val configuration = project.configurations.getByName(SpotBugsPlugin.CONFIG_NAME) val spotbugsSlf4j = project.configurations.getByName(SpotBugsPlugin.SLF4J_CONFIG_NAME) spotbugsClasspath.from( project.layout.files( project.provider { spotbugsSlf4j.files }, - project.provider { configuration.files } - ) + project.provider { configuration.files }, + ), ) } From 71385ea6e3ecd4e9e3515fbe370c9b546840fd39 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 18:08:56 +0800 Subject: [PATCH 35/41] ci: check bytecode version built by Kotlin compiler Signed-off-by: Kengo TODA --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 208d76e0..1a096403 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -39,7 +39,7 @@ jobs: arguments: build -Dsnom.test.functional.gradle=${{ matrix.gradle }} --scan - run: | echo Verifying the java version used in class files... - cd build/classes/groovy/main + cd build/classes/kotlin/main javap -v com.github.spotbugs.snom.SpotBugsPlugin | grep -q 'major version: 52' - name: Run Semantic Release run: | From 3863ab07aac5c4b04de8520e453b4096def276a4 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 19:04:38 +0800 Subject: [PATCH 36/41] chore: simplify path to import for prop-assign Signed-off-by: Kengo TODA --- .../snom/KotlinBuildScriptFunctionalTest.groovy | 3 +-- .../kotlin/com/github/spotbugs/snom/Confidence.kt | 6 ------ src/main/kotlin/com/github/spotbugs/snom/Effort.kt | 7 ------- .../kotlin/com/github/spotbugs/snom/Extensions.kt | 13 +++++++++++++ 4 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/com/github/spotbugs/snom/Extensions.kt diff --git a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy index e6ade5b4..f388f2a6 100644 --- a/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy +++ b/src/functionalTest/groovy/com/github/spotbugs/snom/KotlinBuildScriptFunctionalTest.groovy @@ -32,8 +32,7 @@ class KotlinBuildScriptFunctionalTest extends Specification { rootDir = Files.createTempDirectory("KotlinBuildScriptFunctionalTest").toFile() buildFile = new File(rootDir, 'build.gradle.kts') buildFile << """ -import com.github.spotbugs.snom.Confidence.Companion.assign -import com.github.spotbugs.snom.Effort.Companion.assign +import com.github.spotbugs.snom.assign plugins { `java` id("com.github.spotbugs") diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index 976e607d..9d0d85f9 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -70,10 +70,4 @@ enum class Confidence { @Internal("This is internally used property so no need to refer to judge out-of-date or not.") abstract fun toCommandLineOption(): Optional - - companion object { - fun Property.assign(string: String) { - set(Confidence.valueOf(string)) - } - } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt index 95f82675..2a1950d8 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -65,11 +65,4 @@ enum class Effort { * of Referenced Classes. */ MAX, - - ; - companion object { - fun Property.assign(string: String) { - set(Effort.valueOf(string)) - } - } } diff --git a/src/main/kotlin/com/github/spotbugs/snom/Extensions.kt b/src/main/kotlin/com/github/spotbugs/snom/Extensions.kt new file mode 100644 index 00000000..94c68701 --- /dev/null +++ b/src/main/kotlin/com/github/spotbugs/snom/Extensions.kt @@ -0,0 +1,13 @@ +package com.github.spotbugs.snom + +import org.gradle.api.provider.Property + +@JvmName("assignConfidence") +fun Property.assign(string: String) { + set(Confidence.valueOf(string)) +} + +@JvmName("assignEffort") +fun Property.assign(string: String) { + set(Effort.valueOf(string)) +} From 699c54f550e07705344c620027d3d352205ebdb4 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 19:15:08 +0800 Subject: [PATCH 37/41] build: remove completed TODO comment Signed-off-by: Kengo TODA --- .../spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts index 9c192be9..93b2f870 100644 --- a/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts +++ b/buildSrc/src/main/kotlin/com/github/spotbugs/com.github.spotbugs.gradle-plugin.gradle.kts @@ -2,8 +2,6 @@ plugins { id("com.diffplug.spotless") } -// TODO introduce KDoc - spotless { kotlin { ktlint() From 0d0b702d270cd3559b9833bbbb82fa320592e6fa Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Fri, 11 Aug 2023 19:38:12 +0800 Subject: [PATCH 38/41] chore: format code by spotless Signed-off-by: Kengo TODA --- src/main/kotlin/com/github/spotbugs/snom/Confidence.kt | 1 - src/main/kotlin/com/github/spotbugs/snom/Effort.kt | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt index 9d0d85f9..f86d86be 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Confidence.kt @@ -13,7 +13,6 @@ */ package com.github.spotbugs.snom -import org.gradle.api.provider.Property import org.gradle.api.tasks.Internal import java.util.Optional diff --git a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt index 2a1950d8..92758e3a 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/Effort.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/Effort.kt @@ -13,8 +13,6 @@ */ package com.github.spotbugs.snom -import org.gradle.api.provider.Property - /** * The [Effort] is configuration to adjust SpotBugs detectors. Use lower effort to reduce * computation cost. From 57bec8233bb09bf161197458609b445165b21473 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sat, 12 Aug 2023 12:54:40 +0800 Subject: [PATCH 39/41] build: replace SpotBugs with detekt Signed-off-by: Kengo TODA --- .github/workflows/codeql.yml | 13 +++++++------ .gitignore | 1 - build.gradle.kts | 11 +++-------- detekt-baseline.xml | 34 ++++++++++++++++++++++++++++++++++ gradle.properties | 5 +++++ 5 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 detekt-baseline.xml create mode 100644 gradle.properties diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2c49fff7..05eb4bd7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -5,9 +5,11 @@ on: push: branches: - master + - v6 pull_request: branches: - master + - v6 jobs: CodeQL-Build: @@ -29,15 +31,14 @@ jobs: with: distribution: 'temurin' java-version: 11 - cache: gradle - name: Gradle Wrapper Validation uses: gradle/wrapper-validation-action@v1 - - - name: Build - run: | - ./gradlew spotbugsMain + - name: Build with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: detekt --scan - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v1 with: # Path to SARIF file relative to the root of the repository - sarif_file: build/reports/spotbugs/main.sarif + sarif_file: build/reports/detekt/detekt.sarif diff --git a/.gitignore b/.gitignore index 3674b13e..39224459 100644 --- a/.gitignore +++ b/.gitignore @@ -311,7 +311,6 @@ $RECYCLE.BIN/ ### Gradle ### .gradle build/ -gradle.properties # Ignore Gradle GUI config gradle-app.setting diff --git a/build.gradle.kts b/build.gradle.kts index d0185d9b..8db6e0a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ plugins { id("com.github.spotbugs.plugin-publish") id("com.github.spotbugs.test") id("org.sonarqube") - id("com.github.spotbugs") version "5.1.1" + id("io.gitlab.arturbosch.detekt") version "1.23.1" } java { @@ -51,15 +51,10 @@ signing { } } -spotbugs { - ignoreFailures.set(true) -} tasks { - named("spotbugsMain") { + named("detekt") { reports { - register("sarif") { - required.set(true) - } + sarif.required.set(true) } } val processVersionFile by registering(WriteProperties::class) { diff --git a/detekt-baseline.xml b/detekt-baseline.xml new file mode 100644 index 00000000..d0a0ae96 --- /dev/null +++ b/detekt-baseline.xml @@ -0,0 +1,34 @@ + + + + + CyclomaticComplexMethod:SpotBugsRunner.kt$SpotBugsRunner$protected fun buildArguments(task: SpotBugsTask): List<String> + ImplicitDefaultLocale:SpotBugsBasePlugin.kt$SpotBugsBasePlugin$String.format( "Gradle version %s is unsupported. Please use %s or later.", version, SUPPORTED_VERSION, ) + ImplicitDefaultLocale:SpotBugsReport.kt$SpotBugsReport$String.format( "stylesheet property is not available in the %s type report", name, ) + ImplicitDefaultLocale:SpotBugsReport.kt$SpotBugsReport$String.format("%s type report generated by the task %s", name, task.path) + ImplicitDefaultLocale:SpotBugsTask.kt$SpotBugsTask$String.format("%s (%s)", p, name) + ImplicitDefaultLocale:SpotBugsTextReport.kt$SpotBugsTextReport$String.format("Text type report generated by the task %s", task.path) + ImplicitDefaultLocale:SpotBugsXmlReport.kt$SpotBugsXmlReport$String.format("XML type report generated by the task %s", task.path) + LongMethod:SpotBugsRunner.kt$SpotBugsRunner$protected fun buildArguments(task: SpotBugsTask): List<String> + MagicNumber:SemanticVersion.kt$SemanticVersion$10 + MagicNumber:SemanticVersion.kt$SemanticVersion$3 + MaxLineLength:SemanticVersion.kt$SemanticVersion.Companion$"^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + MaxLineLength:SpotBugsBasePlugin.kt$SpotBugsBasePlugin.Companion$* + MaxLineLength:SpotBugsExtension.kt$SpotBugsExtension$* + MaxLineLength:SpotBugsExtension.kt$SpotBugsExtension$* Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. + MaxLineLength:SpotBugsExtension.kt$SpotBugsExtension$* Property to specify the name of project. Some reporting formats use this property. Default value is the name of your Gradle project. + MaxLineLength:SpotBugsHtmlReport.kt$SpotBugsHtmlReport$private + MaxLineLength:SpotBugsHtmlReport.kt$SpotBugsHtmlReport$val + MaxLineLength:SpotBugsRunnerForHybrid.kt$SpotBugsRunnerForHybrid.Companion$* + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$* + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$* Property to set the directory to generate report files. Default is {@code "$buildDir/reports/spotbugs/$taskName"}. + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$* Property to specify the extra arguments for JVM process. Default value is empty so JVM process will get no extra argument. + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$* Property to specify the extra arguments for SpotBugs. Default value is empty so SpotBugs will get no extra argument. + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$* Property to specify the release identifier of project. Some reporting formats use this property. Default value is the version of your Gradle project. + MaxLineLength:SpotBugsTask.kt$SpotBugsTask$fun + NestedBlockDepth:SpotBugsRunnerForWorker.kt$SpotBugsRunnerForWorker.SpotBugsExecutor$override fun execute() + TooGenericExceptionCaught:SpotBugsRunner.kt$SpotBugsRunner$e: Exception + TooGenericExceptionCaught:SpotBugsRunnerForWorker.kt$SpotBugsRunnerForWorker.SpotBugsExecutor$e: Exception + TooManyFunctions:SpotBugsReport.kt$SpotBugsReport : SingleFileReportCustomizableHtmlReport + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..70a7e67b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +detekt.use.worker.api=true +kotlin.code.style=official +org.gradle.caching=true +org.gradle.configureondemand=true +org.gradle.parallel=true From 7381fd67fc9f1d0e78fe86181a199a5f96641c57 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sat, 12 Aug 2023 13:16:25 +0800 Subject: [PATCH 40/41] docs: explain the motivation to keep legacy APIs Signed-off-by: Kengo TODA --- src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt index 1f79b68a..83149c8a 100644 --- a/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt +++ b/src/main/kotlin/com/github/spotbugs/snom/SpotBugsTask.kt @@ -150,7 +150,7 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { abstract val reportsDir: DirectoryProperty /** - * Property to specify which report you need. + * Property defined to keep the backward compatibility with [org.gradle.api.reporting.Reporting] interface. * * @see SpotBugsReport */ @@ -400,10 +400,16 @@ abstract class SpotBugsTask : DefaultTask(), VerificationTask { } } + /** + * Function defined to keep the backward compatibility with [org.gradle.api.reporting.Reporting] interface. + */ fun reports(closure: Closure>): NamedDomainObjectContainer { return reports(ClosureBackedAction(closure)) } + /** + * Function defined to keep the backward compatibility with [org.gradle.api.reporting.Reporting] interface. + */ fun reports( configureAction: Action>, ): NamedDomainObjectContainer { From 9ccf193d7d09f012e794db90cdfb5b023e1f35c5 Mon Sep 17 00:00:00 2001 From: Kengo TODA Date: Sun, 13 Aug 2023 09:14:02 +0800 Subject: [PATCH 41/41] ci: mark the beta branch Signed-off-by: Kengo TODA --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7fe11477..4785c315 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "name": "master" }, { + "channel": "beta", "name": "v6", "prerelease": true }