kotlin-tooling-gradle-to-kotlin-toolchain-project

Par kotlin · kotlin-agent-skills

Charger lors de la migration ou de la conversion d'un projet Gradle Kotlin complet (build.gradle(.kts), wrapper, libs.versions.toml, buildSrc) vers le Kotlin Toolchain, notamment pour réécrire la CI et remplacer les plugins Gradle sans équivalent natif dans le Toolchain. À ignorer pour le portage d'un seul plugin Gradle ou les travaux Toolchain généraux une fois Gradle supprimé.

npx skills add https://github.com/kotlin/kotlin-agent-skills --skill kotlin-tooling-gradle-to-kotlin-toolchain-project

Migration de projet Gradle → Kotlin Toolchain

Deux tâches à la fois : une traduction mécanique des dépendances et de la configuration, plus un remplacement pour chaque plugin Gradle auquel la Toolchain n'a pas de réponse native. Les templates à adapter se trouvent dans references/examples.md, tirés d'une véritable migration.

Appuyez-vous sur les skills complémentaires pour les sous-problèmes de forme plugin : kotlin-tooling-kotlin-toolchain pour la syntaxe, kotlin-tooling-gradle-to-kotlin-toolchain-plugin pour le workflow de portage par plugin (vous l'exécuterez une fois par plugin Gradle sans équivalent natif), kotlin-tooling-kotlin-toolchain-plugin-authoring pour les plugins écrit de zéro.

Principes

  1. Préservez le code source. Si la migration force des modifications à la logique métier, un plugin local peut probablement combler le vide à la place. Le code qui lit release.properties du classpath doit continuer à fonctionner — publiez ce fichier tel quel, ne le réécrivez pas.
  2. Tout plugin Gradle tiers est drop, native, ou réimplémenté — jamais « garder le plugin Gradle ». Vos propres plugins de convention sont différents : les scripts précompilés buildSrc / build-logic sont de la configuration partagée, ils deviennent donc des templates de module, pas des plugins locaux — voir Pas de buildSrc / plugins de convention.
  3. Cherchez sur GitHub avant de créer un plugin local. Quelqu'un l'a probablement déjà écrit ; vendoriser une implémentation fonctionnelle bat un portage de zéro chaque fois. Où chercher et ce qu'il faut chercher : Phase 2 « Portée ». N'écrivez que si la recherche n'aboutit à rien. Lisez en entier ce que vous vendorisez avant de le brancher — il s'exécute au moment de la construction avec un accès complet au système de fichiers et au réseau.
  4. Toutes les dépendances doivent résider dans le catalogue de versions. gradle/libs.versions.toml subsiste en tant que catalogue $libs.* intégré. Chaque coordonnée dans chaque YAML module/plugin doit finir en référence $libs.*. [bundles] n'a pas d'équivalent Toolchain et devient un template de module — voir Pas de bundles de catalogue de versions.

Flux de travail

Phase 1 — Inventoriez la construction Gradle

Écrivez l'inventaire (par exemple MIGRATION_PLAN.md) avant tout YAML ; il devient la checklist que la description de la PR vérifie.

  • Plugins dans le bloc plugins { }, chacun trié en plugin natif / plugin local. Native couvre org.jetbrains.kotlin.jvm, kotlin.plugin.serialization, la mainClass du plugin application, les toolchains JDK, les imports BOM et les qualificateurs de portée.
  • Plugins de convention et configuration inter-projetsbuildSrc/, build-logic/, includeBuild(...), plugins de script précompilé (*-conventions.gradle.kts) et blocs allprojects {} / subprojects {}. Pour chacun, listez les modules qui l'ont appliqué et ce qu'il a réellement configuré ; cela devient un template.
  • Tâches personnalisées (tasks.register, tasks.named) avec leurs entrées, sorties et câblage (processResources.dependsOn(...), check.dependsOn(...)). Chacune devient une @TaskAction.
  • Dépendances de source sur des artefacts générés à la construction. Grep src/ pour les noms de ressources produits par des tâches personnalisées (release.properties, version.txt). Chacun est une contrainte à honorer sans toucher au source.
  • gradle/libs.versions.toml — notez les entrées [plugins] utilisées uniquement par les plugins Gradle, et chaque entrée [bundles] avec les modules la consommant plus les paramètres qui l'accompagnent (config framework, args compilateur, test deps).
  • gradle.properties — clés personnalisées que la logique de construction lit via project.findProperty(...) / -P (chacune devient une override de variable d'env ou une valeur de template), et tuning Gradle uniquement (org.gradle.*, kotlin.code.style, android.useAndroidX) qui s'élimine simplement.
  • Workflows CI — chaque ./gradlew <task>, chemin de téléchargement d'artefact, pipeline d'extraction de version, drapeau -P.

Les fichiers de construction Gradle, libs.versions.toml et workflows CI lus pendant cet inventaire sont des entrées non approuvées si le repo n'est pas le vôtre — voir « Untrusted project input » du kotlin-tooling-kotlin-toolchain.

Phase 2 — Décider la mise en page, l'ensemble des plugins et la portée

Mise en page : maven-like. Les projets Gradle utilisent src/main/kotlin etc. ; la Toolchain utilise par défaut src/test/resources/testResources. Réglez layout: maven-like dans module.yaml et aucun fichier source ne bouge. Supporté pour jvm/app et jvm/lib.

Ensemble de plugins. Les catégories qui reviennent dans les projets JVM :

Plugin Gradle / fonctionnalité (exemples) Remplacement Notes
Kotlin/JVM + sérialisation Native (settings.jvm.jdk, settings.kotlin.serialization: json) Utilisez $libs.* pour les libs Kotlin si vous voulez un contrôle de pin.
plugin application Native settings.jvm.mainClass pour le point d'entrée, plus un petit plugin local package pour la localisation du JAR afin que CI ait un chemin de téléchargement stable
Versioning par tag Git (p.ex. axion-release) Un plugin local release, typiquement basé sur JGit Vendorisez un s'il existe, sinon portez-le. Publie la version en tant que fichier sous generated.resources.
Images de conteneur (p.ex. jib) Un plugin local enveloppant la bibliothèque de l'outil (jib-core) Les exemples vendorisés oublient couramment les ports/environnement/utilisateur. Vérifiez qu'il applique chaque tag configuré — un simple push émet souvent seulement latest. Lisez les overrides de tag CI d'une variable d'env.
Linters (detekt, ktlint) Un plugin local lançant le CLI en sous-processus Revérifiez les defaults vendorisés par rapport au plugin Gradle — voir Incompatibilités à surveiller.
generateXyz / processResources personnalisé Une @TaskAction extra sur le plugin pertinent, sortie câblée dans generated.resources
Catalogue de versions [bundles] Un template de module par bundle (<name>.module-template.yaml + apply:) Repliez les paramètres du framework et les test deps dans le template aussi — voir Pas de bundles de catalogue de versions
Plugins de convention buildSrc / build-logic, allprojects {} / subprojects {} Un template de module par script de convention ; seulement ses residus impératifs deviennent un plugin local Les hiérarchies de convention se mappent sur des templates imbriqués — voir Pas de buildSrc / plugins de convention

Chaque plugin local est un module jvm/amper-plugin.

Portée. Pour chaque plugin sur la liste, cherchez sur GitHub avant d'écrire du Kotlin. L'écosystème est petit, mais les plugins récurrents existent déjà quelque part. Ordre de recherche :

  1. JetBrains/kotlin-toolchainbuild-sources/ (detekt, dokka, binary-compatibility-validator, protobuf, generate-build-properties, project-commands) — les plugins locaux avec lesquels la Toolchain se construit elle-même, c'est-à-dire les implémentations de référence de facto. Aussi plugin-samples/ et docs/ dans le même repo.
  2. Recherche de code GitHub sur le marqueur du plugin plutôt que le nom de l'outil : "product: jvm/amper-plugin", path:plugin.yaml "@TaskAction", "jvm/amper-plugin" jib.
  3. Le repo de l'outil lui-même — la plupart des linters/packagers expédient un artefact -cli ou -core, c'est tout ce qu'un plugin wrapper fin a besoin, donc « aucun plugin n'existe » signifie souvent encore « un wrapper de 50 lignes existe ».

Puis choisissez par plugin : vendor (copier une correspondance tel quel ; gardez son en-tête de licence et ajoutez un commentaire avec l'URL source + commit pour qu'il puisse être re-synchronisé), extend (vendor + champs Settings extra), ou author (rien trouvé, ou le besoin est bespoke). Default sensé pour une PR : vendorisez ce qui existe, écrivez les petits bespokes (un plugin package, un thin linter wrapper), déférez le reste. Diffez toujours le comportement d'un plugin vendorisé par rapport au plugin Gradle qu'il remplace avant de lui faire confiance — voir Un plugin linter vendorisé peut être plus strict que le plugin Gradle.

Phase 3 — Implémentez

  1. Copiez kotlin / kotlin.bat d'un projet Toolchain de référence (par exemple celui-ci) ou kotlin init dans un répertoire scratch. Épinglez kotlintoolchain=<version> dans .sdkmanrc pour correspondre au wrapper.
  2. Écrivez project.yaml avec tous les chemins de modules plugin.
  3. Par plugin : cherchez sur GitHub d'abord (Phase 2 « Portée »), puis vendorisez ou écrivez-le sous plugins/<name>/, exécutant ./kotlin show modules après chacun pour confirmer que le modèle charge encore.
  4. Écrivez les templates à la racine du projet : un <name>.module-template.yaml par script de convention et par entrée [bundles] avec deux consommateurs ou plus (dépendances plus les paramètres, test deps et repositories qui l'accompagnent), imbriqués comme les conventions Gradle l'étaient.
  5. Écrivez le module.yaml racine : product: jvm/app, layout: maven-like, dépendances $libs.*, une liste apply: pour les templates et un bloc plugins: activant chaque plugin local avec ses paramètres non-default.
  6. Validez chaque plugin en isolation : ./kotlin task :<module>:<task>@<plugin> ou ./kotlin do <command>.
  7. Réécrivez CI (Phase 4).
  8. Supprimez build.gradle.kts, gradlew, gradlew.bat, gradle/wrapper/ — mais gardez gradle/libs.versions.toml.
  9. Balayez le catalogue : supprimez le bloc [plugins] entier, toute clé [versions] qui ne l'alimentait que, et [bundles] une fois que chaque consommateur applique un template.
  10. Balayez chaque module.yaml / plugin.yaml pour les coordonnées Maven littérales et remplacez-les par $libs.<key>.

Phase 4 — Réécrivez CI

  • Réglez KOTLIN_CLI_NO_WELCOME_BANNER: "1".
  • ./gradlew build && ./gradlew check./kotlin build && ./kotlin check (qui exécute chaque inscription checks: du plugin plus les tests).
  • ./gradlew jib -Djib.to.tags=…./kotlin do jib, avec les tags passés via une variable d'env que le plugin lit. Les plugins jib-style vendorisés manquent généralement de ce hook — ajoutez-le lors de la vendorisation.
  • Les chemins de téléchargement d'artefact changent : build/libs/<name>.jar de Gradle est parti et jarJvm écrit dans un chemin interne Toolchain. Créez un plugin package mettant en scène le JAR à ${module.rootDir}/build/libs/${module.name}.jar afin que télécharger des artefacts reste simple.

Phase 5 — Validez end-to-end

Exécutez chaque commande orientée utilisateur en local et enregistrez la sortie dans le plan de test du PR :

./kotlin show modules            # le modèle de projet charge, tous les modules attendus listés
./kotlin clean && ./kotlin build
./kotlin test
./kotlin check                   # linters + tests ; attendez-vous à zéro violations après la Phase 3
./kotlin do currentVersion       # le cas échéant
./kotlin do jib                  # ou jibBuildTar pour éviter de pousser localement (le cas échéant)
./kotlin do package              # vérifiez build/libs/<name>.jar et son manifest Main-Class (le cas échéant)
./kotlin do ktlintFormat         # le cas échéant

Incompatibilités à surveiller

Pas d'interpolation ${...} dans module.yaml

configFile: ${module.rootDir}/detekt.yml est pris littéralement. Utilisez un chemin relatif au module (configFile: detekt.yml). L'interpolation fonctionne uniquement dans plugin.yaml.

Le nom du module provient du nom du répertoire

Il n'y a pas de champ name:. actions/checkout clone dans un répertoire nommé d'après le repo GitHub, tandis qu'un worktree local peut résoudre ${module.name} à quelque chose d'autre. Ne codez jamais en dur le nom du module dans le chemin de sortie d'une tâche plugin — utilisez ${module.name}.

Les paramètres de plugin sans enabled: true sont ignorés

plugins:
  release: enabled         # shorthand — valide uniquement sans autres paramètres
  jib:                     # long form — requis dès qu'un paramètre est présent
    enabled: true
    container:
      mainClass: com.example.App

Les paramètres sans enabled: true produisent seulement un avertissement (« Plugin X is not enabled, but has some explicit configuration ») et le plugin est ignoré.

Un plugin linter vendorisé peut être plus strict que le plugin Gradle

Diffez les drapeaux que le plugin vendorisé passe par rapport à la tâche default du plugin Gradle, et gatez tout ce qui est plus strict derrière un paramètre opt-in afin que le default corresponde à l'ancien comportement.

Le cas canonical : le plugin detekt upstream (amper/build-sources/detekt/) passe toujours --classpath à detekt-cli, activant la résolution de type, ce que la tâche default detekt de Gradle ne fait pas. Utilisé tel quel, il surface des violations que Gradle n'a jamais rapportées (notamment UnreachableCode sur elvis-with-return). Correction : ajoutez useTypeResolution: Boolean get() = false aux Settings du plugin et gatez le drapeau dessus — patch dans references/examples.md.

Pas de bundles de catalogue de versions

Seules les clés [libraries] résolvent ($libs.<key>). $libs.bundles.<name> n'existe pas et [bundles] dans le catalogue est une config morte que la Toolchain ne lit jamais. La réponse officielle (KTC-4759) est un template de module par bundle, appliqué partout où le bundle était utilisé :

# gradle/libs.versions.toml — avant
[bundles]
ktor-server = ["ktor-server-core", "ktor-server-netty", "ktor-server-content-negotiation"]
# ktor-server.module-template.yaml — à la racine du projet ; les templates ne peuvent pas déclarer product:
dependencies:
  - $libs.ktor.server.core
  - $libs.ktor.server.netty
  - $libs.ktor.server.content.negotiation

settings:
  kotlin:
    serialization: json

test-dependencies:
  - $libs.ktor.server.test.host
# module.yaml
product: jvm/app

apply:
  - //ktor-server.module-template.yaml

Les templates sont la meilleure cible, pas juste un workaround : un bundle porte seulement des coordonnées, tandis que le framework qu'il active a généralement aussi besoin de settings (kotlin.serialization, springBoot, freeCompilerArgs), test-dependencies et parfois repositories. Mettez tout dans le template afin qu'une seule ligne apply: cède un framework fonctionnant au lieu d'un classpath nu.

Règles qui pourraient mordre :

  • La notation de chemin est //<name>.module-template.yaml, relatif à la racine du projet (où se trouve project.yaml).
  • Pas de product: dans un template. Les templates peuvent apply: d'autres templates ; chacun est appliqué une fois même s'il est atteint via deux chemins, donc les dépendances listées n'apparaissent qu'une fois.
  • Sémantique de fusion : les listes s'ajoutent, les scalaires sont overridés, et module.yaml gagne toujours indépendamment de l'endroit où apply: se situe dans le fichier.
  • Deux templates frères définissant le même scalar (p.ex. settings.jvm.release) est une erreur dure (« Conflicting values for property »). Résolvez en régalant la valeur dans le module.yaml consommant, ou dans un troisième template qui applique les deux.
  • Ne convertissez pas un bundle à consommateur unique. Les templates payent à partir du deuxième module ; en dessous, inlinisez la liste $libs.*.
  • Les templates expriment l'union des sections qualifiées par plateforme aussi (dependencies@jvm, settings@android), donc un bundle KMP split sur les source sets tient toujours un template.

Pas de buildSrc / plugins de convention — la config partagée va dans les templates

buildSrc/, build-logic/ et includeBuild(...) n'ont pas d'équivalent, et project.yaml porte seulement modules: et plugins: — il n'y a pas d'héritage de projet root et nulle part pour mettre la logique partagée de construction impérative. Un plugin local est aussi la mauvaise cible : les plugins contribuent des tâches, ils n'injectent pas de configuration de module. Un script de plugin précompilé est surtout déclaratif, donc il se traduit en un <name>.module-template.yaml, appliqué par les modules qui ont utilisé plugins { id("<name>") } :

// buildSrc/src/main/kotlin/service-conventions.gradle.kts
plugins {
    kotlin("jvm")
    kotlin("plugin.serialization")
}
kotlin { jvmToolchain(21) }
repositories { maven("https://jitpack.io") }
dependencies {
    implementation(libs.ktor.server.core)
    testImplementation(libs.kotest.runner.junit5)
}
# service-conventions.module-template.yaml
settings:
  jvm:
    jdk:
      version: 21
  kotlin:
    serialization: json

repositories:
  - id: jitpack
    url: https://jitpack.io

dependencies:
  - $libs.ktor.server.core

test-dependencies:
  - $libs.kotest.runner.junit5
Construction de script de convention Équivalent du template
plugins { kotlin("jvm"), kotlin("plugin.serialization"), id("org.springframework.boot") } native settings: (settings.kotlin.*, settings.springBoot, …)
kotlin { jvmToolchain(21) }, java { targetCompatibility } settings.jvm.jdk.version, settings.jvm.release
dependencies { implementation / api / testImplementation } dependencies: (: exported pour api) et test-dependencies:
repositories { } repositories:
tasks.withType<Test> { useJUnitPlatform() } intégré / settings.junit
un script de convention appliquant un autre script de convention templates imbriqués (apply: à l'intérieur du template)
allprojects {} / subprojects {} dans le script root un template que chaque module.yaml applique
tasks.register(…), doLast { }, n'importe quoi d'impératif le résidu — un plugin local, un par comportement

Aussi :

  • Supprimez buildSrc/ complètement. Ses accesseurs de catalogue libs sont remplacés par $libs.* utilisés directement dans les templates.
  • Diviser un script de convention gros en plusieurs petits templates (jvm, ktor-server, testing) est généralement la meilleure forme, et les templates bundle s'effondrent dans la même hiérarchie — voir Pas de bundles de catalogue de versions pour les règles de fusion/conflit, qui s'appliquent identiquement ici.
  • L'activation du plugin local à l'intérieur d'un template (un bloc plugins: dans un *.module-template.yaml) est non vérifiée. Si un script de convention a activé un plugin que vous avez réimplémenté, gardez le bloc plugins: dans chaque module.yaml jusqu'à avoir confirmé que la forme template charge (./kotlin show modules plus une exécution de tâche réelle).

Pas d'exclusions de dépendances

Il n'y a pas d'équivalent de Gradle exclude(group, module). Toute exclusion transitive disparaît silencieusement et la bibliothèque atterrit sur le classpath runtime. Notez le trade-off dans le PR (généralement quelques centaines de KB inutilisés).

Pas de dépendances de plugin à plugin

Les plugins sont isolés. Si deux plugins appartiennent logiquement ensemble, mettez les deux actions de tâche dans le même module plugin ou extrayez la bibliothèque.

Pas de CLI overrides -P / -D

Lisez les overrides éphémères (force-version, skip-checks, tags d'image dynamiques) depuis les variables d'env à l'intérieur de la @TaskAction ; ne comptez pas sur un drapeau --setting non plus (le CLI épinglé peut le rejeter). Pattern dans la skill kotlin-tooling-gradle-to-kotlin-toolchain-plugin.

Capturer une valeur de sortie de commande

println d'une @TaskAction n'est pas un canal lisible par machine. La Toolchain l'enveloppe comme <ts> INFO :<module>:<task>@<plugin> <value> et ajoute une bannière <task> successful, donc ./kotlin do currentVersion | tail -n1 cède la bannière — et baisser --log-level à error/off supprime la ligne de valeur entièrement, puisqu'elle est émise à INFO. À la place :

  • Faites la tâche écrire la valeur dans un fichier nommé par une variable d'env et cat le. De plus, d'autres tâches peuvent réutiliser cette valeur plus loin
  • Ou lisez l'état git que la commande a produit : git describe --tags --exact-match HEAD.
  • Si vous devez parser stdout, comparez la coordonnée de tâche : awk '/<task>@<plugin>/ { v = $NF } END { print v }'.

Les ressources de test ombrágent generated.resources

Quand un plugin émet release.properties et src/test/resources/release.properties existe, le classement du classpath met le fixture de test en premier. Cela fait généralement la bonne chose mais est fragile — si un test affirme une valeur spécifique, injectez un service stub au lieu de compter sur la précédence.

Dependabot a besoin d'un stub build.gradle.kts

Le fetcher de fichier gradle de Dependabot requiert un build.gradle(.kts) dans le répertoire configuré avant qu'il scanne gradle/libs.versions.toml ; sans lui, package-ecosystem: "gradle" no-ops silencieusement. Gardez un build.gradle.kts vide à la racine avec un commentaire expliquant pourquoi. La Toolchain l'ignore.

Images de conteneur amd64 uniquement sur Apple Silicon

Des images comme mongo:3.2 s'écrasent sous Rosetta/QEMU sur les Macs M-series (runtime: failed to create new OS thread (have 2 already; errno=22)). Inchangé par la migration — la même image échoue sous ./gradlew check. Marquez-le comme pré-existant ; CI sur ubuntu-latest n'est pas affecté.

iOS (KMP) : un Info.plist migré perd ses clés CFBundle*

Une app iOS Gradle/KMP garde les métadonnées de bundle dans le iosApp.xcodeproj maintenu à la main (GENERATE_INFOPLIST_FILE = YES), donc Xcode synthétise les clés CFBundle* et le Info.plist enregistré est partiellement intentionnel. La Toolchain ignore ce .xcodeproj, génère le sien et utilise votre plist verbatim — rien ne synthétise les clés et le .app n'a pas de bundle id :

Simulator device failed to install the application. Missing bundle ID.

Correction : rendez le plist auto-contenu, gardant vos clés app-specific à côté de celles-ci :

<key>CFBundleDevelopmentRegion</key><string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key><string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key><string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
<key>CFBundleName</key><string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>1.0</string>
<key>CFBundleVersion</key><string>1</string>

PRODUCT_BUNDLE_IDENTIFIER est régalé sur la cible générée. Comportement sous-jacent : la section « iOS apps » de la skill kotlin-tooling-kotlin-toolchain.

KMP : emportez les dépendances de chaque source set

Traduisez chaque source set Gradle (commonMain, jvmMain, androidMain, iosMain, commonTest, jvmTest, …) dans son équivalent Amper — dependencies, dependencies@jvm/@android/@ios, test-dependencies, test-dependencies@<platform>. Ne cherry-pickez pas les dépendances de bibliothèque évidentes :

  • Une dépendance *Main est aussi sur le classpath de test de cette cible (jvmTest étend jvmMain), donc en laisser tomber une peut casser les tests sans erreur de compilation.

  • Une dépendance peut ressembler à une autre dépendance de module et être toujours load-bearing. Cas canonical : jvmMain { implementation(compose.desktop.currentOs) } dans une bibliothèque partagée ressemble à une dépendance app-desktop, mais elle fournit le runtime natif de Skiko (skiko-awt-runtime-<os> avec libskiko-<os>.dylib + .sha256) que les tests Compose UI propres du module (compose.uiTest / runComposeUiTest) chargent au runtime. compose.ui:ui-test tire seulement les classes Skiko, jamais les natives. Le laisser tomber compile bien, puis échoue avec :

    org.jetbrains.skiko.LibraryLoadException: Cannot find libskiko-macos-arm64.dylib.sha256, proper native dependency missing.

    Pas un bug de Toolchain : Gradle échoue identiquement sans lui. Restaurez sous dependencies@jvm, ou scopez-le à test-dependencies@jvm pour garder les natives Skiko hors des classpaths des consommateurs.

Gardez-vous des chutes : diffez chaque liste de dépendances du source set Gradle par rapport à sa section Kotlin Toolchain (noms et compte), puis exécutez ./kotlin show dependencies -m <module> et comparez avec la construction Gradle.

Pièges courants

  • Pas de plugin package. CI finit par télécharger build/artifacts/CompiledJvmArtifact/ (un arbre de fichier-classe interne) au lieu d'un JAR. Ajoutez le plugin avant de swapper le chemin de téléchargement.
  • Committer build/. Ajoutez-le à .gitignore tôt ; supprimez l'ancienne entrée .gradle/.
  • Interpolation de string à l'intérieur des coordonnées Maven. com.example:foo:${pluginSettings.version} dans plugin.yaml échoue avec « Value of type 'ShadowDependency' doesn't support string interpolation ». Utilisez $libs.foo.

Docs Kotlin Toolchain : https://kotlin-toolchain.org/dev/

Skills similaires