Migration KMP AGP 9.0
Android Gradle Plugin 9.0 rend les plugins d'application et de bibliothèque Android incompatibles avec le plugin Kotlin Multiplatform dans le même module. Ce guide vous accompagne dans la migration.
Étape 0 : Analyser le projet
Avant d'apporter des modifications, comprenez la structure du projet :
- Lisez
settings.gradle.kts(ou.gradle) pour trouver tous les modules - Pour chaque module, lisez son
build.gradle.ktspour identifier les plugins appliqués - Vérifiez si le projet utilise un catalogue de versions Gradle (
gradle/libs.versions.toml). S'il existe, lisez-le pour trouver les versions AGP/Gradle/Kotlin actuelles. Sinon, trouvez les versions directement dans les fichiersbuild.gradle.kts(généralement dans le blocbuildscript {}ouplugins {}racine). Adaptez tous les exemples de ce guide en conséquence — les exemples de catalogue de versions utilisentalias(libs.plugins.xxx)tandis que l'utilisation directe utiliseid("plugin.id") version "x.y.z" - Lisez
gradle/wrapper/gradle-wrapper.propertiespour la version Gradle - Vérifiez
gradle.propertiespour les contournements existants (android.enableLegacyVariantApi) - Cherchez l'utilisation du plugin
org.jetbrains.kotlin.android— AGP 9.0 dispose de Kotlin intégré et ce plugin doit être supprimé - Cherchez l'utilisation du plugin
org.jetbrains.kotlin.kapt— incompatible avec Kotlin intégré, doit migrer vers KSP oucom.android.legacy-kapt - Cherchez les plugins tiers qui peuvent être incompatibles avec AGP 9.0 (voir la section « Compatibilité des plugins » ci-dessous)
Si Bash est disponible, exécutez scripts/analyze-project.sh depuis le répertoire de ce guide pour obtenir un résumé structuré.
Classifier chaque module
Pour chaque module, déterminez son type :
| Plugins actuels | Chemin de migration |
|---|---|
kotlin.multiplatform + com.android.library |
Chemin A — Remplacement du plugin de bibliothèque |
kotlin.multiplatform + com.android.application |
Chemin B — Séparation Android obligatoire |
kotlin.multiplatform avec plusieurs points d'entrée de plateforme dans un module |
Chemin C — Restructuration complète (recommandée) |
com.android.application ou com.android.library (pas KMP) |
Voir « Conseils Android pur » ci-dessous |
Déterminer la portée
- Le chemin B est obligatoire pour tout module combinant KMP + plugin d'application Android
- Le chemin C est recommandé quand le projet dispose d'un module monolithique
composeApp(ou similaire) contenant des points d'entrée pour plusieurs plateformes (Android, Desktop, Web). Cela s'aligne avec la nouvelle structure de projet par défaut de JetBrains où chaque plateforme obtient son propre module d'application. - Demandez à l'utilisateur s'il souhaite le chemin B uniquement (minimum requis) ou le chemin C (restructuration complète recommandée)
Chemin A : Migration du module de bibliothèque
Utilisez ceci quand un module applique kotlin.multiplatform + com.android.library.
Voir references/MIGRATION-LIBRARY.md pour le code complet avant/après.
Résumé :
-
Remplacer le plugin :
com.android.library→com.android.kotlin.multiplatform.library -
Supprimer le plugin
org.jetbrains.kotlin.androids'il est présent (AGP 9.0 dispose du support Kotlin intégré) -
Migrer le DSL : Déplacez la configuration du bloc
android {}de haut niveau verskotlin { android {} }:kotlin { android { namespace = "com.example.lib" compileSdk = 35 minSdk = 24 } } -
Renommer les répertoires sources (uniquement si le module utilise la disposition Android classique au lieu de la disposition KMP) :
src/main→src/androidMainsrc/test→src/androidHostTestsrc/androidTest→src/androidDeviceTest- Si le module utilise déjà
src/androidMain/, aucun renommage de répertoire n'est nécessaire
-
Déplacer les dépendances de
dependencies {}de haut niveau verssourceSets:kotlin { sourceSets { androidMain.dependencies { implementation("androidx.appcompat:appcompat:1.7.0") } } } -
Activer les ressources explicitement si le module utilise les ressources Android ou Compose Multiplatform :
kotlin { android { androidResources { enable = true } } } -
Activer Java si le module contient des fichiers sources
.java:kotlin { android { withJava() } } -
Activer les tests explicitement si le module contient des tests unitaires ou instrumentés :
kotlin { android { withHostTest { isIncludeAndroidResources = true } withDeviceTest { instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } } } -
Mettre à jour la dépendance Compose Tooling :
// Ancien : debugImplementation(libs.androidx.compose.ui.tooling) // Nouveau : androidRuntimeClasspath(libs.androidx.compose.ui.tooling) -
Publier les règles ProGuard du consommateur explicitement si applicable :
kotlin { android { consumerProguardFiles.add(file("consumer-rules.pro")) } } -
Résoudre les variantes de dépendances secondaires (Product Flavors / Build Types) : Parce que le nouveau plugin KMP Android Multiplatform applique une architecture à variante unique, il ne comprend pas nativement comment résoudre les dépendances qui publient plusieurs variantes (comme les build types
debug/release, ou les flavorsfree/paid). Configurez les comportements de secours aveclocalDependencySelection:kotlin { android { localDependencySelection { // Déterminer quel build type consommer des dépendances de bibliothèque Android, dans l'ordre de préférence selectBuildTypeFrom.set(listOf("debug", "release")) // Si la dépendance dispose d'une dimension 'tier', sélectionner le flavor 'free' productFlavorDimension("tier") { selectFrom.set(listOf("free")) } } } }
Chemin B : Séparation du module partagé + application Android
Utilisez ceci quand un module applique kotlin.multiplatform + com.android.application. C'est obligatoire pour la compatibilité AGP 9.0.
Voir references/MIGRATION-APP-SPLIT.md pour le guide complet.
Résumé :
-
Créer le module
androidAppavec son proprebuild.gradle.kts:plugins { alias(libs.plugins.androidApplication) // N'appliquez PAS kotlin-android — AGP 9.0 inclut le support Kotlin alias(libs.plugins.composeMultiplatform) // si vous utilisez Compose alias(libs.plugins.composeCompiler) // si vous utilisez Compose } android { namespace = "com.example.app" compileSdk = 35 defaultConfig { applicationId = "com.example.app" minSdk = 24 targetSdk = 35 versionCode = 1 versionName = "1.0" } buildFeatures { compose = true } } dependencies { implementation(projects.shared) // ou le nom du module partagé implementation(libs.androidx.activity.compose) } -
Déplacer le code du point d'entrée Android de
src/androidMain/versandroidApp/src/main/:MainActivity.kt(et toute autre Activity/Fragment)AndroidManifest.xml(manifeste au niveau application avec<application>et Activity de lancement<activity>) — vérifiez queandroid:namesur<activity>utilise le nom de classe entièrement qualifié à son nouvel emplacement- Classe Application Android si présente
- Ressources au niveau application (icônes de lancement, thème, etc.)
-
Ajouter à
settings.gradle.kts:include(":androidApp") -
Ajouter à
build.gradle.ktsracine : déclarations de plugins avecapply false -
Convertir le module original d'application en bibliothèque en utilisant les étapes du chemin A
-
S'assurer de namespaces différents : le module d'application et le module de bibliothèque doivent avoir des namespaces distincts
-
Supprimer du module partagé :
applicationId,targetSdk,versionCode,versionName -
Mettre à jour les configurations de lancement IDE : changer le module du module ancien vers
androidApp
Chemin C : Restructuration complète (recommandée)
Utilisez ceci quand le projet dispose d'un module monolithique (généralement composeApp) contenant des points
d'entrée pour plusieurs plateformes. C'est optionnel mais s'aligne avec la nouvelle structure de projet par défaut de JetBrains.
Voir references/MIGRATION-FULL-RESTRUCTURE.md pour le guide complet.
Structure cible
project/
├── shared/ ← Bibliothèque KMP (était composeApp), code partagé pur
├── androidApp/ ← Point d'entrée Android uniquement
├── desktopApp/ ← Point d'entrée Desktop uniquement (si la cible desktop existe)
├── webApp/ ← Point d'entrée Wasm/JS uniquement (si la cible web existe)
├── iosApp/ ← Projet Xcode iOS (généralement déjà séparé)
└── ...
Étapes
- Appliquer le chemin B en premier — extraire
androidApp(obligatoire pour AGP 9.0) - Extraire
desktopApp(si la cible desktop existe) :- Créer un module avec les plugins
org.jetbrains.composeetapplication {} - Déplacer la fonction
main()dedesktopMainversdesktopApp/src/main/kotlin/ - Déplacer la configuration
compose.desktop { application { ... } }versdesktopApp/build.gradle.kts - Ajouter une dépendance au module
shared
- Créer un module avec les plugins
- Extraire
webApp(si la cible wasmJs/js existe) :- Créer un module avec la configuration Kotlin/JS ou Kotlin/Wasm appropriée
- Déplacer le point d'entrée web de
wasmJsMain/jsMainverswebApp/src/wasmJsMain/kotlin/ - Déplacer la configuration du navigateur/distribution vers
webApp/build.gradle.kts - Ajouter une dépendance au module
shared
- iOS — généralement déjà dans un répertoire
iosAppséparé. Vérifiez :- La configuration d'export du framework (
binaries.framework) reste dans le moduleshared - Le projet Xcode référence le chemin du framework correct
- La configuration d'export du framework (
- Renommer le module de
composeAppversshared:- Renommer le répertoire
- Mettre à jour
settings.gradle.ktsinclude - Mettre à jour toutes les références de dépendances entre les modules
- Nettoyer le module partagé : supprimer tout code de point d'entrée de plateforme et configuration spécifique à l'application qui a été déplacé vers les modules d'application de plateforme
Variante : Interface utilisateur native
Si certaines plateformes utilisent une interface utilisateur native (par exemple, SwiftUI pour iOS), divisez shared en :
sharedLogic— logique métier consommée par TOUTES les plateformessharedUI— interface utilisateur Compose Multiplatform consommée uniquement par les plateformes utilisant l'interface partagée
Variante : Serveur
Si le projet inclut une cible serveur :
- Ajouter le module
serverà la racine - Déplacer tous les modules clients sous un répertoire
app/ - Ajouter le module
corepour le code partagé entre serveur et client (modèles, validation)
Mises à jour de version
Celles-ci sont requises indépendamment du chemin de migration :
-
Wrapper Gradle — mettre à jour vers 9.1.0+ :
# gradle/wrapper/gradle-wrapper.properties distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip -
Version AGP — mettre à jour vers 9.0.0+ et ajouter le plugin de bibliothèque KMP.
Avec catalogue de versions (
gradle/libs.versions.toml) :[versions] agp = "9.0.1" [plugins] android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }Sans catalogue de versions — mettez à jour les versions du plugin
com.android.*et ajoutez dans lebuild.gradle.ktsracine :plugins { id("com.android.application") version "9.0.1" apply false id("com.android.kotlin.multiplatform.library") version "9.0.1" apply false } -
JDK — assurez-vous que JDK 17+ est utilisé (requis par AGP 9.0)
-
SDK Build Tools — mettre à jour vers 36.0.0 :
Installer via SDK Manager ou configurer dans android { buildToolsVersion = "36.0.0" } -
Examen gradle.properties — supprimer les propriétés causant des erreurs et revoir les valeurs par défaut modifiées (voir la section « Changements des propriétés Gradle par défaut »)
Migration du Kotlin intégré
AGP 9.0 active le support Kotlin intégré par défaut pour tous les modules com.android.application et com.android.library.
Le plugin org.jetbrains.kotlin.android n'est plus nécessaire et entrera en conflit s'il est appliqué.
Important : Le Kotlin intégré NE remplace PAS le support KMP. Les modules de bibliothèque KMP ont toujours besoin
de org.jetbrains.kotlin.multiplatform + com.android.kotlin.multiplatform.library.
Étape 1 : Supprimer le plugin kotlin-android
Supprimez de tous les fichiers de build au niveau du module et au niveau racine :
// Supprimer du build.gradle.kts du module
plugins {
// SUPPRIMER : alias(libs.plugins.kotlin.android)
// SUPPRIMER : id("org.jetbrains.kotlin.android")
}
// Supprimer du build.gradle.kts racine
plugins {
// SUPPRIMER : alias(libs.plugins.kotlin.android) apply false
}
Supprimer du catalogue de versions (gradle/libs.versions.toml) :
[plugins]
# SUPPRIMER : kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
Étape 2 : Migrer kapt vers KSP ou legacy-kapt
Le plugin org.jetbrains.kotlin.kapt est incompatible avec le Kotlin intégré.
Préféré : Migrer vers KSP — voir le guide de migration KSP pour chaque processeur d'annotations.
Secours : Utiliser com.android.legacy-kapt (même version qu'AGP) :
# gradle/libs.versions.toml
[plugins]
legacy-kapt = { id = "com.android.legacy-kapt", version.ref = "agp" }
// Module build.gradle.kts — remplacer kotlin-kapt par legacy-kapt
plugins {
// SUPPRIMER : alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.legacy.kapt)
}
Étape 3 : Migrer kotlinOptions vers compilerOptions
Pour les modules Android purs (non-KMP), migrez android.kotlinOptions {} vers le haut-niveau
kotlin.compilerOptions {} :
// Ancien
android {
kotlinOptions {
jvmTarget = "11"
languageVersion = "2.0"
freeCompilerArgs += listOf("-Xopt-in=kotlin.RequiresOptIn")
}
}
// Nouveau
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
optIn.add("kotlin.RequiresOptIn")
}
}
Note : Avec le Kotlin intégré, jvmTarget utilise par défaut android.compileOptions.targetCompatibility, il peut donc être optionnel si vous avez déjà configuré compileOptions.
Étape 4 : Migrer kotlin.sourceSets vers android.sourceSets
Avec le Kotlin intégré, seul android.sourceSets {} avec l'ensemble kotlin est supporté :
// NON SUPPORTÉ avec le Kotlin intégré :
kotlin.sourceSets.named("main") {
kotlin.srcDir("additionalSourceDirectory/kotlin")
}
// Correct :
android.sourceSets.named("main") {
kotlin.directories += "additionalSourceDirectory/kotlin"
}
Pour les sources générées, utilisez l'API Variant :
androidComponents.onVariants { variant ->
variant.sources.kotlin!!.addStaticSourceDirectory("additionalSourceDirectory/kotlin")
}
Stratégie de migration par module
Pour les grands projets, migrez module par module :
- Désactiver globalement :
android.builtInKotlin=falsedansgradle.properties - Activer par module migré en appliquant le plugin de consentement :
plugins { id("com.android.built-in-kotlin") version "AGP_VERSION" } - Suivre les étapes 1-4 pour ce module
- Une fois tous les modules migrés, supprimez
android.builtInKotlin=falseet tous les pluginscom.android.built-in-kotlin
Optionnel : Désactiver Kotlin pour les modules sans Kotlin
Pour les modules qui ne contiennent aucune source Kotlin, désactivez le Kotlin intégré pour économiser du temps de build :
android {
enableKotlin = false
}
Consentement négatif (temporaire)
Si bloqué par des incompatibilités de plugins, consentement négatif temporaire :
# gradle.properties
android.builtInKotlin=false
android.newDsl=false # également requis si vous utilisez la consentement négatif du nouveau DSL
Avertissement : Demandez à l'utilisateur s'il souhaite un consentement négatif, et dans ce cas, rappelez-lui que c'est une mesure temporaire.
Compatibilité des plugins
Voir references/PLUGIN-COMPATIBILITY.md pour le tableau de compatibilité complet avec les versions compatibles connues, les contournements d'indicateurs de consentement négatif, et les plugins cassés.
Avant de migrer, inventoriez tous les plugins du projet et vérifiez chacun par rapport à ce tableau. Si un plugin est cassé sans contournement, informez l'utilisateur. Si des plugins nécessitent des indicateurs de consentement négatif, ajoutez-les à gradle.properties et notez-les comme des contournements temporaires.
Changements des propriétés Gradle par défaut
AGP 9.0 change les valeurs par défaut de nombreuses propriétés Gradle. Vérifiez gradle.properties pour toute valeur définie explicitement qui peut maintenant entrer en conflit.
Changements clés :
| Propriété | Ancienne valeur par défaut | Nouvelle valeur par défaut | Action |
|---|---|---|---|
android.uniquePackageNames |
false |
true |
S'assurer que chaque bibliothèque a un namespace unique |
android.enableAppCompileTimeRClass |
false |
true |
Refactoriser switch sur les champs R vers if/else |
android.defaults.buildfeatures.resvalues |
true |
false |
Activer resValues = true où nécessaire |
android.defaults.buildfeatures.shaders |
true |
false |
Activer les shaders où nécessaire |
android.r8.optimizedResourceShrinking |
false |
true |
Vérifier les règles keep R8 |
android.r8.strictFullModeForKeepRules |
false |
true |
Mettre à jour les règles keep pour être explicites |
android.proguard.failOnMissingFiles |
false |
true |
Supprimer les références de fichiers ProGuard invalides |
android.r8.proguardAndroidTxt.disallowed |
false |
true |
Utiliser proguard-android-optimize.txt uniquement |
android.r8.globalOptionsInConsumerRules.disallowed |
false |
true |
Supprimer les options globales des règles du consommateur de bibliothèque |
android.sourceset.disallowProvider |
false |
true |
Utiliser l'API Sources sur androidComponents |
android.sdk.defaultTargetSdkToCompileSdkIfUnset |
false |
true |
Spécifier targetSdk explicitement |
android.onlyEnableUnitTestForTheTestedBuildType |
false |
true |
Uniquement si vous testez les build types non-défaut |
Vérifiez et supprimez les propriétés qui causent maintenant des erreurs :
android.r8.integratedResourceShrinking— supprimée, toujours activéeandroid.enableNewResourceShrinker.preciseShrinking— supprimée, toujours activée
Conseils Android pur
Pour les modules Android non-KMP mettant à niveau vers AGP 9.0, suivez les étapes « Migration du Kotlin intégré » ci-dessus, puis consultez le tableau « Changements des propriétés Gradle par défaut ». Changements supplémentaires :
- Examiner les nouvelles interfaces DSL —
BaseExtensionest supprimée ; utilisezCommonExtensionou des types d'extension spécifiques - La valeur par défaut Java a changé de Java 8 à Java 11 — assurez-vous que
compileOptionsle reflète
Vérification
Après la migration, vérifiez à l'aide de la liste de contrôle. Vérifications clés :
./gradlew buildréussit sans erreurs- Tous les cibles de plateforme se compilent correctement (Android, iOS via
xcodebuild, Desktop, JS/Wasm) ./gradlew :shared:allTestset les tests unitaires Android réussissent- Pas de
com.android.libraryoucom.android.applicationdans les modules KMP - Pas de
org.jetbrains.kotlin.androiddans les modules AGP 9.0 - Les ensembles de sources utilisent les noms corrects (
androidMain,androidHostTest,androidDeviceTest) - Aucun avertissement de dépréciation concernant l'API de variante ou le DSL
Problèmes courants
Voir references/KNOWN-ISSUES.md pour les détails. Pièges clés :
Problèmes du plugin de bibliothèque KMP
- BuildConfig indisponible dans les modules de bibliothèque — utiliser DI/interface
AppConfiguration, ou utiliser BuildKonfig ou gradle-buildconfig-plugin pour les constantes en temps de compilation - Pas de variantes de build — architecture à variante unique ; les constantes en temps de compilation peuvent utiliser les flavors BuildKonfig/gradle-buildconfig-plugin, mais les dépendances/ressources/signature spécifiques à la variante doivent passer au module d'application
- NDK/JNI non supporté dans le nouveau plugin — extraire vers un module
com.android.libraryséparé - Les ressources Compose plantent sans
androidResources { enable = true } - Les règles ProGuard du consommateur sont supprimées silencieusement si elles ne sont pas migrées vers
consumerProguardFiles.add(file(...))dans le nouveau DSL - KSP nécessite la version 2.3.1+ pour la compatibilité AGP 9.0
Problèmes généraux AGP 9.0
- BaseExtension supprimée — les plugins de convention utilisant les anciens types DSL doivent être réécrits pour utiliser
CommonExtension - APIs Variant supprimées —
applicationVariants,libraryVariants,variantFilterremplacés parandroidComponents - Plugins de convention nécessitent une refactorisation — les anciens helpers d'extension
android {}sont obsolètes
Fichiers de référence
- Référence DSL — mappage côte à côte ancien→nouveau DSL
- Matrice de version — compatibilité AGP/Gradle/KGP/Compose/IDE
- Compatibilité des plugins — état et contournements des plugins tiers