Are you using Kotlin in your project? Let’s use the Kotlin DSL to configure the builds of our project with the Gradle Kotlin DSL. In the post, we will cover several configuration tricks I found very useful for my Kotlin projects in Gradle.
New to Gradle Kotlin DSL? Take a look at the previous post for practical recommendations on migrating from Groovy to Kotlin build scripts.
Enable Kotlin Plugin
The quickest way to enable Kotlin in your Gradle build is
plugins {
kotlin("jvm") version "1.3.21"
}
Gradle adds the kotlin()
helper function to simplify your build scripts. The Kotlin standard
library dependencies can be configured in the same way with the help of different kotlin
functions
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
}
You don’t need to include the Kotlin version dependencies. The Kotlin plugin for Gradle configures the dependencies resolution, so every Kotlin standard library dependency will use the same Kotlin version out of the box.
Configuring Kotlin Compilation Tasks
You can configure Kotlin in the Gradle scripts in Groovy. It will look something like this
compileKotlin {
kotlinOptions {
// ...
suppressWarnings = true
}
}
Do you see the downside? Yes, you may have seen there are compileKotlin
and compileTestKotlin
tasks,
which means the second one is not configured. Let’s fix it in the Kotlin script.
The code from above will unfortunately not work in a Kotlin Gradle DSL. We need to refer explicitly to
a task element of the tasks
property of a project
. Let’s not forget to configure
all the Kotlin tasks for the project.
Here is what I found works for configuring all the Kotlin tasks in a Kotlin DSL in one fell swoop:
import org.jetbrains.kotlin.gradle.tasks.*
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = listOf("-progressive")
}
}
The asks.withType<T>
function will run the lambda for every existing and newly
added task of the given type.
Kotlin Version in Dependencies
With transitive dependencies it could turn out that we use are using older libraries in our project. Kotlin guarantees compatibility, but still, I’d make sure I am using the actual libraries when possible.
Let’s check to make sure we are using the same version of the kotlin-stdlib
and
the other kotlin-*
libraries, like for instance, kotlin-reflect
.
The following Gradle script works for this in Kotlin:
import org.jetbrains.kotlin.gradle.plugin.*
//https://youtrack.jetbrains.com/issue/KT-19788
val kotlinVersion by lazy {
plugins.withType<KotlinBasePluginWrapper>().map { it.kotlinPluginVersion }.distinct().single()
}
configurations.forEach { config ->
config.resolutionStrategy.eachDependency {
if (requested.group == "org.jetbrains.kotlin" && requested.name.startsWith("kotlin-")) {
useVersion(kotlinVersion)
}
}
}
You may be asking yourself, why do we need to use code trickery for the kotlinVersion
variable?
The simple answer is, in Gradle we cannot use variables inside the plugins{..}
block.
This means that it’s impossible to share the Kotlin version as a variable between the plugins{..}
block and the rest of the script.
The ancient buildscript{..}
block does allow it. I prefer the shorter and more explicit
plugins{..}
block instead to configure Gradle plugins in builds.
You may want to vote on this issue KT-19788 to make the Gradle Kotlin plugin declare its version.
Kotlin-Dsl Plugin and buildSrc
There is yet another experimental plugin, maintained by Gradle, that helps us to use Kotlin for build logic development in Gradle.
The plugin is called kotlin-dsl
, you can find more details on it
in the
documentation.
The plugin is an excellent choice for a buildSrc
project. It simplifies the setup,
configures all the dependencies, including kotlin-stdlib
and Gradle’s own build script related
classes. We will discuss buildSrc
projects and my findings in more detail in the next post.
Conclusion
Kotlin as a statically typed programming language seems to play well with writing Gradle build scripts. Thanks to the static type inference, the Kotlin compiler detects errors earlier and shows helpful compilation error messages and warnings. Both the IDE and the compiler use information about types to infer the available functions and properties in a given scope, even inside a 5th level nested lambda with receivers.
You may remember from the previous post that our example project is written entirely in Kotlin, in that post we learned how to configure Kotlin compilation tasks and dependencies in Kotlin.
I will cover more aspects in the coming posts, stay tuned! Check out the:
- first post - First steps of the migration
- third post - Diving deeper with plugins, extension, buildSrc
- fourth post - Groovy Closure and Kotlin DSL