Move Qt/Tauri/full Web SPA under archive/, ship hermit as CLI/TUI plus libcstcore and cst-webui/1 HTTP+SSE (turn lifecycle hooks, capabilities). Co-authored-by: Cursor <cursoragent@cursor.com>
170 lines
7.8 KiB
Bash
Executable File
170 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ARCHIVED: Qt APK front end under archive/qt-app; not part of default Hermit builds.
|
|
# Build the Qt Quick Android APK of archive/qt-app/ (the engine is linked in-process, so
|
|
# the APK opens no port). This is the front end counterpart of
|
|
# scripts/build_android.sh, which cross-builds the engine alone.
|
|
#
|
|
# scripts/build_apk_android.sh [--install] [--logs] [--clean]
|
|
#
|
|
# --install adb install -r the debug APK on the connected device
|
|
# --logs stream logcat after installing (Qt + engine tags)
|
|
# --clean wipe the build directory first
|
|
#
|
|
# Needs, all overridable through the environment:
|
|
# QT_ANDROID Qt for Android (default ~/Qt/<ver>/android_arm64_v8a)
|
|
# QT_HOST matching host Qt (default ~/Qt/<ver>/gcc_64)
|
|
# ANDROID_SDK_ROOT SDK with platforms/ + build-tools/ (see docs/android.md)
|
|
# ANDROID_NDK_HOME NDK, same one as scripts/build_third_party_android.sh
|
|
# JAVA_HOME JDK 17+ for Gradle (falls back to the javac on PATH)
|
|
set -euo pipefail
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
ABI="${ABI:-arm64-v8a}"
|
|
API_LEVEL="${API_LEVEL:-24}"
|
|
BUILD="$ROOT/build-apk-android"
|
|
PREFIX="$ROOT/third_party/prefix-android-$ABI"
|
|
JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}"
|
|
|
|
DO_INSTALL=0
|
|
DO_LOGS=0
|
|
for a in "$@"; do
|
|
case "$a" in
|
|
--install) DO_INSTALL=1 ;;
|
|
--logs) DO_INSTALL=1; DO_LOGS=1 ;;
|
|
--clean) rm -rf "$BUILD" ;;
|
|
*) echo "usage: $0 [--install] [--logs] [--clean]" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# --- Qt: Android target first, then the host tools of the same version ----
|
|
if [[ -z "${QT_ANDROID:-}" ]]; then
|
|
# aqt names the directory after the ABI with underscores: android_arm64_v8a
|
|
for q in "$HOME"/Qt/*/android_"${ABI//-/_}"/lib/cmake/Qt6/qt.toolchain.cmake; do
|
|
[[ -f "$q" ]] && QT_ANDROID="${q%/lib/cmake/Qt6/qt.toolchain.cmake}"
|
|
done
|
|
fi
|
|
if [[ -z "${QT_ANDROID:-}" || ! -x "$QT_ANDROID/bin/qt-cmake" ]]; then
|
|
echo "set QT_ANDROID to a Qt for Android install ('aqt install-qt all_os android ...')" >&2
|
|
exit 1
|
|
fi
|
|
QT_VER="$(basename "$(dirname "$QT_ANDROID")")"
|
|
QT_HOST="${QT_HOST:-$(dirname "$QT_ANDROID")/gcc_64}"
|
|
if [[ ! -x "$QT_HOST/bin/qmake" && ! -x "$QT_HOST/bin/androiddeployqt" ]]; then
|
|
echo "set QT_HOST to the host Qt $QT_VER (same version, needed for androiddeployqt)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
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 scripts/build_third_party_android.sh)" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "${ANDROID_SDK_ROOT:-}" || ! -d "$ANDROID_SDK_ROOT/platforms" ]]; then
|
|
echo "set ANDROID_SDK_ROOT to an SDK with platforms/android-<n> and build-tools/" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "${JAVA_HOME:-}" ]]; then
|
|
JAVAC="$(command -v javac || true)"
|
|
[[ -n "$JAVAC" ]] && JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$JAVAC")")")"
|
|
fi
|
|
if [[ -z "${JAVA_HOME:-}" || ! -x "$JAVA_HOME/bin/java" ]]; then
|
|
echo "set JAVA_HOME to a JDK 17+ (Gradle needs it)" >&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
|
|
export ANDROID_SDK_ROOT ANDROID_NDK_HOME="$NDK" ANDROID_NDK_ROOT="$NDK" JAVA_HOME
|
|
export PATH="$JAVA_HOME/bin:$PATH"
|
|
|
|
# androiddeployqt shells out to the generated project's gradlew, which picks up
|
|
# init scripts from $GRADLE_USER_HOME/init.d. Install ours (release lint off)
|
|
# so every build behaves the same. See the file for why lint has to go.
|
|
GRADLE_INIT_DIR="${GRADLE_USER_HOME:-$HOME/.gradle}/init.d"
|
|
mkdir -p "$GRADLE_INIT_DIR"
|
|
if ! cmp -s "$ROOT/scripts/gradle-android-init.gradle" "$GRADLE_INIT_DIR/cst-android.gradle"; then
|
|
install -m644 "$ROOT/scripts/gradle-android-init.gradle" "$GRADLE_INIT_DIR/cst-android.gradle"
|
|
echo "installed $GRADLE_INIT_DIR/cst-android.gradle"
|
|
fi
|
|
|
|
# The release APK has to be signed to install. With CST_ANDROID_KEYSTORE the
|
|
# init script uses that keystore; otherwise it falls back to the standard
|
|
# Android debug key, created here on first use (same one Android Studio makes).
|
|
KEYSTORE="${CST_ANDROID_KEYSTORE:-$HOME/.android/debug.keystore}"
|
|
if [[ ! -f "$KEYSTORE" && -z "${CST_ANDROID_KEYSTORE:-}" ]]; then
|
|
mkdir -p "$(dirname "$KEYSTORE")"
|
|
keytool -genkeypair -keystore "$KEYSTORE" -storepass android -keypass android \
|
|
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
|
|
-dname "CN=Android Debug,O=Android,C=US" >/dev/null
|
|
chmod 600 "$KEYSTORE"
|
|
echo "created debug keystore: $KEYSTORE"
|
|
fi
|
|
# androiddeployqt reads these (the path/alias/passwords never reach the CMake
|
|
# command line, only the QT_ANDROID_SIGN_APK flag does).
|
|
export QT_ANDROID_KEYSTORE_PATH="$KEYSTORE"
|
|
export QT_ANDROID_KEYSTORE_ALIAS="${CST_ANDROID_KEY_ALIAS:-androiddebugkey}"
|
|
export QT_ANDROID_KEYSTORE_STORE_PASS="${CST_ANDROID_KEYSTORE_PASS:-android}"
|
|
export QT_ANDROID_KEYSTORE_KEY_PASS="${CST_ANDROID_KEY_PASS:-android}"
|
|
|
|
# NixOS has no /bin/bash, and build-tools' shell wrappers (apksigner is the one
|
|
# androiddeployqt calls, d8 and friends are the rest) start with #!/bin/bash.
|
|
# Point them at the bash that exists here; the SDK is a local copy, so this
|
|
# sticks.
|
|
BT_DIR="$(ls -d "$ANDROID_SDK_ROOT"/build-tools/* 2>/dev/null | sort -V | tail -1)"
|
|
BASH_BIN="$(command -v bash || echo /bin/sh)"
|
|
if [[ -n "$BT_DIR" ]]; then
|
|
while IFS= read -r f; do
|
|
sed -i "1s|^#!/bin/bash|#!$BASH_BIN|" "$f" && echo "fixed shebang: ${f#"$ANDROID_SDK_ROOT"/}"
|
|
done < <(grep -rls '^#!/bin/bash' "$BT_DIR" 2>/dev/null)
|
|
fi
|
|
|
|
# Qt6Gui wants GLESv2/EGL: the NDK sysroot has both, but Qt's Find modules
|
|
# only search CMAKE_FIND_ROOT_PATH prefixes (<sysroot>/include), while the
|
|
# headers live in <sysroot>/usr/include -- point them there directly.
|
|
NDK_SYSROOT_INC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include"
|
|
[[ -d "$NDK_SYSROOT_INC/GLES2" ]] || echo "warning: no GLES2 headers under $NDK_SYSROOT_INC" >&2
|
|
|
|
echo "Qt for Android : $QT_ANDROID ($QT_VER)"
|
|
echo "host Qt : $QT_HOST"
|
|
echo "SDK : $ANDROID_SDK_ROOT ($(ls "$ANDROID_SDK_ROOT/build-tools" | tr '\n' ' '))"
|
|
echo "NDK : $NDK"
|
|
echo "JDK : $JAVA_HOME"
|
|
echo "ABI : $ABI, minSdk $API_LEVEL"
|
|
|
|
# The engine comes from libcstcore.a, exactly as on the desktop: no TUI, no
|
|
# HTTP server (src/webui_stub.c stands in), no bundled busybox inside an APK.
|
|
"$QT_ANDROID/bin/qt-cmake" -S "$ROOT" -B "$BUILD" -G Ninja \
|
|
-DQT_HOST_PATH="$QT_HOST" \
|
|
-DANDROID_ABI="$ABI" -DANDROID_PLATFORM="android-$API_LEVEL" \
|
|
-DANDROID_SDK_ROOT="$ANDROID_SDK_ROOT" -DANDROID_NDK="$NDK" \
|
|
-DGLESv2_INCLUDE_DIR="$NDK_SYSROOT_INC" -DEGL_INCLUDE_DIR="$NDK_SYSROOT_INC" \
|
|
-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" \
|
|
-DCMAKE_SHARED_LINKER_FLAGS="-L$PREFIX/lib" \
|
|
-DCMAKE_MODULE_LINKER_FLAGS="-L$PREFIX/lib" \
|
|
-DCST_BUILD_TUI=OFF -DCST_BUILD_WEBUI=OFF -DCST_BUILD_APP=ON \
|
|
-DCST_BUNDLE_BUSYBOX=OFF
|
|
cmake --build "$BUILD" --target apk -j"$JOBS"
|
|
|
|
# AGP names the file after the variant and signing state
|
|
# (android-build-release.apk vs ...-unsigned.apk), so take the newest one.
|
|
APK="$(find "$BUILD/archive/qt-app/android-build/build/outputs/apk" "$BUILD" -name '*.apk' \
|
|
-printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)"
|
|
[[ -n "$APK" && -f "$APK" ]] || { echo "no APK produced" >&2; exit 1; }
|
|
echo
|
|
echo "APK: $APK ($(du -h "$APK" | cut -f1))"
|
|
[[ "$DO_INSTALL" == 1 ]] || exit 0
|
|
|
|
ADB="${ADB:-adb}"
|
|
command -v "$ADB" >/dev/null || { echo "adb not found (nix-shell -p android-tools)" >&2; exit 1; }
|
|
"$ADB" install -r "$APK"
|
|
echo "installed: $("$ADB" shell pm list packages | grep cstagent)"
|
|
[[ "$DO_LOGS" == 1 ]] || exit 0
|
|
echo "--- am start (Ctrl-C to stop; logcat follows)"
|
|
"$ADB" shell monkey -p xyz.whcself.cstagent -c android.intent.category.LAUNCHER 1 >/dev/null
|
|
sleep 2
|
|
exec "$ADB" logcat -s cst-agent:V Qt:V AndroidRuntime:E libc:V DEBUG:V
|