diff --git a/iris/android-app/build-apk.sh b/iris/android-app/build-apk.sh new file mode 100755 index 0000000..97eb699 --- /dev/null +++ b/iris/android-app/build-apk.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# Builds iris-android-app end to end: the cdylib (cargo ndk, straight into +# app/src/main/jniLibs/) then the APK (Gradle). Written to stop re-typing +# the same incantation by hand every time (ANDROID_HOME/NDK exports, the +# cargo ndk invocation, the keystore env for a release build, apksigner/ +# aapt2 verification) -- see docs/RUST.md's P0 box. Same shape as `app/ +# build-apk.sh` (the Compose app's own build script) and `app/ +# iris-scroll.sh` (no coordinates, set -eu, exit 0 on success). +# +# Usage: ./build-apk.sh [debug|release] [--abi arm64-v8a|x86_64] [--features "a b c"] +# debug/release default to debug (matches this-machine-android's "the +# emulator stays on debug" rule -- pass `release` explicitly for a phone +# build). --abi defaults to arm64-v8a (a phone/real device); pass +# x86_64 for this checkout's own AVD. --features defaults to +# "transcript-screen force-gles bench", P0's exact combination. +set -eu +cd "$(dirname "$0")" + +BUILD_TYPE="debug" +ABI="arm64-v8a" +FEATURES="transcript-screen force-gles bench" +case "${1:-}" in + debug|release) BUILD_TYPE="$1"; shift ;; +esac +while [ $# -gt 0 ]; do + case "$1" in + --abi) ABI="$2"; shift 2 ;; + --features) FEATURES="$2"; shift 2 ;; + *) echo "build-apk.sh: unknown argument: $1" >&2; exit 1 ;; + esac +done + +SDK_ROOT="$HOME/Android/Sdk" +export ANDROID_HOME="$SDK_ROOT" +export ANDROID_SDK_ROOT="$SDK_ROOT" +NDK_DIR=$(ls -d "$SDK_ROOT"/ndk/*/ 2>/dev/null | sort -V | tail -1) +if [ -z "$NDK_DIR" ]; then + echo "build-apk.sh: no NDK found under $SDK_ROOT/ndk" >&2 + exit 1 +fi +export ANDROID_NDK_HOME="$NDK_DIR" + +echo "build-apk.sh: cargo ndk -t $ABI build ${BUILD_TYPE:+(${BUILD_TYPE})} --features \"$FEATURES\"" +if [ "$BUILD_TYPE" = "release" ]; then + cargo ndk -t "$ABI" -P 26 -o app/src/main/jniLibs/ build --release --features "$FEATURES" +else + cargo ndk -t "$ABI" -P 26 -o app/src/main/jniLibs/ build --features "$FEATURES" +fi + +GRADLE_TASK="assembleDebug" +APK_DIR="app/build/outputs/apk/debug" +APK_NAME="app-debug.apk" +if [ "$BUILD_TYPE" = "release" ]; then + GRADLE_TASK="assembleRelease" + APK_DIR="app/build/outputs/apk/release" + APK_NAME="app-release.apk" + # Same key `app/build-apk.sh` (the Compose app) generates once under + # ~/.config/ai-app/release.jks -- see AGENTS.md's "Checking your work". + export AI_APP_KEYSTORE="$HOME/.config/ai-app/release.jks" + if [ ! -f "$AI_APP_KEYSTORE" ]; then + echo "build-apk.sh: no release key at $AI_APP_KEYSTORE -- run app/build-apk.sh once first" >&2 + exit 1 + fi + export AI_APP_KEYSTORE_PASSWORD + AI_APP_KEYSTORE_PASSWORD=$(cat "$AI_APP_KEYSTORE.password") +fi + +gradle ":app:$GRADLE_TASK" --console=plain + +APK_PATH="$(pwd)/$APK_DIR/$APK_NAME" +BUILD_TOOLS=$(ls -d "$SDK_ROOT"/build-tools/*/ | sort -V | tail -1) +echo "--- aapt2 dump badging ---" +"${BUILD_TOOLS}aapt2" dump badging "$APK_PATH" | head -5 +if [ "$BUILD_TYPE" = "release" ]; then + echo "--- apksigner verify ---" + "${BUILD_TOOLS}apksigner" verify --print-certs "$APK_PATH" +fi +echo "$APK_PATH" diff --git a/iris/android-app/run-bench.sh b/iris/android-app/run-bench.sh new file mode 100755 index 0000000..b0ec16c --- /dev/null +++ b/iris/android-app/run-bench.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# Installs and runs the iris `bench` build on this checkout's own emulator +# (per this-machine-android's per-checkout-AVD rule; `emu serial` picks it) +# and prints the report -- the iris half of `app/transcript-bench.sh`'s +# job. No coordinates: the button is found by its accessibility label +# through `ui-trace`, per AGENTS.md's "Driving the UI". +# +# Usage: ./run-bench.sh [--apk PATH] +# Defaults to this checkout's own release APK +# (app/build/outputs/apk/release/app-release.apk) if it exists, else the +# debug one -- build one first with ./build-apk.sh. +set -eu +cd "$(dirname "$0")" + +APK="" +while [ $# -gt 0 ]; do + case "$1" in + --apk) APK="$2"; shift 2 ;; + *) echo "run-bench.sh: unknown argument: $1" >&2; exit 1 ;; + esac +done +if [ -z "$APK" ]; then + if [ -f app/build/outputs/apk/release/app-release.apk ]; then + APK=app/build/outputs/apk/release/app-release.apk + else + APK=app/build/outputs/apk/debug/app-debug.apk + fi +fi +if [ ! -f "$APK" ]; then + echo "run-bench.sh: no APK at $APK -- run ./build-apk.sh first" >&2 + exit 1 +fi + +SERIAL=$(emu serial) +PKG=$(aapt2 dump badging "$APK" 2>/dev/null | sed -n "s/^package: name='\\([^']*\\)'.*/\\1/p") +if [ -z "$PKG" ]; then + BUILD_TOOLS=$(ls -d "$HOME"/Android/Sdk/build-tools/*/ | sort -V | tail -1) + PKG=$("${BUILD_TOOLS}aapt2" dump badging "$APK" | sed -n "s/^package: name='\\([^']*\\)'.*/\\1/p") +fi + +echo "run-bench.sh: installing $APK ($PKG) on $SERIAL" +adb -s "$SERIAL" install -r "$APK" >/dev/null +adb -s "$SERIAL" shell am force-stop "$PKG" +adb -s "$SERIAL" logcat -c +adb -s "$SERIAL" shell am start -n "$PKG/dev.iris.android.demo.MainActivity" >/dev/null + +ui-trace record -s "$SERIAL" -d 3000 --do "tap 'Run benchmark'" -o /tmp/run-bench-tap.txt >/dev/null + +# Poll for the report line rather than a fixed sleep -- the run itself is a +# fixed script (24 swipes + a 20s streaming phase) but device speed varies. +i=0 +while [ "$i" -lt 90 ]; do + LINE=$(adb -s "$SERIAL" logcat -d -s iris-android-app:I 2>/dev/null | grep "iris bench report:" || true) + if [ -n "$LINE" ]; then + break + fi + i=$((i + 1)) + sleep 1 +done +if [ -z "$LINE" ]; then + echo "run-bench.sh: no report after 90s -- check logcat by hand" >&2 + exit 1 +fi +adb -s "$SERIAL" logcat -d -s iris-android-app:I | grep -A 6 "iris bench report:"