kotlin-tooling-agp9-migration

Par kotlin · kotlin-agent-skills

Migre les projets Kotlin Multiplatform (KMP) vers Android Gradle Plugin 9.0+. Gère le remplacement de plugin (`com.android.kotlin.multiplatform.library`), la décomposition de modules, la migration DSL et la nouvelle structure de projet par défaut. À utiliser lors d'une mise à niveau d'AGP, en cas d'échec du build dû à une incompatibilité KMP+AGP, ou lorsque l'utilisateur mentionne AGP 9.0, le plugin Android Multiplatform, la migration KMP, ou `com.android.kotlin.multiplatform.library`.

npx skills add https://github.com/kotlin/kotlin-agent-skills --skill kotlin-tooling-agp9-migration

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 :

  1. Lisez settings.gradle.kts (ou .gradle) pour trouver tous les modules
  2. Pour chaque module, lisez son build.gradle.kts pour identifier les plugins appliqués
  3. 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 fichiers build.gradle.kts (généralement dans le bloc buildscript {} ou plugins {} racine). Adaptez tous les exemples de ce guide en conséquence — les exemples de catalogue de versions utilisent alias(libs.plugins.xxx) tandis que l'utilisation directe utilise id("plugin.id") version "x.y.z"
  4. Lisez gradle/wrapper/gradle-wrapper.properties pour la version Gradle
  5. Vérifiez gradle.properties pour les contournements existants (android.enableLegacyVariantApi)
  6. Cherchez l'utilisation du plugin org.jetbrains.kotlin.android — AGP 9.0 dispose de Kotlin intégré et ce plugin doit être supprimé
  7. Cherchez l'utilisation du plugin org.jetbrains.kotlin.kapt — incompatible avec Kotlin intégré, doit migrer vers KSP ou com.android.legacy-kapt
  8. 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é :

  1. Remplacer le plugin : com.android.librarycom.android.kotlin.multiplatform.library

  2. Supprimer le plugin org.jetbrains.kotlin.android s'il est présent (AGP 9.0 dispose du support Kotlin intégré)

  3. Migrer le DSL : Déplacez la configuration du bloc android {} de haut niveau vers kotlin { android {} } :

    kotlin {
        android {
            namespace = "com.example.lib"
            compileSdk = 35
            minSdk = 24
        }
    }
  4. Renommer les répertoires sources (uniquement si le module utilise la disposition Android classique au lieu de la disposition KMP) :

    • src/mainsrc/androidMain
    • src/testsrc/androidHostTest
    • src/androidTestsrc/androidDeviceTest
    • Si le module utilise déjà src/androidMain/, aucun renommage de répertoire n'est nécessaire
  5. Déplacer les dépendances de dependencies {} de haut niveau vers sourceSets :

    kotlin {
        sourceSets {
            androidMain.dependencies {
                implementation("androidx.appcompat:appcompat:1.7.0")
            }
        }
    }
  6. Activer les ressources explicitement si le module utilise les ressources Android ou Compose Multiplatform :

    kotlin {
        android {
            androidResources { enable = true }
        }
    }
  7. Activer Java si le module contient des fichiers sources .java :

    kotlin {
        android {
            withJava()
        }
    }
  8. 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"
            }
        }
    }
  9. Mettre à jour la dépendance Compose Tooling :

    // Ancien : 
    debugImplementation(libs.androidx.compose.ui.tooling)
    // Nouveau :
    androidRuntimeClasspath(libs.androidx.compose.ui.tooling)
  10. Publier les règles ProGuard du consommateur explicitement si applicable :

     kotlin {
         android {
             consumerProguardFiles.add(file("consumer-rules.pro"))
         }
     }
  11. 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 flavors free/paid). Configurez les comportements de secours avec localDependencySelection :

     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é :

  1. Créer le module androidApp avec son propre build.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)
    }
  2. Déplacer le code du point d'entrée Android de src/androidMain/ vers androidApp/src/main/ :

    • MainActivity.kt (et toute autre Activity/Fragment)
    • AndroidManifest.xml (manifeste au niveau application avec <application> et Activity de lancement <activity>) — vérifiez que android:name sur <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.)
  3. Ajouter à settings.gradle.kts : include(":androidApp")

  4. Ajouter à build.gradle.kts racine : déclarations de plugins avec apply false

  5. Convertir le module original d'application en bibliothèque en utilisant les étapes du chemin A

  6. S'assurer de namespaces différents : le module d'application et le module de bibliothèque doivent avoir des namespaces distincts

  7. Supprimer du module partagé : applicationId, targetSdk, versionCode, versionName

  8. 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

  1. Appliquer le chemin B en premier — extraire androidApp (obligatoire pour AGP 9.0)
  2. Extraire desktopApp (si la cible desktop existe) :
    • Créer un module avec les plugins org.jetbrains.compose et application {}
    • Déplacer la fonction main() de desktopMain vers desktopApp/src/main/kotlin/
    • Déplacer la configuration compose.desktop { application { ... } } vers desktopApp/build.gradle.kts
    • Ajouter une dépendance au module shared
  3. 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/jsMain vers webApp/src/wasmJsMain/kotlin/
    • Déplacer la configuration du navigateur/distribution vers webApp/build.gradle.kts
    • Ajouter une dépendance au module shared
  4. iOS — généralement déjà dans un répertoire iosApp séparé. Vérifiez :
    • La configuration d'export du framework (binaries.framework) reste dans le module shared
    • Le projet Xcode référence le chemin du framework correct
  5. Renommer le module de composeApp vers shared :
    • Renommer le répertoire
    • Mettre à jour settings.gradle.kts include
    • Mettre à jour toutes les références de dépendances entre les modules
  6. 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 plateformes
  • sharedUI — 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 core pour le code partagé entre serveur et client (modèles, validation)

Mises à jour de version

Celles-ci sont requises indépendamment du chemin de migration :

  1. 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
  2. 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 le build.gradle.kts racine :

    plugins {
        id("com.android.application") version "9.0.1" apply false
        id("com.android.kotlin.multiplatform.library") version "9.0.1" apply false
    }
  3. JDK — assurez-vous que JDK 17+ est utilisé (requis par AGP 9.0)

  4. SDK Build Tools — mettre à jour vers 36.0.0 :

    Installer via SDK Manager ou configurer dans android { buildToolsVersion = "36.0.0" }
  5. 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 :

  1. Désactiver globalement : android.builtInKotlin=false dans gradle.properties
  2. Activer par module migré en appliquant le plugin de consentement :
    plugins {
        id("com.android.built-in-kotlin") version "AGP_VERSION"
    }
  3. Suivre les étapes 1-4 pour ce module
  4. Une fois tous les modules migrés, supprimez android.builtInKotlin=false et tous les plugins com.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ée
  • android.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 DSLBaseExtension est supprimée ; utilisez CommonExtension ou des types d'extension spécifiques
  • La valeur par défaut Java a changé de Java 8 à Java 11 — assurez-vous que compileOptions le reflète

Vérification

Après la migration, vérifiez à l'aide de la liste de contrôle. Vérifications clés :

  1. ./gradlew build réussit sans erreurs
  2. Tous les cibles de plateforme se compilent correctement (Android, iOS via xcodebuild, Desktop, JS/Wasm)
  3. ./gradlew :shared:allTests et les tests unitaires Android réussissent
  4. Pas de com.android.library ou com.android.application dans les modules KMP
  5. Pas de org.jetbrains.kotlin.android dans les modules AGP 9.0
  6. Les ensembles de sources utilisent les noms corrects (androidMain, androidHostTest, androidDeviceTest)
  7. 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.library sé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éesapplicationVariants, libraryVariants, variantFilter remplacés par androidComponents
  • Plugins de convention nécessitent une refactorisation — les anciens helpers d'extension android {} sont obsolètes

Fichiers de référence

Skills similaires