Files
chrisandClaude Opus 5.5 c7bd8f0ad3 Faster Android builds: ccache, Gradle build cache, 64-bit only, no release lint
Most of a build was compiling the same C++ twice per APK with nothing reused between builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
2026-10-15 15:21:35 -04:00

73 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build both release APKs into an output folder:
# musicshare-<version>.apk the normal app (with Google's barcode scanner)
# musicshare-<version>-foss.apk no closed-source code (MUSICSHARE_FOSS=1), as F-Droid builds it
#
# apps/mobile/scripts/build-android.sh [out-dir] (default: apps/mobile/dist)
#
# Both are signed with the upload key when it's configured (see plugins/withReleaseSigning.js;
# in CI it comes from ORG_GRADLE_PROJECT_MUSICSHARE_UPLOAD_* variables). Set REQUIRE_SIGNED=1 to
# fail rather than produce unsigned APKs. ABIS picks the CPU types (default: 64-bit phones, which is
# nearly every phone from the last several years; ABIS=armeabi-v7a,arm64-v8a adds old 32-bit ones).
# Regenerates apps/mobile/android each time (prebuild --clean); Gradle's build cache and ccache (when
# installed) make the rebuild cheap anyway.
set -euo pipefail
cd "$(dirname "$0")/.."
OUT=$(realpath -m "${1:-dist}")
VERSION=$(node -p "require('./app.json').expo.version")
ABIS=${ABIS:-arm64-v8a}
BUILD_TOOLS=$(ls -d "$ANDROID_HOME"/build-tools/*/ | sort -V | tail -1)
mkdir -p "$OUT"
say() { printf '\n==> %s\n' "$*"; }
# Most of a build is compiling the C++ in React Native and its libraries, which rarely changes:
# with ccache, CMake (3.17+) reuses earlier results via these launcher variables.
if command -v ccache > /dev/null; then
export CMAKE_C_COMPILER_LAUNCHER=ccache CMAKE_CXX_COMPILER_LAUNCHER=ccache
export CCACHE_BASEDIR=${CCACHE_BASEDIR:-$PWD} CCACHE_COMPILERCHECK=content CCACHE_MAXSIZE=${CCACHE_MAXSIZE:-6G}
ccache --zero-stats > /dev/null
fi
build() {
local suffix=$1 foss=$2
say "Building musicshare-$VERSION$suffix.apk"
MUSICSHARE_FOSS=$foss CI=1 npx expo prebuild --platform android --clean --no-install
# --build-cache reuses compiled Kotlin/Java, dex and resources from earlier builds (kept in ~/.gradle).
# Release lint is skipped: it takes a while and TypeScript, ESLint and the tests cover what matters.
(cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures="$ABIS" --build-cache \
-x lintVitalAnalyzeRelease -x lintVitalReportRelease -x lintVitalRelease --console=plain)
local apk
apk=$(ls android/app/build/outputs/apk/release/*.apk | head -1)
if "$BUILD_TOOLS/apksigner" verify "$apk" > /dev/null 2>&1; then
"$BUILD_TOOLS/apksigner" verify --print-certs "$apk" | grep 'SHA-256 digest'
elif [ "${REQUIRE_SIGNED:-0}" = 1 ]; then
echo "The APK isn't signed: is the upload key configured?" >&2
exit 1
else
echo "Unsigned (no upload key configured)."
fi
if [ "$foss" = 1 ]; then
# Nothing from Google's closed-source libraries may be inside.
local tmp count
tmp=$(mktemp -d)
unzip -q "$apk" 'classes*.dex' -d "$tmp"
count=$(for f in "$tmp"/classes*.dex; do "$BUILD_TOOLS/dexdump" "$f" 2>/dev/null | grep 'Class descriptor'; done | grep -cE 'Lcom/google/(mlkit|android/gms)/' || true)
rm -rf "$tmp"
if [ "$count" != 0 ]; then
echo "The FOSS build contains $count ML Kit / Play Services classes." >&2
exit 1
fi
echo "No ML Kit / Play Services classes."
fi
cp "$apk" "$OUT/musicshare-$VERSION$suffix.apk"
}
build "" 0
build "-foss" 1
(cd "$OUT" && sha256sum musicshare-"$VERSION"*.apk > SHA256SUMS && cat SHA256SUMS)
if command -v ccache > /dev/null; then say "ccache"; ccache --show-stats | grep -iE 'hits|misses|cache size' || true; fi