Updating to Gradle 8 in Android: implicit dependency error between variants

796 Views Asked by At

I have an Android app with 4 variants, and I have a Bitrise instance that runs this command:

./gradlew "testDevADebugUnitTestCoverage" "testDevBDebugUnitTest" "testDevCDebugUnitTest" "testDevCDebugUnitTest" "--continue" "-PjacocoEnabled=true"

in the develop branch to make sure everything's all right.

I have recently upgraded to Gradle 8 and AGP 8.0.1 using the AGP Upgrade Assistant from Android Studio, and I'm running into the several errors just like the one below when I'm running the command above either locally or in Bitrise.

I have to say that the Gradle errors are for a lot of different types of tasks: KotlinCompile, JacocoReport, MergeSourceSetFolders, ProcessTestManifest.

Some problems were found with the configuration of task ':domain:compileDevADebugKotlin' (type 'KotlinCompile').
  - Gradle detected a problem with the following location: '/Users/MyUser/Developer/yapp/domain/build/tmp/kotlin-classes/devADebug'.

    Reason: Task ':domain:testDevBDebugUnitTestCoverage' uses this output of task ':domain:compileDevADebugKotlin' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.

    Possible solutions:
      1. Declare task ':domain:compileDevADebugKotlin' as an input of ':domain:testDevBDebugUnitTestCoverage'.
      2. Declare an explicit dependency on ':domain:compileDevADebugKotlin' from ':domain:testDevBDebugUnitTestCoverage' using Task#dependsOn.
      3. Declare an explicit dependency on ':domain:compileDevADebugKotlin' from ':domain:testDevBDebugUnitTestCoverage' using Task#mustRunAfter.

    Please refer to https://docs.gradle.org/8.0.2/userguide/validation_problems.html#implicit_dependency for more details about this problem.

I have tried to update Jacoco to the latest version (0.8.10) but the problem still remains (I'm not even sure it's a Jacoco problem though).

How can I resolve this issue? I have been looking around and I haven't been able to find any solution.

Edit

Here's my jacoco.gradle file:

apply plugin: 'jacoco'
apply from: "$project.rootDir/test_coverage_excludes.gradle"

jacoco {
    toolVersion = libs.versions.jacocoVersion.get()
    reportsDir = file("$buildDir/reports/jacoco")
}

android {
    testOptions {
        unitTests.all {
            jacoco {
                // Required for tests ran with RobolectricTestRunner to be included in coverage
                includeNoLocationClasses = true

                // Required for execution using Java 11
                excludes = ['jdk.internal.*']
            }
        }
    }
}

project.afterEvaluate {
    def variants

    if (android.hasProperty('applicationVariants')) {
        variants = android.applicationVariants.collect { it.name }
    } else {
        variants = android.libraryVariants.collect { it.name }
    }

    variants = variants.findAll { it.endsWith("Debug") || it.endsWith("debug") }

    // Generate a coverage report for each variant
    variants.each { variantName ->
        def unitTestTaskName = "test${variantName.capitalize()}UnitTest"
        def coverageTaskName = "${unitTestTaskName}Coverage"

        // Create the coverage tasks
        task(coverageTaskName, type: JacocoReport, dependsOn: "$unitTestTaskName") {
            group = "Reporting"
            description "Generate coverage reports for ${variantName.capitalize()}."

            // Location of Kotlin classes
            def kotlinClassDirectory = "$buildDir/tmp/kotlin-classes/$variantName"
            def kotlinClassTree = fileTree(dir: kotlinClassDirectory, excludes: fileFilter)
            getClassDirectories().setFrom(files([kotlinClassTree]))

            // Location of the actual source
            def coverageSourceDirs = [
                    "src/main/java",
                    "src/$variantName/java"
            ]

            getAdditionalSourceDirs().setFrom(coverageSourceDirs)
            getSourceDirectories().setFrom(coverageSourceDirs)

            // Include both unit test and instrumentation test execution data
            executionData.setFrom(fileTree(dir: buildDir, includes: ['**/*.exec', '**/*.ec']))

            reports {
                xml.getRequired().set(true)
                html.getRequired().set(true)
                csv.getRequired().set(false)
            }
        }
    }
}
1

There are 1 best solutions below

0
On

executionData need to be passed as dependency on concrete file. In configuration you had, the report task was depending on arbitraty files from build directory, and gradle was complained to add dependency on all tasks, while we only need the result of unitTestTaskName task

executionData = tasks."$unitTestTaskName".outputs.files.filter { it.name.endsWith(".exec") }

this way task is depending only on files that are produced from 'test' task and gradle sees that only this one task need to be done.