1

I'm trying to build convention plugins for a common configuration, e.g. ktlint. They are stored under build-configuration/plugin/convention.

class KtlintConventionPlugin : Plugin<Project> {

   override fun apply(target: Project) {
      return with(target) {
         pluginManager.apply(deps.plugins.ktlint.get().pluginId)

         extensions.configure<KtlintExtension> {
            // ...
         }
      }
   }
}

build-configuration/plugin/convention/build.gradle.kts

plugins {
   `kotlin-dsl`
}

group = "com.example"

dependencies {
   compileOnly(deps.kotlin.jvm.gradle.plugin)
   compileOnly(deps.ktor.gradle.plugin)
   compileOnly(deps.ktlint.gradle.plugin)

   compileOnly(files(deps.javaClass.superclass.protectionDomain.codeSource.location))
}

gradlePlugin {
   plugins {
      register("ktlint-plugin") {
         id = "ktlint-plugin"
         implementationClass = "KtlintConventionPlugin"
      }
   }
}

I also have an extension function to get dependencies defined in TOML file (stored in gradle/deps.versions.toml) in build-configuration/plugin/convention/src/main/kotlin/com/example/VersionCatalog.kt

import org.gradle.accessors.dm.LibrariesForDeps
import org.gradle.api.Project
import org.gradle.kotlin.dsl.the

internal val Project.deps: LibrariesForDeps
   get() = the()

build-configuration/settings.gradle.kts

dependencyResolutionManagement {
   repositories {
      gradlePluginPortal()
      mavenLocal()
      mavenCentral()
   }
   versionCatalogs {
      create("deps") {
         from(files("../gradle/deps.versions.toml"))
      }
   }
}
rootProject.name = "build-configuration"
include(":plugin:convention")

When I include ktlint plugin in root build.gradle.kts, Gradle synchronization always fails.

An exception occurred applying plugin request [id: 'ktlint-plugin']
> Failed to apply plugin 'ktlint-plugin'.
   > Type org.gradle.accessors.dm.LibrariesForDeps not present

I can see the class generated in .gradle/.../LibrariesForDeps, but under the project's root, not the plugin's one.

The whole build-configuration module is included in root settings.gradle.kts:

includeBuild("build-configuration")
Aleksei Tirman
  • 4,658
  • 1
  • 5
  • 24
peter.o
  • 3,460
  • 7
  • 51
  • 77

2 Answers2

2

Using the libs doesn't work in convention plugins, but there are some workarounds in the issue: https://github.com/gradle/gradle/issues/15383

But I don't think you need it. Instead the version can be defined in a single place: the included build's build.gradle.kts, which has access to the version catalog DSL.

  1. define the Maven coordinates of the ktlint plugin in libs.versions.toml

    [libraries]
    
    gradlePlugin-ktlint = { module = "org.jlleitschuh.gradle:ktlint-gradle", version = "11.1.0" }
    

    I prefer prefixing such dependencies with gradlePlugin to distinguish them from 'regular' project dependencies.

    (The Maven coordinates are listed in the Gradle Plugin Portal, under the 'legacy' application as a classpath dependency)

  2. Add a dependency on ktlint in the included build, build-configuration/build.gradle.kts

    plugins {
      `kotlin-dsl`
    }
    
    dependencies {
      implementation(libs.deps.gradlePlugin.ktlint)
    }
    
  3. Now the ktlint plugin will be available on the classpath, and you can apply it as normal in a convention plugin, no version necessary.

    In a precompiled script plugin:

    // ./build-configuration/src/main/kotlin/my-ktlint-convention.gradle.kts
    
    plugins {
      id("org.jlleitschuh.gradle.ktlint") // version is provided by build.gradle.kts
    }
    

    Or in a traditional class plugin (which will require registering via gradlePlugins {}):

    // ./build-configuration/src/main/kotlin/KtlintConventionPlugin.kt
    class KtlintConventionPlugin : Plugin<Project> {
      override fun apply(target: Project) {
        target.apply(plugin = "org.jlleitschuh.gradle.ktlint")
        target.extensions.configure<KtlintExtension> {
             // ...
        }
      }
    }
    

See also

For more information about buildSrc convention plugins, these answers are related:

aSemy
  • 5,485
  • 2
  • 25
  • 51
  • Thanks for the reply, I've followed [this repo](https://github.com/MaximillianLeonov/Cinemax/tree/main/build-logic) and [this one](https://github.com/PoisonedYouth/gradle-convention-plugin). The former looks basically what I wanted to achieve. However, I cannot sync my project, I'm most likely missing something. – peter.o Jan 31 '23 at 02:53
0

I have created a new project and followed this approach. There is only one difference. I had to have the block below in both settings.gradle.kts files, root, and build-configuration:

dependencyResolutionManagement {
   versionCatalogs {
      create("deps") {
         from(files("../gradle/deps.versions.toml"))
      }
   }
}

It just doesn't work for me without this duplication.

Also, I'm not using any module within the build-configuration plugin.

peter.o
  • 3,460
  • 7
  • 51
  • 77