#!/usr/bin/env bash # Сборка debug-APK клиента gmod-rs под Android (arm64-v8a). # # Без Gradle: SDK/NDK + cargo + aapt2/d8/apksigner. Так проще в окружении без # скачивания Android Gradle Plugin, а результатом всё равно является обычный # подписанный APK. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ANDROID_DIR="$ROOT/android" BUILD="$ANDROID_DIR/build" OUT_DIR="$ROOT/target/android" API=31 ABI="${ANDROID_ABI:-arm64-v8a}" case "$ABI" in arm64-v8a) TARGET=aarch64-linux-android; CLANG_PREFIX=aarch64-linux-android ;; x86_64) TARGET=x86_64-linux-android; CLANG_PREFIX=x86_64-linux-android ;; armeabi-v7a) TARGET=armv7-linux-androideabi; CLANG_PREFIX=armv7a-linux-androideabi ;; x86) TARGET=i686-linux-android; CLANG_PREFIX=i686-linux-android ;; *) echo "неизвестный ABI: $ABI" >&2; exit 1 ;; esac if [[ "$ABI" == "arm64-v8a" ]]; then OUT="$OUT_DIR/gmod-rs-debug.apk" else OUT="$OUT_DIR/gmod-rs-debug-$ABI.apk" fi SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" if [[ -z "$SDK" || ! -d "$SDK" ]]; then for candidate in "$HOME/Android/Sdk" /opt/android-sdk; do if [[ -d "$candidate" ]]; then SDK="$candidate"; break; fi done fi if [[ -z "$SDK" || ! -d "$SDK" ]]; then echo "не найден Android SDK (задайте ANDROID_HOME)" >&2 exit 1 fi NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK:-}}" if [[ -z "$NDK" || ! -d "$NDK" ]]; then NDK="$(find "$SDK/ndk" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort -V | tail -1 || true)" fi if [[ -z "$NDK" || ! -d "$NDK" ]]; then NDK=/opt/android-ndk fi if [[ ! -f "$NDK/build/cmake/android.toolchain.cmake" ]]; then echo "не найден Android NDK (задайте ANDROID_NDK_HOME)" >&2 exit 1 fi BUILD_TOOLS="$(find "$SDK/build-tools" -maxdepth 1 -mindepth 1 -type d | sort -V | tail -1)" PLATFORM_DIR="$(find "$SDK/platforms" -maxdepth 1 -mindepth 1 -type d | sort -V | tail -1)" ANDROID_JAR="$PLATFORM_DIR/android.jar" if [[ ! -f "$ANDROID_JAR" ]]; then echo "не найден android.jar в $PLATFORM_DIR" >&2 exit 1 fi TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin" CC_BIN="$TOOLCHAIN/${CLANG_PREFIX}${API}-clang" if [[ ! -x "$CC_BIN" ]]; then CC_BIN="$TOOLCHAIN/${CLANG_PREFIX}-clang" fi if [[ ! -x "$CC_BIN" ]]; then echo "не найден clang NDK для $TARGET в $TOOLCHAIN" >&2 exit 1 fi # --- нативная часть ------------------------------------------------------- export ANDROID_NDK="$NDK" export CMAKE_TOOLCHAIN_FILE="$NDK/build/cmake/android.toolchain.cmake" export ANDROID_PLATFORM="android-$API" TARGET_U="${TARGET//-/_}" export "CC_${TARGET_U}=$CC_BIN" export "AR_${TARGET_U}=$TOOLCHAIN/llvm-ar" export "RANLIB_${TARGET_U}=$TOOLCHAIN/llvm-ranlib" LINKER_VAR="CARGO_TARGET_$(echo "$TARGET_U" | tr '[:lower:]' '[:upper:]')_LINKER" export "$LINKER_VAR=$CC_BIN" echo "== cargo build ($TARGET / $ABI, release)" (cd "$ROOT" && cargo build --release -p client --lib --target "$TARGET") LIB_DIR="$ROOT/target/$TARGET/release" SDL_SO="$LIB_DIR/libSDL3.so" CLIENT_SO="$LIB_DIR/libclient.so" if [[ ! -f "$SDL_SO" ]]; then # sdl3-sys кладёт собранную SDL3 в каталог сборки, если не смог # скопировать её наверх. SDL_SO="$(find "$ROOT/target/$TARGET/release/build" -path '*/out/lib/libSDL3.so' | head -1)" fi if [[ ! -f "$SDL_SO" || ! -f "$CLIENT_SO" ]]; then echo "не найдены собранные libSDL3.so/libclient.so" >&2 exit 1 fi # --- Java + пакет --------------------------------------------------------- rm -rf "$BUILD" mkdir -p "$BUILD/classes" "$BUILD/dex" "$BUILD/lib/$ABI" "$OUT_DIR" echo "== javac" find "$ANDROID_DIR/app/src/main/java" -name '*.java' > "$BUILD/sources.txt" javac --release 11 -Xlint:-options -cp "$ANDROID_JAR" -d "$BUILD/classes" @"$BUILD/sources.txt" echo "== d8" find "$BUILD/classes" -name '*.class' > "$BUILD/classes.txt" "$BUILD_TOOLS/d8" --release --lib "$ANDROID_JAR" --min-api "$API" \ --output "$BUILD/dex" @"$BUILD/classes.txt" echo "== aapt2 link" "$BUILD_TOOLS/aapt2" link -o "$BUILD/unsigned.apk" \ --manifest "$ANDROID_DIR/AndroidManifest.xml" \ -I "$ANDROID_JAR" cp "$SDL_SO" "$BUILD/lib/$ABI/libSDL3.so" # SDLActivity грузит «main»: имя файла обязано быть libmain.so, а не # libclient.so (внутри — наш cdylib с экспортом SDL_main). cp "$CLIENT_SO" "$BUILD/lib/$ABI/libmain.so" "$TOOLCHAIN/llvm-strip" --strip-debug "$BUILD/lib/$ABI/libSDL3.so" 2>/dev/null || true "$TOOLCHAIN/llvm-strip" --strip-debug "$BUILD/lib/$ABI/libmain.so" 2>/dev/null || true (cd "$BUILD/dex" && zip -q "$BUILD/unsigned.apk" classes.dex) (cd "$BUILD" && zip -q -r "$BUILD/unsigned.apk" "lib") # Встроенные ассеты (необязательно): `fs pack` кладёт сюда garrysmod/. ASSETS_DIR="${GMOD_RS_ANDROID_ASSETS:-$ANDROID_DIR/assets}" if [[ -d "$ASSETS_DIR/gmod" ]]; then echo "== assets: $(du -sh "$ASSETS_DIR/gmod" | cut -f1)" rm -rf "$BUILD/assets" mkdir -p "$BUILD/assets" cp -a "$ASSETS_DIR/gmod" "$BUILD/assets/gmod" (cd "$BUILD" && zip -q -r "$BUILD/unsigned.apk" "assets") fi echo "== zipalign" "$BUILD_TOOLS/zipalign" -f 4 "$BUILD/unsigned.apk" "$BUILD/aligned.apk" KEYSTORE="$ANDROID_DIR/debug.keystore" if [[ ! -f "$KEYSTORE" ]]; then echo "== debug keystore" keytool -genkeypair -v -keystore "$KEYSTORE" \ -storepass android -keypass android \ -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \ -dname "CN=Android Debug,O=Android,C=US" >/dev/null fi echo "== apksigner" "$BUILD_TOOLS/apksigner" sign \ --ks "$KEYSTORE" --ks-pass pass:android --key-pass pass:android \ --ks-key-alias androiddebugkey \ --out "$OUT" "$BUILD/aligned.apk" "$BUILD_TOOLS/apksigner" verify --verbose "$OUT" echo echo "APK: $OUT" echo "install: adb install -r \"$OUT\""