Plugins and `apply from` in the Kotlin DSL


(Rob Fletcher) #1

I often use supplementary .gradle files to apply standard behavior that subprojects can use or not as appropriate. For example I might have a gradle/publishing.gradle with all the config for the Bintray plugin, a gradle/junit.gradle with all the config for testing, etc. If a sub-module needs that behavior I can include it with apply from: "$rootDir/gradle/publishing.gradle.

I’m attempting to port my project’s build to the Kotlin DSL and finding it almost impossible to follow this pattern. Problems I’m running into are:

  • If I add the plugin in the sub-project’s build.gradle.kts the included files can’t reference the plugin’s classes or DSL extensions.
  • If I add the plugin using a plugins block in the included file, the same is true.
  • If I create a buildscript block in the included file with a classpath dependency on the plugin then apply the plugin by class (e.g. apply<NebulaKotlinPlugin>()) I can reference classes and get the DSL to work but I can’t override config in a particular subproject (e.g. referencing an external lib’s documentation URL in Dokka config) because then the subproject build.gradle.kts can’t reference the DSL/classes of the plugin.
  • If I apply the plugin or use a buildscript block in both the build.gradle.kts and the included file the scripts compile but fail at runtime as the plugin’s classes are loaded with separate classloaders.

Is there actually a way to do this? I’ve looked at the modularity sample in the kotlin-dsl repo but it doesn’t deal with plugins.

I don’t think I can be the only person who’d want to do such a thing. The only alternatives seem to be copy-pasting common config into every sub-project’s build.gradle.kts or applying everything in a subprojects block in the root build.gradle.kts which assumes every subproject needs all the same things.


(Stefan Oehme) #2

The samples don’t show this well, but the way to do this is to put script plugins into buildSrc/src/main/kotlin. The plugin ID is equal to the file name. They are then compiled like a normal plugin in buildSrc, minus the boilerplate class definiton. If you need any external dependencies for your plugins, they should be dependencies of buildSrc.

@eskatos there should be a sample for this. The precompiled plugin sample is too complicated for what most people want. The buildSrc samples on the other hand only show traditional plugins. An “organizing build logic” sample would be ideal.


(Rob Fletcher) #3

The plugin should be a .gradle.kts file?

I’ve added one at that location called publishing.gradle.kts added id("publishing") to my subproject’s build file but I just get Plugin [id: 'publishing'] was not found in any of the following sources:

Edit: never mind. Needed to add kotlin-dsl to the buildSrc itself. I have other errors now but at least it seems to be finding the plugin.


(Louis Jacomet) #4

A minimal example of this, with a plugin defined as a Kotlin class can be found here:


(Stefan Oehme) #5

@ljacomet that’s a traditional plugin though, not a script. We need a sample that shows the same with scripts.


(Rob Fletcher) #6

I’m not clear what I need to put in the file. Everything I’m trying is unrecognized.

Should I include plugins using the plugins { id("whatever") } DSL? Or by including them on the buildSrc compile classpath and then trying to apply them by type? I’ve tried both with no success. Some other way? An example here of a project that already does this would be super helpful.

I can get somewhere with @ljacomet’s method but there’s some boilerplate involved so I’d love to understand the script plugin approach.


(Rob Fletcher) #7

I’m able to use something like https://github.com/gradle/kotlin-dsl/tree/master/samples/multi-project-with-buildSrc but it sounds like that’s not exactly what you’re talking about.


(Paul Merlin) #8

There still is some ceremony to use precompiled script plugins. Agreed that the project-with-buildSrc sample should be enhanced to demonstrate this. The precompiled-script-plugins do demonstrate their use but in a “binary plugin” context instead of for local build logic. We’ll look into that.

In the meantime here are some instructions for using precompiled script plugins in buildSrc:

#1 add to your buildSrc/build.gradle.kts:

plugins {
    `java-gradle-plugin`
    `kotlin-dsl`
    `kotlin-dsl-precompiled-script-plugins`
}

dependencies {
    // the dependencies used by your precompiled script plugins
}

#2 create a precompiled script at buildSrc/src/main/kotlin/my-plugin.gradle.kts

plugins { // Can use the plugins block but dependencies must be declared, see above
    java
    id("some-other-plugin")
}

// Custom build logic

#3 in your build scripts, you can then apply the my-plugin plugin, e.g. in sub-project/build.gradle.kts:

plugins {
    id("my-plugin")
}

All build scripts can see classes from plugin coming from buildSrc.

The gradle/kotlin-dsl build make use of those precompiled scripts: https://github.com/gradle/kotlin-dsl/tree/develop/buildSrc/src/main/kotlin/plugins

HTH


(Rob Fletcher) #9

Wonderful. I’ll give that a try. Thanks for the pointers.


(Rob Fletcher) #10

Appreciate the help @eskatos. I have things working nicely now: https://github.com/robfletcher/strikt/tree/master/buildSrc/src/main/kotlin