The Kotlin Gradle Plugin was loaded multiple times in different subprojects

I’m trying to create a Kotlin library project that publishes build logic so that consuming applications can use that logic. I’m following the guide and sample included here.

My structure is essentially to have multiple modules in my library that produce separate artifacts. Each project here is using the latest Gradle version 8.14. I am also using Kotlin version 2.1.21. Here is my library project structure:

sample-lib/
	build-conventions/
		src/main/kotlin/org/example/
			application.gradle.kts (1)
			common.gradle.kts (2)
			library.gradle.kts (3)
	build.gradle.kts (4)
	settings.gradle.kts (5)
cli/
	build.gradle.kts (6)
core/
	build.gradle.kts (7)
settings.gradle.kts (8)

My build-conventions module has the following build scripts:

  • In build-conventions/settings.gradle.kts (5):
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") { from(files("../gradle/libs.versions.toml")) }
    }
}

rootProject.name = "build-conventions"
  • In build-conventions/build.gradle.kts (4):
plugins {
    `kotlin-dsl`
}

repositories {
    gradlePluginPortal()
}

dependencies {
    implementation(libs.kotlin.gradle.plugin)
    implementation(libs.shadow.gradle.plugin)
    implementation(libs.spotbugs.gradle.plugin)
    implementation(libs.gradle.versions.plugin)
}

My convention plugins contain:

  • In common (2):
plugins {  
    kotlin("jvm")
    id("com.github.ben-manes.versions")  
    id("com.github.spotbugs")  
}  
  
group = project.property("group") as String  
  
repositories {  
    mavenCentral()  
}  
  
dependencies {  
    testImplementation(kotlin("test"))  
}  
  
java {  
    toolchain {  
        languageVersion = JavaLanguageVersion.of(11)  
    }  
}  
  
tasks.test {  
    useJUnitPlatform()  
}
  • In application (1):
plugins {  
    id("org.example.conventions.common")  
    application  
    distribution
    id("com.gradleup.shadow")  
}
  • In library (3):
plugins {  
    id("org.example.conventions.common")  
    `java-library`  
    `maven-publish`
    signing
}  
  
version = project.property("version") as String  
  
java {  
    withSourcesJar()  
    withJavadocJar()  
}  
  
publishing {  
    publications {  
        create<MavenPublication>("mavenJava") {  
            from(components["java"])  
        }  
    }
}

As for my other build scripts:

  • In cli/build.gradle.kts (6):
plugins {
    id("org.example.conventions.library")
}
  • In core/build.gradle.kts (7):
plugins {
    id("org.example.conventions.library")
    alias(libs.plugins.serialization)
}
  • In settings.gradle.kts (8):
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}

rootProject.name = "sample-lib"

includeBuild("build-conventions")
include("core", "cli")

Using this project structure, if I then create a sample application which includes this library via includeBuild() and implement both core and cli as dependencies, I get the following warning from the Gradle build output:

The Kotlin Gradle plugin was loaded multiple times in different subprojects, which is not supported and may break the build. 
This might happen in subprojects that apply the Kotlin plugins with the Gradle 'plugins { ... }' DSL if they specify explicit versions, even if the versions are equal.
Please add the Kotlin plugin to the common parent project or the root project, then remove the versions in the subprojects.
If the parent project does not need the plugin, add 'apply false' to the plugin line.
See: https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl
The Kotlin plugin was loaded in the following projects: ':cli', ':core'

How can I get rid of this error? I don’t see how I can avoid applying the Kotlin Gradle plugin in core and cli while using my convention plugins. Simply adding apply false in common.gradle.kts does not make the warning go away. I’m wondering if it’s possible share Kotlin build logic at all, or if there is no way to do this and I need to apply the plugin and write related logic in each of my consumer projects (and library modules) individually?

I have attached my sample projects, both the sample library and sample application.

sample-sharing-convention-plugins-with-build-logic-nathlrowe.zip (379.4 KB)

How can I get rid of this error?

That’s more a question to the Kotlin Gradle Plugin, as it comes from that.
But actually, the error tells you exactly what the problem is and how to solve it, so what is your actual question?

I don’t see how I can avoid applying the Kotlin Gradle plugin in core and cli while using my convention plugins.

Noone said you should avoid that.

Simply adding apply false in common.gradle.kts does not make the warning go away.

Of course not, besides that the apply false is ignored there though it should throw an exception, it would be non-sense if it worked and nothing suggested you do that there. You need the plugins applied so not applying the plugin is hardly a solution. Well, if it would work, it would make the error go away, but additionally your precompiled script plugins would not even compile.

I’m wondering if it’s possible share Kotlin build logic at all

Of course, exactly like you do it … almost.
Just do what the error told you to do and it will work.


Btw, the lines

group = project.property("group") as String
version = project.property("version") as String

are superfluous, the values are taken from project properties anyway if you don’t set something different in your build script.

Noone said you should avoid that.

The error output literally says:

The Kotlin plugin was loaded in the following projects: ':cli', ':core'

But actually, the error tells you exactly what the problem is and how to solve it, so what is your actual question?

If that were true, I wouldn’t be posting here now would I? Perhaps you could elaborate a bit for me and be more specific on exactly what I should do?

The error output literally says:

Exactly.
It does not say that the problem is that you apply it to those two projects.
It says the the problem is, that you load it in those two projects instead of loading it once in a common parent class loader.

If that were true, I wouldn’t be posting here now would I?

It is true, even though post here. :wink:
It instructs you explicitly to fix it by adding the KGP to a common parent project like the root project and there use apply false if you don’t need it applied there, so that it is only loaded once in that parent project and then the same classes applied in the subprojects.

So just as the error says, add

plugins {
    <any kotlin plugin here> apply false
}

to your root project and the problem is gone, as you have the plugin loaded only once in the common parent project.

Alternatively, you can also instead do the same using your convention plugin, as it depends on the the Kotlin plugin, so you could die example also do

plugins {
    id("org.example.conventions.common") apply false
}

to achieve the same.

I added apply false to my application (which I assume is my “root project” in this case?), like so:

plugins {
    id("org.example.conventions.common") apply false
}

However this does not work. Some things:

  • I need to add the KGP plugin manually, as well as all the other plugins I have in my convention plugins (e.g. the application plugin).
  • I need to add a repositories block, as without it I get build errors.
  • After adding the above, I still get the “Kotlin Gradle Plugin was loaded multiple times” error, so this does not actually fix my issue.

So, it sounds like I need to specify a version in my root project, but not in my sub-projects. My first question here is how can I not specify the KGP version in my sub-projects? Currently I specify those versions in build-conventions/build.gradle.kts, with the line implementation(libs.kotlin.gradle.plugin). I need to implement the dependency here or else I can’t load the plugin in my convention plugins (commenting it out gives me an error saying the plugin cannot be found).

It feels like a catch-22 where I have to specify the KGP version in my convention plugins, but at the same time that gives a warning. Is this a situation where I should use pluginManagement?

I added apply false to my application (which I assume is my “root project” in this case?),

I don’t know what you call “my application”.
The root project is the only one left in the build.
You have cli, core, and the root project which is the parent of both.

However this does not work

It should, if you added it to the correct project.
But hard to guess what you do if you don’t tell or show. :slight_smile:

I need to add the KGP plugin manually, as well as all the other plugins I have in my convention plugins (e.g. the application plugin).

No

I need to add a repositories block,

Where? Why?

as without it I get build errors.

Which?

After adding the above, I still get the “Kotlin Gradle Plugin was loaded multiple times” error, so this does not actually fix my issue.

Then you probably added it at the wrong place, you did not tell where you tried adding it.

So, it sounds like I need to specify a version in my root project, but not in my sub-projects.

As long as the version is the same, and Gradle knows the versions, all can have a version since quite some time and versions, no problem.

My first question here is how can I not specify the KGP version in my sub-projects ?

That “remove versions” is not really applicable to your situation and also not necessary since several versions, as long as the versions are known by Gradle and all the same.
The KGP just cannot recognize your exact situation, and recommends removing the versions to be on the safe side and also be compatible with older Gradle versions I guess.

Currently I specify those versions in build-conventions/build.gradle.kts , with the line implementation(libs.kotlin.gradle.plugin) .

If you would add the KGP itself to the the root project, not your convention plugin, you could make it compileOnly instead of implementation. That would be the according action in your situation to “remove the versions”. But if you use the convention plugin to add to the root project, it would then not help, because then you would completely miss the KGP at execution time unless you add it manually additionally. But as the versions will be the same and known, just keep them.

I need to implement the dependency

“to implement” something means developing something, writing the code for it.
You are not “implementing” it, you “depend on it in your implementation” and thus add it to the “implementation” configuration.

Sorry, thought I was clear. I have two IntelliJ projects: sample-app and sample-lib. When I say “my application” I am referring to sample-app, which is a single-project Gradle build. sample-lib is a multi-project Gradle build.

No

You are wrong. You can simply download my sample project that I’ve shared in my original post and add apply false yourself to see.

Where? Why? Which?

Adding apply false causes the build to generate errors indicating that it cannot locate any dependencies. You can see which dependencies I have in sample-app in my project. Here is the output:

FAILURE: Build failed with an exception.

* Where:
Precompiled script plugin '/Users/nathlrowe/Projects/Temp/sample-app-model/sample-lib/build-conventions/src/main/kotlin/org/example/conventions/application.gradle.kts' line: 1

* What went wrong:
Plugin [id: 'org.jetbrains.kotlin.jvm'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Included Builds (No included builds contain this plugin)
- Plugin Repositories (plugin dependency must include a version number for this source)

Then you probably added it at the wrong place, you did not tell where you tried adding it.

Again, I did tell you, I thought it was clear that “my application” refers to sample-app. I’m guessing you didn’t download my sample project, did you? I have a consuming application project named sample-app, which is a single-project Gradle build. I added apply false to that project’s build script.

“to implement” something means developing something

You are being pedantic. I’m obviously referring to the implementation function.

You are wrong.

I am not, you are just misinterpreting the instructions.

You are being pedantic

No, I’m just for clear communication. :stuck_out_tongue:
With clearer communication and clear understanding of the correct terms, you would probably solved your problem already long time ago. :wink:

I have a consuming application project named sample-app , which is a single-project Gradle build. I added apply false to that project’s build script.

No, that is not the root project this is about, that is the root project of a totally different build.
The root project where you need to do it is the root project in the build where it is complaining.
So sample-lib/build.gradle(.kts). :slight_smile:

I am not, you are just misinterpreting the instructions.

Can you please elaborate? I’m too stupid to understand.

Can you please elaborate? I’m too stupid to understand.

Or maybe I did not understand what you tried to say.
Where do you need to add “KGP and all other plugins you have in your convention plugin including application plugin” and why?