Files
games/docs/development/006-ANDROID_RUST_NATIVE_BUILD.md
2026-09-21 10:32:31 +02:00

4.3 KiB

Build Rust Android natif

Contrat SDL

SDLActivity charge SDL3, puis game_android_entrypoint, et invoque le symbole SDL_main.

Le crate Rust game-android-entrypoint est un cdylib. Il est compilé avec exactement une feature métier lors du packaging d'une application :

  • reflex ;
  • snake.

Le même nom de bibliothèque native peut être utilisé dans les deux APK puisque chaque module Android possède son propre ensemble de jniLibs générés.

SDL3 AAR et Prefab

L'AAR officiel SDL3 expose ses bibliothèques natives avec Prefab. Pour chaque ABI, la logique Gradle commune cherche d'abord :

prefab/modules/SDL3/libs/android.<abi>/libSDL3.so

Elle accepte aussi jni/<abi>/libSDL3.so, puis un fallback Prefab univoque. Pour chaque ABI elle :

  1. extrait libSDL3.so depuis l'AAR dans un répertoire temporaire de linkage ;
  2. stage libSDL3.so dans les jniLibs générés du variant ;
  3. lance cargo ndk avec exactement une feature jeu ;
  4. stage libgame_android_entrypoint.so dans le même arbre généré ;
  5. vérifie la présence des deux bibliothèques avant de rendre l'output au variant AGP.

Les jniLibs générés sont déclarés à la Variant API via variant.sources.jniLibs.addGeneratedSourceDirectory. Gradle possède ainsi la dépendance entre le packaging Android et le build natif au lieu d'exiger une commande préalable séparée.

Toolchain

Le contrat projet utilise :

AGP               9.4.0
Gradle minimum    9.6.0
JDK runtime       >= 17, fourni par l'environnement
Java source/target 17
compileSdk        36
minSdk            21
NDK               28.2.13676358 / r28c
SDL3 AAR          3.4.16
cargo-ndk         requis côté développeur

Android/settings.gradle compare GradleVersion.current() au minimum projet 9.6.0. Le projet n'épingle donc pas une distribution Gradle : un Gradle local plus récent est accepté tant qu'il satisfait le minimum.

Le projet Android natif n'impose pas JAVA_HOME. Le JDK courant du shell/IDE exécute Gradle ; java -version et gradle --version sont relevés pendant les gates. Le niveau de bytecode/source Java de la glue reste explicitement 17, indépendamment du JDK de build.

cargo-ndk détecte le NDK désigné pour le sous-processus via ANDROID_NDK_HOME. La tâche Gradle résout cette valeur à partir de ANDROID_HOME ou ANDROID_SDK_ROOT et de androidNdkVersion; aucun ANDROID_NDK_HOME global n'est requis.

État 0.3.3-0-pre.3

La preuve mono-ABI de 0-pre.2 a validé que assembleDebug déclenche correctement le build Rust/SDL3 ARM64 sans orchestrateur Python préalable.

0-pre.3 élargit ce même graphe à :

arm64-v8a
x86_64

pour Snake et Reflex :

:<game>:assembleDebug
  -> buildDebugSasedevRustArm64V8a
  -> buildDebugSasedevRustX8664
  -> deux sources jniLibs générées
  -> APK Debug universal

Chaque arbre ABI contient :

build/generated/sasedevNative/debug/<abi>/jniLibs/<abi>/
├── libSDL3.so
└── libgame_android_entrypoint.so

Depuis la racine :

java -version
(cd Android && gradle --version)
(cd Android && gradle :game-snake-poc:assembleDebug)
(cd Android && gradle :game-reflex-poc:assembleDebug)

Il ne faut pas appeler scripts/build_android_rust.py avant ces tâches. Le script Python historique reste temporairement versionné jusqu'à la fermeture complète du chemin natif en 0-pre.4.

Les outputs natifs restent sous Android/<module>/build/ et ne sont jamais versionnés.

Smoke appareil

Le premier APK universal doit être inspecté mécaniquement avant installation. Il doit contenir exactement les couples attendus pour arm64-v8a et x86_64, sans dépendre de armeabi-v7a ou x86.

Le plan prévoit ensuite un smoke sur l'AVD x86_64 disponible et sur l'appareil ARM64 réel. Ces smokes attestent le packaging multi-ABI ; ils ne changent pas le minSdk, qui reste un axe séparé et sera consolidé avec l'AAB/compatibilité dans 0-pre.4.

Exception FFI Rust

Rust 2024 exige un attribut unsafe pour imposer un nom de symbole d'export. Le projet autorise donc exclusivement #[unsafe(export_name = "SDL_main")] dans game-android-entrypoint. Cette exception ne permet ni bloc unsafe, ni fonction unsafe, ni déréférencement des pointeurs argc/argv.