Exclude classes from JaCoCo coverage report on multiple levels - gradle

I have a multi-module gradle project configured with convention plugins. I'd like to be able to exclude classes from the code coverage report both in the convention plugin and in the module level build script.
Here are the relevant files from the project:
buildSrc/src/main/kotlin/mapstruct-conventions.gradle.kts
// ...other configurations here
tasks.jacocoTestReport {
classDirectories.setFrom(
files(
classDirectories.files.map {
fileTree(it).apply {
exclude("**/*MapperImpl*")
}
}
)
)
}
database-jooq/build.gradle.kts
plugins {
id("mapstruct-conventions")
}
tasks.jacocoTestReport {
classDirectories.setFrom(
files(
classDirectories.files.map {
fileTree(it).apply {
exclude("**/jooq/generated/**")
}
}
)
)
}
For some reason the two exclusions seem to override each other. If both are present, then only the one in the convention plugin will be applied. If I remove the exclusion from the convention plugin script, then the exclusion in the database-jooq module works fine.
I tried to define them in an afterEvaluate block as suggested in some answers but I get the following exception:
Caused by: org.gradle.api.internal.AbstractMutationGuard$IllegalMutationException: Project#afterEvaluate(Action) on project ':database-jooq' cannot be executed in the current context.
Is there a way to define JaCoCo exclusions in different places and apply them together?

Related

How to import code from outside into gradle build file?

My build files are large and messy, making them difficult to read. like below:
plugins {
...
id "com.google.protobuf" version "0.8.17"
}
dependencies {
implementation "androidx.datastore:datastore-core:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
...
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
I want to define the above code into an external file, and then introduce it into the build file, how should I do it?
According to the Gradle documentation, as of now it is not possible to move the plugin block to other file than the project’s build script or settings.gradle file.
For the other sections, let's say dependencies or protobuf, then you can move these sections on a separate gradle files and import them by using the following statement:
apply from: "${project.rootDir}/your-gradle-file"
Of course the path of your-gradle-file should be adjusted according to the project's folder structure you decide.
If you want to split the dependencies into multiple gradle file you can do the following:
on your main gradle file:
dependencies {
apply from: "${project.rootDir}/depsGroup1.gradle"
apply from: "${project.rootDir}/depsGroup2.gradle"
}
and within each depsGroup file:
dependencies {
implementation xyz
}

Configure plugin in separate file using Kotlin DSL

to differenciate diferent plugins configurations, I use separate files.
For example:
./build.gradle.kts
./detekt.gradle.kts
./settings.gradle.kts
./module1
./module2
...
In the root build.gradle.kts I have this:
plugins {
id("io.gitlab.arturbosch.detekt") version DependencyVersion.Detekt
}
buildscript {
dependencies {
classpath(io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.1.1)
}
}
And to configure it I go to the detekt.gradle.kts and put:
apply(plugin = "io.gitlab.arturbosch.detekt")
detekt {
// configure
}
But detekt lambda is not found. Also tried with:
apply(plugin = "io.gitlab.arturbosch.detekt")
configure<io.gitlab.arturbosch.detekt.Detekt> {
// configure
}
But it doesn't find .Detekt.
With JaCoCo I haven't got any problems using the second approach, but it doesn't work with Detekt or SonarQube.
How can I configure plugins in a separate file?
Thanks.
Try something like below. I have declared a plugin "sonarqube" in my main gradle. I then apply the file sonar.gradle.kts towards the end of the build.gradle.kts file.
build.gradle.kts:
plugins {
id("org.sonarqube") version "2.8" apply false
}
...
apply(from="$rootDir/gradle/includes/sonar.gradle.kts")
gradle/includes/sonar.gradle.kts:
apply(plugin="org.sonarqube")
Using a setup like above, I can then run "gradle sonarqube"
I faced a similar issue. Everything that you need to do is to call
configure<io.gitlab.arturbosch.detekt.extensions.DetektExtension> {
// configure
}
More info, you can find here: https://docs.gradle.org/current/userguide/migrating_from_groovy_to_kotlin_dsl.html#configuring-plugins

Can a Gradle plugin modify the list of subprojects in a multi-module project?

I've hacked together combination of build.gradle and settings.gradle below for creating an ad-hoc multi-module project out of several single-module projects (e.g., an application and all of its dependencies, or a shared library and everything that uses that library).
settings.gradle:
// find all subprojects and include them
rootDir.eachFileRecurse {
if (it.name == "build.gradle") {
def projDir = it.parentFile
if (projDir != rootDir) {
include projDir.name
project(":${projDir.name}").projectDir = projDir
}
}
}
build.gradle::
// Make sure we've parsed subproject dependencies
evaluationDependsOnChildren()
// Map of all projects by artifact group and name
def declarationToProject = subprojects.collectEntries { p -> [toDeclaration(p), p] }
// Replace artifact dependencies with subproject dependencies, if possible
subprojects.each { p ->
def changes = [] // defer so we don't get ConcurrentModificationExceptions
p.configurations.each { c ->
c.dependencies.each { d ->
def sub = declarationToProject[[group:d.group, name:d.name]]
if (sub != null) {
changes.add({
c.dependencies.remove(d)
p.dependencies.add(c.name, sub)
})
}
}
}
for (change in changes) {
change()
}
}
This works, but it's hard to share -- if somebody else wants to do something similar they have to copy my *.gradle files or cut and paste.
What I'd like to do is take this functionality and encapsulate it in a plugin. The build.gradle part looks easy enough to do in the plugin apply() method, but it seems like the list of subprojects is already set in stone before the plugin gets a chance at it. Is there any way to get in earlier in the build process, e.g. by applying to something other than Project? Or should I resign myself to giving my plugin a task for overwriting settings.gradle?
Solution: Per Peter Niederweiser's answer, I moved the code above into two plugins, one to be called from settings.gradle and the other to be called from build.gradle. In settings.gradle:
buildscript {
repositories { /* etc... */ }
dependencies { classpath 'my-group:my-plugin-project:1.0-SNAPSHOT' }
}
apply plugin: 'find-subprojects'
And in build.gradle:
buildscript {
repositories { /* etc... */ }
dependencies { classpath 'my-group:my-plugin-project:1.0-SNAPSHOT' }
}
evaluationDependsOnChildren()
apply plugin: 'local-dependencies'
Note that calling the plugin from settings.gradle doesn't work in Gradle 1.11 or 1.12 but does work in Gradle 2.0.
You'd need to apply a plugin in settings.gradle, which I believe is supported in recent versions.

Extract specific JARs from dependencies

I am new to gradle but learning quickly. I need to get some specific JARs from logback into a new directory in my release task. The dependencies are resolving OK, but I can't figure out how, in the release task, to extract just logback-core-1.0.6.jar and logback-access-1.0.6.jar into a directory called 'lib/ext'. Here are the relevant excerpts from my build.gradle.
dependencies {
...
compile 'org.slf4j:slf4j-api:1.6.4'
compile 'ch.qos.logback:logback-core:1.0.6'
compile 'ch.qos.logback:logback-classic:1.0.6'
runtime 'ch.qos.logback:logback-access:1.0.6'
...
}
...
task release(type: Tar, dependsOn: war) {
extension = "tar.gz"
classifier = project.classifier
compression = Compression.GZIP
into('lib') {
from configurations.release.files
from configurations.providedCompile.files
}
into('lib/ext') {
// TODO: Right here I want to extract just logback-core-1.0.6.jar and logback-access-1.0.6.jar
}
...
}
How do I iterated over the dependencies to locate those specific files and drop them in the lib/ext directory created by into('lib/ext')?
Configurations are just (lazy) collections. You can iterate over them, filter them, etc. Note that you typically only want to do this in the execution phase of the build, not in the configuration phase. The code below achieves this by using the lazy FileCollection.filter() method. Another approach would have been to pass a closure to the Tar.from() method.
task release(type: Tar, dependsOn: war) {
...
into('lib/ext') {
from findJar('logback-core')
from findJar('logback-access')
}
}
def findJar(prefix) {
configurations.runtime.filter { it.name.startsWith(prefix) }
}
It is worth nothing that the accepted answer filters the Configuration as a FileCollection so within the collection you can only access the attributes of a file. If you want to filter on the dependency itself (on group, name, or version) rather than its filename in the cache then you can use something like:
task copyToLib(type: Copy) {
from findJarsByGroup(configurations.compile, 'org.apache.avro')
into "$buildSrc/lib"
}
def findJarsByGroup(Configuration config, groupName) {
configurations.compile.files { it.group.equals(groupName) }
}
files takes a dependencySpecClosure which is just a filter function on a Dependency, see: https://gradle.org/docs/current/javadoc/org/gradle/api/artifacts/Dependency.html

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