Aggregate Report for Gradle Multi-Project build - gradle

I need to create an aggregate code coverage report for my multiple-module Gradle project using the JaCoCo plugin for Gradle, similar to the one generated by the jacoco:aggregate-report of Maven's jaoco-maven-plugin.
I have been googling for a solution the last couple of days but nothing has worked so far. Most of the proposed solutions involve defining a task of type JacocoReport in the root project, that aggregates the execution data and produces the html code coverage report. However everything I tried so far either fails with an error or does not generate any report.
For example, this code snippet that I have tried:
def publishedProjects = subprojects.findAll()
task jacocoRootReport(type: JacocoReport) {
dependsOn(publishedProjects.test)
additionalSourceDirs = files(publishedProjects.sourceSets.main.allSource.srcDirs)
sourceDirectories = files(publishedProjects.sourceSets.main.allSource.srcDirs)
classDirectories = files(publishedProjects.sourceSets.main.output)
executionData = files(publishedProjects.jacocoTestReport.executionData)
doFirst {
executionData = files(executionData.findAll { it.exists() })
}
reports {
html.enabled = true
xml.enabled = false
}
}
gives the error groovy.lang.GroovyRuntimeException: Cannot set the value of read-only property 'additionalSourceDirs' for task ':jacocoRootReport' of type org.gradle.testing.jacoco.tasks.JacocoReport
The version of Gradle that I am using is 7.3. Unfortunately I'm very new to Gradle and I still cannot fiddle with the code snippets that I've found to make them work for my case.
Any help will be much appreciated. Thank you very much in advance.

There is an official example in the Gradle docs for this use case:
https://docs.gradle.org/7.3.3/samples/sample_jvm_multi_project_with_code_coverage.html
It is a somewhat complex and requires an additional project that will serve as the aggregate. The way you have above is generally discouraged by Gradle for reasons outside of this question/answer.
Edit 2022-09-11
As of Gradle 7.4, Gradle now offers a plugin to aggregate reports for multi-module projects.
See the documentation for the JaCoCo Report Aggregation Plugin for more details.

Related

Gradle creating a additional task for int tests

So I found a few tutorials on how to create a seperate task to run integration tests in gradle. My int tests are in a separate folder.. ie src/integrationTest
This is what one of them had set up, I just dont understand the line "val integrationTest by sourceSets.creating"? Is this creating a source set? Other examples have explicit creation of source set in the gradle file..
val integrationTest by sourceSets.creating
dependencies {
"integrationTestImpl"(project)
}
val integrationTestTask = tasks.register<Test>("integrationTest") {
description = "Runs the integration tests."
group = "verification"
testClassesDirs = integrationTest.output.classesDirs
classpath = integrationTest.runtimeClasspath
mustRunAfter(tasks.test)
}
tasks.check {
dependsOn(integrationTestTask)
}
Yes, it is creating a new source set named integrationTest.
Refer to this answer on Kotlin delegated properties. It is not specific to Gradle, but the Kotlin language itself. Gradle provides a variety of extensions to make the Kotlin DSL a rich experience compared to the Groovy DSL.

Gradle Scripts Syntax

I am learning Gradle these days and trying to understand how the syntax in gradle maps to the Gradle docs provided.
Can someone help me understand how the jarTaskName in the below snippet is derived? I can't see any attribute/method with the name jarTaskName in sourceSets class.
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html
def integrationTest = sourceSets.create('integrationTest')
configurations[integrationTest.implementationConfigurationName].extendsFrom(configurations.testImplementation)
configurations[integrationTest.runtimeOnlyConfigurationName].extendsFrom(configurations.testRuntimeOnly)
def integrationTestJarTask = tasks.register(sourceSets.integrationTest.**jarTaskName**, Jar) {
archiveClassifier = 'integration-tests'
from integrationTest.output
}
Any documentation or quick pointers to understand above mapping will be helpful?
jarTaskName is documented in the API documentation to SourceSet. The documentation you linked is the DSL documentation which covers Gradle's build language.

Gradle Kotlin DSL: get sourceSet of another project

Currently, we're trying to migrate our existing build.gradle scripts to the new Kotlin DSL. Right now, we are struggling with the jar task configuration.
Our project is a simple multi-project. Let's say we've Core and Plugin and Plugin uses classes from Core. Now, when building Plugin, the target jar should include any used classes from Core.
This is how it looked like before:
jar {
from sourceSets.main.output
from project(':Core').sourceSets.main.output
}
And this is the current solution we've with Kotlin DSL:
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").the<SourceSetContainer>()["main"].allSource)
}
However, the above example just gives me an Extension of type 'SourceSetContainer' does not exist. Currently registered extension types: [ExtraPropertiesExtension] error. I've also tried other code snippets I've found, but none of them have been working so far.
I have also tried this (like suggested in the first answer):
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").sourceSets.getByName("main").allSource)
}
But then the IDE (and also the jar task) argues that sourceSets is not available: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: public val KotlinJvmProjectExtension.sourceSets: NamedDomainObjectContainer<DefaultKotlinSourceSet> defined in org.gradle.kotlin.dsl.
I hope that someone can help us, because it is very frustrating to spend hours in configuration instead of writing any usefull code.
Thank you very much in advance.
You can access the SourceSetContainer by
project(":Core").extensions.getByType(SourceSetContainer::class)
it seems <T : Any> Project.the(extensionType: KClass<T>): T looks in the convention of the project, while val Project.sourceSets: SourceSetContainer get() looks in the extensions ExtensionContaier instead. This is somewhat odd, as the documentation for the says "Returns the plugin convention or extension of the specified type."
Note that you may need to do your sourceSet manipulation in gradle.projectsEvaluated, because otherwise the sourceSet in question may not be configured yet if the corresponding project is not yet evaluated.
If you get access to the project, then everything should looks like your actual groovy gradle script:
project(":Core").sourceSets.getByName("main").allSource
So regarding your actual code:
val jar: Jar by tasks
jar.apply {
from(java.sourceSets["main"].allSource)
from(project(":Core").sourceSets.getByName("main").allSource)
}

sonarqube + lombok = false positives

import lombok.Data;
#Data
public class Filter {
private Operator operator;
private Object value;
private String property;
private PropertyType propertyType;
}
For code above there are 4 squid:S1068 reports about unused private fields. (even they are used by lombok generated getters). I've seen that some fixes related to support of "lombok.Data" annotation have been pushed, but still having these annoying false positives.
Versions:
SonarQube 6.4.0.25310
SonarJava 4.13.0.11627
SonarQube scanner for Jenkins (2.6.1)
This case should be perfectly handled by SonarJava. Lombok annotations are taken into account at least since version 3.14 (SONARJAVA-1642). The issues you are getting are resulting from a misconfiguration of your Java project. No need to write any custom rules to handle this, this is natively supported by the analyzer.
SonarJava reads bytecode to know which annotation are used. Consequently, if you are not providing bytecode from your dependencies, on top of bytecode from your own code, the analyzer will behave erratically.
In particular, setting property sonar.java.libraries should solve your issue. Note that this property is normally automatically set when using SonarQube maven or gradle scanners.
Please have a look at documentation in order to correctly configure your project: https://docs.sonarqube.org/display/PLUG/Java+Plugin+and+Bytecode
I added following property to Sonar analysis properties file. And it works for me.
sonar.java.libraries=${env.HOME}/.m2/repository/org/projectlombok/lombok/**/*.jar
lombok v1.16.20 is lombok version on my project.
I'm using sonar-maven-plugin 3.4.0.905, lombok 1.16.18, with SonarQube CE Server v8.3.1.
I resolved the issue by adding
<sonar.java.libraries>target/classes</sonar.java.libraries> to the POM properties.
The answer suggested by Wohops and Barış Özdemir worked for me. Posting this answer because in my scenario, it took some time to figure out how to implement it because my CI builds are running in Travis and we don't know the path where the lombok-x.x.x.jar file will be downloaded because there is no much control we have on travis environment where the build runs.
I used my build tool (Gradle) to implement it. Following configuration in build.gradle ensured that as part of building of the project, all the jar dependencies get copied to ${buildDir}/output/libs
task copyToLib(type: Copy) {
into "${buildDir}/output/libs"
from configurations.runtime
}
build.dependsOn(copyToLib)
And then as mentioned in the previous answers, I configured the property in the sonar-project.properties file to this libs directory.
sonar.java.libraries=/home/travis/build/xxxxxx/build/output/libs/lombok-1.16.20.jar
Hope this helps.
Cheers.
You can configure the ignore issue rules:
sonar.issue.ignore.multicriteria=e1
sonar.issue.ignore.multicriteria.e1.ruleKey=java:S1068

How to run gradle task of liquibase „dropAll“ twice?

We’re using liquibase, gradle and the com.augusttechgroup:gradle-liquibase-plugin. We’re currently facing the bug
https://liquibase.jira.com/browse/CORE-1803
which kills our continuoes integration server. Until this problem is resolved, we would like to use a workaround so that we can run the „dropAll“ task in gradle twice.
gradle dropAll dropAll
doesn’t work and
gradle dropAll && gradle dropAll
is no option because of our continuos integration servers which can’t manage this.
Is there a way to make a task run twice or do I have to work around the gradle plugin and write my own dropAll task like #judoole proposed here Liquibase 3.0.1 Gradle integration?
The simplest solution I found was just to declare two databases.
liquibase {
activities {
main {
url 'jdbc:postgresql://localhost:5432/db'
username 'user'
password 'passwd'
...
}
secondRun {
url 'jdbc:postgresql://localhost:5432/db'
username 'user'
password 'passwd'
...
}
}
runList = 'main'
}
dropAll.doFirst { liquibase.runList = 'main,secondRun' }
dropAll.doLast { liquibase.runList = 'main' }
Per default the 'runList' only contains the main db. But for dropAll he sets both databases active, so that dropAll will run on both ... which are actually the same.
I would consider using JavaExec, like the linked StackOverflow question, especially if you're only using xml and not Groovy to write your changelog. The beauty of the liquibase-gradle-plugin is just these strange cases you describe. From the Github repo:
Let's suppose that for each deployment, you need to update the data
model for your application's database, and wou also need to run some
SQL statements in a separate database used for security.
Now I haven't used the plugin, because I didn't need those features. But I did run into bugs in liquibase and needed to use a spesific version, namely 3.0.4, as generateChangelog has a strange bug since 3.0.5. Using the JavaExec version I was able to track down a version that suited my needs.
Now, the liquibase-gradle-plugin doesn't need to be your only weapon of choice. Gradle has plenty of room for writing your own little tasks. Also those who do some sql. Maybe try something along these lines and see if that works:
configurations {
driver
}
dependencies {
driver '<your-sql-driver>'
}
//"Bug" in Gradle. Groovy classes are loaded first. They need to know about sql driver
//Or I think it's still so
URLClassLoader loader = GroovyObject.class.classLoader
configurations.driver.each { File file ->
loader.addURL(file.toURL())
}
task deleteFromTables(description: 'Deletes everything.') <<{
def props = [user: "<username>", password: "<password>", allowMultiQueries: 'true'] as Properties
def sql = Sql.newInstance("<url>", props, "<driver-classname>)
try {
//Here you can do your magic. Delete something, or simple drop the database.
//After dropping it, you'd probably want another task for creating
//it back up again
sql.execute("DELETE ...")
} finally {
sql.close()
}
}
Gradle makes sure that each task gets executed at most once per Gradle invocation. If you need to do some work twice, you need to declare two tasks. Chances are that the plugin provides a suitable task type for you to declare a second dropAll task.

Resources