Custom PMD rule with Gradle - gradle

I would like to use the gradle PMD plugin in an enterprise project which is built with gradle.
I have a pmd_rules.xml file which already works, but I can't add own java rules (I get a class not found exception). I followed the tutorial on it's website.
Where do I have to put my own rules so they get recognized by gradle and PMD? Has somebody already done something like that?
pmd.gradle:
apply from: rootProject.file("core/modules.gradle"), to : ext
if(project.name in (modules["modules"] +modules["modules"])){
apply plugin: 'pmd'
pmd {
ignoreFailures = true
ruleSetFiles = rootProject.files("../repo/pmd_rules.xml")
sourceSets = [sourceSets.main,sourceSets.test]
targetJdk = org.gradle.api.plugins.quality.TargetJdk.VERSION_1_7
ruleSets = []
toolVersion = "5.0.5"
}
}

tasks.withType(Pmd) {
pmdClasspath += file("path/to/rules.jar")
}

For latest Gradle pmd plugin you need to do the following things:
Add your library with pmd rules to plugin classpath
https://github.com/dgroup/arch4u-pmd#gradle-buildgradle
dependencies {
...
pmd "io.github.groupid:libid:versionid"
pmd "commons-io:commons-io:2.11.0"
...
}
Include ruleset from your lib
https://github.com/dgroup/arch4u-pmd#include-arch4u-pmd-rules-into-your-existing-custom-ruleset
<?xml version="1.0"?>
<ruleset name="pmd ruleset with your rules">
...
<rule ref="io/github/path-to-ruleset-from-your-lib.xml"/>
...
</ruleset>
or define particular rules in your
https://github.com/dgroup/arch4u-pmd#reconfigure-a-rule
<?xml version="1.0"?>
<ruleset name="pmd ruleset with your rules">
...
<!-- 2. Reconfigure rule with expected property -->
<rule name="RuleFromYourLibrary" class="io.github.RuleFromYourLibrary">
<priority>3</priority>
<properties>
...
</properties>
</rule>
...
</ruleset>

Related

Gradle DSL method not found: 'destination()' after update to Gradle 5.2.1

After updating to Gradle 5.2.1 my build is failing with this error:
Gradle DSL method not found: 'destination()'
I figured out that this error has something todo with my analysis.gradle
My analysis.gradle looks like that
apply plugin: 'checkstyle'
apply plugin: 'pmd'
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.7.201606060606"
}
check.dependsOn 'checkstyle', 'pmd', 'lint'
task checkstyle(type: Checkstyle) {
println "----- checkstyle -----"
configFile file(projectDir.getAbsolutePath() + '/analysis/checkstyle-ruleset.xml')
source 'src'
source '../domain/src'
source '../util/src'
include '**/*.java'
exclude '**/gen/**'
exclude '**/java-gen/**'
exclude '**/androidTest/**'
exclude '**/test/**'
ignoreFailures = true
classpath = files()
reports {
xml {
destination buildDir.absolutePath + "/outputs/reports/checkstyle_report.xml"
}
}
}
I think I have to replace the destination flag but I have no idea how to replace it.
Before Gradle 5.0 the method setDestination(Object file) was already deprecated, see here : setDestination(Object file)
In Gradle 5.x this method has been removed, you must now use setDestination(File file) which takes a File parameter (see setDestination(File file) )
So you need to change your code into:
reports {
xml {
destination file("$buildDir/outputs/reports/checkstyle_report.xml")
}
}
All adjustment was done in my quality.gradle. Check config folder for quality.gradle file and change all usage of
destination "$reportsDir/pmd/pmd.xml"
to
destination file("$reportsDir/pmd/pmd.html")

PMD exclude-pattern with gradle

I'm attempting to create some exclude patterns for a PMD task in Gradle.
My task is generated in the next way:
/* Allows generation of pmd config */
allprojects {
apply plugin: 'pmd'
}
gradle.projectsEvaluated {
subprojects.each() { project ->
if (project.hasProperty('android')) {
project.task("runPmd", type: Pmd) {
description "Run pmd"
group 'verification'
source = fileTree("${project.projectDir}/src/main/java")
ruleSetFiles = files("${project.rootDir}/build-tools/pmd.xml")
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
}
}
}
And the ruleSet is:
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="MyCompany ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>
MyCompany ruleset for Android PMD
</description>
<exclude-pattern>.*/org/jivesoftware/.*</exclude-pattern>
<exclude-pattern>.*/net/java/.*</exclude-pattern>
...rules...
</ruleset>
But in my reports, I'm getting:
Am I doing something wrong? Checking this answer seems that I'm defining the exclude-pattern right, but pmd is analyzing those files.
I was running into the same issue and adding an empty ruleSets [] property seemed to fix it for me.
Make sure to define the rules that you actually want to apply in your ruleset file - i.e. Move them from the ruleSet property block to the file (if you had any there).
This is what my task generation looks like:
// PMD
afterEvaluate {
def variants = plugins.hasPlugin('com.android.application') ?
android.applicationVariants : android.libraryVariants
variants.each { variant ->
def task = tasks.create("pmd${variant.name.capitalize()}", Pmd)
task.group = 'verification'
task.description = "Run PMD for the ${variant.description}."
task.ruleSetFiles = files("pmd-ruleset.xml")
task.ruleSets = []
task.reports {
xml.enabled = false
html.enabled = true
}
def variantCompile = variant.javaCompile
task.source = variantCompile.source
task.dependsOn(variantCompile)
tasks.getByName('check').dependsOn(task)
}
}
I got the hint from this thread:
http://sourceforge.net/p/pmd/discussion/188193/thread/6e9c6017/
Add below snippet in your build.gradle, you can exclude Classes and packages also
pmd {
sourceSets = [ project.sourceSets.main ]
ruleSetFiles = rootProject.files("codequality/pmd-ruleset.xml")
ruleSets = []
pmdMain {
excludes = [
'**/Application.*',
'**/jivesoftware/.*'
]
}
}
For my ruleset.xml for Gradle pmd plugin the following command works:
<ruleset name="Custom Rules">
<!-- Exclude generated open api classes -->
<exclude-pattern>.*/src/main/java/com/tbd/tbd/tbd/.*</exclude-pattern>
<exclude-pattern>.*/src/main/java/com/tbd/tbd/tbd/tbd/.*</exclude-pattern>
...
</ruleset>
Could you please try the command below?
...
<exclude-pattern>.*/src/main/java/net/java/.*</exclude-pattern>
...

Using "excludes" config in Findbugs and Checkstyle plugin in Gradle

I have the following Gradle build file: https://github.com/markuswustenberg/jsense/blob/a796055f984ec309db3cc0f3e8340cbccac36e4e/jsense-protobuf/build.gradle which includes:
checkstyle {
// TODO The excludes are not working, ignore failures for now
//excludes '**/org/jsense/serialize/protobuf/gen/**'
ignoreFailures = true
showViolations = false
}
findbugs {
// TODO The excludes are not working, ignore failures for now
//excludes '**/org/jsense/serialize/protobuf/gen/**'
ignoreFailures = true
}
As you can see, I'm trying to exclude auto-generated code in the package org.jsense.serialize.protobuf.gen. I cannot figure out the format of the strings given to the excludes parameter, and the documentation isn't of much help: http://www.gradle.org/docs/1.10/dsl/org.gradle.api.plugins.quality.FindBugs.html#org.gradle.api.plugins.quality.FindBugs:excludes (it just says "The set of exclude patterns.").
So my question is: How should the excludes pattern strings be formatted for both the Findbugs and Checkstyle plugins?
I'm running Gradle 1.10.
Thanks!
EDIT 1: I got the Checkstyle exclude working with the following:
tasks.withType(Checkstyle) {
exclude '**/org/jsense/serialize/protobuf/gen/**'
}
However, using the exact same exclude on the Findbugs plugin doesn't work:
tasks.withType(FindBugs) {
exclude '**/org/jsense/serialize/protobuf/gen/*'
}
EDIT 2: The accepted answer works, and so does using an XML file and filtering on that, like so:
findbugs {
excludeFilter = file("$projectDir/config/findbugs/excludeFilter.xml")
}
and
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Package name="org.jsense.serialize.protobuf.gen"/>
</Match>
</FindBugsFilter>
EDIT 3: This works great, and no XML file is needed:
def excludePattern = 'org/jsense/serialize/protobuf/gen/'
def excludePatternAntStyle = '**/' + excludePattern + '*'
tasks.withType(FindBugs) {
classes = classes.filter {
!it.path.contains(excludePattern)
}
}
tasks.withType(Checkstyle) {
exclude excludePatternAntStyle
}
tasks.withType(Pmd) {
exclude excludePatternAntStyle
}
SourceTask#exclude filters source files. However, FindBugs primarily operates on class files, which you'll have to filter as well. Try something like:
tasks.withType(FindBugs) {
exclude '**/org/jsense/serialize/protobuf/gen/*'
classes = classes.filter {
!it.path.contains(new File("org/jsense/serialize/protobuf/gen/").path)
}
}
PS: It could be that filtering source files makes no difference (and therefore isn't necessary) in case of FindBugs. (I haven't tried though.)
If someone looking for a modern day solution:
For checkstyle, you can use something like this in build.gradle:
checkstyleMain.exclude '**/org/jsense/serialize/protobuf/gen/**'
If you want to exclude more than one path
solution 1:
checkstyleMain.exclude '**/org/jsense/serialize/protobuf/gen/**'
checkstyleMain.exclude '**/org/example/some/random/path/**'
solution 2:
checkstyleMain {
setExcludes(new HashSet(['**/org/jsense/serialize/protobuf/gen/**', '**/org/example/some/random/path/**']))
}

FindBugs does not exclude the filtered patterns with Gradle

I want to setup the FindBugsExtension to Gradle. It works but I'm unable to exclude specific patterns with the excludeFilter option.
I have the following gradle FindBugs definition:
findbugs {
toolVersion = "2.0.1"
reportsDir = file("$project.buildDir/findbugsReports")
effort = "max"
reportLevel = "high"
excludeFilter = file("$rootProject.projectDir/config/findbugs/excludeFilter.xml")
}
In the excludeFilter.xml I have the following exclude defined:
<FindBugsFilter>
<Match>
<Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE"/>
</Match>
</FindBugsFilter>
But when I run gradle findBugsMain it fails because it could find FindBugs errors:
<BugCollection version="2.0.1" sequence="0" timestamp="1348055542169" analysisTimestamp="1348055545581" release="">
<!-- ... -->
<BugInstance type="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" priority="2" abbrev="NP" category="STYLE">
<!-- ... -->
Okay, I've found the solution from here.
Opposed to the documentation the excludeFilter needs to be defined per task due to a bug in Gradle version 1.2.
So the full configuration would look like this for 1.2:
findbugs {
toolVersion = "2.0.1"
reportsDir = file("$project.buildDir/findbugsReports")
effort = "max"
reportLevel = "high"
}
tasks.withType(FindBugs) {
excludeFilter = file("$rootProject.projectDir/config/findbugs/excludeFilter.xml")
}

gradle checkstyle error:Expected file collection to contain exactly one file, however, it contains 14 files

I am using Java 8 with Gradle and trying to add the Google checkstyle rules into the build, but what I get is this error:
"Expected file collection to contain exactly one file, however, it contains 14 files."
My configuration is:
apply plugin: 'checkstyle'
configurations {
checkstyleConfig
}
def versions = [
checkstyle: '8.8',
]
dependencies {
checkstyleConfig "com.puppycrawl.tools:checkstyle:${versions.checkstyle}"
}
checkstyle {
toolVersion = "${versions.checkstyle}"
config = resources.text.fromArchiveEntry(configurations.checkstyleConfig, 'google_checks.xml')
}
The issue here is that configurations.checkstyleConfig includes multiple JAR files: com.puppycrawl.tools:checkstyle, as well as all of its transitive dependencies. Debugging the issue locally, I see that these dependencies are being included:
antlr:antlr:2.7.7
com.google.code.findbugs:jsr305:1.3.9
com.google.errorprone:error_prone_annotations:2.1.3
com.google.guava:guava:23.6-jre
com.google.j2objc:j2objc-annotations:1.1
com.puppycrawl.tools:checkstyle:8.8
commons-beanutils:commons-beanutils:1.9.3
commons-cli:commons-cli:1.4
commons-collections:commons-collections:3.2.2
commons-logging:commons-logging:1.2
net.sf.saxon:Saxon-HE:9.8.0-7
org.antlr:antlr4-runtime:4.7.1
org.checkerframework:checker-compat-qual:2.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.14
The fix for this is fortunately very simple. All you need to do is exclude the transitive dependencies from the Checkstyle dependency, and the rest of your script will work the way you want it to:
dependencies {
checkstyleConfig("com.puppycrawl.tools:checkstyle:${versions.checkstyle}") { transitive = false }
}
btw, for future reference, there's no need to add a new configuration to use this, it's just a matter of filtering the checkstyle dependency from the existing configuration used by the plgin.
This is the config I use:
checkstyle {
config = resources.text.fromArchiveEntry(
configurations.checkstyle.find { it.name.contains('checkstyle') },
'google_checks.xml'
)
}
For anyone interested, this is the Kotlin DSL variant of the config from #thiago answer:
checkstyle {
config = resources.text.fromArchiveEntry(
configurations.checkstyle.get().find { it.name.contains("checkstyle") }!!,
"google_checks.xml"
)
}

Resources