De Groovy à Kotlin DSL. Ce qu’il faut retenir.

Gabriel TEKOMBO
AndroidMood
Published in
6 min readAug 30, 2023

Nous avons passé nos scripts Gradle de Groovy à Kotlin DSL ! Voici notre retour d’experience.

Gradle x Kotlin = Kotlin DSL
Gradle x Kotlin = Kotlin DSL ❤️

Kotlin est depuis quelques années déjà reconnu comme le langage de référence pour le développement d’applications Android, la vision à 180° qu’a pris Google a changé complètement les habitudes et la manière de coder. Kotlin ne cesse de prendre du terrain et se retrouve désormais dans les scripts Gradle grâce à Kotlin DSL. Nous avons donc décidé de migrer nos scripts Gradle de Groovy à Kotlin DSL afin d’avoir un avis tranché sur les tenants et les aboutissants de ladite technologie. Dans cet article nous ferons un resumé détaillé de notre expérience, tout en vous présentant les avantages et les inconvénients de Kotlin DSL par rapport à Groovy. Si vous vouliez en savoir plus sur Kotlin DSL, cet article est fait pour vous !

Qu’est ce que Kotlin DSL ?

DSL est une abbreviation de “Domain Specific Language”. Plus précisément, il s’agit d’un langage de programmation dont les spécifications sont conçues pour répondre aux contraintes d’un domaine d’application précis. Il s’oppose conceptuellement aux langages de programmation classiques (ou généralistes) comme Java ou C, qui tendent à traiter un ensemble de domaines

Kotlin DSL est donc un langage qui définit une manière de configurer vos scripts de compilation en utilisant (principalement) des blocs d’instructions propres à Kotlin.

C’est à partir de la version 4.0 du plugin Android Gradle qu’a été ajouté la prise en charge des scripts Kotlin (.kts) dans les configurations de build Gradle en remplacement de Groovy, le langage de programmation traditionnellement utilisé dans les fichiers de configuration Gradle.

Et depuis la version Giraffe d’Android Studio, Google le définit désormais comme langage de configuration de build par défaut et recommandé pour AGP ≥ 8.1

Configuration d'un nouveau projet Android
Note à propos de Kotlin DSL sur Android Developers

Avantages

  • La syntaxe Kotlin : Elle nous permet de profiter de la flexibilité (propriétés et extensions) et de la popularité sans cesse croissante du langage.
  • L’autocompletion et l’assistance de l’IDE qui améliore l’expérience de développement
  • Navigation à la source : Kotlin DSL permet de naviguer entre les déclarations de variables et de propriétés car elle crée des références implicites de chaque objet déclaré, chose que ne permettait pas Groovy.

Inconvénients

  • Ne supporte pas les importations de dépendances depuis des fichiers (Type-safe dependency accessors) : Impossible d’importer des dépendances depuis un fichier .gradle ou .kts dans le build.gradle.kts sauf en usant de manipulations dans le package buildSrc qui servira de passerelle aux fichiers compilés, ce qui n’est pas vraiment la meilleure approche. L’idéal pour ce qui est donc du versioning sera d’utiliser Catalog Versions.
  • Est plus lent à la compilation : Beaucoup plus lent que Groovy d’après plusieurs benchmark de l’équipe Gradle. Bien que KTS offre actuellement une meilleure intégration dans l’éditeur Android Studio par rapport à Groovy, les builds utilisant Kotlin DSL ont tendance à être plus lents que les builds utilisant Groovy, donc la migration vers KTS doit être envisagée en tenant compte des performances de build. Mais elle tend à s’améliorer car elle reste une préoccupation d’actualité.
Benchmark Groovy vs Kotlin DSL sur AGP 8.0* (16/02/2023)
  • Une documentation officielle focus sur l’écosystème Android assez limitée, si vous voulez migrer un gros projet vers Kotlin DSL vous serez quelque fois contraint d’aller chercher l’info dans des codes sources, des réponses stackOverFlow, des vidéos youtube ou des blogs medium en ce qui concerne les build flavors, les schemes, la gestion de compatibilité entre fichiers kts etc, ce qui peut être déconcertant.

Comment effectuer la migration ?

Pour migrer les scripts Groovy vers Kotlin DLS, nous vous recommandons de le faire en 2 étapes:

  1. Définir comment vous souhaiter configurer votre versioning de dépendances
    Il y'a 3 façons de gérer votre versioning : Dans un fichier buildSrc, directement dans le build.gradle ou alors en utilisant Version Catalogs. En fonction du choix que ferez, l'implémentation sera différente.
  2. Traduire tous vos fichiers de configurations Gradle de Groovy vers Kts

build.gradle -> build.gradle.kts

settings.gradle -> settings.gradle.kts

Commencez par créer un nouveau fichier avec l'extension .kts ensuite vous pourrez commencer à traduire manuellement vos scripts blocs par blocs en vous basant sur le fichier .gradle précédent.
Plus votre projet est complexe, plus cette étape sera difficile. En fonction de votre configuration, vous serez parfois emmené à faire des recherches pour trouver la bonne syntaxe et itérer en faisant des tests, ce qui peut s'avérer frustrant; on revient au soucis de manque de documentation officielle propre aux problématiques Android.
Cependant, voici quelques exemples qui vous aideront :

Plugins

//Groovy
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-parcelize'
id 'com.google.devtools.ksp' version '1.6.21-1.0.5'
}

//Kotlin DSL
plugins {
id("com.android.application")
id("kotlin-android")
id("com.google.devtools.ksp")
id("kotlin-parcelize")
}

Déclaration des dépendances

//Groovy
def retrofit = '2.9.0'
def firebase_bom = "29.1.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation platform("com.google.firebase:firebase-bom:$firebase_bom")

//Kotlin DSL
val retrofit = "2.9.0"
val firebaseBom = "30.1.0"
implementation("com.squareup.retrofit2:retrofit:$retrofit")
implementation(platform("com.google.firebase:firebase-bom:$firebaseBom"))

SignIn configs

//Groovy
signingConfigs {
debug {
storeFile file("../keystore/android_app_debug.jks")
storePassword "androidApp"
keyAlias "androidapp"
keyPassword "myAndroidApp"
}
release {
storeFile file("../keystore/android_app_release.jks")
storePassword localProperties.get("release.key.storePassword")
keyAlias localProperties.get("release.key.alias")
keyPassword localProperties.get("release.key.password")
}
}
//Kotlin DSL
signingConfigs {
getByName("debug") {
storeFile = file("../keystore/android_app_debug.jks")
storePassword "androidApp"
keyAlias "androidapp"
keyPassword "myAndroidApp"
}
create("release") {
storeFile = file("../keystore/android_app_release.jks")
storePassword = localProperties["release.key.storePassword"] as String
keyAlias = localProperties["release.key.alias"] as String
keyPassword = localProperties["release.key.password"] as String
}
}

Product Flavors

//Groovy
flavorDimensions 'partner'
productFlavors {
kfc {
dimension = "partner"
applicationId = "com.sample.myapp.kfc"
resValue "string", "some_random_api_key", "jhsd873hi8o823y09sdej93493u47hi"
buildConfigField "Boolean", "randomFeatureEnabled", "false"
manifestPlaceholders.scheme = "mykfc"
}

mcdo {
dimension = "partner"
applicationId = "com.sample.myapp.mcdonalds"
resValue "string", "some_random_api_key", "978sds8d6s8d6s8d9sdsydsbgdjsd5h"
buildConfigField "Boolean", "randomFeatureEnabled", "true"
manifestPlaceholders.scheme = "mymcdo"
}

burgerking {
dimension = "partner"
applicationId = "com.sample.myapp.burgerking"
resValue "string", "some_random_api_key", "8ghsd6sjdhs0dakle809dvndv8993enj"
buildConfigField "Boolean", "randomFeatureEnabled", "true"
manifestPlaceholders.scheme = "myburgerking"
}
}
//Kotlin DSL 
flavorDimensions.add("partner")
productFlavors {
create("kfc") {
dimension = "partner"
applicationId = "com.sample.myapp.kfc"
resValue("string", "some_random_api_key", "jhsd873hi8o823y09sdej93493u47hi")
buildConfigField("Boolean", "randomFeatureEnabled", "false")
manifestPlaceholders["scheme"] = "mykfc"
}

create("mcdo") {
dimension = "partner"
applicationId = "com.sample.myapp.mcdonalds"
resValue("string", "some_random_api_key", "978sds8d6s8d6s8d9sdsydsbgdjsd5h")
buildConfigField("Boolean", "randomFeatureEnabled", "true")
manifestPlaceholders["scheme"] = "mymcdo"
}

create("burgerking") {
dimension = "partner"
applicationId = "com.sample.myapp.burgerking"
resValue("string", "some_random_api_key", "8ghsd6sjdhs0dakle809dvndv8993enj")
buildConfigField("Boolean", "randomFeatureEnabled", "true")
manifestPlaceholders["scheme"] = "myburgerking"
}
}

Tasks

//Groovy
task clean(type: Delete) {
delete rootProject.buildDir
}

//Kotlin DSL
tasks.register("clean", Delete::class){
delete(rootProject.buildDir)
}
//Groovy   
tasks.named('dependencyUpdates').configure() {
rejectVersionIf {
isNonStable(it.candidate.version)
}
}

//Kotlin DSL
tasks.withType<DependencyUpdatesTask> {
rejectVersionIf {
isNonStable(candidate.version)
}
}

Fonctions

//Groovy
static def getDate() {
return new Date().format('yyyyMMddHHmm')
}

//Kotlin DSL
fun getDate() = SimpleDateFormat("yyyyMMddHHmm").format(Date())

//Groovy
def isNonStable = { String version ->
def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { version.toUpperCase().contains(it) }
def regex = /^[0-9,.v-]+(-r)?$/
return !stableKeyword && !(version ==~ regex)
}

//Kotlin DSL
fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
}

En bonus
Parallèlement, un outil open-source ❤️ a été développé afin de palier à ce problème, il permet gagner en temps en convertissant automatiquement les scripts Groovy en Kotlin DSL : Gradle Kotlin Converter. Il reste encore assez limité sur certains points mais il aide à traduire la majorité des syntaxes habituelles beaucoup plus facilement.

En conclusion

La volonté de Google d'imposer Kotlin comme le standard de développement sur tous les plans ne cesse de se matérialiser à travers de plus en plus de migrations (Jetpack Compose, KSP, Kotlin DSL …), il est parfois difficile pour les développeurs de suivre mais il est important d'en être au moins informé. Bien que Kotlin DSL possède encore des lacunes, il tend à s'améliorer afin de proposer un meilleure experience de développement. Cette progression passera nécessairement par de meilleurs temps de compilation, qui pour le moment demeure l'une de ses principales faiblesses. En attendant, si vous en avez la possibilité et aussi l'envie bien entendu, nous ne pouvons que vous recommander de migrer vers Kotlin DSL et de vous faire votre propre idée.

Merci pour votre attention, en espérant que cet article pour a apporté plus d'éclairements, n'hésitez pas à laisser quelques applaudissements 👏 pour nous encourager, ça fait toujours plaisir. Et à bientôt pour de nouvelles aventures sur Android Mood

--

--

Gabriel TEKOMBO
AndroidMood

Android Engineer 📱. I write about Kotlin, Jetpack, best practices, engineering processes for mobile apps. — gabrielthecode.com