This message was deleted.
# community-support
s
This message was deleted.
v
Some notes in unspecific order: • iterates over each subproject and applies
jacoco
plugin for each subproject in
suproject.afterEvaluate {}
block Actually two reasons to strongly consider not using that plugin imho. The first half means cross-project configuration which introduces project coupling and does not work well with more advanced Gradle optimizations like the upcoming configuration cache, the second half like any
afterEvaluate
most probably mainly introduces race conditions and timing problems as it for example does not see changes made in later
afterEvaluate
blocks. Most usages of
afterEvaluate
are a code smell and should be changed to something else. • Actually what the SonarQube plugin does also does not sound much cleaner and should for example be replaced by the JaCoCo aggregation plugin to collect all JaCoCo reports instead of doing unsafe cross-project publication as detailed in the Userguide. But if you want to follow the bad practice of the plugin, just use the same tactic to depend on, using
withType<JacocoReport>()
as that is lazy and will also catch all tasks of that type added later. Of course your control "println" would still print an empty list, as at the time you evaluate it, the tasks are not yet there. And you also must not eagerly resolve the provider using
stream
and
toList
or you also destroy the laziness, but instead depend on the actual providers. If you also need to check the
skipProject
property, you probably have to further follow the bad practice and do the configuration in an
afterEvaluate
or you are not seeing the
skipProejct
values that were set as they are not set yet.
m
Thank you for quick reply. So, what I have in root-project build.gradle.kts, can be reduced to:
Copy code
tasks.withType<SonarQubeTask>().configureEach {
    val jacocoTasks = project.subprojects.stream()
        .map { p->
            p.tasks.withType<JacocoReport>()
        }
        .collect(Collectors.toList())

    dependsOn(jacocoTasks)
}```
Regardless of bad practices this snippet follows, as well as both 3rd-party plugins (cross-config, non-laziness, etc), my main concern is - how to make some task A which is supposed to be invoked from the root project (
SonarQubeTask
) to depend on all tasks of a particular type (
JacocoReport
) from every subproject (given that their names might be dynamic)? —— Also, what could be “the best practices” approach to accomplish that? With configuration avoidance/caching in mind.
v
I guess for the "best practice" those plugins you use need to be adapted first. Doesn't that snippet work for what you wanted? Regarding best practices, actually any
dependsOn
you write is wrong. Whenever you do a manual
dependsOn
(except for lifecycle tasks like
check
or
build
that are designed to just trigger some other tasks), it is a sign of something not being idiomatic. Usually some task A does not depend on some other task B being executed. Usually some task A needs the output of some other task B as input. So task A should use the corresponding output property of task B as value for its input property and thus get the task dependency automatically implied. But this is only to be done within one project. Cross-project you then have configurations or variants to share outputs of tasks with other tasks. For more information on how to do that properly, you can read https://docs.gradle.org/current/userguide/cross_project_publications.html And especially for aggregating JaCoCo results cross-project there is even https://docs.gradle.org/current/userguide/jacoco_report_aggregation_plugin.html. It adds a task for an aggregated JaCoCo report, but it also provides the JaCoCo result files needed for the report and thus needed for SonarQube in an added configuration like described in above mentioned cross project publication section.
m
Thank you for sharing. I will plan to migrate to Jacoco Aggregation plugin. However, it’s not quite feasible to do quickly. Btw, there is no “compliant” SonarQube plugin for now, so I have to stick with that not compliant with the best practices. And after all, my snippet doesn’t work, and running just
,/gradlew sonarqube
from the root project still requires all jacoco XML reports to be generated in advance, so I have to run:
./gradlew jacocoTestReport jacocoTestReport<BuildVariant> sonarqube
(Which relies on
sonarqube.mustRunAfter(jacocoReport)
). Do you think it doesn’t work because those 3rd-party plugins (com,vanniktech.android.junit.jacoco, org.sonarqube) are not implemented properly? So they don’t work correctly when i try to do whatever looks logical, because of internal bad-timing, concurrency and code-smell? So that means, these plugins prevent me from having what I want to achieve
m
This code snippet has eventually solved my issue: (from root project build.gradle.kts (in my case, from convention plugin applied at root project)
Copy code
tasks.withType<SonarQubeTask>().configureEach {
    rootProject.subprojects.forEach { p ->
        dependsOn(p.tasks.withType<JacocoReport>())
    }
}
now,
./gradlew sonarqube
also involves running all
test<Variant>UnitTest
and
jacocoTestReport<Variant>
tasks first, generation jacoco reporst, so SonarQube could find them. My principle here was to rely strongly on liveness of
withType()
method w/o adding extra complexity that might let that liveness averted. @Vampire what do you think, how compliant with the best practices that code snippet might be? My main concern is whether this code compliant with Configuration Avoidance? Regardless of usage dependsOn.
v
Regarding task configuration avoidance it should be fine. Regarding best practices, nothing changed from what I said above. 🙂
🙃 1
m
Now the issue is - how to make some exclusions for the generic filter
withType<JacocoReport>
? This filter can collect both
jacocoTestReportDebug
and
jacocoTestReportRelease
variants of the task. When I build for release build type, how could I exclude the latter from consideration (ignore it completely)? Basically, I want this code snippet to only work for debug-variants of the tasks (android subprojects), but still collect
jacocoTestReport
(“unscoped”) tasks for pure Java/Kotlin subprijects.
v
tasks.withType<JacocoReport>().matching { !it.name.endsWith("Release") }
maybe? Actually this will again work against task configuration avoidance though, as for
matching
all tasks on the left have to be realized to be given to the predicate.
Otherwise you could enumerate them manually of course.
m
So, this cannot be solved with configuration avoidance, i guess? Since for android subprojects tasks are generated dynamically. Or we can make this sample with configuration avoidance? For java/kotlin subprojects, where task names are known in advance, I believe we cannot come up with the example, when we’d need to filter out some tasks based on it’s “property”. So it’d be always using
withType
and hence, with configuration avoidance? Unless we use
matching
for some purpose. Is there any use case, where we could use
matching
for whatever reason to filter tasks, but preserving configuration avoidance?
v
Again, to check the predicate you give to
matching
, the task must be realized, so you cannot have configuration avoidance while using
matching
. That's also the reason you should never do
tasks.matching
if you can avoid it, because it would cause each and every task to be eagerly realized, but always do a
withType
before that at least only tasks of that type are realized due to the
matching
. As I said, if you want to do it while preserving configuration avoidance, you could for example enumerate the task names manually, using
tasks.named
instead.
1