Search code examples
androidandroid-gradle-pluginjacocogradle-kotlin-dslmulti-module

KTS gradle configuration at Android multi-module (feat. Jacoco)


I'm struggling with setting up gradle using KTS on Android Multi-module.

What I ultimately want to accomplish is to use jacoco with kts gradle, which is the link below.
Migrate to KTS Jacoco

First of all, let me give you the library version and IDE information.
IDE : Android EEL
AGP : 7.4.2
Gradle : 7.6.1
Kotlin : 1.8.0
(+Jacoco : 0.8.9)

Currently, gradle is configured as follows

// Top-level build.gradle.kts
    
buildscript {
    // nothing special in here.
    // Android Developer doesn't use classpath, is that possible?
    ...
}
    
plugins {
    id("com.android.application") version PluginVersion.ANDROID_GRADLE_VERSION apply false
    id("com.android.library") version PluginVersion.ANDROID_GRADLE_VERSION apply false
    kotlin("android") version PluginVersion.KOTLIN_VERSION apply false
    kotlin("kapt") version PluginVersion.KOTLIN_VERSION apply false
    kotlin("jvm") version PluginVersion.KOTLIN_VERSION apply false
    
    jacoco // The key is that I want to use jacoco.
    java // I don't know why but, when does this plugin used?
}

And,

// Top-level setting.gradle
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
rootProject.name = "ProjectName"
include(
    ":app",
    ":common",
    ":domain",
    ":presentation",
    ":data"
)

In this case, the buildSrc/build.gradle.kts for using kts looks like this

plugins {
    `kotlin-dsl`
}
repositories {
    google()
    mavenCentral()
}

Okay, here's the question 1)

To create a CustomPlugin (JacocoReportsPlugin according to the link), I need an import for BaseExtension, as the guide in the link.
So, in buildSrc/build.gradle.kts, I add

implementation("com.android.tools.build:gradle:7.4.2")

in buildSrc/build.kts, I get the following error.

Error resolving plugin [id: 'com.android.application', version: '7.4.2', apply: false] The request for this plugin could not be satisfied because the plugin is already on the classpath with an unknown version, so compatibility cannot be checked.

So, I removed the dependency like I did for the other submodules, and replaced the plugin block with this

plugins {
    `kotlin-dsl
    id("com.android.application")
}

But as usual, I get this error.

Plugin [id: 'com.android.library'] was not found in any of the following sources:

I don't know if build.gradle.kts in buildSrc is affected by build.gradle.kts and settings.gradle in Root Project or not. Which one is the cause?

PS) After some searching, I found that if I change the implementation to compileOnly at the dependency block like this, I can build successfully without any errors.
But, What happened? Why can I build??

dependencies {
    compileOnly("com.android.tools.build:gradle:7.4.2") // implementation("xxx") made error before.
}

Question 2)

I also added the following to buildSrc/build.gradle.kts to use the custom plugin.

gradlePlugin {
    // register JacocoReportsPlugin as a plugin
    plugins {
        register("jacoco-reports") {
            id = "jacoco-reports"
            implementationClass = "plugins.CustomPlugin"
        }
    }
}

Then, when I put the plugin in the plugin block of the module I want to use with id("jacoco-reports") and build it, this time I get this problem.

An exception occurred applying plugin request [id: 'jacoco-reports']
Failed to apply plugin 'jacoco-reports'.
Could not create plugin of type 'CustomPlugin'.
Could not generate a decorated class for type CustomPlugin.
com/android/build/gradle/BaseExtension

But. I have no idea how to fix it from here, and I really need your help.

Any hint of a solution would be great, so please feel free to give me some advice.

Or, suggest me about any other way to apply jacoco with KTS gradle at android multi-module project.

Thanks in advance!!

I'm struggling with setting up gradle using KTS on Android Multi-module.


Solution

  • Aligning plugin versions

    The request for this plugin could not be satisfied because the plugin is already on the classpath with an unknown version, so compatibility cannot be checked.

    The version of a Gradle plugin should only be defined in one place. Unfortunately there are about 10 completely different ways to set the plugin version, and some of them are incompatible. It's an unclear mess.

    Since you are using buildSrc, I recommend setting the versions of all Gradle plugins in buildSrc/build.gradle.kts. This will align the versions across all subprojects, and in any plugins or pre-compiled script plugins under buildSrc/src/main/.

    Note: Using buildSrc/build.gradle.kts to align the plugin versions might not work if you are using includeBuild() to define composite builds

    Setting up buildSrc/build.gradle.kts

    First, make sure you have a Java based plugin applied to buildSrc/build.gradle.kts. I recommend the Kotlin DSL plugin, because this allows for writing pre-compiled script plugins.

    // buildSrc/build.gradle.kts
    
    plugins {
      `kotlin-dsl`
    }
    

    Defining Repositories

    Make sure to define the repositories Gradle will search for the plugins. I like using a centralized repositories declaration in buildSrc/settings.gradle.kts

    // buildSrc/settings.gradle.kts
    rootProject.name = "buildSrc"
    
    pluginManagement {
      repositories {
        mavenCentral()
        gradlePluginPortal()
        // There's no need to apply the Android plugin to buildSrc/build.gradle.kts, 
        // so don't add the google() repo 
        // (it won't hurt, but it's just not necessary)
        //google() 
      }
    }
    
    @Suppress("UnstableApiUsage")
    dependencyResolutionManagement {
      repositories {
        mavenCentral()
        google() // if you're using Android, add the Google repo
        
        // We're fetching Gradle plugins as regular build dependencies, 
        // so the Gradle Plugin Portal must be added to as a 'regular' 
        // dependency repository. Normally GPP should only be defined 
        // as a plugin repository.
        gradlePluginPortal() 
      }
    }
    

    Adding dependencies on plugins

    The plugins must be added as dependencies in buildSrc/build.gradle.kts using either

    • the Maven coordinates of the plugin, e.g. for the Android plugin

      // buildSrc/build.gradle.kts
      
      dependencies {
         implementation("com.android.tools.build:gradle:8.0.0")
      }
      

      (I found these coordinates by searching mvnrepository.com, since the Android plugin isn't published to the Gradle Plugin Portal. The Maven coordinates can usually be found on the Gradle Plugin Portal page for the plugin, in classpath(...) under the 'legacy' section)

    • the Gradle Plugin Marker Maven coordinates, which has the format

      ${pluginId}:${pluginId}.gradle.plugin:${pluginVersion}
      

      E.g. for the Android plugin (which has plugin ID com.android.application)

      // buildSrc/build.gradle.kts
      
      dependencies {
         val androidPluginId = "com.android.application"
         implementation("${androidPluginId}:${androidPluginId}.gradle.plugin:8.0.0")
      }
      

    Note that one dependency can contain multiple Gradle Plugin IDs. In the case of the Android dependency, it contains both com.android.application and com.android.library, and more!

    Remove plugin versions in other places

    Once plugins are added as dependencies in buildSrc/build.gradle.kts, remove the Gradle plugin version from all subprojects and pre-compiled script plugins.

    // my-subproject/build.gradle.kts
    
    plugins {
      id("com.android.application") // version "8.0.0" <- version not necessary, it's set in buildSrc/build.gradle.kts
    }