0.1.0-0-pre.7-fix.1
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-reflex-poc/build.gradle
|
// file: Android/game-reflex-poc/build.gradle
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-0-pre.7'
|
versionName '0.1.0-0-pre.7.fix.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: Android/game-snake-poc/build.gradle
|
// file: Android/game-snake-poc/build.gradle
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '0.1.0-0-pre.7'
|
versionName '0.1.0-0-pre.7.fix.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 14
|
# version: 15
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -16,7 +16,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0-0-pre.7"
|
version = "0.1.0-0-pre.7.fix.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/games"
|
repository = "https://git.sasedev.com/Sasedev/games"
|
||||||
|
|||||||
129
deltas/0.1.0/0-pre.7.fix.1.md
Normal file
129
deltas/0.1.0/0-pre.7.fix.1.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<!-- file: deltas/0.1.0/0-pre.7.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.1.0-0-pre.7.fix.1
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
Base déclarée : `0.1.0-0-pre.7`, non validée intégralement.
|
||||||
|
|
||||||
|
## Validation du delta précédent
|
||||||
|
|
||||||
|
L'utilisateur a exécuté les validations de `0.1.0-0-pre.7` le 2026-09-16.
|
||||||
|
|
||||||
|
Résultats propres :
|
||||||
|
|
||||||
|
- `cargo fmt --all` ;
|
||||||
|
- `cargo fmt --all -- --check` ;
|
||||||
|
- audit Rust/workspace ;
|
||||||
|
- audit Markdown : `1 table(s), 51 file(s)` ;
|
||||||
|
- `cargo check --workspace` ;
|
||||||
|
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` ;
|
||||||
|
- `cargo test -p game-android-entrypoint --no-default-features` ;
|
||||||
|
- `cargo-ndk 4.1.2` installé ;
|
||||||
|
- target Rust `aarch64-linux-android` installé ;
|
||||||
|
- builds Gradle Reflex et Snake propres.
|
||||||
|
|
||||||
|
## Échec du build natif Android
|
||||||
|
|
||||||
|
Les deux commandes :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build_android_rust.py reflex
|
||||||
|
python3 scripts/build_android_rust.py snake
|
||||||
|
```
|
||||||
|
|
||||||
|
ont échoué avant cross-compilation avec :
|
||||||
|
|
||||||
|
```text
|
||||||
|
jni/arm64-v8a/libSDL3.so is missing from Android/libs/SDL3-3.4.16.aar
|
||||||
|
```
|
||||||
|
|
||||||
|
La cause est une hypothèse incorrecte du script.
|
||||||
|
|
||||||
|
L'AAR SDL3 officiel expose ses bibliothèques natives via Prefab, sous une structure de type :
|
||||||
|
|
||||||
|
```text
|
||||||
|
prefab/modules/SDL3/libs/android.arm64-v8a/libSDL3.so
|
||||||
|
```
|
||||||
|
|
||||||
|
et non obligatoirement sous `jni/arm64-v8a/`.
|
||||||
|
|
||||||
|
## Correctif
|
||||||
|
|
||||||
|
Le script `build_android_rust.py` :
|
||||||
|
|
||||||
|
- recherche d'abord la structure Prefab officielle ;
|
||||||
|
- conserve un fallback `jni/<abi>/libSDL3.so` ;
|
||||||
|
- sait détecter une unique variante compatible sous `*/libs/android.<abi>/libSDL3.so` ;
|
||||||
|
- extrait `libSDL3.so` dans le répertoire de liaison temporaire ;
|
||||||
|
- stage aussi `libSDL3.so` dans le `jniLibs/<abi>/` de l'application ;
|
||||||
|
- vérifie après `cargo ndk` que `libgame_android_entrypoint.so` existe réellement.
|
||||||
|
|
||||||
|
Les `versionName` Android passent à `0.1.0-0-pre.7.fix.1`.
|
||||||
|
|
||||||
|
## ADB et NDK
|
||||||
|
|
||||||
|
L'absence initiale de `adb` n'est pas considérée comme un défaut du projet.
|
||||||
|
|
||||||
|
Préflight recommandé :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "ndk;28.2.13676358" "platform-tools"
|
||||||
|
export PATH="${ANDROID_HOME}/platform-tools:${PATH}"
|
||||||
|
adb version
|
||||||
|
```
|
||||||
|
|
||||||
|
Le NDK r28c correspond à `28.2.13676358`.
|
||||||
|
|
||||||
|
## Validation à exécuter par l'utilisateur
|
||||||
|
|
||||||
|
Le fix modifie un script Python et des fichiers Gradle, mais aucun fichier Rust.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 deltas history
|
||||||
|
cargo check --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
Build natif :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build_android_rust.py reflex
|
||||||
|
python3 scripts/build_android_rust.py snake
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -f Android/game-reflex-poc/src/main/jniLibs/arm64-v8a/libSDL3.so
|
||||||
|
test -f Android/game-reflex-poc/src/main/jniLibs/arm64-v8a/libgame_android_entrypoint.so
|
||||||
|
test -f Android/game-snake-poc/src/main/jniLibs/arm64-v8a/libSDL3.so
|
||||||
|
test -f Android/game-snake-poc/src/main/jniLibs/arm64-v8a/libgame_android_entrypoint.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Android
|
||||||
|
gradle :game-reflex-poc:assembleDebug
|
||||||
|
gradle :game-snake-poc:assembleDebug
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
Le smoke appareil est demandé seulement si `adb` et un appareil/émulateur sont disponibles :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb devices
|
||||||
|
```
|
||||||
|
|
||||||
|
puis installation/lancement des deux APK.
|
||||||
|
|
||||||
|
## Règle de transition
|
||||||
|
|
||||||
|
Si les gates légères, les deux builds Rust Android et les deux APK sont propres, `0.1.0-0-pre.7.fix.1` peut être validé même sans appareil disponible.
|
||||||
|
|
||||||
|
Si un appareil/émulateur est disponible, le premier smoke runtime Android doit également être exécuté avant de passer à `0.1.0-0-pre.8`.
|
||||||
|
|
||||||
|
En cas d'échec imputable au projet, produire `0.1.0-0-pre.7.fix.2`.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/development/006-ANDROID_RUST_NATIVE_BUILD.md -->
|
<!-- file: docs/development/006-ANDROID_RUST_NATIVE_BUILD.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Build Rust Android natif
|
# Build Rust Android natif
|
||||||
|
|
||||||
@@ -14,6 +14,28 @@ Le crate Rust `game-android-entrypoint` est un `cdylib`. Il est compilé avec ex
|
|||||||
|
|
||||||
Le même nom de bibliothèque native peut être utilisé dans les deux APK puisque chaque module Android possède son propre répertoire `jniLibs`.
|
Le même nom de bibliothèque native peut être utilisé dans les deux APK puisque chaque module Android possède son propre répertoire `jniLibs`.
|
||||||
|
|
||||||
|
## SDL3 AAR et Prefab
|
||||||
|
|
||||||
|
L'AAR officiel SDL3 expose ses bibliothèques natives avec Prefab.
|
||||||
|
|
||||||
|
Pour `arm64-v8a`, la structure attendue est notamment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
prefab/modules/SDL3/libs/android.arm64-v8a/libSDL3.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script `scripts/build_android_rust.py` accepte cette structure officielle et conserve un fallback `jni/<abi>/libSDL3.so` pour compatibilité.
|
||||||
|
|
||||||
|
Pour chaque application, le script :
|
||||||
|
|
||||||
|
1. extrait `libSDL3.so` depuis l'AAR ;
|
||||||
|
2. l'utilise comme bibliothèque de liaison pour le build Rust ;
|
||||||
|
3. copie `libSDL3.so` dans le `jniLibs/<abi>/` du module Android ;
|
||||||
|
4. lance `cargo ndk` ;
|
||||||
|
5. vérifie la présence de `libgame_android_entrypoint.so`.
|
||||||
|
|
||||||
|
Ainsi, `SDLActivity` dispose au runtime de `libSDL3.so` et de la bibliothèque Rust applicative.
|
||||||
|
|
||||||
## Toolchain
|
## Toolchain
|
||||||
|
|
||||||
SDL Android requiert actuellement SDK 35 ou ultérieur, NDK r28c ou ultérieur et API minimale 21. La baseline projet conserve `compileSdk 36`, `minSdk 21` et l'AAR SDL3 3.4.16.
|
SDL Android requiert actuellement SDK 35 ou ultérieur, NDK r28c ou ultérieur et API minimale 21. La baseline projet conserve `compileSdk 36`, `minSdk 21` et l'AAR SDL3 3.4.16.
|
||||||
@@ -25,8 +47,28 @@ rustup target add aarch64-linux-android
|
|||||||
cargo install cargo-ndk
|
cargo install cargo-ndk
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NDK de référence :
|
||||||
|
|
||||||
|
```text
|
||||||
|
r28c = 28.2.13676358
|
||||||
|
```
|
||||||
|
|
||||||
|
Avec le SDK manager :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
"${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "ndk;28.2.13676358" "platform-tools"
|
||||||
|
```
|
||||||
|
|
||||||
`cargo-ndk` détecte un NDK installé par Android Studio ou utilise `ANDROID_NDK_HOME` lorsqu'il est défini.
|
`cargo-ndk` détecte un NDK installé par Android Studio ou utilise `ANDROID_NDK_HOME` lorsqu'il est défini.
|
||||||
|
|
||||||
|
Pour utiliser directement `adb` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="${ANDROID_HOME}/platform-tools:${PATH}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Cet export peut être ajouté au fichier de configuration shell local de la machine.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Depuis la racine :
|
Depuis la racine :
|
||||||
@@ -47,6 +89,19 @@ cd ..
|
|||||||
|
|
||||||
Les bibliothèques générées sous `Android/*/src/main/jniLibs/` sont des artefacts de build et ne sont pas versionnées.
|
Les bibliothèques générées sous `Android/*/src/main/jniLibs/` sont des artefacts de build et ne sont pas versionnées.
|
||||||
|
|
||||||
|
## Smoke appareil
|
||||||
|
|
||||||
|
Préflight :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb version
|
||||||
|
adb devices
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis installation/lancement des APK si un appareil ou émulateur est disponible.
|
||||||
|
|
||||||
|
L'absence de `adb` ou d'appareil n'est pas un défaut fonctionnel du projet ; elle bloque uniquement le smoke appareil.
|
||||||
|
|
||||||
## Exception FFI Rust
|
## 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`.
|
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`.
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# file: scripts/build_android_rust.py
|
# file: scripts/build_android_rust.py
|
||||||
# version: 1
|
# version: 2
|
||||||
|
|
||||||
"""Build one Android Rust game entrypoint and stage it as Gradle jniLibs."""
|
"""Build one Android Rust game entrypoint and stage SDL3 and Rust jniLibs."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import zipfile
|
import zipfile
|
||||||
@@ -29,16 +30,42 @@ def read_sdl3_aar_name(properties_path: pathlib.Path) -> str:
|
|||||||
return line.split("=", 1)[1].strip()
|
return line.split("=", 1)[1].strip()
|
||||||
raise RuntimeError("sdl3AarName is missing from Android/gradle.properties")
|
raise RuntimeError("sdl3AarName is missing from Android/gradle.properties")
|
||||||
|
|
||||||
def extract_sdl3_link_library(aar_path: pathlib.Path, abi: str, output_dir: pathlib.Path) -> pathlib.Path:
|
def find_sdl3_member(archive: zipfile.ZipFile, abi: str) -> str:
|
||||||
"""Extract the SDL3 shared library used only for the Rust linker search path."""
|
"""Return the SDL3 shared-library member for one Android ABI."""
|
||||||
expected = f"jni/{abi}/libSDL3.so"
|
candidates = (
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
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:
|
with zipfile.ZipFile(aar_path) as archive:
|
||||||
if expected not in archive.namelist():
|
member = find_sdl3_member(archive, abi)
|
||||||
raise RuntimeError(f"{expected} is missing from {aar_path}")
|
payload = archive.read(member)
|
||||||
target = output_dir / "libSDL3.so"
|
link_target = link_dir / "libSDL3.so"
|
||||||
target.write_bytes(archive.read(expected))
|
runtime_target = runtime_dir / "libSDL3.so"
|
||||||
return target
|
link_target.write_bytes(payload)
|
||||||
|
runtime_target.write_bytes(payload)
|
||||||
|
return link_target
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
"""Build the requested game entrypoint for one Android ABI."""
|
"""Build the requested game entrypoint for one Android ABI."""
|
||||||
@@ -53,27 +80,44 @@ def main() -> int:
|
|||||||
if not aar_path.is_file():
|
if not aar_path.is_file():
|
||||||
print(f"missing SDL3 AAR: {aar_path}", file=sys.stderr)
|
print(f"missing SDL3 AAR: {aar_path}", file=sys.stderr)
|
||||||
return 2
|
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
|
link_dir = root / "Android" / "build" / "rust-link" / arguments.abi
|
||||||
try:
|
try:
|
||||||
extract_sdl3_link_library(aar_path, arguments.abi, link_dir)
|
extract_sdl3_library(aar_path, arguments.abi, link_dir, runtime_dir)
|
||||||
except (OSError, RuntimeError, zipfile.BadZipFile) as error:
|
except (OSError, RuntimeError, zipfile.BadZipFile) as error:
|
||||||
print(str(error), file=sys.stderr)
|
print(str(error), file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
module = GAME_TO_MODULE[arguments.game]
|
|
||||||
output = root / "Android" / module / "src" / "main" / "jniLibs"
|
|
||||||
environment = os.environ.copy()
|
environment = os.environ.copy()
|
||||||
rustflags = environment.get("RUSTFLAGS", "").strip()
|
rustflags = environment.get("RUSTFLAGS", "").strip()
|
||||||
link_flag = f"-L native={link_dir}"
|
link_flag = f"-L native={link_dir}"
|
||||||
environment["RUSTFLAGS"] = f"{rustflags} {link_flag}".strip()
|
environment["RUSTFLAGS"] = f"{rustflags} {link_flag}".strip()
|
||||||
environment["CARGO_NDK_PLATFORM"] = "21"
|
environment["CARGO_NDK_PLATFORM"] = "21"
|
||||||
command = [
|
command = [
|
||||||
"cargo", "ndk", "-t", arguments.abi, "-o", str(output), "build",
|
"cargo",
|
||||||
"-p", "game-android-entrypoint", "--no-default-features", "--features", arguments.game,
|
"ndk",
|
||||||
|
"-t",
|
||||||
|
arguments.abi,
|
||||||
|
"-o",
|
||||||
|
str(jni_root),
|
||||||
|
"build",
|
||||||
|
"-p",
|
||||||
|
"game-android-entrypoint",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
arguments.game,
|
||||||
]
|
]
|
||||||
if arguments.release:
|
if arguments.release:
|
||||||
command.append("--release")
|
command.append("--release")
|
||||||
completed = subprocess.run(command, cwd=root, env=environment, check=False)
|
completed = subprocess.run(command, cwd=root, env=environment, check=False)
|
||||||
|
if completed.returncode != 0:
|
||||||
return completed.returncode
|
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__":
|
if __name__ == "__main__":
|
||||||
raise SystemExit(main())
|
raise SystemExit(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user