ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Migrating Android build scripts from Groovy to Kotlin DSL

Oussama Hafferssas
ProAndroidDev
Published in
10 min readDec 8, 2018

Gradle 5.0 has been released with lots of features and especially with the support of production-ready Kotlin DSL 🎉 In this article I’ll be sharing with you the process I’ve followed to migrate Gradle build scripts from Groovy to Kotlin DSL in one of my Android side projects, and also my personal opinion on this process.

Summary

  • What is Kotlin DSL ?
  • Promises made by Kotlin DSL
  • Why should I even care ?
  • Migration —Step By Step
  • Evaluating promises
  • Personal opinion

What is Kotlin DSL ?

If you’ve ever used SQL before or Groovy with Gradle, it means that you’ve used a DSL language before. DSL stands for Domain Specefic Language, which is a type of programming language that cares mainly about the readability by enforcing the use of declarative code with minimal boilerprate (declare to use) instead of imperative code (explaining how to solve a problem) but as a drawback, it can be used only inside a specific context or domain.

Kotlin is already proven to be a very powerful and a safe production-ready programming language especially. As a GPL (General Purpose Langage) Kotlin has some great features that enables writing very clean code with minimal syntax, things like Lambdas with receivers that can be used outside of method parentheses and infix function notation. These features permit to write DSL-like code with minimal effort (Fré Dumazy’s article in ProAndroidDev explains this in details)

Jetbrains’ Anko library brought the power of Kotlin DSL to clean verbose Android API especially XML layouts. This is one example :

DSL for ConstraintLayout with Anko

The Gradle team liked it a lot and decided to use it for Gradle 🙌

Gradle’s Kotlin DSL Primer is a good entry point to the world of Kotlin DSL scripts

Promises made by Kotlin DSL

Being built on top of the core langage, makes Kotlin DSL considered as an Internal DSL which means that it does not have a syntax on its own, rather being a library that offers DSL readability by exploiting the mother GPL language features, in this case Kotlin.

What we should end up having is :

  • A statically typed and a type-safe DSL (inherited from Kotlin)
  • An enhanced IDE editing experience (inherited from Kotlin)
  • Interoperability with existing scripts (as a JVM language)
  • Maximum readability (as any DSL)
  • Consistency and super power (by using the same language across the project — source code and configuration 💯 Kotlin)

Why should I even care ?

Shortly, because Groovy is a dynamically typed language, and because it’s very likely that you have come across one of these problems when dealing with build script files for Android :

  • Very poor IDE assistant when writing a Groovy DSL script
  • Performance issues*
  • Errors at build runtime instead of compile time
  • Painful build script debugging experience
  • Refactoring is a pain in 🤬

Enough said ! let’s try migrating…

Migration — Step By Step

It will not be that easy !

The migration from Groovy DSL scripts to Kotlin DSL is a like a battle, the more you’ve prepared yourself the more you increase your chances to win !

So, this is out strategy : Groovy DSL and Kotlin DSL have lots of similarities, therefor we will not re-write build scripts from scratch, in fact we will be transforming the existing Groovy scripts into Kotlin DSL by applying minimum amount of modification, once finished, we rename build scripts file extension and by then we will be done !

Sounds cool.. yeah.. only, and only if you have well prepared your build files, because after renaming files to .gradle.kts the IDE will not be able to offer support, the lights will go off and you’ll be alone against build scripts in your battle — so be prepared !

..After renaming files to .gradle.kts the IDE will not be able to offer support, the lights will go off and you’ll be alone against build scripts in your battle — so be prepared !

Chapter I / Preparation

This is where you should really invest time and effort.

  • The right (and the latest) IDE — Because it doesn’t make sense to use Kotlin DSL with unsupported IDE, make sure that you use one of these IDEs (I prefer IntelliJ IDEA CE for Android)
  • Update to Gradle Wrapper 5.0 or higher

Tip : use gradle-5.0-all instead of gradle-5.0-bin. The “all” distribution contains sources that provides IDE with Gradle API and Groovy DSL documentation.

⚠️ On Android projects, it is recommended to use Gradle 5.0 with Android Gradle Plugin 3.4 (not released yet)

⚠️ Gradle 5.0 is compatible only with Java 8 or higher ! Yess !

  • Update all your Gradle plugins — As they may bring support for Kotlin DSL if they are not already supporting it.
  • Stop Gradle auto-sync (optional) — in the upcoming steps you’ll start disambiguating build scripts, we will apply small changes practically in every line of the script’s source code. Having Gradle syncing each time can cause you high blood pressure 😉
Disabling Gradle auto-import in IntelliJ’s preferences
  • Fix ALL String quotes — In Groovy it’s possible to use both single quotes or double quotes for strings which is not the case in Kotlin DSL. You’ll need to put every-single-string in double quotes, otherwise it will be very tedious to finalize migration after renaming files.

⚠️ Dont forget the settings.gradle file

  • Disambiguating Groovy DSL — Without knowing the context, the expression of type “xx zzzz” in Groovy can represent a property assignment or a function invocation. These two cases will have different syntax for each in Kotlin DSL. Therefor we need to distinguish them by being more explicit. Actually, the explicit format is the same format that will be used by Kotlin DSL !
Before Disambiguation — Implicit and not compatible with Kotlin DSL
After Disambiguation — Explicit and compatible with Kotlin DSL

Tip : use the IDE assistant to tell the difference, if you cmd+click on the word on the left a.e implementation, the IDE will open a source file which means that it’s a function but when you try to cmd+click on applicationId you will get nothing which means that it’s a property 😉

  • Respect patterns — The goal of DSL is to be as declarative as possible, one of the anti-patterns of that is to use apply plugin instead of the plugins bloc. The Kotlin DSL will need this bloc to generate static extension functions to make use of these plugins. Therefor we need to move all apply plugin into one plugins bloc.
Imperative style — not compatible with Kotlin DSL
Declarative style — compatible with Kotlin DSL

Chapter II / Conversion

Now we will proceed to the conversion of existing build scripts into Kotlin DSL by renaming them from xx.gradle to xx.gradle.kts

Mixing Groovy scripts and Kotlin DSL scripts in the same project is possible 👌

  • It’s better to start by renaming settings.gradle files to settings.gradle.kts and re-syncing the project just after (Do this for all of your build settings files) Your IDE should show this :
IDE hints in Kotlin DSL 🙌

Because we have already prepared the settings.gradle file we didn’t need to do anything to make it compile after converting it to Kotlin DSL !

  • Now let’s do the same thing for the top level build.gradle file and add the extension .kts — Try to spot the differences 🔍
IDE view of a standard Gradle Groovy root build script file before conversion
The IDE view of the script after converting it to Kotlin DSL

As you can see in terms of syntax, in this file the only difference between the Groovy DSL and Kotlin DSL version is the declaration of clean task which you can read as : clean is registerd as a Delete task that deletes the buildDir when executed.

In terms of IDE assistant, we have way more better assistant. The IDE shows hints for all scopes of the script 👌 It provides also more assistant when writing the actual Kotlin DSL script. Let’s see..

IDE assistant with Kotlin DSL

Tip : take a look at this Gradle sample for an in depth look.

  • We still need to convert the last file which is in our case the app module’s build.gradle file. The screenshot bellow is what the IDE shows just after renaming the file to build.gradle.kts
app module build gradle file after renaming to file to .kts

As you can see, after syncing the project there are parts that have been compiled properly and others not.

⚠️ Sometimes the IDE indicated visually a lot of errors whereas Gradle indicates much more less in the sync logs. Always use the log errors

Tip : The best thing to do is to look to the sync error logs and fix them one by one. To make sure that your fix is good, run the command ./gradlew tasks

In our case we have mainly three erros in the log :

  • The release build type error — under the hood, release is just a string passed to a function invoked by Groovy therefor Kotlin will not be able to invoke it as a function. We need to use the actual function invoked behind which is getByName(String)
  • The minifyEnabled error — minifyEnabled is not the actual name of the property therefor Kotlin can’t find it. We need to use its original name for assignment which is isMinifyEnabled.
  • The fileTree error — before trying to fix it, it’s better to take a look at its declaration (This is why we imported all sources and docs of Gradle 😉). fileTree(Map<String, ?> args)The method accepts args of map of string keys. We will pass this as Tuples the Kotlin way.

The result after applying these fixes will look like this..

An app module build script file in Kotlin DSL that actually compiles 🎉

If your build scripts still can’t compile, please keep runing ./gradlew tasks command and fix issues indicated in the log, and double check if your Gradle plugins are fully compatible with Kotlin DSL. If it compiles then..

Congratulations 🎉

Note on organizing Gradle project dependency wise — In the case of Android, it was recommended to use Gradle extra properties to store versions of dependencies in order to use them across the project modules. This solution has been crippled with the lack of IDE assistance do navigate to these versions declaration and properly refactor them. Gradle now recommends declaring them in a dedicated folder buildSrc that contains imperative build code to be shared between modules (I personally find it an over-kill for my small project)

Sam Edwards explained it here.

Evaluating promises

  • A Type-safe DSL — ✓ — Because it’s statically typed, Kotlin DSL provides more safety on passed parameters when invoking Gradle build functions.
  • An enhanced IDE editing experience — ✓ — In terms of IDE assistance, it’s much more pleasant to write build scripts in Kotlin DSL
  • Interoperability with existing scripts — ⚠︎—because both are JVM languages (and because Jetbrains has done great job) Kotlin DSL scripts are compatible with Groovy but not fully because of Groovy’s dynamic types. Some Gradle plugins will need to be modified in order to be used in Kotlin DSL
  • Maximum readability— ✓ — As any DSL, I think that Kotlin DSL is very readable even if it’s semi-declarative.
  • Consistency and super power — ✓ — Having a declarative type-safe language for build is very promising. Gradle plugins authors will be 10 times more creative and productive using such a language. For the years to come, I think we will see a lot of 100% Kotlin Android projects.

Personal opinion

I’ve always believed in the proverb : you’ve got to crack a few eggs to make an omelette, but for this kind of migrations it would be better to have some kind of assistant to indicate to developers at least where they probably should modify script files in order to make them Kotlin DSL-conversion-ready.

*Performance issues — for now, Kotlin DSL falls short in terms of build speed because of compile time needed to resolve that extra type safety. It’s indicated clearly here and I noticed it even in my small project. I may benchmark it on a big test project before using it in a big production project.

If Null Pointer Exception is the Billion dollar mistake, build waiting time is easily the Trillion Dollar mistake !

Before even thinking about a migration to Kotlin DSL, it’s mantadory to be already applying build best practices otherwise it will be very tedious.

I agree with Christina Lee when she said, strings are danger. I think it’s time to find a solution for those horrible strings that represent dependencies and buildTypes (release, debug..). Why not making a Gradle plugin that gathers all maven dependencies by package name and exposes them as type-safe Kotlin constants for build scripts !?

Thanks for the Gradle team and for the community for trying always to make build smoother, safer and healthier 🙏

Sign up to discover human stories that deepen your understanding of the world.

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Responses (9)

Write a response