#!/bin/bash # ============================================================ # OpenDance Studio —— Android APK 打包(T-AND-009) # # 文档 §6.3 方式二:命令行 Gradle # # 前置: # JAVA_HOME 指向 JDK 17(AGP 8.7 要求) # ANDROID_HOME / ANDROID_SDK_ROOT 指向 Android SDK # 需要 platforms;android-34 与 build-tools;34.0.0 # # 用法: # bash build/build-apk.sh # 同时产出 debug 与 release # bash build/build-apk.sh debug # bash build/build-apk.sh release # ============================================================ set -euo pipefail PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" cd "$PROJECT_DIR" TARGET="${1:-all}" case "$TARGET" in debug|release|all) ;; *) echo "用法:bash build/build-apk.sh [debug|release|all]" >&2; exit 2 ;; esac log() { printf '\033[1;34m[apk]\033[0m %s\n' "$*"; } die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; } # ---- 环境检查 ---- # 优先使用 JAVA_HOME 指定的 JDK:PATH 上的 java 可能是更新的版本, # AGP 8.7 需要 17~21,检测时必须与 Gradle 实际使用的 JVM 一致。 JAVA_BIN="java" if [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ]; then JAVA_BIN="$JAVA_HOME/bin/java" fi command -v "$JAVA_BIN" >/dev/null 2>&1 || die "未找到 java,请安装 JDK 17 或设置 JAVA_HOME" JAVA_MAJOR="$("$JAVA_BIN" -version 2>&1 | head -1 | sed -E 's/.*version "([0-9]+).*/\1/')" log "Java:$JAVA_BIN (主版本 $JAVA_MAJOR,AGP 8.7 需要 17+)" if [ "$JAVA_MAJOR" -lt 17 ] 2>/dev/null; then die "JDK 版本过低:需要 17 或更高(当前 $JAVA_MAJOR)" fi SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" if [ -z "$SDK" ]; then for candidate in "$HOME/Android/Sdk" /opt/android-sdk /usr/lib/android-sdk; do [ -d "$candidate" ] && SDK="$candidate" && break done fi [ -n "$SDK" ] && [ -d "$SDK" ] || die "未找到 Android SDK,请设置 ANDROID_HOME" log "Android SDK:$SDK" if [ ! -f "$PROJECT_DIR/local.properties" ]; then echo "sdk.dir=$SDK" > "$PROJECT_DIR/local.properties" log "已写入 local.properties" fi [ -f "$PROJECT_DIR/gradlew" ] || die "缺少 gradlew(请先执行 gradle wrapper)" # ---- 签名检查 ---- if [ "$TARGET" != "debug" ]; then if [ -f "$PROJECT_DIR/keystore.properties" ]; then log "使用 keystore.properties 中的发布签名" else log "提示:未找到 keystore.properties,release 将回退为 debug 签名" log " 正式发布请先执行:bash build/gen-keystore.sh" fi fi # ---- 构建 ---- if [ "$TARGET" = "debug" ] || [ "$TARGET" = "all" ]; then log "构建 debug APK…" ./gradlew assembleDebug --console=plain fi if [ "$TARGET" = "release" ] || [ "$TARGET" = "all" ]; then log "构建 release APK…" ./gradlew assembleRelease --console=plain fi # ---- 校验产物 ---- DEBUG_APK="app/build/outputs/apk/debug/app-debug.apk" RELEASE_APK="app/build/outputs/apk/release/app-release.apk" verify_apk() { local apk="$1" local label="$2" [ -f "$apk" ] || { echo " ✗ 未生成 $label:$apk"; return 1; } local size size=$(stat -c%s "$apk") # APK 是 zip,校验魔数 PK local magic magic=$(head -c 2 "$apk" | od -An -tx1 | tr -d ' \n') [ "$magic" = "504b" ] || { echo " ✗ $label 不是合法 APK(魔数 $magic)"; return 1; } # 校验是否已签名:APK v2 签名块附近含 "APK Sig Block 42" local signed="未检出 v2 签名块" if grep -qa "APK Sig Block 42" "$apk"; then signed="已签名(APK Signature Scheme v2)" elif unzip -l "$apk" 2>/dev/null | grep -q "META-INF/.*\.RSA"; then signed="已签名(v1 JAR 签名)" fi echo " ✓ $label:$apk" echo " 大小 $((size / 1024)) KiB | $signed" return 0 } echo "" log "产物校验:" FAILED=0 if [ "$TARGET" = "debug" ] || [ "$TARGET" = "all" ]; then verify_apk "$DEBUG_APK" "Debug APK" || FAILED=1 fi if [ "$TARGET" = "release" ] || [ "$TARGET" = "all" ]; then verify_apk "$RELEASE_APK" "Release APK" || FAILED=1 fi [ "$FAILED" -eq 0 ] || die "有产物未通过校验" echo "" log "安装到已连接设备:" echo " adb install -r $DEBUG_APK" echo " adb install -r $RELEASE_APK" echo "" log "查看签名信息:" echo " \$ANDROID_HOME/build-tools/34.0.0/apksigner verify --print-certs $RELEASE_APK"