openlang

After the Kotlin DSL move, subprojects can no longer find implementation

Moving an old multi-project build from the Groovy DSL to the Kotlin DSL. The root build.gradle had this block.

subprojects {
    apply plugin: 'java-library'
    repositories { mavenCentral() }
}

The root I converted mechanically.

subprojects {
    apply(plugin = "java-library")
    repositories { mavenCentral() }
}

In order, then.

The root script compiles. No problem there.

In the subproject core/build.gradle.kts, the line implementation(project(":util")) inside dependencies stops script compilation with an unresolved reference to implementation. The same line worked under Groovy.

Case three is the one I can't explain. Put plugins { id("java-library") } at the top of that same file and the error goes away, even though the root was already applying the plugin. Same plugin, same project, so why do only the accessors disappear? And is repeating a plugins block in every subproject really the intended fix?

The conversation continues 2

Follow the reasoning
  1. Permalink

    Plugins block, yes. Not copied into every subproject though.

    Gradle builds the accessors from what that script's own plugins block applies. A plugin that arrives any other way, apply(plugin = ...) or a root subprojects block, works fine at run time and gets no accessors. The docs list exactly those cases.

    Delete the subprojects block and put one convention plugin in buildSrc.

    // buildSrc/build.gradle.kts
    plugins {
        `kotlin-dsl`
    }
    repositories { gradlePluginPortal() }
    
    // buildSrc/src/main/kotlin/java-conventions.gradle.kts
    plugins {
        `java-library`
    }
    repositories { mavenCentral() }
    
    // core/build.gradle.kts
    plugins {
        id("java-conventions")
    }
    dependencies {
        implementation(project(":util"))
    }

    The subproject's plugins block applies the convention plugin, that pulls in java-library, and the accessors come with it.

  2. Permalink

    Agreed as the end state. While some subprojects still get the plugin from the root block, though, the Kotlin DSL has a fallback that needs no accessors.

    dependencies {
        "implementation"(project(":util"))
    }
    configure<JavaPluginExtension> {
        toolchain.languageVersion.set(JavaLanguageVersion.of(17))
    }

    It reads worse, and a typo in the string only shows up when the build runs (an unknown configuration name fails at that point, not during script compilation). That cost is fine for the length of the migration and not after it.

Other sides of the same question

  1. tryEval passed a package I had broken on purpose

    Wrote a small sweep over an overlay that wraps each package in builtins.tryEval and lists the ones that fail. It reported eve…

    Replies 4nix
  2. for_each keys are addresses, not labels

    A for_each key isn't a label. It's half of the resource address, terraform_data.queue["prod-orders"], and the state tracks th…

    Replies 6hcl
  3. Launching the consumer on a stopped event

    Got a question about a supervisor rule that treats a stopped process as a finished one, and a tiny repro makes the problem ob…

    Replies 17bashpython