diff --git a/.tangled/workflows/android-apk.yml b/.tangled/workflows/android-apk.yml index aa5ed24..92621dc 100644 --- a/.tangled/workflows/android-apk.yml +++ b/.tangled/workflows/android-apk.yml @@ -34,12 +34,16 @@ dependencies: - file - which - git + - jdk + - zlib environment: CARGO_TERM_COLOR: "always" GLOBAL_CI_INSTALL_NDK: "1" - # NDK lands under $HOME in the container workspace home. + # NDK + minimal SDK under workspace (ephemeral runner FS). ANDROID_NDK_HOME: "/tangled/workspace/.android-ndk-r29" + ANDROID_HOME: "/tangled/workspace/.android-sdk" + ANDROID_SDK_ROOT: "/tangled/workspace/.android-sdk" steps: - name: "Rust toolchain + android target" diff --git a/scripts/ci-build-apk.sh b/scripts/ci-build-apk.sh index ea80e19..e6dc1ec 100755 --- a/scripts/ci-build-apk.sh +++ b/scripts/ci-build-apk.sh @@ -10,8 +10,9 @@ # Env: # GLOBAL_ROOT checkout root (default: repo root of this script) # ANDROID_NDK_HOME NDK root (default: $HOME/.local/share/android-ndk-r29) +# 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 side-by-side if missing (heavy; Spindle) +# GLOBAL_CI_INSTALL_NDK=1 download NDK r29 + minimal SDK (build-tools/platforms) if missing set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -24,9 +25,25 @@ NDK_DEFAULT="${HOME}/.local/share/android-ndk-r29" export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$NDK_DEFAULT}" export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME" export ANDROID_HOME="${ANDROID_HOME:-${HOME}/.local/share/android-sdk}" +export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" log() { echo "[ci-build-apk] $*" >&2; } +# First matching directory under $1 matching basename glob $2 (no find(1)). +first_dir() { + local base="$1" pattern="$2" d + shopt -s nullglob + for d in "$base"/$pattern; do + if [[ -d "$d" ]]; then + shopt -u nullglob + printf '%s\n' "$d" + return 0 + fi + done + shopt -u nullglob + return 1 +} + ensure_vidya() { local dest="$ROOT/.flake/vidya" mkdir -p "$ROOT/.flake" @@ -78,22 +95,98 @@ ensure_ndk() { extract_dir="$(mktemp -d)" unzip -q "$zip" -d "$extract_dir" # Prefer bash glob over find(1) — Spindle nixery images may omit findutils. - local found="" d - shopt -s nullglob - for d in "$extract_dir"/android-ndk-*; do - if [[ -d "$d" ]]; then - found="$d" - break - fi - done - shopt -u nullglob - [[ -n "$found" ]] || { log "NDK extract failed under $extract_dir"; exit 1; } + local found + found="$(first_dir "$extract_dir" 'android-ndk-*' || true)" + [[ -n "${found:-}" ]] || { log "NDK extract failed under $extract_dir"; exit 1; } rm -rf "$ANDROID_NDK_HOME" mv "$found" "$ANDROID_NDK_HOME" rm -rf "$extract_dir" log "NDK installed: $ANDROID_NDK_HOME" } +# cargo-apk/ndk-build require ANDROID_HOME/{build-tools,platforms}. +ensure_sdk() { + local bt_ok=0 plat_ok=0 + if [[ -d "$ANDROID_HOME/build-tools" ]]; then + if first_dir "$ANDROID_HOME/build-tools" '[0-9]*' >/dev/null; then + bt_ok=1 + fi + fi + if [[ -d "$ANDROID_HOME/platforms" ]]; then + if first_dir "$ANDROID_HOME/platforms" 'android-*' >/dev/null; then + plat_ok=1 + fi + fi + if [[ "$bt_ok" -eq 1 && "$plat_ok" -eq 1 ]]; then + log "SDK ok: $ANDROID_HOME (build-tools + platforms)" + return 0 + fi + if [[ "${GLOBAL_CI_INSTALL_NDK:-0}" != "1" ]]; then + log "ANDROID_HOME incomplete: $ANDROID_HOME (build-tools=$bt_ok platforms=$plat_ok)" + log "Install Android SDK build-tools + platforms, or set GLOBAL_CI_INSTALL_NDK=1." + exit 1 + fi + + log "Downloading minimal Android SDK into $ANDROID_HOME…" + mkdir -p "$ANDROID_HOME" + local tmp zip found + + if [[ "$bt_ok" -ne 1 ]]; then + zip="/tmp/android-build-tools-r34-linux.zip" + if [[ ! -f "$zip" ]]; then + curl -fsSL -o "$zip" \ + "https://dl.google.com/android/repository/build-tools_r34-linux.zip" + fi + tmp="$(mktemp -d)" + unzip -q "$zip" -d "$tmp" + # Zip layout is typically android-14/ → build-tools/34.0.0 + found="$(first_dir "$tmp" 'android-*' || first_dir "$tmp" '*' || true)" + [[ -n "${found:-}" ]] || { log "build-tools extract failed"; exit 1; } + mkdir -p "$ANDROID_HOME/build-tools" + rm -rf "$ANDROID_HOME/build-tools/34.0.0" + mv "$found" "$ANDROID_HOME/build-tools/34.0.0" + rm -rf "$tmp" + log "build-tools → $ANDROID_HOME/build-tools/34.0.0" + fi + + if [[ "$plat_ok" -ne 1 ]]; then + zip="/tmp/android-platform-34.zip" + if [[ ! -f "$zip" ]]; then + curl -fsSL -o "$zip" \ + "https://dl.google.com/android/repository/platform-34-ext7_r03.zip" + fi + tmp="$(mktemp -d)" + unzip -q "$zip" -d "$tmp" + found="$(first_dir "$tmp" 'android-*' || true)" + [[ -n "${found:-}" ]] || { log "platform extract failed"; exit 1; } + mkdir -p "$ANDROID_HOME/platforms" + rm -rf "$ANDROID_HOME/platforms/android-34" + mv "$found" "$ANDROID_HOME/platforms/android-34" + rm -rf "$tmp" + log "platforms → $ANDROID_HOME/platforms/android-34" + fi + + # platform-tools optional for packaging but cargo-apk may probe paths. + if [[ ! -d "$ANDROID_HOME/platform-tools" ]]; then + zip="/tmp/android-platform-tools-linux.zip" + if [[ ! -f "$zip" ]]; then + curl -fsSL -o "$zip" \ + "https://dl.google.com/android/repository/platform-tools_r34.0.5-linux.zip" || true + fi + if [[ -f "$zip" ]]; then + tmp="$(mktemp -d)" + unzip -q "$zip" -d "$tmp" || true + found="$(first_dir "$tmp" 'platform-tools' || true)" + if [[ -n "${found:-}" ]]; then + rm -rf "$ANDROID_HOME/platform-tools" + mv "$found" "$ANDROID_HOME/platform-tools" + log "platform-tools installed" + fi + rm -rf "$tmp" + fi + fi +} + setup_rust_android() { local prebuilt="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64" local rust_tc="" @@ -106,7 +199,7 @@ setup_rust_android() { fi rust_tc="" done - export PATH="${rust_tc:+${rust_tc}:}${HOME}/.cargo/bin:${prebuilt}/bin:${ANDROID_HOME}/platform-tools:${PATH}" + export PATH="${rust_tc:+${rust_tc}:}${HOME}/.cargo/bin:${prebuilt}/bin:${ANDROID_HOME}/build-tools/34.0.0:${ANDROID_HOME}/platform-tools:${PATH}" export CC_x86_64_linux_android="${CC_x86_64_linux_android:-x86_64-linux-android28-clang}" export CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="$CC_x86_64_linux_android" export AR_x86_64_linux_android="${AR_x86_64_linux_android:-llvm-ar}" @@ -124,6 +217,7 @@ setup_rust_android() { build() { ensure_vidya ensure_ndk + ensure_sdk setup_rust_android log "cargo apk build --target x86_64-linux-android --no-default-features" ( diff --git a/test/ci_apk_workflow_test.sh b/test/ci_apk_workflow_test.sh index bf5b104..cf828d0 100755 --- a/test/ci_apk_workflow_test.sh +++ b/test/ci_apk_workflow_test.sh @@ -37,6 +37,9 @@ check "script tolerates cargo-apk panic" grep -q 'cargo apk build' "$SCRIPT" # Spindle nixery may omit findutils; NDK extract must not require bare find(1). check "script NDK extract avoids find(1)" bash -c "! grep -E '(^|[^[:alnum:]_])find ' '$SCRIPT'" check "workflow includes findutils (belt)" grep -q 'findutils' "$WF" +check "script bootstraps SDK build-tools" grep -q 'build-tools_r34' "$SCRIPT" +check "script bootstraps platform android-34" grep -q 'platform-34' "$SCRIPT" +check "workflow sets ANDROID_HOME" grep -q 'ANDROID_HOME' "$WF" # If APK present (after a real build), validate it is a non-empty APK/ZIP. if [[ -f "$APK_REL" ]]; then