#!/usr/bin/env bash # Собирает преднастроенный RustDesk APK (arm64-v8a) под self-hosted сервер. Linux/VM 118. # Тулчейн — provision-android.sh (пишет ~/dev/rd-android.env). Логика зеркалит build-windows.ps1. # # Конфиг сервера: env RUSTDESK_SERVER/RUSTDESK_PUB_KEY (CI secrets) или config/server.env. # Клон rustdesk: RUSTDESK_SRC_DIR (по умолч. ~/rd-build/rustdesk). set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" # --- пины --- declare -A P while IFS='=' read -r k v; do [[ "$k" =~ ^[A-Z] ]] && P[$k]="${v%$'\r'}"; done < "$ROOT/upstream.txt" TAG="${P[RUSTDESK_TAG]}"; REPO="${P[RUSTDESK_REPO]}"; RUST_V="${P[RUST_VERSION]}" echo "== RustDesk android build == tag=$TAG (arm64-v8a)" # --- тулчейн-env --- [ -f "$HOME/dev/rd-android.env" ] || { echo "нет ~/dev/rd-android.env — прогони provision-android.sh" >&2; exit 1; } # shellcheck disable=SC1090 source "$HOME/dev/rd-android.env" export ANDROID_NDK="$ANDROID_NDK_HOME" # build_android_deps.sh использует $ANDROID_NDK command -v cargo >/dev/null || { echo "нет cargo в PATH" >&2; exit 1; } command -v flutter >/dev/null || { echo "нет flutter в PATH" >&2; exit 1; } # --- конфиг сервера --- if [ -z "${RUSTDESK_SERVER:-}" ] || [ -z "${RUSTDESK_PUB_KEY:-}" ]; then if [ -f "$ROOT/config/server.env" ]; then set -a; . "$ROOT/config/server.env"; set +a; fi fi : "${RUSTDESK_SERVER:?не задан RUSTDESK_SERVER}"; : "${RUSTDESK_PUB_KEY:?не задан RUSTDESK_PUB_KEY}" # --- клон/сброс до пина --- SRC="${RUSTDESK_SRC_DIR:-$HOME/rd-build/rustdesk}" if [ ! -d "$SRC/.git" ]; then mkdir -p "$(dirname "$SRC")" git clone --branch "$TAG" --depth 1 --recurse-submodules --shallow-submodules "$REPO" "$SRC" else git -C "$SRC" checkout -- .; git -C "$SRC" submodule foreach --recursive 'git checkout -- .' >/dev/null fi ( cd "$SRC" && rustup override set "$RUST_V" >/dev/null && rustup component add rustfmt >/dev/null 2>&1 || true ) # --- preconfigure: вшить сервер+ключ --- RUSTDESK_SERVER="$RUSTDESK_SERVER" RUSTDESK_PUB_KEY="$RUSTDESK_PUB_KEY" bash "$ROOT/scripts/preconfigure.sh" "$SRC" cd "$SRC" # --- vcpkg-зависимости под android arm64 (ffmpeg/aom/… из исходников — ДОЛГО) --- echo "[deps] build_android_deps.sh arm64-v8a ..." ./flutter/build_android_deps.sh arm64-v8a # --- flutter_rust_bridge codegen --- echo "[codegen] flutter_rust_bridge ..." ( cd flutter && flutter pub get >/dev/null ) LLVM_ROOT="$(dirname "$(dirname "$(find /usr/lib -name 'libclang.so*' 2>/dev/null | head -1)")")" RUSTC_BOOTSTRAP=1 flutter_rust_bridge_codegen \ --rust-input ./src/flutter_ffi.rs \ --dart-output ./flutter/lib/generated_bridge.dart \ --c-output ./flutter/macos/Runner/bridge_generated.h \ ${LLVM_ROOT:+--llvm-path "$LLVM_ROOT"} [ -f src/bridge_generated.rs ] && [ -f flutter/lib/generated_bridge.dart ] && [ -f flutter/lib/generated_bridge.freezed.dart ] \ || { echo "codegen неполный: нет dart-моста" >&2; exit 1; } # --- rust .so под arm64 через cargo-ndk --- # По умолчанию БЕЗ hwcodec: его build.rs гоняет host-bindgen android-clang'ом и падает на # glibc-заголовках (bits/libc-header-start.h). Софт-кодеки (vpx/aom) работают. hwcodec-android # — отдельная доработка (RUSTDESK_ANDROID_FEATURES="flutter,hwcodec" когда починим bindgen). FEATURES="${RUSTDESK_ANDROID_FEATURES:-flutter}" # bindgen (magnum-opus/hwcodec/scrap) при кросс-сборке резолвит #include в HOST # /usr/include (glibc) -> не находит bits/libc-header-start.h. Указываем NDK-sysroot и target # через target-specific env (bindgen читает подчёркнутую форму). NDK_SYSROOT="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/sysroot" export BINDGEN_EXTRA_CLANG_ARGS_aarch64_linux_android="--sysroot=$NDK_SYSROOT --target=aarch64-linux-android21" echo "[rust] cargo ndk build --features $FEATURES (bindgen sysroot=$NDK_SYSROOT) ..." cargo ndk --platform 21 --target aarch64-linux-android build --locked --release --features "$FEATURES" # --- jniLibs: librustdesk.so + libc++_shared.so --- JNI=flutter/android/app/src/main/jniLibs/arm64-v8a mkdir -p "$JNI" cp ./target/aarch64-linux-android/release/liblibrustdesk.so "$JNI/librustdesk.so" cp "$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so" "$JNI/" # --- подпись: без key.properties (keystore Глеба) release подписываем DEBUG-ключом --- # (APK ставится для теста; преднастройка от подписи не зависит — она в librustdesk.so). # Дашь свой keystore -> положи flutter/android/key.properties, тогда подпишется им (для раздачи/F-Droid). if ! grep -q 'keystorePropertiesFile.exists() ? signingConfigs.release' flutter/android/app/build.gradle; then sed -i 's|signingConfig signingConfigs.release|signingConfig keystorePropertiesFile.exists() ? signingConfigs.release : signingConfigs.debug|' flutter/android/app/build.gradle fi # --- APK --- # Gradle 7.6.4 не совместим с Java 21 (дефолт VM) -> используем Java 17 (как RustDesk CI). export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export PATH="$JAVA_HOME/bin:$PATH" echo "[apk] flutter build apk (JAVA_HOME=$JAVA_HOME) ..." ( cd flutter && flutter build apk --release --target-platform android-arm64 --split-per-abi ) # --- вынести артефакт --- DIST="$ROOT/dist"; mkdir -p "$DIST" APK="flutter/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" OUT="$DIST/rustdesk-$TAG-arm64.apk" cp "$SRC/$APK" "$OUT" echo "ГОТОВО: $OUT ($(du -h "$OUT" | cut -f1))"