Applying and configuring an other plugin from a plugin

Hi all,

I’m getting started with plugins and I struggle a bit to understand how a plugin can apply another plugin. I would like to create a KotlinApplicationPlugin which applies both the Kotlin plugin and the application plugin. Roughly, I’d like my build.gradle file to look like this:

apply<com.github.jdemeulenaere.plugins.KotlinApplication>()

application {
    mainClass.set("com.github.jdemeulenaere.application.MainKt")
}

For the application side of things, my plugin simply calls project.apply(plugin = "org.gradle.application") in my plugin (in buildSrc/src/main/kotlin/com/github/jdemeulenaere/plugins/KotlinApplication.kt). Unfortunately if I do that the build fails with:

$ ./gradlew :application:run

> Configure project :application
e: application/build.gradle.kts:3:1: Expression 'application' cannot be invoked as a function. The function 'invoke()' is not found
e: application/build.gradle.kts:3:1: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val PluginDependenciesSpec.application: PluginDependencySpec defined in org.gradle.kotlin.dsl
e: application/build.gradle.kts:4:5: Unresolved reference: mainClass

It looks like the application {} configuration is not recognized even though my KotlinApplication is applied to the project. Is it because the file is still executing the top-level functions like application {} before the "org.gradle.application" plugin is actually applied by my plugin? What is the typical way to apply a plugin in another plugin and let the build script still configure it and/or have my plugin also configure it? I think I can make it work by creating my own extension that captures an Action<JavaApplication> then apply that project.extensions.findByType<JavaApplication>() but I’m not sure that this is how I’m supposed to do it?

Thanks a lot for the help!

Update: It looks like the issue comes from the fact that I’m using a Kotlin DSL build script. AFAIK I have to choose between:

// build.gradle.kts
apply<KotlinApplication>()

configure<JavaApplication> {
    mainClass.set("com.github.jdemeulenaere.application.MainKt")
}

dependencies {
    add("implementation", project(":library"))
}

or

// build.gradle
apply plugin: KotlinApplication

application {
    mainClass = "com.github.jdemeulenaere.application.MainKt"
}

dependencies {
    implementation project(":library")
}

Unless there is a way to tell gradle about which plugins my plugin is applying and therefore which DSLs are available?

Your problem simply is, that you apply your plugin the legacy way. The necessary type-safe accessors for a Kotlin DSL are generated for plugins applied using the plugins { ... } block. So apply your plugin using the plugins block instead of the legacy way and it will work as you want.

Unfortunately IIUC there is no way to apply a plugin with plugins {} and using the class name, and therefore I can’t apply a plugin that is in buildSrc/src right? Having my plugin in buildSrc/ is important for me because those plugins will be used in a monorepo and I want to be able to make a change to a plugin that will directly be applied to all my projects, without having to publish an intermediate jar in-between.

IIUC there is no way to apply a plugin with plugins {} and using the class name

That’s correct, you always apply using the ID in the plugins { ... } block.

and therefore I can’t apply a plugin that is in buildSrc/src right?

That’s not correct in any way.
Why should this be the case? o_O
Just apply the Gradle Plugin Development Plugin and define your plugin in your build script or create the plugin meta-inf file manually if you prefer and then apply it via ID as usual.

It works indeed! I had tried it yesterday and it broke but that was probably because of some other error I made somewhere else. Thanks a lot, I’m pretty happy with the result so far, it makes the configuration of my build files much less verbose :slight_smile:

1 Like