diff --git a/.tangled/workflows/android-apk.yml b/.tangled/workflows/android-apk.yml index 8f3eb1b..5a68077 100644 --- a/.tangled/workflows/android-apk.yml +++ b/.tangled/workflows/android-apk.yml @@ -2,8 +2,8 @@ # Artifact path: dist-ui/target/debug/apk/global-dist-ui.apk # Local equivalent: ./scripts/ci-build-apk.sh # -# Use microvm+nixos (not nixery): nixery is non-FHS and cannot exec Google -# aapt/NDK ELF prebuilts (ENOENT). steam-run provides an FHS user env. +# microvm+nixos (not nixery): nixery is non-FHS and cannot exec Google aapt/NDK +# glibc prebuilts. We wrap cargo-apk in a free buildFHSEnv (not steam-run/unfree). when: - event: ["push", "manual"] @@ -19,7 +19,6 @@ clone: depth: 1 submodules: false -# microvm/nixos: flat dependency list → mkShellNoCC for each step dependencies: - bash - coreutils @@ -37,13 +36,13 @@ dependencies: - git - jdk - zlib - - steam-run - patchelf + - nix environment: CARGO_TERM_COLOR: "always" GLOBAL_CI_INSTALL_NDK: "1" - # Resolved to absolute paths in the build step (repo CWD). + GLOBAL_CI_USE_FHS: "1" ANDROID_NDK_HOME: ".android-ndk-r29" ANDROID_HOME: ".android-sdk" @@ -55,8 +54,6 @@ steps: rustup target add x86_64-linux-android cargo --version rustc --version - command -v steam-run - steam-run true - name: "Install cargo-apk" command: | @@ -69,7 +66,7 @@ steps: set -euo pipefail export GLOBAL_ROOT="$PWD" export GLOBAL_CI_INSTALL_NDK="${GLOBAL_CI_INSTALL_NDK:-1}" - # Resolve relative SDK/NDK paths against the repo root. + export GLOBAL_CI_USE_FHS="${GLOBAL_CI_USE_FHS:-1}" case "${ANDROID_NDK_HOME:-}" in /*) ;; *) export ANDROID_NDK_HOME="$PWD/${ANDROID_NDK_HOME:-.android-ndk-r29}" ;; diff --git a/scripts/ci-build-apk.sh b/scripts/ci-build-apk.sh index 75b693c..bbdc4ea 100755 --- a/scripts/ci-build-apk.sh +++ b/scripts/ci-build-apk.sh @@ -13,6 +13,7 @@ # ANDROID_HOME SDK root (default: $HOME/.local/share/android-sdk) # GLOBAL_VIDYA_SRC optional vidya sources (else in-tree vidya/ or $HOME/code/vidya) # GLOBAL_CI_INSTALL_NDK=1 download NDK r29 + minimal SDK (build-tools/platforms) if missing +# GLOBAL_CI_USE_FHS=1 wrap cargo-apk in nixpkgs buildFHSEnv (Spindle/Nix non-FHS hosts) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -245,26 +246,76 @@ preflight_cargo_apk_env() { log "missing platforms.mk (cargo-apk Ndk::from_env needs it)" exit 1 } - # Prove aapt is executable on this host (catches non-FHS / wrong libc early). - local aapt="" - local d - shopt -s nullglob - for d in "$ANDROID_HOME"/build-tools/*/; do - if [[ -x "${d}aapt" ]]; then - aapt="${d}aapt" - break - fi - done - shopt -u nullglob - if [[ -n "$aapt" ]]; then - if "$aapt" version >/dev/null 2>&1; then - log "aapt ok: $aapt" +} + +# Free FHS user env via nixpkgs buildFHSEnv (avoids unfree steam-run). +# Used on Spindle microvm/Nix hosts so Google aapt/NDK ELF prebuilts can exec. +ensure_fhs_runner() { + FHS_RUN=() + if command -v steam-run >/dev/null 2>&1; then + FHS_RUN=(steam-run) + log "FHS runner: steam-run" + return 0 + fi + if [[ "${GLOBAL_CI_USE_FHS:-0}" != "1" ]]; then + log "FHS runner: none (GLOBAL_CI_USE_FHS!=1)" + return 0 + fi + if ! command -v nix-build >/dev/null 2>&1 && ! command -v nix >/dev/null 2>&1; then + log "FHS runner: none (no nix; prebuilts may fail on non-FHS hosts)" + return 0 + fi + local out="$ROOT/.ci-fhs" + local drv="$ROOT/.ci-fhs.nix" + cat >"$drv" <<'NIX' +{ pkgs ? import { } }: +pkgs.buildFHSEnv { + name = "global-android-fhs"; + targetPkgs = p: with p; [ + zlib + zlib.dev + ncurses + stdenv.cc.cc + openssl + openssl.dev + jdk + ]; + multiPkgs = p: with p; [ zlib stdenv.cc.cc ]; + runScript = "bash"; +} +NIX + log "building free FHS env (buildFHSEnv)…" + if command -v nix-build >/dev/null 2>&1; then + nix-build "$drv" -o "$out" >/dev/null + else + nix build -f "$drv" -o "$out" + fi + if [[ -x "$out/bin/global-android-fhs" ]]; then + FHS_RUN=("$out/bin/global-android-fhs" -c) + log "FHS runner: $out/bin/global-android-fhs" + else + log "WARNING: FHS build produced no runner at $out" + fi +} + +run_cargo_apk() { + local cmd=(cargo apk build --target x86_64-linux-android --no-default-features) + ( + cd "$ROOT/dist-ui" + export RUST_BACKTRACE="${RUST_BACKTRACE:-1}" + export ANDROID_HOME ANDROID_NDK_HOME ANDROID_NDK_ROOT + unset ANDROID_SDK_ROOT || true + if [[ ${#FHS_RUN[@]} -eq 0 ]]; then + "${cmd[@]}" || true + elif [[ "${FHS_RUN[1]:-}" == "-c" ]]; then + # buildFHSEnv wrapper: fhs -c 'shell command' + local quoted + quoted=$(printf '%q ' "${cmd[@]}") + "${FHS_RUN[@]}" "export PATH=$(printf %q "$PATH"); export HOME=$(printf %q "$HOME"); export CARGO_HOME=$(printf %q "${CARGO_HOME:-$HOME/.cargo}"); export RUSTUP_HOME=$(printf %q "${RUSTUP_HOME:-$HOME/.rustup}"); export ANDROID_HOME=$(printf %q "$ANDROID_HOME"); export ANDROID_NDK_HOME=$(printf %q "$ANDROID_NDK_HOME"); export ANDROID_NDK_ROOT=$(printf %q "$ANDROID_NDK_ROOT"); unset ANDROID_SDK_ROOT; ${quoted}" || true else - log "WARNING: aapt not executable ($aapt) — host may lack glibc loader (use alpine/gcompat or FHS)" - file "$aapt" >&2 || true - ldd "$aapt" >&2 || true + "${FHS_RUN[@]}" "${cmd[@]}" || true fi - fi + ) } build() { @@ -273,19 +324,9 @@ build() { ensure_sdk setup_rust_android preflight_cargo_apk_env + ensure_fhs_runner log "cargo apk build --target x86_64-linux-android --no-default-features" - # Prefer steam-run when available (FHS wrap for Google prebuilts on Nix). - local runner=() - if command -v steam-run >/dev/null 2>&1; then - runner=(steam-run) - log "using steam-run for FHS prebuilts" - fi - ( - cd "$ROOT/dist-ui" - export RUST_BACKTRACE="${RUST_BACKTRACE:-1}" - # cargo-apk may panic after packaging; APK is often still written. - "${runner[@]}" cargo apk build --target x86_64-linux-android --no-default-features || true - ) + run_cargo_apk [[ -f "$APK" ]] || { log "APK missing: $APK"; exit 1; } local size size=$(stat -c %s "$APK" 2>/dev/null || echo 0) diff --git a/test/ci_apk_workflow_test.sh b/test/ci_apk_workflow_test.sh index 50c47f1..b7440ca 100755 --- a/test/ci_apk_workflow_test.sh +++ b/test/ci_apk_workflow_test.sh @@ -32,7 +32,8 @@ check "workflow has manual trigger" grep -q 'manual' "$WF" check "workflow targets main" grep -q 'main' "$WF" check "workflow invokes ci-build-apk" grep -q 'ci-build-apk' "$WF" check "workflow mentions APK path" grep -q 'global-dist-ui.apk' "$WF" -check "workflow includes steam-run" grep -q 'steam-run' "$WF" +check "workflow enables FHS wrap" grep -q 'GLOBAL_CI_USE_FHS' "$WF" +check "script has buildFHSEnv path" grep -q 'buildFHSEnv' "$SCRIPT" check "script emits APK_REL path" grep -q 'dist-ui/target/debug/apk/global-dist-ui.apk' "$SCRIPT" check "script checks ZIP magic" grep -q '504b' "$SCRIPT" check "script tolerates cargo-apk panic" grep -q 'cargo apk build' "$SCRIPT"