mirror of
https://github.com/ApfelTeeSaft/OpenRoyale.git
synced 2026-08-26 19:33:32 +00:00
119 lines
5.6 KiB
Bash
119 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
# build_apk_manual.sh - build a signed debug APK WITHOUT Gradle/AGP.
|
|
#
|
|
# This environment blocks dl.google.com, so the Android Gradle Plugin + AndroidX cannot be fetched.
|
|
# But the app is intentionally dependency-free, and the local SDK ships the full build toolchain
|
|
# (aapt2, d8, zipalign, apksigner + android-34.jar), so we can build a real APK directly:
|
|
# aapt2 compile+link (res -> resources.arsc + R.java) -> javac (app + stubs + R) -> d8 (-> dex)
|
|
# -> assemble (dex + assets + jniLibs) -> zipalign -> apksigner.
|
|
#
|
|
# Usage: build_apk_manual.sh <armeabi-v7a|arm64-v8a|universal> (default armeabi-v7a)
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJ="$(cd "$HERE/../.." && pwd)"
|
|
ABI="${1:-armeabi-v7a}"
|
|
|
|
SDK="${ANDROID_SDK_ROOT:-$PROJ/tools/android-sdk}"
|
|
BT="$SDK/build-tools/34.0.0"
|
|
ANDROID_JAR="$SDK/platforms/android-34/android.jar"
|
|
AAPT2="$BT/aapt2"; D8="$BT/d8"; ZIPALIGN="$BT/zipalign"; APKSIGNER="$BT/apksigner"
|
|
for t in "$AAPT2" "$D8" "$ZIPALIGN" "$APKSIGNER" "$ANDROID_JAR"; do
|
|
[ -e "$t" ] || { echo "ERROR: missing $t (is the SDK at $SDK?)"; exit 2; }
|
|
done
|
|
|
|
case "$ABI" in
|
|
32) ABI=armeabi-v7a;; 64) ABI=arm64-v8a;; both|all) ABI=universal;;
|
|
armeabi-v7a|arm64-v8a|universal) ;;
|
|
*) echo "invalid ABI '$ABI'"; exit 1;;
|
|
esac
|
|
case "$ABI" in universal) ABIS=(armeabi-v7a arm64-v8a);; *) ABIS=("$ABI");; esac
|
|
|
|
OUT="$PROJ/out"; W="$PROJ/.build/manual/$ABI"; rm -rf "$W"; mkdir -p "$W" "$OUT"
|
|
APPID="com.oldcrcell.clashroyale"
|
|
echo "==> Manual build for ABI=$ABI (appId=$APPID)"
|
|
|
|
# 1) Resources: compile + link (inject the package aapt2 needs; AGP would inject it from 'namespace')
|
|
echo "[1/6] aapt2 compile+link resources"
|
|
"$AAPT2" compile --dir "$PROJ/app/src/main/res" -o "$W/res.flata"
|
|
sed -e "s|<manifest |<manifest package=\"$APPID\" |" "$PROJ/app/src/main/AndroidManifest.xml" > "$W/AndroidManifest.xml"
|
|
"$AAPT2" link -o "$W/base.apk" -I "$ANDROID_JAR" \
|
|
--manifest "$W/AndroidManifest.xml" --min-sdk-version 21 --target-sdk-version 34 \
|
|
--version-code 155 --version-name 1.9.2 \
|
|
-R "$W/res.flata" --java "$W/gen" --auto-add-overlay
|
|
|
|
# 2) Java -> classes
|
|
echo "[2/6] javac (app + stubs + generated R)"
|
|
mkdir -p "$W/classes"
|
|
find "$PROJ/app/src/main/java" "$PROJ/app/src/stubs/java" "$W/gen" -name '*.java' > "$W/srcs.txt"
|
|
javac -d "$W/classes" -cp "$ANDROID_JAR" -source 8 -target 8 -encoding UTF-8 \
|
|
-nowarn -Xlint:none @"$W/srcs.txt" 2>"$W/javac.log" || { echo "javac FAILED"; cat "$W/javac.log"; exit 1; }
|
|
|
|
# 3) classes -> dex
|
|
# NOTE: the d8 shipped in build-tools 34.0.0 is a broken 8.2.2-dev build that NPEs on every enum
|
|
# class (verified). The app + stubs contain enums, so we dex with dex2jar's jar2dex (classic `dx`),
|
|
# which handles them. Code is compiled -source/-target 8 (no Java-8+ language features), so `dx` is
|
|
# sufficient. Override with DEXER=d8 to use the SDK d8 once a fixed build-tools is available.
|
|
echo "[3/6] jar2dex -> classes.dex"
|
|
( cd "$W/classes" && jar cf "$W/classes.jar" . )
|
|
DEXER="${DEXER:-dx}"
|
|
if [ "$DEXER" = d8 ]; then
|
|
"$D8" --min-api 21 --lib "$ANDROID_JAR" --output "$W" "$W/classes.jar"
|
|
else
|
|
D2J="$PROJ/../tools/dex-tools-v2.4/d2j-jar2dex.sh"
|
|
[ -x "$D2J" ] || chmod +x "$D2J" 2>/dev/null || true
|
|
bash "$D2J" "$W/classes.jar" -o "$W/classes.dex" >/dev/null 2>"$W/dex.log" \
|
|
|| { echo "jar2dex FAILED"; tail -20 "$W/dex.log"; exit 1; }
|
|
fi
|
|
[ -f "$W/classes.dex" ] || { echo "no classes.dex produced"; exit 1; }
|
|
|
|
# 4) Assemble: start from the linked resources APK, add dex + assets + native libs
|
|
echo "[4/6] assemble APK (dex + assets + jniLibs/$ABI)"
|
|
cp "$W/base.apk" "$W/unsigned.apk"
|
|
( cd "$W" && zip -q "$W/unsigned.apk" classes.dex )
|
|
if [ -d "$PROJ/app/src/main/assets" ]; then
|
|
( cd "$PROJ/app/src/main" && find assets -type f | zip -q -X "$W/unsigned.apk" -@ )
|
|
fi
|
|
# Required libs: libfmod + libg. libcr is OPTIONAL (mod loader; loadLibrary("cr") is caught) -
|
|
# it is packaged only if present for the ABI.
|
|
for abi in "${ABIS[@]}"; do
|
|
d="$PROJ/app/src/main/jniLibs/$abi"
|
|
[ -d "$d" ] || { echo "ERROR: jniLibs/$abi missing (need the .so for $abi)"; exit 3; }
|
|
for req in libfmod.so libg.so; do
|
|
[ -f "$d/$req" ] || { echo "ERROR: $abi/$req missing (required)"; exit 3; }
|
|
done
|
|
for so in "$d"/*.so; do
|
|
[ -e "$so" ] || continue
|
|
if [ "$abi" = arm64-v8a ] && ! file -b "$so" | grep -qi '64-bit'; then
|
|
echo "ERROR: $abi/$(basename "$so") is not ELF64"; exit 3; fi
|
|
done
|
|
[ -f "$d/libcr.so" ] || echo " (note: $abi has no libcr.so - optional, omitted)"
|
|
done
|
|
# copy .so into lib/<abi>/ layout inside the apk
|
|
mkdir -p "$W/lib"
|
|
for abi in "${ABIS[@]}"; do
|
|
mkdir -p "$W/lib/$abi"; cp "$PROJ/app/src/main/jniLibs/$abi"/*.so "$W/lib/$abi/"
|
|
done
|
|
( cd "$W" && find lib -name '*.so' | zip -q -X "$W/unsigned.apk" -@ )
|
|
|
|
# 5) zipalign
|
|
echo "[5/6] zipalign"
|
|
"$ZIPALIGN" -f -p 4 "$W/unsigned.apk" "$W/aligned.apk"
|
|
|
|
# 6) sign (debug keystore, created if absent)
|
|
echo "[6/6] apksigner (debug key)"
|
|
KS="${DEBUG_KEYSTORE:-$HOME/.android/debug.keystore}"
|
|
if [ ! -f "$KS" ]; then
|
|
mkdir -p "$(dirname "$KS")"
|
|
keytool -genkeypair -v -keystore "$KS" -storepass android -keypass android \
|
|
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
|
|
-dname "CN=Android Debug,O=Android,C=US" >/dev/null 2>&1
|
|
fi
|
|
DEST="$OUT/app-${ABI}-debug.apk"
|
|
"$APKSIGNER" sign --ks "$KS" --ks-key-alias androiddebugkey \
|
|
--ks-pass pass:android --key-pass pass:android \
|
|
--v1-signing-enabled true --v2-signing-enabled true --v3-signing-enabled true \
|
|
--out "$DEST" "$W/aligned.apk"
|
|
"$APKSIGNER" verify --print-certs "$DEST" >/dev/null && echo " signature OK"
|
|
echo "==> BUILT $DEST"
|
|
ls -la "$DEST" | awk '{print " size:", $5, "bytes"}'
|