Mixed Groovy/Kotlin DSL multiproject build, How can Kotlin read ext variables

Viewed 657

I'm exploring converting our Groovy DSL based build files to Kotlin and would like to do this incrementally. However, I can't figure out how to read ext variables set in the top level build.gradle in a sub-project build.gradle.kts.

My experimental repository is available here

It has this layout:

.
├── README.md
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── groovy
│   └── build.gradle
├── kts
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── java
│               └── xyz
│                   └── abc
│                       └── def
│                           └── Main.java
└── settings.gradle

The top level file defines an ext variable containing common library versions like so:

plugins {
    id("java")
}

ext.libraries = [ shared: "xyz.abc.def:art:1.2" ]

There are two sub-projects, one using the Groovy DSL and one using the Kotlin DSL. They both attempt to declare a dependency on libraries.shared.

╰─➤  cat groovy/build.gradle 
plugins {
    id("java")
}

dependencies {
    implementation(libraries.shared)
}

╰─➤  cat kts/build.gradle.kts 
plugins {
    id("java")
}

dependencies {
    implementation(libraries.shared)
}

settings.gradle includes both projects.

╰─➤  cat settings.gradle 
include "groovy"
include "kts"

When I run ./gradlew projects I get this error:

  Line 6:     implementation(libraries.shared)
                             ^ Unresolved reference: libraries

When the include for the kts sub-project is commented out it succeeds, displaying the groovy sub-project.

I've tried a number of different things to reference the libraries, such as project.extra or extra but so far haven't hit anything that works.

EDIT:

After digging around in the Gradle API docs I came up with this, which works but is absolutely horrid.

implementation((project.getParent()!!.extra.properties["libraries"]!! as HashMap<String, String>).get("shared")!!);
1 Answers

You could use this in the kotlin script

plugins {
    java
}

val libraries: Map<String, String> by rootProject.extra

repositories {
    mavenCentral()
}

dependencies {
    implementation(libraries["shared"]!!)
}

Edit

If you are using a current (7 at time of writing) version of Gradle, you could try out the new version catalogues feature -- which is in preview at time of writing, so may be a bumpy road to take...

To do this, you can remove your ext.libraries from your root project, and replace it with this in settings.gradle

enableFeaturePreview('VERSION_CATALOGS')

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            alias('shared').to('com.bloidonia:ascii-image:1.1')
        }
    }
}

Then in both Groovy and Kotlin DSL build scripts, you can reference these dependencies via:

    implementation(libs.shared)

Edit 2 -- after some coffee

Ahhh, so I think this answers Jeff's question in the comments, and shows an alternative

Yes, we could add a function to ext in the root Project like so:

ext.libraries = [ shared: "com.bloidonia:ascii-image:1.1" ]
ext.library = { String name ->
    ext.libraries."$name"
}

And then in the Kotlin project, we can do:

val library: groovy.lang.Closure<String> by rootProject.extra

dependencies {
    implementation(library("shared"))
}
Related