diff --git a/kazeia-android/app/build.gradle.kts b/kazeia-android/app/build.gradle.kts index b0ca83d..fcdfe73 100644 --- a/kazeia-android/app/build.gradle.kts +++ b/kazeia-android/app/build.gradle.kts @@ -108,6 +108,16 @@ android { } } +// Reproductibilité des jniLibs : les .so (gitignorés, 5 chaînes de build) doivent +// correspondre au manifest versionné. Un build RELEASE avec des libs divergentes +// échoue ; le debug reste libre (itération engine). Cf scripts/jnilibs.sh. +val verifyJniLibs = tasks.register("verifyJniLibs") { + commandLine("bash", rootProject.file("scripts/jnilibs.sh").absolutePath, "verify") +} +tasks.matching { it.name == "preReleaseBuild" }.configureEach { + dependsOn(verifyJniLibs) +} + dependencies { // Tests unitaires JVM (src/test/) — logique pure : CrisisGuard, VectorIndex, Chunker. // Lancer : ./gradlew :app:testDebugUnitTest diff --git a/kazeia-android/scripts/jnilibs.MANIFEST.sha256 b/kazeia-android/scripts/jnilibs.MANIFEST.sha256 new file mode 100644 index 0000000..f66fbf6 --- /dev/null +++ b/kazeia-android/scripts/jnilibs.MANIFEST.sha256 @@ -0,0 +1,29 @@ +# jniLibs MANIFEST — état attendu de app/src/main/jniLibs/arm64-v8a/ +# Régénéré par scripts/jnilibs.sh update-manifest le 2026-06-11. +# Provenances : kazeia-engine dist (libllama/libggml*/libkazeia_*), +# ExecuTorch build local (libexecutorch*, libqnn_executorch_backend, libfbjni), +# QNN SDK 2.42 (libQnn*), Genie (libGenie), TTS pipeline (libtts_pipeline), libomp. +# Les binaires ne sont PAS dans git ; ce manifest fait foi (verify en preBuild). +dd0506873aeb45e103ef0023678a7f14938a0f147f2348d1efc366f1dce40863 libexecutorch_jni.so +0b6f86e114f0cf9f2940194797f06ded576e6b3272a5ffbb2c4b8cdc02850ef8 libexecutorch.so +34a0af206f660461e3da687f64172830cc61a32a93732fd7e984c43996d6fd91 libfbjni.so +d49b53137f478b7b4ccbd58b9166d83e832c299b681609c70628291c61ae7204 libGenie.so +616fa830f6fd3396ea7f0ca92f348c2146efb0b1154ab6ed2e6709655905ead3 libggml-base.so +66ea0c948385d8060a7296285d7a85738c21318a88ef767986444a3d9683aeea libggml-cpu.so +7aeb209ec5c1da8857779b8f3b4c3e7519ed69c6211491948d3c87349dbced57 libggml.so +daf3695e82debf425e4a53b73e96fcb9e9e01ab2fe8513bf6db8f53b09450a34 libkazeia_engine.so +fdd730af3daf132d0819b3fc99abd8aae79800aab7a9aad371e117c16bd3a3aa libkazeia_stt.so +114795bf0256c380dc87971598707e9e0b745a7fc868f3d1fb460e94cecdd69c libkazeia_tts.so +349ba592462113c6e871b9c3ee508727a9e5f154a33905f946bda97535eff586 libllama-common.so +2ca77a28421fc0c680e0a5ddf0208a9471268370893f8dd4eba2a10a8aacb4ac libllama.so +a207169a2696e8f0f7639090c2882152c734368b5696246a514d63b3ce0769c1 libomp.so +d1e8fc79e47fe04ed00aa9bbcc5ccb84d187f0160b1d77ae5bd012aedea9c938 libqnn_executorch_backend.so +414794639e2897120143cff80aaf73e830e305357b6c1aa8343de380c89b8c36 libQnnGpuNetRunExtensions.so +26f13b297d375e9629eb997b7adb05a4ce9e1c17d306baaefe905563fee43eb8 libQnnGpuProfilingReader.so +b5b52b66f3fc5567429024409dcedb006ca6a9115f271689810589b7d79af430 libQnnGpu.so +2daafb376fe6904aa325e29810480e3d76dc392991d1367ae6ea3508db2b60ac libQnnHtpPrepare.so +10eb1923b34ac9a726e4a446b395d5eee2ea5388487917778aaf954e8bfb2d92 libQnnHtp.so +7ee72b438c97c13ceb96d18fe255d1f156afbdc4c43669752f349dd5a56e62a3 libQnnHtpV79Skel.so +a08fb34747345125fd3cef3fc10046c663465dd60fddbac9cc37436b3d2e68a9 libQnnHtpV79Stub.so +a69cfc4350c16b226bcaa19c657234820bb93e3ff6116cf3845ae77557f1f7c3 libQnnSystem.so +d35a02f70cb63ec60c8ae78011f5e93aecbb31396d5395a4a82cb1bc71803956 libtts_pipeline.so diff --git a/kazeia-android/scripts/jnilibs.sh b/kazeia-android/scripts/jnilibs.sh new file mode 100755 index 0000000..c03743c --- /dev/null +++ b/kazeia-android/scripts/jnilibs.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# Gestion reproductible des jniLibs de l'app Kazeia. +# +# Les .so de app/src/main/jniLibs/arm64-v8a/ sont des ARTEFACTS (gitignorés), +# venant de 5 chaînes de build différentes (kazeia-engine, ExecuTorch, QNN SDK, +# Genie, TTS pipeline). La SOURCE DE VÉRITÉ versionnée est le manifest sha256 : +# kazeia-android/scripts/jnilibs.MANIFEST.sha256 +# +# Usage : +# ./jnilibs.sh verify # compare jniLibs au manifest (CI/Gradle l'appelle) +# ./jnilibs.sh sync-engine # rafraîchit le sous-ensemble kazeia-engine depuis /opt/Kazeia-engine/dist +# ./jnilibs.sh update-manifest # re-fige le manifest sur l'état courant (à committer !) +# ./jnilibs.sh export # tarball des jniLibs (transfert vers une autre machine) +# ./jnilibs.sh import # restaure les jniLibs depuis un export, puis verify +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +LIBS="$HERE/../app/src/main/jniLibs/arm64-v8a" +MANIFEST="$HERE/jnilibs.MANIFEST.sha256" +ENGINE_DIST="/opt/Kazeia-engine/dist" + +verify() { + [ -f "$MANIFEST" ] || { echo "ERREUR: manifest absent ($MANIFEST)"; exit 1; } + local fail=0 + # 1) tous les fichiers du manifest présents et conformes + while read -r sum path; do + [ -z "$sum" ] && continue; case "$sum" in \#*) continue;; esac + local f="$LIBS/$path" + if [ ! -f "$f" ]; then echo " MANQUANT $path"; fail=1 + elif [ "$(sha256sum "$f" | cut -d' ' -f1)" != "$sum" ]; then echo " MODIFIÉ $path"; fail=1 + fi + done < "$MANIFEST" + # 2) pas de .so non déclaré (un intrus = build non reproductible ailleurs) + for f in "$LIBS"/*.so; do + local b; b="$(basename "$f")" + grep -q " $b\$" "$MANIFEST" || { echo " NON DÉCLARÉ $b (update-manifest ?)"; fail=1; } + done + if [ $fail -eq 0 ]; then echo "jniLibs OK ($(grep -cv '^#' "$MANIFEST" | cat) libs conformes au manifest)"; + else echo ""; echo "jniLibs NON conformes. Corriger puis './jnilibs.sh update-manifest' si le changement est voulu."; exit 1; fi +} + +sync_engine() { + # Sous-ensemble possédé par kazeia-engine (build CPU-only, cf RAG_INTEGRATION.md engine) + [ -d "$ENGINE_DIST" ] || { echo "ERREUR: $ENGINE_DIST absent (machine sans le dépôt engine — utiliser import)"; exit 1; } + for f in lib-cpu/libllama.so lib-cpu/libggml.so lib-cpu/libggml-base.so lib-cpu/libggml-cpu.so \ + b-jni-cpu/libkazeia_engine.so b-jni-cpu/libkazeia_tts.so; do + cp -v "$ENGINE_DIST/$f" "$LIBS/" || true + done + echo "→ vérifier l'app sur device, puis './jnilibs.sh update-manifest' + commit." +} + +update_manifest() { + { + echo "# jniLibs MANIFEST — état attendu de app/src/main/jniLibs/arm64-v8a/" + echo "# Régénéré par scripts/jnilibs.sh update-manifest le $(date +%F)." + echo "# Provenances : kazeia-engine dist (libllama/libggml*/libkazeia_*)," + echo "# ExecuTorch build local (libexecutorch*, libqnn_executorch_backend, libfbjni)," + echo "# QNN SDK 2.42 (libQnn*), Genie (libGenie), TTS pipeline (libtts_pipeline), libomp." + echo "# Les binaires ne sont PAS dans git ; ce manifest fait foi (verify en preBuild)." + ( cd "$LIBS" && sha256sum *.so | sort -k2 ) + } > "$MANIFEST" + echo "manifest re-figé : $MANIFEST ($(grep -cv '^#' "$MANIFEST") libs) — À COMMITTER." +} + +export_tar() { + local out="/opt/Kazeia/_archives/kazeia-jnilibs-$(date +%Y%m%d).tar.gz" + mkdir -p /opt/Kazeia/_archives + tar czf "$out" -C "$LIBS/.." arm64-v8a + echo "export : $out ($(du -h "$out" | cut -f1)) — à déposer sur box.kazeia.com (dossier privé) pour les autres machines." +} + +import_tar() { + [ -f "${1:-}" ] || { echo "usage: jnilibs.sh import "; exit 1; } + tar xzf "$1" -C "$LIBS/.." + verify +} + +case "${1:-}" in + verify) verify;; + sync-engine) sync_engine;; + update-manifest) update_manifest;; + export) export_tar;; + import) import_tar "${2:-}";; + *) grep '^#' "$0" | head -14; exit 1;; +esac