0.3.3-0-pre.5
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: Android/README.md -->
|
<!-- file: Android/README.md -->
|
||||||
<!-- version: 12 -->
|
<!-- version: 13 -->
|
||||||
|
|
||||||
# Android
|
# Android
|
||||||
|
|
||||||
@@ -50,8 +50,12 @@ Le build natif normal utilise directement le Gradle de l'environnement :
|
|||||||
```bash
|
```bash
|
||||||
(cd Android && gradle :game-snake-poc:assembleDebug)
|
(cd Android && gradle :game-snake-poc:assembleDebug)
|
||||||
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
||||||
|
(cd Android && gradle :game-snake-poc:bundleRelease)
|
||||||
|
(cd Android && gradle :game-reflex-poc:bundleRelease)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`bundleRelease` ne requiert aucun secret dans le dépôt. Sans configuration de signature externe, il produit un AAB de validation non destiné à être téléversé tel quel sur un store.
|
||||||
|
|
||||||
Le dépôt ne versionne pas de Gradle Wrapper pour le projet Android natif. Le minimum est un contrat de compatibilité, pas une version exacte imposée à toutes les machines.
|
Le dépôt ne versionne pas de Gradle Wrapper pour le projet Android natif. Le minimum est un contrat de compatibilité, pas une version exacte imposée à toutes les machines.
|
||||||
|
|
||||||
## SDL3 AAR
|
## SDL3 AAR
|
||||||
@@ -74,7 +78,7 @@ Le module `common` compile contre cet artefact et `SaseGameActivity` étend `org
|
|||||||
|
|
||||||
Le crate `game-android-entrypoint` produit `libgame_android_entrypoint.so`. `Android/gradle/sasedev-rust-android.gradle` transfère au graphe Gradle les responsabilités historiques d'extraction SDL3, de résolution NDK, de `cargo ndk`, de sélection de feature et de staging `jniLibs`.
|
Le crate `game-android-entrypoint` produit `libgame_android_entrypoint.so`. `Android/gradle/sasedev-rust-android.gradle` transfère au graphe Gradle les responsabilités historiques d'extraction SDL3, de résolution NDK, de `cargo ndk`, de sélection de feature et de staging `jniLibs`.
|
||||||
|
|
||||||
À `0.3.3-0-pre.4`, Snake et Reflex utilisent tous deux le même pipeline commun avec les quatre ABI Android encore supportées par la toolchain retenue :
|
À `0.3.3-0-pre.5`, Snake et Reflex utilisent tous deux le même pipeline commun avec les quatre ABI Android encore supportées par la toolchain retenue :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
arm64-v8a
|
arm64-v8a
|
||||||
@@ -85,17 +89,25 @@ x86
|
|||||||
|
|
||||||
Les ABI historiques supprimées des toolchains Android modernes, notamment `armeabi`, `mips` et `mips64`, ne font pas partie du contrat. Le support maximal signifie ici toutes les ABI encore supportées simultanément par Android NDK/SDL3/Rust, et non des architectures abandonnées par l'écosystème.
|
Les ABI historiques supprimées des toolchains Android modernes, notamment `armeabi`, `mips` et `mips64`, ne font pas partie du contrat. Le support maximal signifie ici toutes les ABI encore supportées simultanément par Android NDK/SDL3/Rust, et non des architectures abandonnées par l'écosystème.
|
||||||
|
|
||||||
Chaque `assembleDebug` doit déclencher, pour chaque ABI, un build `cargo ndk` avec exactement la feature du module concerné. Les outputs sont placés sous :
|
Chaque variante Android déclenche, pour chaque ABI, un build `cargo ndk` avec exactement la feature du module concerné. La variante Debug utilise le profil Cargo `dev`; la variante Release ajoute `--release`. Les outputs sont placés sous :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Android/<game>/build/generated/sasedevNative/debug/<abi>/jniLibs/<abi>/
|
Android/<game>/build/generated/sasedevNative/<variant>/<abi>/jniLibs/<abi>/
|
||||||
├── libSDL3.so
|
├── libSDL3.so
|
||||||
└── libgame_android_entrypoint.so
|
└── libgame_android_entrypoint.so
|
||||||
```
|
```
|
||||||
|
|
||||||
AGP fusionne ces sources `jniLibs` générées dans un APK Debug universal contenant les quatre architectures. Aucune copie durable n'est faite dans `src/main/jniLibs`.
|
AGP fusionne ces sources `jniLibs` générées dans l'APK Debug universal ou dans l'AAB Release. Aucune copie durable n'est faite dans `src/main/jniLibs`.
|
||||||
|
|
||||||
`scripts/build_android_rust.py` reste temporairement comme chemin historique jusqu'au jalon de fermeture `0-pre.5`; il ne fait plus partie du chemin normal `assembleDebug` de Snake ou Reflex.
|
Le builder historique `scripts/build_android_rust.py` est supprimé en `0-pre.5` : toutes ses responsabilités durables (résolution NDK, extraction SDL3, linkage, sélection de feature, `cargo ndk`, profil release et staging) appartiennent désormais au graphe Gradle.
|
||||||
|
|
||||||
|
## Compatibilité 16 KB et minSdk
|
||||||
|
|
||||||
|
Le plancher Android reste `minSdk 21` / Android 5.0, qui correspond au minimum Android documenté par SDL3. Le niveau Rust transmis à `cargo ndk` reste également `21`.
|
||||||
|
|
||||||
|
La baseline utilise AGP `9.4.0` et NDK `r28c`. Les bibliothèques Rust produites par ce NDK doivent être alignées pour des pages de 16 KB ; SDL3 étant fourni sous forme précompilée, `libSDL3.so` doit être vérifiée dans l'artefact final au même titre que `libgame_android_entrypoint.so`. La gate `0-pre.5` contrôle les segments ELF avec `llvm-objdump`, puis l'alignement ZIP de l'APK avec `zipalign -P 16`.
|
||||||
|
|
||||||
|
L'AAB doit contenir les deux bibliothèques pour les quatre ABI sous `base/lib/<abi>/`. Si `bundletool` est disponible dans l'environnement de validation, `bundletool dump config --bundle=<aab>` doit également annoncer `PAGE_ALIGNMENT_16K`.
|
||||||
|
|
||||||
## Bridge JNI et tactile
|
## Bridge JNI et tactile
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/gradle/sasedev-rust-android.gradle
|
// file: Android/gradle/sasedev-rust-android.gradle
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
@@ -27,6 +27,9 @@ abstract class BuildSasedevRustAndroidTask extends DefaultTask {
|
|||||||
@Input
|
@Input
|
||||||
abstract Property<String> getNdkVersion()
|
abstract Property<String> getNdkVersion()
|
||||||
|
|
||||||
|
@Input
|
||||||
|
abstract Property<Boolean> getReleaseBuild()
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
abstract DirectoryProperty getRepositoryRoot()
|
abstract DirectoryProperty getRepositoryRoot()
|
||||||
|
|
||||||
@@ -92,25 +95,32 @@ abstract class BuildSasedevRustAndroidTask extends DefaultTask {
|
|||||||
def linkFlag = "-Lnative=${linkDirectory}"
|
def linkFlag = "-Lnative=${linkDirectory}"
|
||||||
def rustFlags = inheritedRustFlags ? "${inheritedRustFlags} ${linkFlag}" : linkFlag
|
def rustFlags = inheritedRustFlags ? "${inheritedRustFlags} ${linkFlag}" : linkFlag
|
||||||
|
|
||||||
|
def cargoCommand = [
|
||||||
|
"cargo",
|
||||||
|
"ndk",
|
||||||
|
"-t",
|
||||||
|
selectedAbi,
|
||||||
|
"-o",
|
||||||
|
output.absolutePath,
|
||||||
|
"build",
|
||||||
|
"-p",
|
||||||
|
"game-android-entrypoint",
|
||||||
|
]
|
||||||
|
if (releaseBuild.get()) {
|
||||||
|
cargoCommand.add("--release")
|
||||||
|
}
|
||||||
|
cargoCommand.addAll([
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
selectedFeature,
|
||||||
|
])
|
||||||
|
|
||||||
execOperations.exec { spec ->
|
execOperations.exec { spec ->
|
||||||
spec.workingDir(repository)
|
spec.workingDir(repository)
|
||||||
spec.environment("ANDROID_NDK_HOME", configuredNdk.absolutePath)
|
spec.environment("ANDROID_NDK_HOME", configuredNdk.absolutePath)
|
||||||
spec.environment("CARGO_NDK_PLATFORM", androidApi.get().toString())
|
spec.environment("CARGO_NDK_PLATFORM", androidApi.get().toString())
|
||||||
spec.environment("RUSTFLAGS", rustFlags)
|
spec.environment("RUSTFLAGS", rustFlags)
|
||||||
spec.commandLine(
|
spec.commandLine(cargoCommand)
|
||||||
"cargo",
|
|
||||||
"ndk",
|
|
||||||
"-t",
|
|
||||||
selectedAbi,
|
|
||||||
"-o",
|
|
||||||
output.absolutePath,
|
|
||||||
"build",
|
|
||||||
"-p",
|
|
||||||
"game-android-entrypoint",
|
|
||||||
"--no-default-features",
|
|
||||||
"--features",
|
|
||||||
selectedFeature,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def rustLibrary = new File(runtimeDirectory, "libgame_android_entrypoint.so")
|
def rustLibrary = new File(runtimeDirectory, "libgame_android_entrypoint.so")
|
||||||
@@ -153,7 +163,8 @@ if (!unsupportedAbis.isEmpty()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
androidComponents {
|
androidComponents {
|
||||||
onVariants(selector().withBuildType("debug")) { variant ->
|
onVariants(selector().all()) { variant ->
|
||||||
|
def selectedReleaseBuild = variant.buildType == "release"
|
||||||
selectedAbis.each { selectedAbi ->
|
selectedAbis.each { selectedAbi ->
|
||||||
def variantSuffix = variant.name.substring(0, 1).toUpperCase() + variant.name.substring(1)
|
def variantSuffix = variant.name.substring(0, 1).toUpperCase() + variant.name.substring(1)
|
||||||
def abiSuffix = selectedAbi
|
def abiSuffix = selectedAbi
|
||||||
@@ -177,6 +188,7 @@ androidComponents {
|
|||||||
abi.set(selectedAbi)
|
abi.set(selectedAbi)
|
||||||
androidApi.set(selectedAndroidApi)
|
androidApi.set(selectedAndroidApi)
|
||||||
ndkVersion.set(configuredNdkVersion)
|
ndkVersion.set(configuredNdkVersion)
|
||||||
|
releaseBuild.set(selectedReleaseBuild)
|
||||||
repositoryRoot.set(repositoryDirectory)
|
repositoryRoot.set(repositoryDirectory)
|
||||||
outputDirectory.set(layout.buildDirectory.dir("generated/sasedevNative/${variant.name}/${selectedAbi}/jniLibs"))
|
outputDirectory.set(layout.buildDirectory.dir("generated/sasedevNative/${variant.name}/${selectedAbi}/jniLibs"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 83
|
# version: 84
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -21,7 +21,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.3-0-pre.4"
|
version = "0.3.3-0-pre.5"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
|
|||||||
1
deltas/0.3.3/0-pre.5.delete.txt
Normal file
1
deltas/0.3.3/0-pre.5.delete.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
scripts/build_android_rust.py
|
||||||
126
deltas/0.3.3/0-pre.5.md
Normal file
126
deltas/0.3.3/0-pre.5.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<!-- file: deltas/0.3.3/0-pre.5.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.3.3-0-pre.5
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Fermer le chemin Android natif historique après validation des quatre ABI : étendre l'ownership Gradle aux variantes Release/AAB, consolider `minSdk 21`, préparer la vérification 16 KB et supprimer `scripts/build_android_rust.py`.
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base : `0.3.3-0-pre.4`, validée par l'utilisateur le 2026-09-21 avec Temurin 25.0.4.1, Gradle 9.7.1, audits/Cargo/Clippy propres et builds Debug Snake/Reflex réussis sur `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`.
|
||||||
|
|
||||||
|
`history/0.3.3/0-pre.4.md` consigne cette preuve.
|
||||||
|
|
||||||
|
## Changements
|
||||||
|
|
||||||
|
- version workspace portée à `0.3.3-0-pre.5` ;
|
||||||
|
- `Android/gradle/sasedev-rust-android.gradle` enregistre désormais ses sources `jniLibs` pour toutes les variantes, pas seulement Debug ;
|
||||||
|
- les variantes Release déclenchent `cargo ndk ... build --release`, tandis que Debug conserve le profil Cargo `dev` ;
|
||||||
|
- `bundleRelease` de Snake et Reflex devient donc propriétaire de la reconstruction native quatre ABI ;
|
||||||
|
- `minSdk 21` et `CARGO_NDK_PLATFORM=21` deviennent des invariants audités pour les deux applications ;
|
||||||
|
- le builder historique `scripts/build_android_rust.py` est supprimé via manifest ;
|
||||||
|
- l'audit de distribution interdit désormais le retour de ce script et de `src/main/jniLibs` générés ;
|
||||||
|
- les anciennes commandes RC dépendant du builder Python sont remplacées par les tâches Gradle natives ;
|
||||||
|
- documentation Android/build/plan/étude actualisée pour AAB, API 21 et contrôle 16 KB.
|
||||||
|
|
||||||
|
Aucun secret de signature ni keystore n'est ajouté.
|
||||||
|
|
||||||
|
## Référence 16 KB
|
||||||
|
|
||||||
|
Le contrôle suit le chemin recommandé par Android : AGP `>= 8.5.1`, NDK `r28+`, vérification ELF par `llvm-objdump`, puis `zipalign -P 16` sur l'APK. La SDL3 de l'AAR est précompilée et doit donc être contrôlée explicitement avec la bibliothèque Rust.
|
||||||
|
|
||||||
|
## Validation attendue
|
||||||
|
|
||||||
|
Depuis la racine :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -version
|
||||||
|
(cd Android && gradle --version)
|
||||||
|
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android Web deltas history
|
||||||
|
python3 scripts/audit_distribution_layout.py
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
(cd Android && gradle :game-snake-poc:bundleRelease)
|
||||||
|
(cd Android && gradle :game-reflex-poc:bundleRelease)
|
||||||
|
```
|
||||||
|
|
||||||
|
Les AAB attendus sont sous `Android/<game>/build/outputs/bundle/release/`. Pour chaque bundle :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for game in game-snake-poc game-reflex-poc; do
|
||||||
|
AAB="$(find "Android/${game}/build/outputs/bundle/release" \
|
||||||
|
-maxdepth 1 -type f -name '*.aab' -print -quit)"
|
||||||
|
test -n "$AAB"
|
||||||
|
printf 'AAB=%s\n' "$AAB"
|
||||||
|
unzip -Z1 "$AAB" | grep '^base/lib/' | sort
|
||||||
|
for abi in arm64-v8a armeabi-v7a x86_64 x86; do
|
||||||
|
unzip -Z1 "$AAB" | grep -Fx "base/lib/${abi}/libSDL3.so"
|
||||||
|
unzip -Z1 "$AAB" | grep -Fx "base/lib/${abi}/libgame_android_entrypoint.so"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Revalider ensuite l'APK Debug et son alignement ZIP 16 KB avec le `zipalign` des Build Tools installés :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ZIPALIGN="$(find "$ANDROID_HOME/build-tools" -mindepth 2 -maxdepth 2 -type f -name zipalign -print | sort -V | tail -n 1)"
|
||||||
|
test -x "$ZIPALIGN"
|
||||||
|
|
||||||
|
for game in game-snake-poc game-reflex-poc; do
|
||||||
|
(cd Android && gradle ":${game}:assembleDebug")
|
||||||
|
APK="$(find "Android/${game}/build/outputs/apk/debug" -maxdepth 1 -type f -name '*.apk' -print -quit)"
|
||||||
|
"$ZIPALIGN" -c -P 16 -v 4 "$APK"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Contrôler les segments ELF des bibliothèques générées et de SDL3. Sur Linux avec le NDK configuré :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OBJDUMP="$ANDROID_HOME/ndk/28.2.13676358/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objdump"
|
||||||
|
test -x "$OBJDUMP"
|
||||||
|
|
||||||
|
for game in game-snake-poc game-reflex-poc; do
|
||||||
|
for variant in debug release; do
|
||||||
|
for abi in arm64-v8a armeabi-v7a x86_64 x86; do
|
||||||
|
for so in \
|
||||||
|
"Android/${game}/build/generated/sasedevNative/${variant}/${abi}/jniLibs/${abi}/libSDL3.so" \
|
||||||
|
"Android/${game}/build/generated/sasedevNative/${variant}/${abi}/jniLibs/${abi}/libgame_android_entrypoint.so"; do
|
||||||
|
test -f "$so"
|
||||||
|
printf '%s\n' "$so"
|
||||||
|
"$OBJDUMP" -p "$so" | grep 'LOAD'
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun `LOAD` ne doit annoncer un alignement inférieur à `2**14`.
|
||||||
|
|
||||||
|
Si `bundletool` est installé dans l'environnement :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundletool dump config --bundle="Android/game-snake-poc/build/outputs/bundle/release/game-snake-poc-release.aab" | grep alignment
|
||||||
|
bundletool dump config --bundle="Android/game-reflex-poc/build/outputs/bundle/release/game-reflex-poc-release.aab" | grep alignment
|
||||||
|
```
|
||||||
|
|
||||||
|
La valeur attendue est `PAGE_ALIGNMENT_16K`. Son absence locale parce que `bundletool` n'est pas installé doit être consignée ; elle ne doit pas être remplacée par une affirmation fictive.
|
||||||
|
|
||||||
|
Pour le plancher Android, relever les images disponibles :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
emulator -list-avds
|
||||||
|
sdkmanager --list_installed | grep 'system-images;android-' || true
|
||||||
|
```
|
||||||
|
|
||||||
|
Si une image API 21 exploitable est déjà disponible ou raisonnablement installable, effectuer un smoke Snake. Sinon, consigner explicitement cette lacune ; le contrat reste API 21 car SDL3 lui-même documente ce niveau comme minimum et le build Rust/manifest l'imposent déjà.
|
||||||
|
|
||||||
|
## Suite
|
||||||
|
|
||||||
|
Si les AAB quatre ABI et les contrôles 16 KB sont propres, la prochaine tranche est `0.3.3-2-beta.1` conformément au plan révisé. Une défaillance du chemin Release/AAB, de l'alignement ou de la fermeture Python produit d'abord `0.3.3-0-pre.5.fix.N`.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/development/006-ANDROID_RUST_NATIVE_BUILD.md -->
|
<!-- file: docs/development/006-ANDROID_RUST_NATIVE_BUILD.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Build Rust Android natif
|
# Build Rust Android natif
|
||||||
|
|
||||||
@@ -54,35 +54,32 @@ Le projet Android natif n'impose pas `JAVA_HOME`. Le JDK courant du shell/IDE ex
|
|||||||
|
|
||||||
`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.
|
`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.4
|
## État 0.3.3-0-pre.5
|
||||||
|
|
||||||
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.4` a validé les quatre ABI sur Snake et Reflex : `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`. Le même mécanisme Gradle est désormais enregistré pour toutes les variantes Android, et pas seulement Debug.
|
||||||
|
|
||||||
`0-pre.3` a validé le graphe sur les deux ABI 64 bits. `0-pre.4` élargit le même mécanisme à la matrice Android complète encore supportée :
|
Pour Debug :
|
||||||
|
|
||||||
```text
|
|
||||||
arm64-v8a
|
|
||||||
armeabi-v7a
|
|
||||||
x86_64
|
|
||||||
x86
|
|
||||||
```
|
|
||||||
|
|
||||||
pour Snake et Reflex :
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
:<game>:assembleDebug
|
:<game>:assembleDebug
|
||||||
-> buildDebugSasedevRustArm64V8a
|
-> buildDebugSasedevRust<Abi>
|
||||||
-> buildDebugSasedevRustArmeabiV7a
|
-> cargo ndk build
|
||||||
-> buildDebugSasedevRustX8664
|
|
||||||
-> buildDebugSasedevRustX86
|
|
||||||
-> quatre sources jniLibs générées
|
|
||||||
-> APK Debug universal
|
-> APK Debug universal
|
||||||
```
|
```
|
||||||
|
|
||||||
Chaque arbre ABI contient :
|
Pour Release/AAB :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
build/generated/sasedevNative/debug/<abi>/jniLibs/<abi>/
|
:<game>:bundleRelease
|
||||||
|
-> buildReleaseSasedevRust<Abi>
|
||||||
|
-> cargo ndk build --release
|
||||||
|
-> AAB Release multi-ABI
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque arbre généré contient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/generated/sasedevNative/<variant>/<abi>/jniLibs/<abi>/
|
||||||
├── libSDL3.so
|
├── libSDL3.so
|
||||||
└── libgame_android_entrypoint.so
|
└── libgame_android_entrypoint.so
|
||||||
```
|
```
|
||||||
@@ -94,17 +91,37 @@ java -version
|
|||||||
(cd Android && gradle --version)
|
(cd Android && gradle --version)
|
||||||
(cd Android && gradle :game-snake-poc:assembleDebug)
|
(cd Android && gradle :game-snake-poc:assembleDebug)
|
||||||
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
||||||
|
(cd Android && gradle :game-snake-poc:bundleRelease)
|
||||||
|
(cd Android && gradle :game-reflex-poc:bundleRelease)
|
||||||
```
|
```
|
||||||
|
|
||||||
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.5`, après preuve du chemin release/AAB.
|
Le builder historique `scripts/build_android_rust.py` est retiré : il n'existe plus de chemin natif Android qui stage des bibliothèques dans `src/main/jniLibs`. Les sorties natives sont exclusivement générées sous `build/`.
|
||||||
|
|
||||||
Les outputs natifs restent sous `Android/<module>/build/` et ne sont jamais versionnés.
|
### Pages mémoire 16 KB
|
||||||
|
|
||||||
|
La validation porte sur les deux dimensions requises :
|
||||||
|
|
||||||
|
1. segments ELF de chaque `.so` avec un alignement `LOAD` au moins `2**14`, contrôlés par `llvm-objdump -p` depuis le NDK configuré ;
|
||||||
|
2. alignement ZIP de l'APK avec `zipalign -c -P 16 -v 4`.
|
||||||
|
|
||||||
|
AGP `9.4.0` est supérieur au plancher `8.5.1` documenté pour le packaging 16 KB et NDK `r28c` produit les nouvelles bibliothèques avec alignement 16 KB par défaut. Cette propriété ne dispense pas de contrôler `libSDL3.so`, qui est précompilée dans l'AAR.
|
||||||
|
|
||||||
|
Pour l'AAB, les huit bibliothèques doivent être présentes sous `base/lib/<abi>/`. Lorsque `bundletool` est disponible, `bundletool dump config --bundle=<aab>` doit indiquer `PAGE_ALIGNMENT_16K`.
|
||||||
|
|
||||||
|
### Plancher Android
|
||||||
|
|
||||||
|
Le contrat est consolidé à Android 5.0 / API 21 :
|
||||||
|
|
||||||
|
- `minSdk 21` dans chaque application ;
|
||||||
|
- `sasedevRustAndroidApi = 21` pour `cargo ndk` ;
|
||||||
|
- SDL3 documente API 21 comme minimum Android ;
|
||||||
|
- un smoke API 21 est souhaité lorsqu'une image 32/64 bits exploitable est disponible, mais l'absence locale d'une telle image est consignée comme lacune de runtime et ne modifie pas artificiellement le contrat.
|
||||||
|
|
||||||
## Smoke appareil
|
## Smoke appareil
|
||||||
|
|
||||||
L'APK universal doit être inspecté mécaniquement avant installation. Il doit contenir les deux bibliothèques natives pour chacune des quatre ABI : `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`.
|
L'APK universal doit être inspecté mécaniquement avant installation. Il doit contenir les deux bibliothèques natives pour chacune des quatre ABI : `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`.
|
||||||
|
|
||||||
Le smoke réel reste effectué sur les architectures matériellement disponibles : AVD x86_64 et appareil ARM64. Les ABI 32 bits sont néanmoins exigées au build et dans l'APK ; leur exécution réelle dépend de la disponibilité d'un appareil/AVD 32 bits. Ces smokes ne changent pas le `minSdk`, qui reste un axe séparé et sera consolidé avec l'AAB/compatibilité dans `0-pre.5`.
|
Le smoke réel reste effectué sur les architectures matériellement disponibles : AVD x86_64 et appareil ARM64. Les ABI 32 bits sont néanmoins exigées au build et dans l'APK ; leur exécution réelle dépend de la disponibilité d'un appareil/AVD 32 bits. Le `minSdk 21` est consolidé en `0-pre.5`; les smokes disponibles servent de preuve runtime complémentaire et ne redéfinissent pas ce plancher.
|
||||||
|
|
||||||
## Exception FFI Rust
|
## Exception FFI Rust
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/003-V0_3_3_ANDROID_NATIVE_MULTI_ABI_PLAN.md -->
|
<!-- file: docs/plans/003-V0_3_3_ANDROID_NATIVE_MULTI_ABI_PLAN.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Plan 0.3.3 — Android SDL3 natif multi-ABI et packaging Gradle
|
# Plan 0.3.3 — Android SDL3 natif multi-ABI et packaging Gradle
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ La logique durable doit rester répartie ainsi :
|
|||||||
- `Android/gradle/` : logique de build Android commune, y compris orchestration Rust native et staging des sorties générées ;
|
- `Android/gradle/` : logique de build Android commune, y compris orchestration Rust native et staging des sorties générées ;
|
||||||
- `assets/` : sources d'assets canoniques ;
|
- `assets/` : sources d'assets canoniques ;
|
||||||
- `Android/<app>/build/generated/` : outputs jetables de packaging ;
|
- `Android/<app>/build/generated/` : outputs jetables de packaging ;
|
||||||
- `scripts/build_android_rust.py` : mécanisme historique à retirer du chemin normal dès que la responsabilité Gradle est prouvée, puis fichier à supprimer seulement lorsque toutes ses responsabilités sont remplacées.
|
- le builder historique `scripts/build_android_rust.py` est supprimé en `0-pre.5` après migration de toutes ses responsabilités vers Gradle ; aucun équivalent Python ne doit être recréé.
|
||||||
|
|
||||||
Aucune `.so` générée ne doit être écrite durablement dans `src/main/jniLibs/` par le nouveau chemin.
|
Aucune `.so` générée ne doit être écrite durablement dans `src/main/jniLibs/` par le nouveau chemin.
|
||||||
|
|
||||||
@@ -164,9 +164,9 @@ Les smokes matériels restent ciblés sur les environnements disponibles ; l'abs
|
|||||||
|
|
||||||
### `0-pre.5` — AAB, minSdk et fermeture du chemin historique
|
### `0-pre.5` — AAB, minSdk et fermeture du chemin historique
|
||||||
|
|
||||||
Ajouter/valider le chemin `bundleRelease` sans secret, inspecter le bundle et la compatibilité 16 KB, exécuter la stratégie de smoke du plancher Android et supprimer `scripts/build_android_rust.py` si toutes ses responsabilités ont effectivement migré.
|
Étendre l'ownership Gradle aux variantes Release : `bundleRelease` doit reconstruire les quatre ABI en profil Cargo release sans secret embarqué. Inspecter les AAB, les segments ELF et l'alignement APK 16 KB ; consolider `minSdk 21` ; supprimer `scripts/build_android_rust.py` et interdire le retour des `src/main/jniLibs` générés.
|
||||||
|
|
||||||
Mettre à jour les documents Android/JNI/build durables pour décrire le nouveau chemin réel et ajuster l'audit de distribution si le script historique n'est plus un path requis.
|
Mettre à jour les documents Android/JNI/build durables pour décrire le chemin réel et remplacer les anciennes commandes de validation encore dépendantes du builder Python.
|
||||||
|
|
||||||
### `2-beta.1` — validation large et packaging final
|
### `2-beta.1` — validation large et packaging final
|
||||||
|
|
||||||
@@ -220,6 +220,6 @@ La politique a été révisée après validation de `0-pre.2` : le projet Androi
|
|||||||
- le minSdk soutenu et la preuve de smoke disponible sont documentés sans surpromesse ;
|
- le minSdk soutenu et la preuve de smoke disponible sont documentés sans surpromesse ;
|
||||||
- l'appareil ARM64 réel et l'AVD x86_64 passent le smoke prévu ;
|
- l'appareil ARM64 réel et l'AVD x86_64 passent le smoke prévu ;
|
||||||
- Reflex est soit branché naturellement au pipeline commun, soit explicitement borné selon les résultats de factorisation ;
|
- Reflex est soit branché naturellement au pipeline commun, soit explicitement borné selon les résultats de factorisation ;
|
||||||
- le script Python historique est supprimé s'il n'a plus aucune responsabilité durable ;
|
- le script Python historique Android est supprimé et l'audit interdit son retour ;
|
||||||
- les documents Android, build, JNI et validation décrivent le chemin réellement livré ;
|
- les documents Android, build, JNI et validation décrivent le chemin réellement livré ;
|
||||||
- les gates workspace/beta applicables sont propres.
|
- les gates workspace/beta applicables sont propres.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
<!-- file: docs/rules/RULES_COMMANDS.md -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 16 -->
|
||||||
|
|
||||||
# Règles d'exécution des commandes
|
# Règles d'exécution des commandes
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
## Outils de build et scripts d'audit
|
## Outils de build et scripts d'audit
|
||||||
|
|
||||||
- **CMD-BUILD-001** — Les scripts Python du dépôt sont autorisés pour les audits, audits complémentaires, validations et validations complémentaires.
|
- **CMD-BUILD-001** — Les scripts Python du dépôt sont autorisés pour les audits, audits complémentaires, validations et validations complémentaires.
|
||||||
- **CMD-BUILD-002** — À partir de `0.3.x`, aucun chemin de build nouveau ou modifié n’est piloté par Python. Les orchestrateurs historiques `scripts/build_reflex_tauri_wasm.py` et `scripts/build_android_rust.py` restent tolérés uniquement comme mécanismes gelés de la baseline `0.1.0` jusqu’à la tranche qui réactive leur chemin ; ils ne sont ni copiés, ni généralisés, ni utilisés pour un nouveau POC.
|
- **CMD-BUILD-002** — À partir de `0.3.x`, aucun chemin de build nouveau ou modifié n’est piloté par Python. L'orchestrateur historique `scripts/build_reflex_tauri_wasm.py` reste toléré uniquement comme mécanisme gelé de la baseline `0.1.0` jusqu’à la tranche qui réactive son chemin ; il n'est ni copié, ni généralisé, ni utilisé pour un nouveau POC. Le builder Android historique `scripts/build_android_rust.py` a été retiré lorsque son chemin a été réactivé en `0.3.3`.
|
||||||
- **CMD-BUILD-003** — Les builds utilisent l'outil natif approprié au périmètre : Cargo pour Rust, Gradle pour Android, Tauri CLI pour Tauri, ou l'outil officiellement retenu par la plateforme concernée.
|
- **CMD-BUILD-003** — Les builds utilisent l'outil natif approprié au périmètre : Cargo pour Rust, Gradle pour Android, Tauri CLI pour Tauri, ou l'outil officiellement retenu par la plateforme concernée.
|
||||||
- **CMD-BUILD-004** — Les POC `0.3.x` doivent remplacer toute orchestration de build Python restante par des procédures explicites, reproductibles et testées avec les outils natifs.
|
- **CMD-BUILD-004** — Les POC `0.3.x` doivent remplacer toute orchestration de build Python restante par des procédures explicites, reproductibles et testées avec les outils natifs.
|
||||||
- **CMD-BUILD-005** — Les builds, tests unitaires, tests d'intégration et smoke tests de validation sont exécutés côté utilisateur ; les scripts d'audit peuvent vérifier statiquement leur préparation mais ne les simulent pas.
|
- **CMD-BUILD-005** — Les builds, tests unitaires, tests d'intégration et smoke tests de validation sont exécutés côté utilisateur ; les scripts d'audit peuvent vérifier statiquement leur préparation mais ne les simulent pas.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/studies/025-V0_3_3_ANDROID_NATIVE_MULTI_ABI_AUDIT.md -->
|
<!-- file: docs/studies/025-V0_3_3_ANDROID_NATIVE_MULTI_ABI_AUDIT.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# Audit Android SDL3 natif multi-ABI pour 0.3.3
|
# Audit Android SDL3 natif multi-ABI pour 0.3.3
|
||||||
|
|
||||||
@@ -263,3 +263,11 @@ Avant d'ouvrir l'implémentation `0-pre.2`, la gate utilisateur doit donc releve
|
|||||||
## Conclusion de cadrage
|
## 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.
|
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.
|
||||||
|
|
||||||
|
## Révision après validation de la matrice quatre ABI
|
||||||
|
|
||||||
|
La gate `0-pre.4` du 2026-09-21 a confirmé que les quatre targets Rust Android sont installées et que Snake comme Reflex compilent avec succès `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`. Les deux APK Debug universal contiennent les huit bibliothèques attendues.
|
||||||
|
|
||||||
|
La fermeture `0-pre.5` peut donc retirer le builder Python historique sans perdre de responsabilité fonctionnelle. La validation 16 KB suit la méthode Android actuelle : AGP 8.5.1+ pour le packaging, NDK r28+ pour l'alignement ELF par défaut, contrôle direct de tous les `.so` avec `llvm-objdump`, contrôle APK avec `zipalign -P 16`, et contrôle `PAGE_ALIGNMENT_16K` du bundle lorsque `bundletool` est disponible. SDL3 restant un prébuild, son alignement doit être contrôlé explicitement.
|
||||||
|
|
||||||
|
Le plancher `minSdk 21` est consolidé parce qu'il correspond au minimum Android documenté par SDL3 ; un smoke sur API 21 reste une preuve runtime additionnelle lorsque l'image est raisonnablement disponible, pas une condition permettant de prétendre à un niveau inférieur.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/testing/003-RC_VALIDATION_MATRIX.md -->
|
<!-- file: docs/testing/003-RC_VALIDATION_MATRIX.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Matrice de validation RC 0.1.0
|
# Matrice de validation RC 0.1.0
|
||||||
|
|
||||||
@@ -85,13 +85,8 @@ Critères :
|
|||||||
## Android x86_64 / API 36
|
## Android x86_64 / API 36
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/build_android_rust.py reflex --abi x86_64
|
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
||||||
python3 scripts/build_android_rust.py snake --abi x86_64
|
(cd Android && gradle :game-snake-poc:assembleDebug)
|
||||||
|
|
||||||
cd Android
|
|
||||||
gradle :game-reflex-poc:assembleDebug
|
|
||||||
gradle :game-snake-poc:assembleDebug
|
|
||||||
cd ..
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Déployer les deux APK sur l'AVD de référence et revalider :
|
Déployer les deux APK sur l'AVD de référence et revalider :
|
||||||
@@ -105,13 +100,8 @@ Déployer les deux APK sur l'AVD de référence et revalider :
|
|||||||
## Android ARM64 réel
|
## Android ARM64 réel
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/build_android_rust.py reflex --abi arm64-v8a
|
(cd Android && gradle :game-reflex-poc:assembleDebug)
|
||||||
python3 scripts/build_android_rust.py snake --abi arm64-v8a
|
(cd Android && gradle :game-snake-poc:assembleDebug)
|
||||||
|
|
||||||
cd Android
|
|
||||||
gradle :game-reflex-poc:assembleDebug
|
|
||||||
gradle :game-snake-poc:assembleDebug
|
|
||||||
cd ..
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Déployer sur l'appareil ARM64 de référence et revalider Reflex et Snake.
|
Déployer sur l'appareil ARM64 de référence et revalider Reflex et Snake.
|
||||||
|
|||||||
76
history/0.3.3/0-pre.4.md
Normal file
76
history/0.3.3/0-pre.4.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<!-- file: history/0.3.3/0-pre.4.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Historique 0.3.3-0-pre.4
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
`0.3.3-0-pre.4` a été validé par l'utilisateur le 2026-09-21. La tranche confirme la matrice Android native complète encore supportée par la toolchain moderne pour Snake et Reflex : `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`.
|
||||||
|
|
||||||
|
Aucun `0-pre.4.fix.N` n'est requis. La suite peut ouvrir `0-pre.5`.
|
||||||
|
|
||||||
|
## Toolchain réellement validée
|
||||||
|
|
||||||
|
```text
|
||||||
|
OpenJDK / Temurin 25.0.4.1 LTS
|
||||||
|
Gradle 9.7.1
|
||||||
|
Launcher JVM 25.0.4.1
|
||||||
|
Daemon JVM /usr/lib/jvm/temurin-25-jdk-amd64
|
||||||
|
```
|
||||||
|
|
||||||
|
Targets Rust Android présents :
|
||||||
|
|
||||||
|
```text
|
||||||
|
aarch64-linux-android
|
||||||
|
armv7-linux-androideabi
|
||||||
|
i686-linux-android
|
||||||
|
x86_64-linux-android
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gates statiques et Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
General Rust rule audit: clean
|
||||||
|
Rust export completeness audit: 0 candidate(s)
|
||||||
|
games.sasedev workspace audit: clean
|
||||||
|
Markdown table audit: clean (5 table(s), 239 file(s))
|
||||||
|
Distribution layout audit: clean (50 required path(s), 5 forbidden path(s) absent)
|
||||||
|
cargo fmt --all: clean
|
||||||
|
cargo fmt --all -- --check: clean
|
||||||
|
cargo check --workspace: clean
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings: clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Builds quatre ABI
|
||||||
|
|
||||||
|
Snake et Reflex exécutent tous deux avec succès les tâches natives Gradle pour :
|
||||||
|
|
||||||
|
```text
|
||||||
|
arm64-v8a -> aarch64-linux-android
|
||||||
|
armeabi-v7a -> armv7-linux-androideabi
|
||||||
|
x86 -> i686-linux-android
|
||||||
|
x86_64 -> x86_64-linux-android
|
||||||
|
```
|
||||||
|
|
||||||
|
Les deux commandes `assembleDebug` terminent avec `BUILD SUCCESSFUL`.
|
||||||
|
|
||||||
|
## Inspection des APK
|
||||||
|
|
||||||
|
Les APK Debug universal Snake et Reflex contiennent chacun exactement les couples natifs attendus pour les quatre ABI :
|
||||||
|
|
||||||
|
```text
|
||||||
|
lib/arm64-v8a/libSDL3.so
|
||||||
|
lib/arm64-v8a/libgame_android_entrypoint.so
|
||||||
|
lib/armeabi-v7a/libSDL3.so
|
||||||
|
lib/armeabi-v7a/libgame_android_entrypoint.so
|
||||||
|
lib/x86/libSDL3.so
|
||||||
|
lib/x86/libgame_android_entrypoint.so
|
||||||
|
lib/x86_64/libSDL3.so
|
||||||
|
lib/x86_64/libgame_android_entrypoint.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Les répétitions visibles dans la sortie utilisateur proviennent des `grep -Fx` de contrôle exécutés après le listing initial.
|
||||||
|
|
||||||
|
## Conséquence
|
||||||
|
|
||||||
|
La matrice quatre ABI n'est plus seulement une intention documentaire : le même pipeline Gradle/Cargo/SDL3 la compile et la package pour les deux jeux. `0-pre.5` peut donc étendre ce pipeline aux variantes Release/AAB, vérifier la compatibilité 16 KB, consolider API 21 et supprimer le builder Python Android historique.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# file: scripts/audit_distribution_layout.py
|
# file: scripts/audit_distribution_layout.py
|
||||||
# version: 16
|
# version: 17
|
||||||
|
|
||||||
"""Audit the static distribution layout expected by supported POCs."""
|
"""Audit the static distribution layout expected by supported POCs."""
|
||||||
|
|
||||||
@@ -17,6 +17,9 @@ FORBIDDEN_PATHS = (
|
|||||||
"Android/gradlew.bat",
|
"Android/gradlew.bat",
|
||||||
"Android/gradle/wrapper/gradle-wrapper.jar",
|
"Android/gradle/wrapper/gradle-wrapper.jar",
|
||||||
"Android/gradle/wrapper/gradle-wrapper.properties",
|
"Android/gradle/wrapper/gradle-wrapper.properties",
|
||||||
|
"Android/game-reflex-poc/src/main/jniLibs",
|
||||||
|
"Android/game-snake-poc/src/main/jniLibs",
|
||||||
|
"scripts/build_android_rust.py",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +69,6 @@ REQUIRED_PATHS = (
|
|||||||
"Android/game-snake-poc/build.gradle",
|
"Android/game-snake-poc/build.gradle",
|
||||||
"Android/settings.gradle",
|
"Android/settings.gradle",
|
||||||
"Android/gradle/sasedev-rust-android.gradle",
|
"Android/gradle/sasedev-rust-android.gradle",
|
||||||
"scripts/build_android_rust.py",
|
|
||||||
"scripts/build_reflex_tauri_wasm.py",
|
"scripts/build_reflex_tauri_wasm.py",
|
||||||
"assets/common",
|
"assets/common",
|
||||||
"assets/game-reflex-poc",
|
"assets/game-reflex-poc",
|
||||||
@@ -334,9 +336,12 @@ def audit_android_native_gradle(root: pathlib.Path) -> list[str]:
|
|||||||
|
|
||||||
native_script = native_script_path.read_text(encoding="utf-8")
|
native_script = native_script_path.read_text(encoding="utf-8")
|
||||||
native_fragments = (
|
native_fragments = (
|
||||||
'spec.commandLine(',
|
'spec.commandLine(cargoCommand)',
|
||||||
'"cargo",',
|
'"cargo",',
|
||||||
'"ndk",',
|
'"ndk",',
|
||||||
|
'cargoCommand.add("--release")',
|
||||||
|
'releaseBuild.set(selectedReleaseBuild)',
|
||||||
|
'onVariants(selector().all())',
|
||||||
'"--no-default-features",',
|
'"--no-default-features",',
|
||||||
'variant.sources.jniLibs.addGeneratedSourceDirectory',
|
'variant.sources.jniLibs.addGeneratedSourceDirectory',
|
||||||
'generated/sasedevNative/',
|
'generated/sasedevNative/',
|
||||||
@@ -363,6 +368,8 @@ def audit_android_native_gradle(root: pathlib.Path) -> list[str]:
|
|||||||
)
|
)
|
||||||
shared_fragments = (
|
shared_fragments = (
|
||||||
'ext.sasedevRustAndroidAbis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]',
|
'ext.sasedevRustAndroidAbis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]',
|
||||||
|
'ext.sasedevRustAndroidApi = 21',
|
||||||
|
'minSdk 21',
|
||||||
"abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'",
|
"abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'",
|
||||||
'apply from: rootProject.file("gradle/sasedev-rust-android.gradle")',
|
'apply from: rootProject.file("gradle/sasedev-rust-android.gradle")',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# file: scripts/build_android_rust.py
|
|
||||||
# version: 3
|
|
||||||
|
|
||||||
"""Build one Android Rust game entrypoint and stage SDL3 and Rust jniLibs."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import pathlib
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
ABI_TO_TRIPLE = {
|
|
||||||
"arm64-v8a": "aarch64-linux-android",
|
|
||||||
"armeabi-v7a": "armv7-linux-androideabi",
|
|
||||||
"x86_64": "x86_64-linux-android",
|
|
||||||
"x86": "i686-linux-android",
|
|
||||||
}
|
|
||||||
GAME_TO_MODULE = {"reflex": "game-reflex-poc", "snake": "game-snake-poc"}
|
|
||||||
|
|
||||||
def read_gradle_property(properties_path: pathlib.Path, property_name: str) -> str:
|
|
||||||
"""Read one required Android Gradle property."""
|
|
||||||
prefix = f"{property_name}="
|
|
||||||
for raw_line in properties_path.read_text(encoding="utf-8").splitlines():
|
|
||||||
line = raw_line.strip()
|
|
||||||
if line.startswith(prefix):
|
|
||||||
return line.split("=", 1)[1].strip()
|
|
||||||
raise RuntimeError(f"{property_name} is missing from Android/gradle.properties")
|
|
||||||
|
|
||||||
def read_sdl3_aar_name(properties_path: pathlib.Path) -> str:
|
|
||||||
"""Read the configured SDL3 AAR filename."""
|
|
||||||
return read_gradle_property(properties_path, "sdl3AarName")
|
|
||||||
|
|
||||||
def find_sdl3_member(archive: zipfile.ZipFile, abi: str) -> str:
|
|
||||||
"""Return the SDL3 shared-library member for one Android ABI."""
|
|
||||||
candidates = (
|
|
||||||
f"prefab/modules/SDL3/libs/android.{abi}/libSDL3.so",
|
|
||||||
f"jni/{abi}/libSDL3.so",
|
|
||||||
)
|
|
||||||
members = set(archive.namelist())
|
|
||||||
for candidate in candidates:
|
|
||||||
if candidate in members:
|
|
||||||
return candidate
|
|
||||||
suffix = f"/libs/android.{abi}/libSDL3.so"
|
|
||||||
fallback = sorted(member for member in members if member.endswith(suffix))
|
|
||||||
if len(fallback) == 1:
|
|
||||||
return fallback[0]
|
|
||||||
raise RuntimeError(
|
|
||||||
f"SDL3 shared library for ABI {abi} is missing from the AAR; "
|
|
||||||
f"expected Prefab member {candidates[0]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def extract_sdl3_library(
|
|
||||||
aar_path: pathlib.Path,
|
|
||||||
abi: str,
|
|
||||||
link_dir: pathlib.Path,
|
|
||||||
runtime_dir: pathlib.Path,
|
|
||||||
) -> pathlib.Path:
|
|
||||||
"""Extract SDL3 for both Rust linking and APK runtime staging."""
|
|
||||||
link_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
runtime_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
with zipfile.ZipFile(aar_path) as archive:
|
|
||||||
member = find_sdl3_member(archive, abi)
|
|
||||||
payload = archive.read(member)
|
|
||||||
link_target = link_dir / "libSDL3.so"
|
|
||||||
runtime_target = runtime_dir / "libSDL3.so"
|
|
||||||
link_target.write_bytes(payload)
|
|
||||||
runtime_target.write_bytes(payload)
|
|
||||||
return link_target
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
"""Build the requested game entrypoint for one Android ABI."""
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("game", choices=sorted(GAME_TO_MODULE))
|
|
||||||
parser.add_argument("--abi", choices=sorted(ABI_TO_TRIPLE), default="arm64-v8a")
|
|
||||||
parser.add_argument("--release", action="store_true")
|
|
||||||
arguments = parser.parse_args()
|
|
||||||
root = pathlib.Path(__file__).resolve().parent.parent
|
|
||||||
properties_path = root / "Android" / "gradle.properties"
|
|
||||||
aar_name = read_sdl3_aar_name(properties_path)
|
|
||||||
ndk_version = read_gradle_property(properties_path, "androidNdkVersion")
|
|
||||||
aar_path = root / "Android" / "libs" / aar_name
|
|
||||||
if not aar_path.is_file():
|
|
||||||
print(f"missing SDL3 AAR: {aar_path}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
module = GAME_TO_MODULE[arguments.game]
|
|
||||||
jni_root = root / "Android" / module / "src" / "main" / "jniLibs"
|
|
||||||
runtime_dir = jni_root / arguments.abi
|
|
||||||
link_dir = root / "Android" / "build" / "rust-link" / arguments.abi
|
|
||||||
try:
|
|
||||||
extract_sdl3_library(aar_path, arguments.abi, link_dir, runtime_dir)
|
|
||||||
except (OSError, RuntimeError, zipfile.BadZipFile) as error:
|
|
||||||
print(str(error), file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
environment = os.environ.copy()
|
|
||||||
android_home = environment.get("ANDROID_HOME")
|
|
||||||
if not android_home:
|
|
||||||
print("ANDROID_HOME is required to resolve the project NDK", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
ndk_home = pathlib.Path(android_home) / "ndk" / ndk_version
|
|
||||||
if not ndk_home.is_dir():
|
|
||||||
print(f"configured Android NDK is missing: {ndk_home}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
environment["ANDROID_NDK_HOME"] = str(ndk_home)
|
|
||||||
rustflags = environment.get("RUSTFLAGS", "").strip()
|
|
||||||
link_flag = f"-L native={link_dir}"
|
|
||||||
environment["RUSTFLAGS"] = f"{rustflags} {link_flag}".strip()
|
|
||||||
environment["CARGO_NDK_PLATFORM"] = "21"
|
|
||||||
command = [
|
|
||||||
"cargo",
|
|
||||||
"ndk",
|
|
||||||
"-t",
|
|
||||||
arguments.abi,
|
|
||||||
"-o",
|
|
||||||
str(jni_root),
|
|
||||||
"build",
|
|
||||||
"-p",
|
|
||||||
"game-android-entrypoint",
|
|
||||||
"--no-default-features",
|
|
||||||
"--features",
|
|
||||||
arguments.game,
|
|
||||||
]
|
|
||||||
if arguments.release:
|
|
||||||
command.append("--release")
|
|
||||||
completed = subprocess.run(command, cwd=root, env=environment, check=False)
|
|
||||||
if completed.returncode != 0:
|
|
||||||
return completed.returncode
|
|
||||||
expected = runtime_dir / "libgame_android_entrypoint.so"
|
|
||||||
if not expected.is_file():
|
|
||||||
print(f"missing Rust Android library after cargo-ndk build: {expected}", file=sys.stderr)
|
|
||||||
return 3
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
Reference in New Issue
Block a user