2

We have an existing plugin project which configures various things (including static analysis), where we want to apply the plugin to the project itself.

The way this currently works for plugins written in Java is, you add the Java src dir to the buildSrc project, and then classes built there can be used in the main project. So I'm trying to get the same thing working for plugins written as Kotlin scripts.

But when I try to build it, compiling buildSrc fails with:

e: C:\Users\Trejkaz\Documents\test\self-applying-gradle-plugin\src\main\kotlin\example.common.gradle.kts: (1, 1): Unresolved reference: allprojects

> Task :buildSrc:compileKotlin FAILED

What's missing in order to make this work?

Further investigation:

  • If I put a copy of the files in buildSrc/src/main/kotlin, that works.
  • If I put a copy of the files in buildSrc/src/main/kotlin2 and use srcDirs to set that directory, that fails too. So it really looks like something isn't letting me relocate sources at all.

I pushed a repo to play with this here but what follows is the contents of the build scripts in case it's ever deleted.

The main build.gradle.kts:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    `java-gradle-plugin`
    `kotlin-dsl`

    // Matching version in Gradle
    kotlin("jvm") version "1.5.31"
}

apply(from = "common-build.gradle.kts")

apply(plugin = "example.common") //  trying to apply the compiled plugin here

group = "org.example"
version = "1.0-SNAPSHOT"

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "11"
}

In buildSrc/build.gradle.kts, we have this - note that it adds a source dir for the sources in the main directory:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    `java-gradle-plugin`
    `kotlin-dsl`

    // Matching version in Gradle
    kotlin("jvm") version "1.5.31"
}

apply(from = "../common-build.gradle.kts")

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "11"
}

kotlin {
    sourceSets["main"].kotlin.srcDir("../src/main/kotlin")
}

common-build.gradle.kts has everything common to both build scripts which we've figured out how to move to a common location (notably, the KotlinCompile isn't there, later I'll figure out why I can't move that as well):

repositories {
    mavenCentral()
}

dependencies {
    // Needed to compile Kotlin stuff but not added by the plugin for some reason
    "implementation"("org.jetbrains.kotlin:kotlin-scripting-jvm")
}

The plugin script, src/main/kotlin/example.common.gradle.kts, contains:

allprojects {
    // Configure something
}
Hakanai
  • 12,010
  • 10
  • 62
  • 132
  • It's an interesting challenge to apply a plugin to the project itself... Maybe publishing it to Maven Local, or [a local dir](https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver)? Referencing `./src/main/kotlin` from the main project in buildSrc is definitely not a good thing to do! If you just want to test, you can also try using [Gradle TestKit](https://docs.gradle.org/current/userguide/test_kit.html) and setting the project dir to your root project. I don't think composite builds and `includeBuild(...)`can help. – aSemy Jun 10 '22 at 15:32
  • @aSemy we do the same currently referencing `../src/main/java` from `buildSrc`, and that is working fine, but I remember it was a challenge to figure out how to get that to work as well, and people said the same thing at the time. I think most people don't make plugins which perform checks, so it just isn't something they have ever thought about doing, which just makes people not recommend doing it on gut instinct. But certainly if you _do_ write a plugin which checks code, having the code in that plugin also checked is a fairly natural thing to want. (I can also say we're not the only ones!) – Hakanai Jun 13 '22 at 12:07
  • Another thought: you could try creating a Gradle Sync task in `./buld.gradle.kts` to copy your `./src/main/kotlin/my-plugin` code into `./buildSrc/src/main/kotlin/my-plugin`, along with a `my-plugin.gradle.kts` DSL plugin. You could set the Sync task to run after a successful build. It's a little bit hacky, but it might work. I'd git-ignore the `./buildSrc/src/main/kotlin/my-plugin` package. Actually it might be better to sync it to an included build, so the separation is clearer. – aSemy Jun 19 '22 at 21:31
  • @aSemy come to think of it, I haven't yet figured out whether it works if I just copy all the source files into buildSrc... that feels very similar to just adding a srcDir, so I'm not sure whether even that will work without trying it. **Edit** to say, I quickly tried it out, and for some reason, copying the files in there _does_ work, so now I'm curious to know why. As a last resort, this workaround might work, but it really seems like setting the srcDirs should also work. – Hakanai Jun 21 '22 at 05:22
  • At this point I am guessing this is a bug in the `kotlin-dsl` plugin so that's maybe where I should be digging. – Hakanai Jun 21 '22 at 05:34
  • https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins_vs_cross_configuration – Martin Zeitler Jun 21 '22 at 05:54
  • @MartinZeitler if your complaint is about the call to `allprojects`, I know. That was just an example - _any_ calls to the API don't work inside that file, not just that one. – Hakanai Jun 23 '22 at 03:31

1 Answers1

2

This turns out to be a bug in Gradle's kotlin-dsl plugin.

The workaround is to add the source dirs before applying the plugin.

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    `java-gradle-plugin`
    `kotlin-dsl` apply false

    // Matching version in Gradle
    kotlin("jvm") version "1.5.31"
}

apply(from = "../common-build.gradle.kts")

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "11"
}

kotlin {
    sourceSets["main"].kotlin.srcDir("../src/main/kotlin")
}

// Workaround for https://github.com/gradle/gradle/issues/21052 -
// apply kotlin-dsl plugin last, because it erroneously fetches source dirs eagerly.
apply(plugin = "org.gradle.kotlin.kotlin-dsl")

Hakanai
  • 12,010
  • 10
  • 62
  • 132