How can I reuse library declarations in Gradle Kotlin DSL?

Viewed 557

Background:

We are trying to migrate to Gradle's Kotlin DSL, primarily for the additional type safety.

Some of our library definitions are quite large (20+ exclude calls in some extreme cases) so we currently have a giant dependencies.gradle Groovy script which declares a giant map of all libraries, which can then be used like libraries.blah. I'm trying to come up with a working way to do the same in Kotlin.

Investigation:

Dependency constraints (java-platform plugin) allow centralising the version number for the dependency, but that does nothing for the exclusions, and the exclusions are most of the problem in our case. (I'm sure we will eventually use java-platform at some point too.)

The most elegant-looking solution I found was in this one blog post which suggests declaring Kotlin objects, like this:

object Libraries {
    val guava = "..."
}

You can then supposedly use the object like Libraries.guava. But this doesn't actually work, at least in Gradle 6.9.1, because of an issue in Kotlin itself. (I'm assuming that it worked on some older version and got broken by a Kotlin compiler upgrade.)

The workaround specified on the ticket comes out like:

class Libraries(dependencies: DependencyHandler) {
    val guava = dependencies.create(...)
}

val libraries by project.extra { Libraries(dependencies) }

This gets me closer to a working solution in that my dependencies.gradle.kts file now compiles, but I still can't find a way to use the Libraries class I've defined. If you do this:

val libraries: Libraries by project.extra

Kotlin complains that Libraries is not defined. So it's as if one build script can't even export classes for another build script to use.

More Context

The actual project structure is quite complex, and looks a bit like this:

─ repo root
  ├─ mainproject1
  │  ├─ subproject11
  │  │  └─ build.gradle.kts
  │  ├─ subproject12
  │  │  └─ build.gradle.kts
  │  ├─ build.gradle.kts (references dependencies.gradle.kts)
  │  └─ settings.gradle.kts
  ├─ mainproject2
  │  ├─ subproject21
  │  │  └─ build.gradle.kts
  │  ├─ subproject22
  │  │  └─ build.gradle.kts
  │  ├─ build.gradle.kts (references dependencies.gradle.kts)
  │  └─ settings.gradle.kts
  └─ shared
     └─ gradle
        └─ dependencies.gradle.kts

Question:

How are other people with enormous builds with >100 dependencies solving this problem?

1 Answers

Workaround: use buildSrc to instantiate Libraries object.

  1. Create the following directories structure:
.
├── buildSrc
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── kotlin
│               └── Libraries.kt
├── settings.gradle.kts
└── build.gradle.kts
  1. buildSrc/build.gradle.kts is minimalistic:
plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}
  1. Libraries.kt:
object Libraries {
    const val guava = "com.google.guava:guava:31.0.1-jre"
}
  1. Usage (root build.gradle.kts):
plugins {
    `java-library`
}

dependencies {
    implementation(Libraries.guava)
}

repositories {
    mavenCentral()
}
Related