Nucleus
Performance & nativeGraalVM Native Image

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.

Le plugin Gradle Nucleus enregistre un graphe de tâches pour compiler et empaqueter votre application en image native GraalVM. Depuis la 2.2, la surface de tâches publique reflète le pipeline de distribution JVM (run / createDistributable / runDistributable / packageDistributionForCurrentOS), plus les tâches de packaging par format (packageGraalvmDeb, packageGraalvmDmg, packageGraalvmNsis). La plupart des tâches sont câblées en dépendance de l'assembleur interne packageGraalvmNative et s'exécutent automatiquement. Cette page liste les tâches, montre où leur sortie atterrit, et explique comment les lancer sur chaque OS cible en CI.

Tâches Gradle

Surface publique (alignée sur le pipeline JVM)

TâchePendant JVMDescription
runGraalvmNativerunBoucle de dev rapide : force le quick-build native-image (-Ob), puis lance le binaire. Surcharge l'optimization configurée.
createGraalvmNativeDistributablecreateDistributableConstruit le dossier d'app native autonome pour l'OS courant (sans installateur).
runGraalvmNativeDistributablerunDistributableBuild avec l'optimisation configurée (packaging complet) et lance le dossier d'app.
packageGraalvmNativeDistributionForCurrentOSpackageDistributionForCurrentOSEmpaquette la distribution native pour l'OS courant.

runGraalvmNative et les tâches distributable enregistrent une entrée de compile quickBuild, si bien que le basculement entre elles recompile au lieu de servir un binaire périmé de l'autre mode.

Metadata, installateurs et helpers

TâcheDescription
packageGraalvmNativeAssembleur interne : compile l'image et pose le dossier d'app (binaire + libs Skiko/AWT + icônes). Préférez les tâches publiques ci-dessus.
runWithNativeAgentLance l'application avec le native-image-agent de GraalVM pour collecter la metadata de réflexion.
resolveGraalvmReachabilityMetadataRésout la metadata de reachability du dépôt Oracle pour les dépendances runtime. S'exécute automatiquement.
analyzeGraalvmStaticMetadataAnalyse statiquement le bytecode pour détecter l'usage de réflexion, JNI et ressources. S'exécute automatiquement.
filterGraalvmLibraryMetadataFiltre et fusionne la metadata par bibliothèque selon le classpath runtime. S'exécute automatiquement.
generateGraalvmPlatformMetadataGénère la metadata spécifique à l'OS courant. S'exécute automatiquement.
cleanupGraalvmMetadataRetire de votre reachability-metadata.json manuel les entrées que la metadata automatique couvre déjà.
packageGraalvmDebEmpaquette l'image native en installateur .deb (Linux).
packageGraalvmDmgEmpaquette l'image native en installateur .dmg (macOS).
packageGraalvmNsisEmpaquette l'image native en installateur NSIS .exe (Windows).

Les quatre tâches de metadata s'exécutent en dépendance de packageGraalvmNative, vous n'avez donc que rarement à les invoquer directement. Les tâches packageGraalvm<Format> utilisent electron-builder, qui exige Node.js sur le runner.

Les tâches de packaging n'existent que pour les formats que vous demandez dans nativeDistributions.targetFormats. Le nom d'une tâche reprend la constante d'enum du format : TargetFormat.Deb produit packageGraalvmDeb, TargetFormat.Nsis produit packageGraalvmNsis, et ainsi de suite.

Lancer les tâches

# Itération locale rapide (quick-build -Ob)
./gradlew runGraalvmNative

# Dossier d'app en optimisation configurée, puis lancement
./gradlew runGraalvmNativeDistributable

# Construire uniquement le dossier d'app native autonome
./gradlew createGraalvmNativeDistributable

# Empaqueter pour l'OS courant
./gradlew packageGraalvmNativeDistributionForCurrentOS

# Installateurs par plateforme
./gradlew packageGraalvmDeb     # Linux
./gradlew packageGraalvmDmg     # macOS
./gradlew packageGraalvmNsis    # Windows

# Collecte la metadata de réflexion avant une release
./gradlew runWithNativeAgent

# Retire la metadata manuelle redondante
./gradlew cleanupGraalvmMetadata

Le plugin fixe par défaut l'architecture cible de native-image (graalvm.march) par plateforme (COMPATIBILITY partout, NATIVE sous macOS Apple Silicon). Pour forcer un binaire portable partout, utilisez l'enum typé :

build.gradle.kts
nucleus.application {
    graalvm {
        march = NativeImageMarch.COMPATIBILITY
    }
}

Les builds d'exemple Nucleus mappent graalvm.march sur une propriété Gradle pour que vous puissiez la surcharger en ligne de commande. Avec ce câblage, ./gradlew packageGraalvmNative -PnativeMarch=compatibility sélectionne la cible portable. Le flag -PnativeMarch ne fonctionne que si votre script de build lit cette propriété.

Emplacements de sortie

Le dossier d'app native atterrit à côté du layout de distribution JVM (depuis la 2.2) :

<project>/build/compose/binaries/<app>/graalvm-app/
PlateformeSortie
macOSgraalvm-app/<packageName>/<app>.app/ — bundle .app complet avec Info.plist, icônes et dylibs signés
Windowsgraalvm-app/<packageName>/<app>.exe plus les DLLs compagnonnes
Linuxgraalvm-app/<packageName>/<app> plus les fichiers .so compagnons

Les installateurs par format atterrissent sous le répertoire des binaires, un dossier par format :

<project>/build/compose/binaries/<app>/graalvm-<format>/

<format> est l'id du format, par exemple graalvm-deb, graalvm-dmg ou graalvm-nsis.

Builder en CI avec GitHub Actions

Une image native doit être compilée sur chaque OS cible — la compilation croisée n'est pas possible. Lancez une matrice sur ubuntu-latest, macos-latest et windows-latest, et provisionnez la toolchain avec l'action setup-nucleus :

name: GraalVM release

on:
  push:
    tags: ["v*"]

jobs:
  build-natives:
    uses: ./.github/workflows/build-natives.yaml

  graalvm:
    needs: build-natives
    name: GraalVM - ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - { name: Linux x64,    os: ubuntu-latest }
          - { name: macOS ARM64,  os: macos-latest  }
          - { name: Windows x64,  os: windows-latest }

    steps:
      - uses: actions/checkout@v4

      # Téléchargez ici les libs natives JNI pré-buildées par le job build-natives.

      - name: Set up Nucleus (GraalVM)
        uses: NucleusFramework/Nucleus/.github/actions/setup-nucleus@main
        with:
          graalvm: 'true'
          setup-gradle: 'true'
          setup-node: 'true'   # requis pour packageGraalvm<Format>

      - name: Build the GraalVM native package
        shell: bash
        run: |
          case "$RUNNER_OS" in
            Linux)   ./gradlew :myapp:packageGraalvmDeb  -PnativeMarch=compatibility --no-daemon ;;
            macOS)   ./gradlew :myapp:packageGraalvmDmg  -PnativeMarch=compatibility --no-daemon ;;
            Windows) ./gradlew :myapp:packageGraalvmNsis -PnativeMarch=compatibility --no-daemon ;;
          esac

      - uses: actions/upload-artifact@v4
        with:
          name: graalvm-${{ runner.os }}
          path: myapp/build/compose/binaries/**/graalvm-*/**

Les builds natifs téléchargent et mettent en cache GraalVM Community Edition automatiquement, vous n'avez donc plus besoin de provisionner une toolchain GraalVM en CI — le JBR installé par setup-nucleus suffit. Mettez graalvm: 'true' uniquement pour épingler votre propre toolchain (il provisionne BellSoft Liberica NIK et exporte GRAALVM_HOME, qui est prioritaire sur le téléchargement automatique lorsqu'il correspond à la distribution configurée). setup-gradle: 'true' configure Gradle, et setup-node: 'true' installe Node.js pour les tâches de packaging electron-builder. Voir CI/CD pour le workflow de release complet, avec publication sur GitHub Releases.

Cacher le build Gradle

setup-gradle: 'true' exécute gradle/actions/setup-gradle, qui cache les dépendances et les sorties de build de Gradle. Cela couvre déjà le dépôt de metadata de reachability Oracle — il est résolu comme une dépendance normale — et les sorties des tâches de metadata. La compilation native-image de GraalVM est l'étape longue (environ 5 à 15 minutes par plateforme) et repart de zéro à chaque build ; elle n'est pas cachée de façon incrémentale.

Déboguer la réflexion manquante au runtime

Lancez le binaire natif depuis un terminal. Les échecs de réflexion produisent des messages clairs comme ClassNotFoundException ou NoSuchFieldException. Pour capturer les entrées manquantes :

  1. Lancez ./gradlew runWithNativeAgent et parcourez le chemin de code défaillant pour que l'agent enregistre l'entrée.
  2. L'agent fusionne sa sortie dans votre metadata sans écraser les entrées enrichies manuellement, donc seules les entrées réellement nouvelles sont ajoutées.
  3. Rebuild avec ./gradlew packageGraalvmNative.

packageGraalvmDeb échoue tant que homepage n'est pas défini dans nativeDistributions — electron-builder l'exige pour le packaging DEB. Voir Configuration.

Et ensuite