0.3.3-0-pre.1

This commit is contained in:
2026-09-21 08:39:57 +02:00
parent 76a955bd3d
commit f770f99a4a
9 changed files with 680 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/studies/000-README.md -->
<!-- version: 10 -->
<!-- version: 11 -->
# Études
@@ -61,3 +61,7 @@ Ces documents étudient les POC à réaliser ; ils ne lancent encore aucune impl
## Étude de cadrage 0.3.1
- [`024-V0_3_1_TAURI_ANDROID_SNAKE_AUDIT.md`](024-V0_3_1_TAURI_ANDROID_SNAKE_AUDIT.md) — audit de la baseline `0.3.0`, réutilisation Web/WASM, référence Tauri existante, environnement Android et sizing du second host Snake.
## Étude de cadrage 0.3.3
- [`025-V0_3_3_ANDROID_NATIVE_MULTI_ABI_AUDIT.md`](025-V0_3_3_ANDROID_NATIVE_MULTI_ABI_AUDIT.md) — audit de la baseline `0.3.1`, toolchain Android native actuelle, choix ABI/API, packaging APK/AAB et contraintes 16 KB.

View File

@@ -0,0 +1,248 @@
<!-- file: docs/studies/025-V0_3_3_ANDROID_NATIVE_MULTI_ABI_AUDIT.md -->
<!-- version: 1 -->
# Audit Android SDL3 natif multi-ABI pour 0.3.3
## Statut et objet
Cette étude cadre `0.3.3-0-pre.1` à partir de l'archive taggée `v0.3.1`. Elle reste non normative : les décisions opérationnelles retenues pour la version sont portées par `docs/plans/003-V0_3_3_ANDROID_NATIVE_MULTI_ABI_PLAN.md` et deviendront des responsabilités durables dans les documents Android concernés au moment où l'implémentation les rendra réelles.
L'objectif est de remplacer le chemin historique :
```text
scripts/build_android_rust.py -> cargo ndk -> src/main/jniLibs -> Gradle
```
par un chemin possédé par le build Android :
```text
Gradle -> tâches Rust/cargo-ndk -> jniLibs générés -> APK/AAB
```
sans réintroduire un orchestrateur ad hoc externe.
## Base auditée
L'archive fournie correspond à la stable `0.3.1` et déclare `workspace.package.version = "0.3.1"`.
Avant modification, les contrôles statiques exécutables dans l'environnement de génération donnent :
```text
unzip -t : no errors
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
games.sasedev workspace audit: clean
Markdown table audit: clean (5 table(s), 222 file(s))
Distribution layout audit: clean (48 required path(s), 1 forbidden path(s) absent)
```
L'archive contient 378 fichiers, aucun lien symbolique, aucun fichier vide, aucun `.git`, `target/`, `node_modules/` ou `gen/android/` généré.
La sortie locale fournie par l'utilisateur annonce 230 fichiers Markdown avant son build Tauri, contre 222 dans le ZIP taggé. La baseline taggée reste autoritaire. L'écart est compatible avec la présence locale d'un arbre Tauri `gen/android/` déjà initialisé, que l'archive de tag n'embarque pas ; il ne constitue pas à lui seul une divergence de source tant que les audits restent propres.
## Règles et frontières vérifiées
La revue de `RULES.md` et des règles référencées par le prompt confirme notamment :
- `0-pre.1` doit rester une tranche de cadrage, sizing, recherche, risques, validations et planification ;
- une prerelease non-fix synchronise la version technique même si elle est principalement documentaire ;
- les builds, tests et smokes finaux restent côté utilisateur ; le générateur peut exécuter les audits statiques ;
- tout chemin de build nouveau ou réactivé en `0.3.x` ne doit pas être piloté par Python ;
- les commandes Android ciblées sont préférées et tout changement de répertoire doit rester dans un sous-shell ;
- les sorties générées ne doivent pas devenir des sources versionnées ;
- les tests workspace complets restent des gates rares et planifiées ;
- l'historique d'une tranche n'est ajouté que par la tranche suivante après validation réelle.
Aucune règle ne justifie de réécrire Gradle dans `0-pre.1`. La tranche doit donc produire les décisions et le plan, puis laisser l'implémentation à `0-pre.2` et suivantes.
## Pipeline Android natif hérité
Le projet Android est un multi-module Gradle séparé du workspace Cargo avec :
```text
Android/common
Android/game-reflex-poc
Android/game-snake-poc
```
La configuration source déclare actuellement :
```text
AGP 9.4.0
compileSdk 36
targetSdk 36
minSdk 21
JDK source 17
NDK 28.2.13676358 (r28c)
SDL3 AAR SDL3-3.4.16.aar
```
Le ZIP ne vend pas l'AAR SDL3, conformément au contrat `Android/libs/README.md`. Il vérifie donc le nom/version attendu, pas les octets de l'AAR présent sur une machine de développement.
L'audit relève aussi que `Android/README.md` portait encore un paragraphe de toolchain daté de `0.1.0-0-pre.9`, contradictoire avec le pipeline natif réellement stabilisé ensuite. `0-pre.1` corrige cette description sans toucher au build.
Le projet Android natif ne contient pas encore de Gradle Wrapper. `Android/README.md` attend Gradle 9.6.0 globalement. Comme la reproductibilité devient une responsabilité centrale de `0.3.3`, l'introduction d'un wrapper Gradle épinglé doit être traitée avec le premier vrai changement de pipeline, pas dans le cadrage documentaire.
## Responsabilités du script historique
`scripts/build_android_rust.py` remplit aujourd'hui plusieurs responsabilités qu'il faut transposer explicitement au build Gradle plutôt que supprimer aveuglément :
1. associer un module Android à exactement une feature jeu Rust ;
2. lire le nom de l'AAR SDL3 et la version NDK depuis `Android/gradle.properties` ;
3. extraire `libSDL3.so` de l'AAR Prefab pour chaque ABI afin de fournir la bibliothèque de liaison à Rust ;
4. rendre `libSDL3.so` disponible au runtime Android ;
5. résoudre le NDK side-by-side exact et transmettre son chemin uniquement au sous-processus natif ;
6. fixer le niveau Android de `cargo-ndk` à 21 ;
7. invoquer `cargo ndk` avec exactement une feature `reflex` ou `snake` ;
8. vérifier que `libgame_android_entrypoint.so` a réellement été produit.
Le script écrit actuellement des bibliothèques sous `Android/<app>/src/main/jniLibs/<abi>/`. Ces fichiers sont ignorés mais restent des artefacts de build dans un répertoire source. La cible `0.3.3` doit plutôt enregistrer un répertoire de `jniLibs` généré sous `Android/<app>/build/generated/...`, sur le même principe que le staging d'assets déjà piloté par la Variant API AGP.
## Toolchain actuelle vérifiée
La documentation Android officielle d'AGP 9.4.0, mise à jour le 18 septembre 2026, fixe :
```text
Gradle minimum/default 9.6.0
JDK minimum/default 17
Build Tools default 36.0.0
NDK default 28.2.13676358
API maximale AGP 37
```
Source officielle : <https://developer.android.com/build/releases/agp-9-4-0-release-notes>.
La baseline `AGP 9.4.0 + Gradle 9.6.0 + JDK 17 + NDK r28c` est donc cohérente et ne nécessite pas de montée de version pour lancer `0.3.3`.
La sortie utilisateur fournie pour le POC Tauri Android montre un NDK local `30.0.14904198`. Cette valeur appartient au pipeline Tauri généré et ne remplace pas la version NDK native explicitement épinglée à `28.2.13676358` dans `Android/gradle.properties`.
## SDL3 et plancher Android
La documentation SDL Android actuelle demande :
```text
Android SDK 35 ou ultérieur
Android NDK r28c ou ultérieur
API minimale 21 / Android 5.0
```
Source officielle : <https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md>.
SDL `3.4.16` est la release stable publiée le 2 septembre 2026 et l'asset Android officiel `SDL3-devel-3.4.16-android.zip` est disponible dans la release.
Source officielle : <https://github.com/libsdl-org/SDL/releases/tag/release-3.4.16>.
Le `minSdk 21` historique n'est donc pas retenu par simple inertie : il correspond au plancher actuel documenté de SDL3. Descendre sous API 21 exigerait de changer une dépendance structurante ou de maintenir un fork, ce qui est hors scope. Le candidat `minSdk` de `0.3.3` est donc `21`, sous réserve d'un smoke sur API 21 ou sur la plus ancienne image raisonnablement disponible avec écart explicitement documenté.
## Target SDK et distribution
Google Play exige depuis le 31 août 2026 qu'une nouvelle application ou mise à jour téléphone/tablette cible Android 16, API 36, ou plus récent.
Source officielle : <https://developer.android.com/google/play/requirements/target-sdk>.
Le `targetSdk 36` de la baseline reste donc approprié pour `0.3.3`. Il ne doit pas être confondu avec `minSdk 21` : l'un décrit la cible comportementale/politique de distribution, l'autre le plancher d'installation.
## Pages mémoire 16 KB
Le projet package du code natif Rust et SDL3. Les appareils Android 15+ peuvent utiliser des pages mémoire de 16 KB et Google Play impose la compatibilité 16 KB aux applications ciblant API 35+ sur appareils 64 bits.
La documentation Android précise qu'AGP 8.5.1+ et NDK r28+ fournissent le chemin standard de packaging/alignement 16 KB, NDK r28 alignant les bibliothèques produites sur 16 KB par défaut. Les bibliothèques précompilées restent néanmoins à vérifier, donc `libSDL3.so` doit faire partie du contrôle de l'artefact final.
Source officielle : <https://developer.android.com/guide/practices/page-sizes>.
Cette contrainte renforce le maintien de NDK r28c. La validation AAB doit inclure un contrôle d'alignement/compatibilité des bibliothèques natives, et un smoke 16 KB peut être ajouté si une image d'émulateur adéquate est raisonnablement disponible.
## ABI retenues pour le chemin par défaut
Les deux ABI de base proposées sont :
```text
arm64-v8a
x86_64
```
Raisons :
- `arm64-v8a` couvre la cible productive minimale sur appareil Android moderne ;
- `x86_64` conserve un chemin AVD local rapide et déjà exercé par le POC Tauri ;
- les deux sont des ABI 64 bits et répondent au besoin de supporter les architectures 64 bits pour une application native ;
- Rust fournit les targets `aarch64-linux-android` et `x86_64-linux-android` ;
- le build Tauri fourni par l'utilisateur vient de réussir pour ces deux targets, ce qui confirme au moins la présence récente de ces cibles Rust sur sa machine.
`armeabi-v7a` est différée par défaut. Le 32 bits n'abaisse pas le plancher OS puisque SDL3 reste à API 21, augmente la matrice de build/test et la taille d'un APK universal, et aucun besoin produit concret n'est actuellement démontré. Elle pourra être rouverte à partir de données d'appareils/utilisateurs ou d'un besoin de distribution mesuré.
`x86` 32 bits n'est pas retenue dans la matrice de base.
## APK universal, AAB et splits
Pour les tests locaux, la cible est un APK Debug universal contenant les deux ABI retenues :
```text
lib/arm64-v8a/libSDL3.so
lib/arm64-v8a/libgame_android_entrypoint.so
lib/x86_64/libSDL3.so
lib/x86_64/libgame_android_entrypoint.so
```
Aucun split ABI Gradle ne doit être activé par défaut pour cet artefact : un seul APK doit pouvoir être installé sur l'appareil ARM64 et sur l'AVD x86_64.
Pour la distribution, la cible est un AAB. Le format App Bundle laisse au store la génération des APK optimisés par configuration, notamment par ABI ; un AAB n'est pas lui-même un APK installable.
Source officielle : <https://developer.android.com/guide/app-bundle>.
Les APK split ABI restent une option de diagnostic/distribution directe future, pas un artefact par défaut de `0.3.3`.
## Architecture Gradle/Cargo recommandée
La solution retenue pour l'implémentation est une logique Gradle commune sous `Android/gradle/`, consommée par les applications natives. Elle doit :
- déclarer les ABI retenues dans une propriété/configuration centrale explicite ;
- enregistrer des tâches par variante et, si nécessaire, par ABI ;
- déclarer des inputs/outputs Gradle afin de préserver un rebuild incrémental raisonnable ;
- extraire la bibliothèque SDL3 native de l'AAR vers un répertoire de build pour le linkage Rust sans modifier l'AAR source ;
- invoquer `cargo ndk` avec le NDK r28c configuré et `CARGO_NDK_PLATFORM=21` ;
- sélectionner exactement une feature jeu selon le module consommateur ;
- écrire les bibliothèques natives dans un répertoire `build/generated/...` ;
- enregistrer ce répertoire comme `jniLibs` généré de la variante ;
- vérifier explicitement les deux `.so` attendues par ABI avant packaging ;
- échouer avec un diagnostic déterministe si AAR, NDK, cargo-ndk, target Rust ou output manque.
Cette logique n'est pas un nouveau script externe : elle fait partie du build Android qui possède l'artefact final.
## Snake et Reflex
Snake reste le jeu-sonde initial parce que le prompt le demande et que le POC Tauri a déjà prouvé ARM64 + x86_64 pour ce jeu.
Reflex ne doit pas provoquer une seconde architecture. Une fois la factorisation Gradle commune prouvée avec Snake, Reflex peut devenir le second consommateur dans la même version si l'opération se limite essentiellement au mapping `module -> feature`. Si des besoins spécifiques apparaissent, ils doivent être évalués avant d'élargir le scope.
## Risques identifiés
Les principaux risques à fermer pendant les tranches d'implémentation sont :
- absence de Gradle Wrapper dans le projet Android natif ;
- AAR SDL3 absent du ZIP par conception et donc à inventorier sur la machine de validation ;
- `cargo-ndk` présent mais version non encore relevée dans la sortie fraîche fournie ;
- extraction Prefab SDL3 différente de l'hypothèse historique ;
- dépendances Gradle mal ordonnées entre build Rust et merge/package JNI ;
- cache Gradle incorrect si les inputs Rust/assets/toolchain sont sous-déclarés ;
- production accidentelle de deux features jeu dans le même `cdylib` ;
- compatibilité 16 KB d'une bibliothèque précompilée SDL3 non vérifiée dans l'artefact final ;
- confusion entre NDK 30 du POC Tauri et NDK r28c du pipeline Android natif ;
- impossibilité pratique de créer un AVD API 21 récent, auquel cas l'écart de smoke doit rester explicite.
## Inventaire environnement encore à relever côté utilisateur
La sortie locale fournie confirme déjà :
- `JAVA_HOME=/usr/lib/jvm/temurin-17-jdk-amd64` pour le build présenté ;
- un build Tauri Debug universal réussi pour `aarch64` et `x86_64` ;
- un NDK Tauri local `30.0.14904198` ;
- un `cargo check --workspace` propre sur la stable `0.3.1`.
L'historique validé de `0.3.1` indique Rust/Cargo `1.94.1`, les targets Android ARM64/x86_64, ADB `37.0.1`, un appareil ARM64 réel et plusieurs AVD. Ces valeurs sont utiles comme transmission, mais ne remplacent pas l'inventaire frais demandé par le prompt `004`.
Avant d'ouvrir l'implémentation `0-pre.2`, la gate utilisateur doit donc relever explicitement `rustc`, `cargo`, `cargo ndk`, les targets Rust, Java, `JAVA_HOME`, `ANDROID_HOME`, ADB, appareils, AVD, Gradle natif, NDK r28c installé et AAR SDL3 local.
## Conclusion de cadrage
Le scope tient dans `0.3.3` si chaque tranche reste centrée sur un résultat vertical : ownership Gradle mono-ABI, puis multi-ABI/universal, puis AAB/compatibilité et consolidation. Aucun besoin découvert n'impose aujourd'hui de créer une version supplémentaire avant de commencer l'implémentation.