scripts: self-contained in-repo build_emu.sh (replaces /tmp recipe)
The Android build recipe lived at /tmp/build_pad.sh and depended on /tmp/apktool.jar; /tmp gets wiped between sessions (both were lost once mid-project and had to be rebuilt). Move the recipe into the repo as scripts/build_emu.sh: - checks prerequisites (java, gogio, apksigner, adb, debug keystore) with actionable error messages - auto-downloads apktool v3.0.3 to the stable ~/android-sdk/tools/ if missing (never /tmp) - uses a mktemp work dir cleaned via trap; no /tmp clutter - --no-install flag; verifies an adb device is present before installing - verifies the permission injection actually took effect Tested end-to-end (build + install + app relaunch) and idempotent on re-run. doc/README.md and development_plan.md now point here.
This commit is contained in:
parent
c22c21c872
commit
3a39fb8126
|
|
@ -38,21 +38,23 @@ line-by-line code — so they stay true as the implementation evolves.
|
||||||
|
|
||||||
Prereqs on this box (already installed): JDK 17, Go, Android SDK at
|
Prereqs on this box (already installed): JDK 17, Go, Android SDK at
|
||||||
`~/android-sdk` (platform-tools, build-tools 35.0.0, emulator, NDK),
|
`~/android-sdk` (platform-tools, build-tools 35.0.0, emulator, NDK),
|
||||||
`gogio@v0.10.0` in `~/go/bin`, `apktool` at `/tmp/apktool.jar`, debug
|
`gogio@v0.10.0` in `~/go/bin`, debug keystore at
|
||||||
keystore at `~/.android/debug.keystore`. Env vars are set in `~/.bashrc`.
|
`~/.android/debug.keystore`. Env vars are set in `~/.bashrc`.
|
||||||
|
|
||||||
The full recipe is `/tmp/build_pad.sh` (machine-local). Steps:
|
**Build + install with `./scripts/build_emu.sh`** (add `--no-install` to
|
||||||
|
build only). It is self-contained: checks the prereqs, auto-downloads
|
||||||
|
apktool v3.0.3 to `~/android-sdk/tools/apktool.jar` if missing, and uses a
|
||||||
|
temporary work dir. Output: `cmd/pad/pad-emu.apk`.
|
||||||
|
|
||||||
1. `gogio -target android -targetsdk 35 -arch amd64 -o /tmp/pad-raw.apk .`
|
What it does, and why (gogio cannot inject manifest permissions):
|
||||||
(from `cmd/pad/`)
|
|
||||||
2. `apktool d /tmp/pad-raw.apk -o /tmp/pad_decoded -f`
|
1. `gogio -target android -targetsdk 35 -arch amd64` → raw APK (from `cmd/pad/`)
|
||||||
|
2. `apktool d` → decode
|
||||||
3. Inject `<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>`
|
3. Inject `<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>`
|
||||||
into the decoded `AndroidManifest.xml` (Android 15 needs it for
|
(without it, Android 11+ blocks listing `/storage/emulated/0`)
|
||||||
`/storage/emulated/0`).
|
4. `apktool b` → rebuild
|
||||||
4. `apktool b /tmp/pad_decoded -o /tmp/pad-unsigned.apk`
|
5. `apksigner sign` with the debug key → `cmd/pad/pad-emu.apk`
|
||||||
5. `apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android
|
6. `adb install -r` (skipped with `--no-install`)
|
||||||
--key-pass pass:android --ks-key-alias androiddebugkey --out cmd/pad/pad-emu.apk /tmp/pad-unsigned.apk`
|
|
||||||
6. `adb install -r cmd/pad/pad-emu.apk`
|
|
||||||
|
|
||||||
Run the emulator (headless, AVD `pad_avd`, API 35):
|
Run the emulator (headless, AVD `pad_avd`, API 35):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ API 35 `google_apis/x86_64` system image installed to `~/android-sdk`; AVD
|
||||||
`pad_avd` (Pixel 6 profile) boots with KVM (`/dev/kvm` chmod 666). Build recipe:
|
`pad_avd` (Pixel 6 profile) boots with KVM (`/dev/kvm` chmod 666). Build recipe:
|
||||||
`gogio -target android -targetsdk 35 -arch amd64` → inject
|
`gogio -target android -targetsdk 35 -arch amd64` → inject
|
||||||
`MANAGE_EXTERNAL_STORAGE` via apktool → sign with the debug keystore →
|
`MANAGE_EXTERNAL_STORAGE` via apktool → sign with the debug keystore →
|
||||||
`adb install -r` (scripted in `/tmp/build_pad.sh`).
|
`adb install -r` (scripted in `scripts/build_emu.sh`).
|
||||||
|
|
||||||
**On-device IME validation — PASSING** (Gboard, API 35; observed via the gated
|
**On-device IME validation — PASSING** (Gboard, API 35; observed via the gated
|
||||||
`imeDebugLog` trace + logcat + autosaved file diff + screenshots):
|
`imeDebugLog` trace + logcat + autosaved file diff + screenshots):
|
||||||
|
|
@ -248,8 +248,8 @@ but are not needed for a usable v1.
|
||||||
rules; over-detailed companion docs deleted — see `doc/README.md` policy).
|
rules; over-detailed companion docs deleted — see `doc/README.md` policy).
|
||||||
- ✓ Amend `spec.md` per §7 (2026-08-16: rewritten; unbuilt features moved to an
|
- ✓ Amend `spec.md` per §7 (2026-08-16: rewritten; unbuilt features moved to an
|
||||||
explicit “deferred” table; see the corrections noted under §7 below).
|
explicit “deferred” table; see the corrections noted under §7 below).
|
||||||
- ✓ Build/install recipe (2026-08-16: in `doc/README.md`; machine-local script
|
- ✓ Build/install recipe (2026-08-16: in `doc/README.md`; self-contained
|
||||||
`/tmp/build_pad.sh`).
|
in-repo script `scripts/build_emu.sh`).
|
||||||
- ☐ In-repo device test checklist (the adb tap/swipe/IME sequences used in
|
- ☐ In-repo device test checklist (the adb tap/swipe/IME sequences used in
|
||||||
Phases 2–3 and Phase 6 are in this plan's phase notes but not a standalone
|
Phases 2–3 and Phase 6 are in this plan's phase notes but not a standalone
|
||||||
checklist).
|
checklist).
|
||||||
|
|
|
||||||
81
scripts/build_emu.sh
Executable file
81
scripts/build_emu.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build (and optionally install) the Pad Android APK for the local emulator.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/build_emu.sh # build + install to the connected device
|
||||||
|
# ./scripts/build_emu.sh --no-install # build only
|
||||||
|
#
|
||||||
|
# Output: cmd/pad/pad-emu.apk
|
||||||
|
#
|
||||||
|
# Prereqs on this workstation (the script checks each and explains what is
|
||||||
|
# missing):
|
||||||
|
# - JDK 17 (java on PATH)
|
||||||
|
# - Go + gogio v0.10.0 (~/go/bin)
|
||||||
|
# - Android SDK at ~/android-sdk (platform-tools, build-tools 35.0.0)
|
||||||
|
# - debug keystore at ~/.android/debug.keystore
|
||||||
|
# apktool is not a prerequisite: if it is missing, the script downloads
|
||||||
|
# v3.0.3 to the stable location ~/android-sdk/tools/apktool.jar.
|
||||||
|
#
|
||||||
|
# Why the apktool detour: gogio does not offer a way to add
|
||||||
|
# MANAGE_EXTERNAL_STORAGE to the manifest, and Android 11+ blocks the app's
|
||||||
|
# directory listing without it. So we decode the gogio APK, inject the
|
||||||
|
# permission, rebuild, and re-sign with the debug key.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
export ANDROID_HOME=${ANDROID_HOME:-$HOME/android-sdk}
|
||||||
|
export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/35.0.0:$HOME/go/bin
|
||||||
|
|
||||||
|
INSTALL=1
|
||||||
|
[ "${1:-}" = "--no-install" ] && INSTALL=0
|
||||||
|
|
||||||
|
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
command -v java >/dev/null 2>&1 || die "java not found (need JDK 17)"
|
||||||
|
command -v gogio >/dev/null 2>&1 || die "gogio not found (need v0.10.0 in ~/go/bin)"
|
||||||
|
command -v apksigner >/dev/null 2>&1 || die "apksigner not found (need $ANDROID_HOME/build-tools/35.0.0)"
|
||||||
|
command -v adb >/dev/null 2>&1 || die "adb not found (need $ANDROID_HOME/platform-tools)"
|
||||||
|
[ -f "$HOME/.android/debug.keystore" ] || die "debug keystore missing: $HOME/.android/debug.keystore"
|
||||||
|
|
||||||
|
# apktool: auto-download to a stable location if missing (never /tmp, which
|
||||||
|
# gets wiped between sessions).
|
||||||
|
APKTOOL="$ANDROID_HOME/tools/apktool.jar"
|
||||||
|
if [ ! -f "$APKTOOL" ]; then
|
||||||
|
echo "=== apktool missing; downloading v3.0.3 to $APKTOOL ==="
|
||||||
|
mkdir -p "$ANDROID_HOME/tools"
|
||||||
|
curl -fsSL -o "$APKTOOL" \
|
||||||
|
https://github.com/iBotPeaches/Apktool/releases/download/v3.0.3/apktool_3.0.3.jar \
|
||||||
|
|| die "apktool download failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
echo "=== gogio (Go -> APK) ==="
|
||||||
|
(cd "$REPO/cmd/pad" && gogio -target android -targetsdk 35 -arch amd64 -o "$WORK/pad-raw.apk" .)
|
||||||
|
|
||||||
|
echo "=== apktool decode ==="
|
||||||
|
java -jar "$APKTOOL" d "$WORK/pad-raw.apk" -o "$WORK/decoded" -f >/dev/null
|
||||||
|
|
||||||
|
echo "=== inject MANAGE_EXTERNAL_STORAGE ==="
|
||||||
|
grep -q "MANAGE_EXTERNAL_STORAGE" "$WORK/decoded/AndroidManifest.xml" || \
|
||||||
|
sed -i '0,/<application/s#<application#<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>\n <application#' \
|
||||||
|
"$WORK/decoded/AndroidManifest.xml"
|
||||||
|
grep -q "MANAGE_EXTERNAL_STORAGE" "$WORK/decoded/AndroidManifest.xml" || die "permission injection failed"
|
||||||
|
|
||||||
|
echo "=== apktool rebuild ==="
|
||||||
|
java -jar "$APKTOOL" b "$WORK/decoded" -o "$WORK/pad-unsigned.apk" >/dev/null
|
||||||
|
|
||||||
|
echo "=== sign ==="
|
||||||
|
apksigner sign \
|
||||||
|
--ks "$HOME/.android/debug.keystore" --ks-pass pass:android \
|
||||||
|
--key-pass pass:android --ks-key-alias androiddebugkey \
|
||||||
|
--out "$REPO/cmd/pad/pad-emu.apk" "$WORK/pad-unsigned.apk"
|
||||||
|
|
||||||
|
if [ "$INSTALL" = "1" ]; then
|
||||||
|
echo "=== install ==="
|
||||||
|
adb get-state >/dev/null 2>&1 || die "no adb device (start the emulator or plug in a phone)"
|
||||||
|
adb install -r "$REPO/cmd/pad/pad-emu.apk"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== DONE: $REPO/cmd/pad/pad-emu.apk ==="
|
||||||
Loading…
Reference in New Issue
Block a user