Search code examples
gradlegradle-plugin

Gradle Plugin - dependency on Java module also used in main project


I want that my gradle plugin depends on some common code that can be used inside the gradle plugin as well as inside a android library.

Is there a way to share plain java/kotlin code between a gradle plugin and an android library?

When I try to add a module to the gradle plugin I get "could ':...' could not be found in project" error... I tried different combinations of includeBuild and include but they don't solve the issue...

Example:

Gradle Plugin:

plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
    `maven-publish`
}

dependencies {
    // fails
    implementation(project(":Library:Plugin:Shared"))
    //implementation(project(":library:plugin:shared"))
}

Library Module:

plugins {
    id("com.android.library")
    id("kotlin-android")
    id("kotlin-parcelize")
    id("maven-publish")
}

android {

    // ...
}

dependencies {
    api(project(":Library:Plugin:Shared"))
}

Shared Module:

repositories {
    mavenCentral()
}

plugins {
    id("org.jetbrains.kotlin.jvm")
    `java-library`
    `maven-publish`
}

dependencies {
}

settings.gradle.kts:

pluginManagement {
    includeBuild(File("library/plugin/gradle"))
}

include(":Library:Plugin:Shared")
project(":Library:Plugin:Shared").projectDir = file("library/plugin/shared")

include(":Library:Plugin:Gradle")
project(":Library:Plugin:Gradle").projectDir = file("library/plugin/gradle")

EXAMPLE SETUP

1) root/gradle-plugin/build.gradle.kts

repositories {
    mavenCentral()
}

plugins {
    `kotlin-dsl-base`
    `java-gradle-plugin`
    `maven-publish`
}

dependencies {
    //implementation(":gradle-shared")

    // following does not work inside a gradle plugin module...
    // it works in other modules though
    implementation(project(":Library:Plugin:Shared")) 
}

gradlePlugin {
    plugins {
        create("changelog-utils") {
            id = "changelog-utils"
            implementationClass = "<path>.ClassLoaderPlugin"
        }
    }
}

2) root/gradle-shared/build.gradle.kts

repositories {
    mavenCentral()
}

plugins {
    `kotlin-dsl-base`
    `java-library`
    //`maven-publish`
}

3) root/settings.gradle.kts

// --------------
// Gradle Plugin
// --------------

includeBuild("gradle-plugin")

// --------------
// Shared Code (Plugin + Library)
// --------------

include(":MyLibrary:Plugin:Shared")
project(":MyLibrary:Plugin:Shared").projectDir = file("gradle-shared")

Solution

  • Use an included build

    You need an included build to share code between a plugin and the application code of a project.

    It's not clear to me where you are writing your plugin code, but the logical place to put it is as a subproject of the included build, which I will describe below. Here are some step-by-step instructions.

    Setting up the included build
    1. Set up your included build project in a new folder under your main project folder. Call it, say, myIncludedBuild.

    2. Give the included build its own settings.gradle.kts file in that folder. I suggest you write your plugin and the shared library in this project, so with that in mind, include both of them in what is going to be a multiproject project by writing the following in that file:

      include("myPlugin", "myLibrary")
      
    Setting up the plugin
    1. Set up a folder for your plugin at myIncludedBuild/myPlugin. Give it a build.gradle.kts file and, assuming you are writing the plugin in Kotlin, apply the kotlin-dsl plugin1
      plugins {
         `kotlin-dsl`
      }
      
    2. Continue this build.gradle.kts file to set out the details of your plugin in Java Gradle Plugin syntax. This gives that plugin the information it needs to package your plugin in the prescribed way for other projects to locate it.
      gradlePlugin {
         plugins {
             create("myPlugin") {
                 id = "myPluginId"
                 implementationClass = "package.MyPlugin"
             }
         }
      }
      
    3. Put your plugin code in the file myIncludedBuild/myPlugin/src/kotlin/package/MyPlugin.kt (or whatever package suits you, reflecting it in the build file) in a class called MyPlugin implementing Plugin<Project>.
    Setting up the library
    1. For the library, set up a folder at myIncludedBuild/myLibrary and give it a build.gradle.kts file. In there apply the Java Library plugin and give the project a group.

      plugins {
         `java-library`
      }
      
      group = "myIncludedBuildGroup"
      
    2. Write your shared code in myIncludedBuild/myLibrary/src/main/java

    Include the library in the plugin
    1. To include the code of the library in the plugin code, add the following to the plugin's build.gradle.kts, importing it as a sister project:
      dependencies {
          implementation(project(":myLibrary"))
      }
      
    Setting up the main project
    1. Moving to your main project, include the build in your main settings.gradle.kts:
      includeBuild("myIncludedBuild")
      
    2. Now, the plugin will be available to be applied in the main project's build files:
      plugins {
          id("myPluginId")
      }
      
    3. Also, the library will be available as a dependency to any project in your main build using its Maven co-ordinates:
      dependencies {
          implementation("myIncludedBuildGroup:myLibrary")
      }
      

    1The Kotlin DSL plugin includes the Kotlin JVM plugin and the Java Gradle plugin; it puts the Kotlin Gradle DSL on the classpath; and it provides a convenient change to the Kotlin syntax to make regular Kotlin code similar to writing a Kotlin DSL build script, in that Gradle Action parameters are treated with their subject as receivers.