Using Gradle Script Kotlin for Android
As you know, we use Groovy
to write our Gradle build scripts and it has pros and cons. Groovy
is a dynamic language which means that it’s not statically typed, making difficult to write or modify our gradle scripts without making many mistakes as we don’t have autocomplete there.
Last year, Gradle decided to start using Kotlin to writing Gradle build scripts creating, what they call, Gradle Kotlin Script. The project is moving fast (0.9.0 version at the time of writing this article) but, on the other hand, there is practically no documentation.
So, I want to show you how to use Gradle Kotlin Script to create Gradle build scripts written in Kotlin in one Android project.
First Step: Adding Gradle Kotlin Script to the project
We need an Android project to configure the Gradle Kotlin Script on, so I have created a new empty basic project called gradle-script-kotlin-example with Android Studio. The project looks like this way after the creation wizard:
It is pretty easy, only a matter of completing the creation wizard. Now, we have to add Gradle Kotlin Script to the project, it’s not trivial but it’s not too complicated.
At this moment, Gradle Kotlin Script is bundled inside a Gradle distribution so we need to change the Gradle distribution URL modifying the file gradle/wrapper/gradle-wrapper.properties
. It should be something like:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://repo.gradle.org/gradle/dist-snapshots/gradle-script-kotlin-4.0-20170518042627+0000-all.zip
This distributionUrl
contains a special Gradle snapshot which also includes Gradle Script Kotlin 0.9.0.
Second Step: Creating our first Gradle build script written in Kotlin
As you know, the common filename for a Gradle build script is gradle.build
. Instead, we should use the kts
extension when using Kotlin Script.
We’re going to migrate the build.gradle
file located at the root of the project to Kotlin. Currently, it should look this way:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
}
}allprojects {
repositories {
jcenter()
}
}
It’s time to migrate it. First, you can safely delete it. Now, create a new file called build.gradle.kts
and write the following:
import org.gradle.script.lang.kotlin.repositories
buildscript {
repositories {
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:2.3.2")
}
}
allprojects {
repositories {
jcenter()
maven { url = uri("https://maven.google.com") }
}
}
Voilà! Our first Gradle build script written in Kotlin!. You can see that both are very similar but there are some differences, for example, classpath
contains parenthesis which is telling us we are using Kotlin syntax.
Before trying to perform a Gradle sync, we need to take a last step. We need to add a special JSON file to tell some configurations to Gradle Script Kotlin. Create and edit the file buildSrc/src/gradle-script-kotlin/resources/project.schema.json
with the following content:
{
":": {
"conventions": {
},
"extensions": {
"ext": "org.gradle.api.plugins.ExtraPropertiesExtension"
}
},
":app": {
"conventions": {
"base": "org.gradle.api.plugins.BasePluginConvention",
"java": "org.gradle.api.plugins.JavaPluginConvention"
},
"extensions": {
"ext": "org.gradle.api.plugins.ExtraPropertiesExtension",
"defaultArtifacts": "org.gradle.api.internal.plugins.DefaultArtifactPublicationSet",
"reporting": "org.gradle.api.reporting.ReportingExtension",
"android": "com.android.build.gradle.AppExtension",
"kotlin": "org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension",
"kapt": "org.jetbrains.kotlin.gradle.plugin.KaptExtension"
}
}
}
It’s a bit of black magic because there is no documentation about it but we can realize we are configuring the plugins that Gradle Script Kotlin have to use for the root project and for the app
module here.
Finally, if you try to perform a Gradle sync it will fail because the project is not finding our new Gradle build script, it’s expecting to run the previousbuild.gradle
. We only have to add one line to settings.gradle
in order to make Gradle using the build.gradle.kts
file as build script instead of the previous one. Edit the settings.gradle
file and add rootProject.buildFileName = 'build.gradle.kts
:
rootProject.buildFileName = 'build.gradle.kts'
include ':app'
Note: This step is optional since Gradle Kotlin Script 0.9.0 but I can tell you it doesn’t work for me without adding the line.
Note 2: The project won’t sync on Android Studio saying the project is not a Gradle project, but I found a workaround. Create an empty build.gradle
file (with no content) at the root of the project and Android Studio will start to sync without problems.
Note 3: Sometimes Android Studio doesn’t detect the changes in your kts
files, you need to restart the IDE to fix the problem. It’s a known issue with Gradle Kotlin Script and it will be fixed in following versions.
And last but not least, you have autocomplete and source navigation in the Gradle Kotlin scripts now!.
Bonus Step: Using a custom class to group our dependencies
We can go further and use the power of Kotlin to group our dependencies to be used with autocomplete.
The idea is to use buildSrc
feature from Gradle to create a custom class where we’ll store the configuration. Understanding buildSrc
is out of the scope of this article but, if you are interested, you can learn more here.
As we are on fire with Kotlin we want also use Kotlin to write this class so the first thing we have to do is enable Gradle Script Kotlin for buildSrc
. Same as before, create and edit the buildSrc/gradle/wrapper/gradle-wrapper.properties
file and add this content:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://repo.gradle.org/gradle/dist-snapshots/gradle-script-kotlin-4.0-20170518042627+0000-all.zip
Also, create and edit the buildSrc/settings.gradle
to add the following line:
rootProject.buildFileName = 'build.gradle.kts'
Finally, create the buildSrc/build.gradle.kts
file with the next content:
buildscript {repositories {
gradleScriptKotlin()
}dependencies {
classpath(kotlinModule("gradle-plugin"))
}
}apply {
plugin("kotlin")
}dependencies {
compile(gradleScriptKotlinApi())
compile(kotlinModule("stdlib"))
}repositories {
gradleScriptKotlin()
}
Once we have Gradle Kotlin Script configured in buildSrc
we can start to create our new class where we will store the project configuration and dependencies. I’ve called this class: ProjectConfiguration
(very original, right?).
This class will contain three objects:
BuildPlugins
. Contains the android gradle and kotlin gradle build plugins.Android
. Contains build tools version, minSdk version and this kind of things.Libs
. Contains app module dependencies.TestLibs
. Contains app module test dependencies
You are going to understand it better when you see the file but in order to simplify the article, I will show you only the BuildPlugins
section.
Create the builSrc/src/main/kotlin/ProjectConfiguration.kt
file:
class ProjectConfiguration(
val buildPlugins: BuildPlugins
)class BuildPlugins(
val androidGradle: String,
val kotlinGradlePlugin: String
)
I like to create a gradle file where I write all gradle plugins, dependencies versions or common configuration. I usually call the file dependencies.gradle
but, as we are using Gradle Kotlin Script we have to call it dependencies.gradle.kts
:
import org.gradle.script.lang.kotlin.extra
import org.gradle.script.lang.kotlin.getValue
import org.gradle.script.lang.kotlin.setValue
var projectConfiguration: ProjectConfiguration by extra
val kotlinVersion = "1.1.2"
val androidGradleVersion = "2.3.2"
projectConfiguration = ProjectConfiguration(
BuildPlugins(
"com.android.tools.build:gradle:$androidGradleVersion",
"org.jetbrains.kotlin:kotlin-gradle plugin:$kotlinVersion"
)
)
The idea is to create a ProjectConfiguration
instance with the right content and store it in the extra container. This extra container is used to share data between different Gradle scripts.
Once, we have a configured instance of ProjectConfiguration
we have to use it. If you remember, the Android Gradle Plugin is used in the build.gradle.kts
file at the root of the project. Open it and change its content to the following:
import org.gradle.script.lang.kotlin.applyFrom
import org.gradle.script.lang.kotlin.extra
import org.gradle.script.lang.kotlin.repositories
buildscript {
repositories {
jcenter()
}
dependencies {
applyFrom("dependencies.gradle.kts")
val projectConfiguration: ProjectConfiguration by extra
classpath(projectConfiguration.buildPlugins.androidGradle)
classpath(projectConfiguration.buildPlugins.kotlinGradlePlugin)
}
}
allprojects {
repositories {
jcenter()
maven { url = uri("https://maven.google.com") }
}
}
That’s all! We have our build plugins statically typed and organized in one Kotlin file. And, more important, we can write the build script using autocomplete.
The full example project is already available on GitHub: https://github.com/arturogutierrez/gradle-script-kotlin-example
Feel free to take a look, fork it, comment or ask questions, I’m glad to help.