apply plugin: 'jacoco' jacoco { toolVersion(libs.versions.jacoco.get()) } def mergedJacocoExec = file("${project.buildDir}/jacoco/jacocoMerged.exec") def merge = tasks.register('jacocoMergeReports', JacocoMerge) { group = "Reporting" description = "Merge all jacoco reports from projects into one." ListProperty jacocoFiles = project.objects.listProperty(File.class) project.subprojects { subproject -> subproject.plugins.withId("jacoco") { project.logger.info("${subproject.name} has Jacoco plugin applied") subproject.tasks.withType(Test) { task -> File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile if (destFile.exists() && !task.name.contains("Release")) { jacocoFiles.add(destFile) } } } } executionData(jacocoFiles) destinationFile(mergedJacocoExec) } tasks.register('jacocoFullReport', JacocoReport) { dependsOn merge group = "Reporting" description = "Generate full Jacoco coverage report including all modules." getClassDirectories().setFrom(files()) getSourceDirectories().setFrom(files()) getExecutionData().setFrom(files()) reports { xml.enabled = true html.enabled = true csv.enabled = false } // Always run merging, as all input calculation is done in doFirst {} outputs.upToDateWhen { false } // Task will run anyway even if initial inputs are empty onlyIf = { true } project.subprojects { subproject -> subproject.plugins.withId("jacoco") { project.logger.info("${subproject.name} has Jacoco plugin applied") subproject.plugins.withId("kotlin-android") { project.logger.info("${subproject.name} is android project") def mainSources = subproject.extensions.findByName("android").sourceSets['main'] project.logger.info("Android sources: ${mainSources.java.srcDirs}") mainSources.java.srcDirs.forEach { additionalSourceDirs(it) } project.logger.info("Subproject exclude: ${subproject.jacocoExclude}") additionalClassDirs(fileTree( dir: "${subproject.buildDir}/tmp/kotlin-classes/debug", excludes: subproject.jacocoExclude )) } subproject.plugins.withId("kotlin") { plugin -> project.logger.info("${subproject.name} is common kotlin project") SourceDirectorySet mainSources = subproject.extensions.getByName("kotlin") .sourceSets[SourceSet.MAIN_SOURCE_SET_NAME] .kotlin mainSources.srcDirs.forEach { project.logger.debug("Adding sources: $it") additionalSourceDirs(it) } project.logger.info("Subproject exclude: ${subproject.jacocoExclude}") additionalClassDirs(fileTree( dir: "${subproject.buildDir}/classes/kotlin/main", excludes: subproject.jacocoExclude )) } subproject.tasks.withType(Test) { task -> File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile if (destFile.exists() && !task.name.contains("Release")) { project.logger.info("Adding execution data: $destFile") executionData(destFile) } } } } }