Résolution automatique de la metadata
Comment Nucleus résout et fusionne la metadata de reachability GraalVM au build pour que les images natives compilent sans configuration écrite à la main.
Nucleus résout et fusionne la metadata de reachability GraalVM au moment du build, pour que
packageGraalvmNative produise un binaire natif fonctionnel sans que vous écriviez à la main la
moindre configuration de réflexion, de ressources ou de JNI. La metadata provient de cinq
sources. Chacune est générée dans son propre répertoire et passée à native-image, et la
metadata embarquée par le module est découverte depuis le classpath.
Niveau 1 — metadata par bibliothèque
Le plugin embarque 29 fichiers de metadata curés pour l'écosystème Kotlin desktop, couvrant
Compose, Skiko, ktor (plugins client, server et websockets inclus), kotlinx.serialization,
SQLite JDBC, androidx.sqlite bundled, Coil, JNA, FileKit, SLF4J, Sentry et d'autres. Chaque
fichier peut déclarer une condition matchPackages. La tâche filterGraalvmLibraryMetadata
lit ces fichiers depuis le JAR du plugin et n'inclut un fichier que si les packages
correspondants sont présents sur le classpath runtime ; les fichiers sans condition sont
toujours inclus. Les bibliothèques de l'ensemble curé sont couvertes sans configuration
supplémentaire.
Niveau 2 — Reachability Metadata Repository GraalVM
La tâche resolveGraalvmReachabilityMetadata résout les entrées du
Reachability Metadata Repository GraalVM d'Oracle
pour chaque dépendance de votre classpath runtime. La résolution est activée par défaut et
utilise la version 0.10.6 du dépôt.
Configurez cette source avec le bloc metadataRepository { } : enabled, version,
excludedModules et moduleToConfigVersion. Voir
Configuration.
Niveau 3 — metadata spécifique à la plateforme
La tâche generateGraalvmPlatformMetadata écrit la metadata de réflexion pour le système
d'exploitation courant : sun.awt.windows.* sous Windows, sun.lwawt.macosx.* sous macOS et
sun.awt.X11.* sous Linux, avec les pipelines Java2D, les gestionnaires de polices et les
security providers. Elle injecte aussi une entrée de réflexion pour votre classe main. Aucune
configuration par plateforme n'est nécessaire dans votre build script.
Niveau 4 — analyse statique du bytecode
La tâche analyzeGraalvmStaticMetadata scanne chaque classe compilée du classpath runtime, y
compris les classes de votre propre module, et détecte :
- Les méthodes natives et leurs types de paramètres et de retour (metadata JNI).
- Les appels
Class.forName()etMethodHandles.Lookup.findClass()avec un nom de classe littéral (metadata de réflexion). - Les appels
getResource()etgetResourceAsStream()avec un chemin littéral (metadata de ressources). - Les paramètres de callback JNI — les classes passées au code natif qui rappelle Java.
- Les chaînes de superclasses JNI — les classes parentes nécessaires à l'accès aux champs depuis le code natif.
- Les classes
@Serializable— émet l'entrée de réflexionCompanion.serializer().
Détecter les classes projet orphelines
Les processeurs d'annotations et les loaders par convention de nom (Room *_Impl, Dagger/Hilt
*_Factory, adaptateurs Moshi, …) produisent des classes qu'aucun bytecode du classpath ne
référence. L'analyse littérale Class.forName et la metadata L1–L3 ne les voient jamais.
Depuis 2.3, une seule passe dans l'analyseur de bytecode enregistre un <init> public sans
argument pour ces orphelins quand :
- la classe est concrète et publique avec un constructeur public sans argument, et
- un supertype autre que
Object(ou une interface) est référencé par le code applicatif.
graalvm {
detectOrphanProjectClasses = true // défaut
// reflectionForProjectClasses = true // opt-in : toute classe projet avec un ctor public sans arg
}Les logs de build émettent [orphan] fqcn pour chaque enregistrement. Préférez le détecteur par
défaut ; n'activez reflectionForProjectClasses que comme massue temporaire (croissance d'image
mesurable).
Depuis 2.3.1 le détecteur résout les répertoires de classes projet à partir des sorties de
compilation de la cible application (par exemple jvm("desktop") → desktopMainClasses)
au lieu de chemins jvm/main en dur, ce qui couvre les cibles JVM nommées KMP. Les arêtes
nest-internal ne masquent plus les vraies classes Room *_Impl qui contiennent des types
imbriqués.
Niveau 5 — metadata embarquée par le runtime
Le module nucleus.graalvm-runtime embarque de la metadata GraalVM dans son JAR :
- Un
reachability-metadata.jsonavec des entrées de sérialisation. - Un
native-image.propertiesqui enregistre des patterns de ressources (.svg,.ttf,.otf,nucleus/.*,META-INF/services/.*etcomposeResources/.*). - Des substitutions SVM pour les gestionnaires de polices AWT, l'écran de démarrage et la classe de fenêtre X11.
native-image découvre tout cela depuis le classpath, donc aucune configuration n'est requise.
Fusion des sources
packageGraalvmNative lance les tâches de génération puis passe chaque répertoire produit à
native-image via des arguments -H:ConfigurationFileDirectories= répétés — la sortie par
bibliothèque (niveau 1), les répertoires résolus du dépôt (niveau 2), la metadata de plateforme
(niveau 3) et la sortie de l'analyse statique (niveau 4). Les éventuelles entrées manuelles de
votre propre répertoire de config sont incluses également. Le niveau 5 est lu depuis le classpath
par native-image lui-même. La plupart des apps compilent en binaire natif sans aucune metadata
écrite à la main.
Combler les manques avec l'agent de tracing
Les sources statiques peuvent rater la réflexion pilotée par des valeurs runtime, les classes chargées dynamiquement ou les patterns de bibliothèques inhabituels. Lancez l'agent une fois avant une release pour les capturer :
./gradlew runWithNativeAgentParcourez chaque écran et chaque fonctionnalité. L'agent enregistre les accès de réflexion, JNI, ressources et proxies dans un répertoire temporaire et les fusionne dans votre config sans écraser les entrées existantes. La plupart du temps, il ne trouve rien de nouveau.
Nettoyer les entrées manuelles
Si vous avez accumulé des entrées manuelles que les sources automatiques couvrent désormais, retirez celles qui sont redondantes :
./gradlew cleanupGraalvmMetadataLa tâche compare votre reachability-metadata.json manuel à la baseline combinée des niveaux 1 à
4 et retire tout ce qui est déjà géré, en indiquant quelles entrées ont été retirées et
lesquelles restent.
Depuis 2.3 elle signale aussi les entrées dont les types ne se résolvent nulle part sur le
classpath runtime (bruit typique d'agent : kotlin.Any, kotlin.Int, …). Le défaut est
report-only. Pour les purger :
./gradlew cleanupGraalvmMetadata -Pnucleus.graalvm.cleanup.removeUnresolvable=true
# Analyser sans réécrire :
./gradlew cleanupGraalvmMetadata -Pnucleus.graalvm.cleanup.dryRun=trueDepuis 2.3.1, les phantoms bien connus de l'agent de tracing Kotlin (kotlin.Any / Int /
FunctionN / types de collections mappés) sont toujours retirés pendant le cleanup, sans
exiger removeUnresolvable.
Les types sous les packages configurés dans exactReachabilityMetadata ne sont jamais retirés —
un enregistrement manquant en mode exact est ce qui restaure une erreur claire. Les entrées
manuelles restantes sont listées comme kept — not covered by any managed source.
Et ensuite
- Accès natif — patterns de ressources et substitutions de polices au-dessus de ce système de metadata.
- Tâches & CI — le graphe complet des tâches GraalVM.
Configuration
Référence du bloc DSL graalvm { } qui configure les builds native-image GraalVM : toolchain, nom d'image, arguments de build, repository de metadata et réglages par OS.
Tâches & CI
Les tâches Gradle que le plugin Nucleus ajoute pour GraalVM native image, où leur sortie atterrit, et comment les lancer sur chaque OS en CI.