mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 19:41:10 +00:00
112 lines
5.3 KiB
Bash
Executable File
112 lines
5.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# Builds this checkout as a Lab build of the installed DroidDeck and restarts the app on it.
|
|
# Usage: dd-lab-build [--no-restart]
|
|
set -euo pipefail
|
|
pkg=com.droiddeck.launcher
|
|
serial=${ADB_SERIAL:-127.0.0.1:5555}
|
|
restart=1
|
|
[ "${1:-}" = --no-restart ] && restart=0
|
|
started=$(date +%s)
|
|
progress_file=$HOME/.cache/dd-lab-build.json
|
|
mkdir -p "$HOME/.cache"
|
|
# The Agent tab and the agent read this file to show how far the build has got.
|
|
progress() { # stage [done total task]
|
|
printf '{"stage":"%s","done":%d,"total":%d,"task":"%s","elapsed":%d}\n' \
|
|
"$1" "${2:-0}" "${3:-0}" "${4:-}" $(( $(date +%s) - started )) > "$progress_file.part"
|
|
mv "$progress_file.part" "$progress_file"
|
|
}
|
|
fail() { progress failed; echo "dd-lab-build: $*" >&2; exit 1; }
|
|
device() { adb -s "$serial" shell "$@" | tr -d '\r'; }
|
|
|
|
progress checking
|
|
repo=$(git rev-parse --show-toplevel) || fail "run it inside the DroidDeck checkout"
|
|
cd "$repo"
|
|
adb connect "$serial" >/dev/null 2>&1 || true
|
|
git diff --quiet && git diff --cached --quiet || fail "commit your changes first"
|
|
|
|
state=$(python3 tools/droiddeckctl state 2>/dev/null | sed -n '/^{/,$p')
|
|
# A Lab build reports its own label, so the APK's commit comes from apkCommit when the app has it.
|
|
label=$(python3 -c 'import json,sys; s=json.load(sys.stdin); print(s.get("apkCommit") or s["build"].rsplit(" at ", 1)[-1])' <<<"$state")
|
|
base=$(git rev-parse --verify --quiet "$label^{commit}") || fail "the installed build ($label) is not in this checkout; fetch it first"
|
|
git merge-base --is-ancestor "$base" HEAD || fail "HEAD does not build on the installed commit ${base:0:9}; branch from it"
|
|
blocked=$(git diff --name-only "$base" HEAD | grep -E 'AndroidManifest\.xml$|/cpp/|/jniLibs/|\.rs$|(^|/)build\.gradle$|/App\.kt$|/lab/LabOverlay\.kt$' || true)
|
|
[ -z "$blocked" ] || fail "these need a new APK, not a Lab build: $(echo $blocked)"
|
|
apk=$(device pm path $pkg | sed -n 's/^package://p' | grep 'base\.apk$' | head -1)
|
|
[ -n "$apk" ] || fail "DroidDeck is not installed"
|
|
|
|
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-arm64 ANDROID_HOME=$HOME/android-sdk
|
|
printf 'sdk.dir=%s\n' "$ANDROID_HOME" > local.properties
|
|
if [ ! -d "$ANDROID_HOME/platforms/android-34" ]; then
|
|
progress sdk
|
|
yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null
|
|
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "platforms;android-34" "build-tools;35.0.0" >/dev/null
|
|
fi
|
|
|
|
# A full Kotlin compile of the app needs about 3 GB of heap; smaller devices get what they can spare.
|
|
total_mb=$(awk '/^MemTotal:/ {print int($2 / 1024)}' /proc/meminfo)
|
|
heap=$(( total_mb * 2 / 5 )); [ $heap -gt 3072 ] && heap=3072; [ $heap -lt 1536 ] && heap=1536
|
|
heap=${DD_LAB_HEAP_MB:-$heap}
|
|
|
|
# Google publishes aapt2 for x86-64 Linux only; setup wraps it in qemu-user at ~/aapt2/aapt2.
|
|
gradle=(./gradlew :app:processReleaseResources :app:mergeDexRelease --max-workers=2 -PskipRust=true -x buildVkBridgeServer
|
|
-Pandroid.aapt2FromMavenOverride=$HOME/aapt2/aapt2 -Pkotlin.compiler.execution.strategy=in-process
|
|
-Dorg.gradle.jvmargs=-Xmx${heap}m --console=plain)
|
|
progress planning
|
|
total=$("${gradle[@]}" --dry-run -q 2>/dev/null | grep -c '^:' || true)
|
|
current=$(mktemp)
|
|
echo "0 starting" > "$current"
|
|
(
|
|
while sleep 60; do
|
|
read -r n task < "$current"
|
|
echo "[build $n/$total] still on $task, $(( ($(date +%s) - started) / 60 ))m in"
|
|
progress building "$n" "$total" "$task"
|
|
done
|
|
) &
|
|
heartbeat=$!
|
|
trap 'kill $heartbeat 2>/dev/null || true' EXIT
|
|
set +o pipefail
|
|
"${gradle[@]}" 2>&1 | {
|
|
n=0
|
|
while IFS= read -r line; do
|
|
printf '%s\n' "$line"
|
|
case $line in
|
|
"> Task "*)
|
|
n=$((n + 1)); task=${line#> Task }; task=${task%% *}
|
|
echo "$n $task" > "$current"
|
|
echo "[build $n/$total] $task"
|
|
progress building "$n" "$total" "$task" ;;
|
|
esac
|
|
done
|
|
}
|
|
status=${PIPESTATUS[0]}
|
|
set -o pipefail
|
|
kill $heartbeat 2>/dev/null || true
|
|
[ "$status" = 0 ] || fail "Gradle failed (exit $status); the reason is above"
|
|
|
|
progress packaging
|
|
linked=$(ls app/build/intermediates/linked_resources_binary_format/release/processReleaseResources/*.ap_ | head -1)
|
|
work=$(mktemp -d)
|
|
cp "$linked" "$work/overlay.apk"
|
|
(cd app/build/intermediates/dex/release/mergeDexRelease && zip -q "$work/overlay.apk" classes*.dex)
|
|
name="$(git branch --show-current) @ $(git rev-parse --short HEAD)"
|
|
python3 -c 'import json,sys; print(json.dumps({"label": sys.argv[1], "baseCommit": sys.argv[2], "baseApk": sys.argv[3]}))' \
|
|
"$name" "$base" "$apk" > "$work/overlay.json"
|
|
|
|
lab=/data/user/0/$pkg/files/lab
|
|
mkdir -p "$lab"
|
|
rm -rf "$lab/next.part" "$lab/next"
|
|
mv "$work" "$lab/next.part"
|
|
mv "$lab/next.part" "$lab/next"
|
|
progress ready
|
|
echo "Lab build ready: $name ($(du -h "$lab/next/overlay.apk" | cut -f1)) in $(( ($(date +%s) - started) / 60 ))m"
|
|
[ $restart = 1 ] || exit 0
|
|
|
|
# Classes in an app image load before the Lab build can, so the app is recompiled without one.
|
|
device cmd package compile -m verify -f $pkg >/dev/null
|
|
progress restarting
|
|
echo "Restarting DroidDeck on the Lab build"
|
|
# The app's own agent ends with the app, so it is told to carry on once the app is back.
|
|
if [ -n "${DD_AGENT:-}" ]; then mkdir -p "/data/user/0/$pkg/files/agent" && : > "/data/user/0/$pkg/files/agent/resume"; fi
|
|
device "nohup sh -c 'sleep 3; monkey -p $pkg -c android.intent.category.LAUNCHER 1' >/dev/null 2>&1 &"
|
|
for pid in $(device pidof $pkg); do kill "$pid" 2>/dev/null || true; done
|