#!/usr/bin/env bash # Cross-build the engine for Android: libcstcore.a (what the APK embeds) plus # cst-agent and tests/test_embed, which run straight from /data/local/tmp. # No TUI (no ncurses on Android) and no WebUI (the phone app talks to the # engine in-process; the stub compiles instead of the HTTP server). # # scripts/build_android.sh [arm64-v8a|x86_64] [--push] [--run] [--run-tests] # # --push copy the binaries to /data/local/tmp # --run also smoke-test there: cst-agent --version, test_embed # --run-tests build and run the whole tests/ suite on the device # # Needs ANDROID_NDK_HOME (see build_third_party_android.sh) and the deps from # scripts/build_third_party_android.sh ABI all. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" ABI="arm64-v8a" DO_PUSH=0 DO_RUN=0 DO_TESTS=0 for a in "$@"; do case "$a" in arm64-v8a | x86_64) ABI="$a" ;; --push) DO_PUSH=1 ;; --run) DO_PUSH=1; DO_RUN=1 ;; --run-tests) DO_PUSH=1; DO_TESTS=1 ;; *) echo "usage: $0 [arm64-v8a|x86_64] [--push] [--run|--run-tests]" >&2; exit 1 ;; esac done API_LEVEL="${API_LEVEL:-24}" PREFIX="$ROOT/third_party/prefix-android-$ABI" BUILD="$ROOT/build-android-$ABI" JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}" NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}" if [[ -z "$NDK" || ! -f "$NDK/build/cmake/android.toolchain.cmake" ]]; then echo "set ANDROID_NDK_HOME to an NDK (see build_third_party_android.sh)" >&2 exit 1 fi if [[ ! -f "$PREFIX/lib/libcurl.a" ]]; then echo "missing $PREFIX/lib/libcurl.a -- run:" >&2 echo " scripts/build_third_party_android.sh $ABI all" >&2 exit 1 fi cmake -S "$ROOT" -B "$BUILD" -G Ninja \ -DCMAKE_TOOLCHAIN_FILE="$NDK/build/cmake/android.toolchain.cmake" \ -DANDROID_ABI="$ABI" -DANDROID_PLATFORM="android-$API_LEVEL" \ -DANDROID_NDK="$NDK" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH="$PREFIX" \ -DCURL_INCLUDE_DIR="$PREFIX/include" -DCURL_LIBRARY="$PREFIX/lib/libcurl.a" \ -DCMAKE_EXE_LINKER_FLAGS="-L$PREFIX/lib" \ -DCST_BUILD_TUI=OFF -DCST_BUILD_WEBUI=OFF -DCST_BUILD_APP=OFF \ -DCST_BUNDLE_BUSYBOX=OFF if [[ "$DO_TESTS" == 1 ]]; then cmake --build "$BUILD" -j"$JOBS" # every test target else cmake --build "$BUILD" -j"$JOBS" --target cst-agent test_embed fi echo "built in $BUILD" [[ "$DO_PUSH" == 1 ]] || exit 0 ADB="${ADB:-adb}" command -v "$ADB" >/dev/null || { echo "adb not found (nix-shell -p android-tools)" >&2; exit 1; } DEV=/data/local/tmp NAMES=(cst-agent test_embed) FILES=("$BUILD/cst-agent" "$BUILD/test_embed") TESTS=() if [[ "$DO_TESTS" == 1 ]]; then mapfile -t TESTS < <(find "$BUILD" -maxdepth 1 -type f -name 'test_*' -perm -u+x | sort) for t in "${TESTS[@]}"; do NAMES+=("${t##*/}") FILES+=("$t") done fi "$ADB" push "${FILES[@]}" "$DEV/" >/dev/null "$ADB" shell chmod 755 "${NAMES[@]/#/$DEV/}" echo "pushed ${#NAMES[@]} binaries to $DEV" [[ "$DO_PUSH" == 1 && ( "$DO_RUN" == 1 || "$DO_TESTS" == 1 ) ]] || exit 0 echo "--- $DEV/cst-agent --version" "$ADB" shell "$DEV/cst-agent --version" if [[ "$DO_TESTS" == 0 ]]; then echo "--- $DEV/test_embed" "$ADB" shell "cd $DEV && ./test_embed" exit 0 fi # The suite runs on the device as the shell user; tests that want a real # /bin/sh, a host fixture tree or a Linux-specific sandbox report their own # failure, which is the point of running them here. # The sandbox test builds its scratch trees under $PWD/tests, the same way # ctest runs it from the repo root. "$ADB" shell mkdir -p "$DEV/tests" pass=0; fail=0; skipped=() for t in "${TESTS[@]}"; do name="${t##*/}" if "$ADB" shell "cd $DEV && ./$name" >/dev/null 2>&1; then pass=$((pass + 1)); echo "ok $name" else fail=$((fail + 1)); skipped+=("$name"); echo "FAIL $name" fi done echo "--- $pass passed, $fail failed" [[ "$fail" == 0 ]] || { printf 'failed: %s\n' "${skipped[*]}"; exit 1; }