Logo

dev-resources.site

for different kinds of informations.

Understanding (a bit of) the Gradle Kotlin DSL

Published at
1/12/2025
Categories
gradle
kotlin
dsl
androiddev
Author
feregri_no
Categories
4 categories in total
gradle
open
kotlin
open
dsl
open
androiddev
open
Author
10 person written this
feregri_no
open
Understanding (a bit of) the Gradle Kotlin DSL

Ever since I started learning Kotlin I have been intrigued by what is going on behind the scenes in the build.gradle.kts file. At first glance, the file looks confusing, and for a newbie like me, it is hard to comprehend how the snippet below even has Kotlin-valid syntax.

plugins {
  kotlin("jvm") version "1.9.25"
  kotlin("plugin.spring") version "1.9.25"
  id("org.springframework.boot") version "3.4.1"
}
Enter fullscreen mode Exit fullscreen mode

To fully understand how is this Kotlin there are a couple of mechanisms at play here that you must understand.

  • Lambdas with receivers
  • Infix functions

What is plugins?

Internally, plugins is a function whose signature looks somewhat like this:

fun plugins(block: PluginDependenciesSpecScope.() -> Unit): Unit
Enter fullscreen mode Exit fullscreen mode

This tells us that plugins is a function that takes a single argument named block which known as a lambda with receiver.

A lambda with receiver is a special kind of lambda that allows you to call methods on a specific object (the receiver) within the lambda's body without explicitly referencing it.

For example, in our function signature above, we can see that the receiving type is PluginDependenciesSpecScope from the package org.gradle.kotlin.dsl. This lambda function does not receive any arguments, as indicated by the empty parentheses after the receiver type specification .(), and it does not return anything meaningful, as indicated by the -> Unit.

A potential implementation of the plugins function could be:

fun plugins(block: PluginDependenciesSpecScope.() -> Unit): Unit {
    val scope = PluginDependenciesSpecScope()
    scope.block()
}
Enter fullscreen mode Exit fullscreen mode

While Gradle is way more complex, this is just a very simple example of how the function could be implemented. The key part is that behind the scenes, an instance of PluginDependenciesSpecScope is created for you, and block, being an extension method, can be called directly on this instance.

What is kotlin and id?

Both kotlin and id are extension methods of PluginDependenciesSpecScope, the reason behind one being able to call these methods directly without specifying the receiver is because this receiver is implicit, but if you wanted to, you reference it directly using the this keyword:

plugins {
  this.kotlin("jvm") version "1.9.25"
  this.kotlin("plugin.spring") version "1.9.25"
  this.id("org.springframework.boot") version "3.4.1"
}
Enter fullscreen mode Exit fullscreen mode

Both these methods follow the fluent pattern, meaning they return the same object they acted on. For example, the signature of the kotlin method looks like this:

fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec
Enter fullscreen mode Exit fullscreen mode

The difference between kotlin and id

Behind the scenes, kotlin is just a shorthand for id, when you call kotlin("jvm"), this methods calls id but appends the prefix org.jetbrains.kotlin. to whatever string you passed, so kotlin("jvm") is equal to id("org.jetbrains.kotlin.jvm").

So, what is version?

The missing piece in my understanding of the Kotlin Gradle DSL is the version in the plugin definition, at first I thought this was a keyword, however, it turns out is an extension method, but not any kind of method but an infix one.

The signature of the version method looks like:

infix fun PluginDependencySpec.version(version: String): PluginDependencySpec
Enter fullscreen mode Exit fullscreen mode

As you can see, it takes operates on a PluginDependencySpec but takes in a String and returns a PluginDependencySpec.

An infix function can be called without using dot notation and parentheses. It's declared using the infix keyword and must have a single parameter. This allows for more readable, natural language-like expressions in code.

So we could further change our plugin block definition to:

plugins {
  this.kotlin("jvm").version("1.9.25")
  this.kotlin("plugin.spring").version("1.9.25")
  this.id("org.springframework.boot").version("3.4.1")
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

On Lambdas with receivers

While the approach of using lambdas with receivers is powerful for creating DSLs, it can initially be confusing for those new to Kotlin or Gradle. This technique allows for a more natural, declarative syntax, enabling code that looks like special language constructs when it's actually just method calls on an implicit receiver.

Once grasped, this results in more readable and expressive code that closely resembles the domain it's describing - in this case, Gradle build configurations. However, the implicit nature of the receiver can be a source of confusion for beginners trying to understand what's happening "behind the scenes.”

On fluent interfaces and infix methods

The fluent pattern and infix methods aim to create a chain of method calls that read almost like natural language. In theory, this should enhance readability and make the build script more intuitive. However, for those not deeply familiar with Gradle, Kotlin, or the concept of DSLs, this syntax can initially seem magical or confusing.

It's not immediately obvious that version is a method, for instance, or why we can omit parentheses in some places but not others. While it allows for a clear separation of concerns, each method in the chain responsible for a specific aspect of plugin configuration requires some learning and adjustment to fully appreciate and utilise effectively.

As for me…

While I'm still getting accustomed to the Gradle Kotlin DSL and occasionally find it confusing, I've come to appreciate its power and elegance. Building DSLs is indeed one of Kotlin's strengths, and understanding these concepts opens up exciting possibilities for creating expressive and maintainable code.

By exploring the mechanics behind Gradle's Kotlin DSL, I feel I have demystified a bit of its syntax and gained insights into advanced Kotlin features that can be applied in various contexts. When mastered, these patterns and techniques can significantly enhance our ability to write clean, readable, and powerful code.

I hope this dive into the Gradle Kotlin DSL has been as enlightening for you as it has been for me. Whether you're a seasoned Kotlin developer or just starting out, understanding these concepts can greatly improve your grasp of Gradle and Kotlin's capabilities.

gradle Article's
30 articles in total
Favicon
Understanding (a bit of) the Gradle Kotlin DSL
Favicon
Zero Config Spring Batch: Just Write Business Logic
Favicon
JeKa: The Simplest Way to Create Uber and Shade Jars
Favicon
JeKa: The Simplest Way to Publish on Maven Central
Favicon
Gradle extensions part 2: Now with shenanigans
Favicon
Wednesday Links - Edition 2024-11-27
Favicon
A brand new Java scaffolding has been born today for Make Java Great Again!
Favicon
Wednesday Links - Edition 2024-10-16
Favicon
Gradle 8.11: Faster Configuration Cache and Improved Configuration Time
Favicon
react-native duplicate class problem
Favicon
Breaking the build 😝 : Demystifying Gradle
Favicon
Wednesday Links - Edition 2024-09-11
Favicon
One click dependencies fix
Favicon
ACAB: Fire the (code style) cop in your head
Favicon
Telltale: Automating Experimentation in Gradle Builds
Favicon
Minecraft Modpack Development Update: Beta Test and Musical Additions
Favicon
Gradle upgrade
Favicon
Announcing Dependency Analysis Gradle Plugin 2.0.0!
Favicon
Wednesday Links - Edition 2024-07-24
Favicon
Resource observability case study: jemalloc in Android builds
Favicon
How store signing keystore.
Favicon
Simple way to store secrets in Android Project.
Favicon
Developing a Custom Gradle Plugin for Formatting and Static Analysis
Favicon
Gradle Commands Cheat Sheet
Favicon
Wednesday Links - Edition 2024-04-24
Favicon
Gradle DSL: Configurando JaCoco
Favicon
Unearthing the Quirk: Dealing with File Access Issues that arise from Resource Optimization in Android Applications
Favicon
🍒 Cherry-Picked Nx v18.2 Updates
Favicon
Making Your Android Project Modular With Convention Plugins
Favicon
Kradle 9.0: Revolutionizing the JVM Ecosystem with Kotlin at its Core!

Featured ones: