diff --git a/.github/workflows/fdroid.yml b/.github/workflows/fdroid.yml index 2dbc3a936..77eb145bb 100644 --- a/.github/workflows/fdroid.yml +++ b/.github/workflows/fdroid.yml @@ -11,8 +11,8 @@ on: jobs: # https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.carriez.flutter_hbb.yml - # Finds latest release and transforms F-Droid vereion code from version as follows: - # X.Y.Z-A => X * 1e9 + Y * 1e6 + Z * 1e3 + A + # Finds latest release and transforms F-Droid version code from version as follows: + # X.Y.Z-A => X * 1e6 + Y * 1e4 + Z * 1e2 + A update-fdroid-version-file: name: Publish RustDesk version file for F-Droid updater runs-on: ubuntu-latest @@ -20,7 +20,7 @@ jobs: - name: Generate RustDesk version file run: | UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')" - UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000000 * $MAJOR + 1000000 * $MINOR + 1000 * $PATCH + $REV ))"; done)" + UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)" echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt shell: bash diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b9c608dba..e7a438997 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -10,6 +10,13 @@ on: type: string default: "nightly" +# NOTE: F-Droid builder script 'flutter/build_fdroid.sh' reads environment +# variables from this workflow! +# +# It does NOT read build steps, however, so please fix 'flutter/build_fdroid.sh +# whenever you add changes to Android CI build action ('build-rustdesk-android') +# in this file! + env: WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh new file mode 100755 index 000000000..6f04b8001 --- /dev/null +++ b/flutter/build_fdroid.sh @@ -0,0 +1,550 @@ +#!/bin/bash + +set -x + +# +# Script to build F-Droid release of RustDesk +# +# Copyright (C) 2024, The RustDesk Authors +# 2024, Vasyl Gello +# + +# The script is invoked by F-Droid builder system ste-by-step. +# +# It accepts the following arguments: +# +# - versionName from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt +# - versionCode from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt +# - Android architecture to build APK for: armeabi-v7a arm64-v8av x86 x86_64 +# - The build step to execute: +# +# + sudo-deps: as root, install needed Debian packages into builder VM +# + prebuild: patch sources and do other stuff before the build +# + build: perform actual build of APK file +# + +# Parse command-line arguments + +VERNAME="${1}" +VERCODE="${2}" +ANDROID_ABI="${3}" +BUILDSTEP="${4}" + +if [ -z "${VERNAME}" ] || [ -z "${VERCODE}" ] || [ -z "${ANDROID_ABI}" ] || + [ -z "${BUILDSTEP}" ]; then + echo "ERROR: Command-line arguments are all required to be non-empty!" >&2 + exit 1 +fi + +# Set various architecture-specific identifiers + +case "${ANDROID_ABI}" in +arm64-v8a) + FLUTTER_TARGET=android-arm64 + NDK_TARGET=aarch64-linux-android + RUST_TARGET=aarch64-linux-android +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' + ;; +armeabi-v7a) + FLUTTER_TARGET=android-arm + NDK_TARGET=arm-linux-androideabi + RUST_TARGET=armv7-linux-androideabi +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' + ;; +x86_64) + FLUTTER_TARGET=android-x64 + NDK_TARGET=x86_64-linux-android + RUST_TARGET=x86_64-linux-android + RUSTDESK_FEATURES='flutter' + ;; +x86) + FLUTTER_TARGET=android-x86 + NDK_TARGET=i686-linux-android + RUST_TARGET=i686-linux-android + RUSTDESK_FEATURES='flutter' + ;; +*) + echo "ERROR: Unknown Android ABI '${ANDROID_ABI}'!" >&2 + exit 1 + ;; +esac + +# Export necessary variables + +export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools" + +export VCPKG_ROOT="${HOME}/vcpkg" + +# Now act depending on build step + +case "${BUILDSTEP}" in +sudo-deps) + # sudo-deps: as root, install needed Debian packages into builder VM + + set -e + + export DEBIAN_FRONTEND=noninteractive + + apt-get update + + apt-get -yq full-upgrade + + apt-get -yq install \ + clang \ + cmake \ + curl \ + fakeroot \ + gcc \ + g++ \ + git \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk-3-dev \ + make \ + nasm \ + ninja-build \ + openjdk-17-jdk-headless \ + pkg-config \ + tar \ + unzip \ + wget \ + xz-utils \ + yasm \ + yq \ + zip + + update-alternatives --auto java + + ;; +prebuild) + # prebuild: patch sources and do other stuff before the build + + # + # Extract required versions for NDK, Rust, Flutter from + # '.github/workflows/flutter-build.yml' + # + + CARGO_NDK_VERSION="$(yq -r \ + .env.CARGO_NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + FLUTTER_VERSION="$(yq -r \ + .env.ANDROID_FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then + FLUTTER_VERSION="$(yq -r \ + .env.FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + fi + + FLUTTER_RUST_BRIDGE_VERSION="$(yq -r \ + .env.FLUTTER_RUST_BRIDGE_VERSION \ + .github/workflows/flutter-build.yml)" + + NDK_VERSION="$(yq -r \ + .env.NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + RUST_VERSION="$(yq -r \ + .env.RUST_VERSION \ + .github/workflows/flutter-build.yml)" + + VCPKG_COMMIT_ID="$(yq -r \ + .env.VCPKG_COMMIT_ID \ + .github/workflows/flutter-build.yml)" + + if [ -z "${CARGO_NDK_VERSION}" ] || [ -z "${FLUTTER_VERSION}" ] || + [ -z "${FLUTTER_RUST_BRIDGE_VERSION}" ] || + [ -z "${NDK_VERSION}" ] || [ -z "${RUST_VERSION}" ] || + [ -z "${VCPKG_COMMIT_ID}" ]; then + echo "ERROR: Can not identify all required versions!" >&2 + exit 1 + fi + + export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" + export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + + # + # Install the components + # + + set -e + + # Install Android NDK + + if [ ! -d "${ANDROID_NDK_ROOT}" ]; then + pushd "${HOME}" + + wget \ + -q \ + "https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux.zip" + + unzip "android-ndk-${NDK_VERSION}-linux.zip" 1>/dev/null + + rm "android-ndk-${NDK_VERSION}-linux.zip" + + popd # ${HOME} + fi + + # Install Flutter + + if [ ! -f "${HOME}/flutter/bin/flutter" ]; then + pushd "${HOME}" + + wget \ + -q \ + "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + tar xf "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + rm "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + + popd # ${HOME} + + git config --global --add safe.directory "${HOME}/flutter" + + flutter --disable-telemetry + dart --disable-telemetry + fi + + # Install Rust + + if [ ! -f "${HOME}/rustup/rustup-init.sh" ]; then + mkdir "${HOME}/rustup" + pushd "${HOME}/rustup" + wget -O rustup-init.sh 'https://sh.rustup.rs' + popd + fi + + pushd "${HOME}/rustup" + bash rustup-init.sh -y \ + --target "${RUST_TARGET}" \ + --default-toolchain "${RUST_VERSION}" + popd + + if ! command -v cargo 1>/dev/null 2>&1; then + . "${HOME}/.cargo/env" + fi + + # Install cargo-ndk + + cargo install \ + cargo-ndk \ + --version "${CARGO_NDK_VERSION}" + + # Install rust bridge generator + + cargo install cargo-expand + cargo install flutter_rust_bridge_codegen \ + --version "${FLUTTER_RUST_BRIDGE_VERSION}" \ + --features "uuid" + + # Populate native vcpkg dependencies + + if [ ! -d "${VCPKG_ROOT}" ]; then + pushd "${HOME}" + + git clone \ + https://github.com/Microsoft/vcpkg.git + + pushd vcpkg + + git reset --hard "${VCPKG_COMMIT_ID}" + + sh bootstrap-vcpkg.sh -disableMetrics + + popd # vcpkg + + popd # ${HOME} + fi + + # Install depot-tools for x86 + + if [ "${ANDROID_ABI}" = "x86" ]; then + if [ ! -d "${HOME}/depot_tools" ]; then + pushd "${HOME}" + + git clone \ + --depth 1 \ + https://chromium.googlesource.com/chromium/tools/depot_tools.git + + popd # ${HOME} + fi + fi + + # Patch the RustDesk sources + + git apply res/fdroid/patches/*.patch + + sed \ + -i \ + -e '/gms/d' \ + flutter/android/build.gradle \ + flutter/android/app/build.gradle + + sed \ + -i \ + -e '/firebase_analytics/d' \ + flutter/pubspec.yaml + + sed \ + -i \ + -e '/ firebase/,/ version/d' \ + flutter/pubspec.lock + + sed \ + -i \ + -e '/firebase/Id' \ + flutter/lib/main.dart + + if [ "${FLUTTER_VERSION}" = "3.13.9" ]; then + # Fix for android 3.13.9 + # https://github.com/rustdesk/rustdesk/blob/285e974d1a52c891d5fcc28e963d724e085558bc/.github/workflows/flutter-build.yml#L862 + + sed \ + -i \ + -e 's/uni_links_desktop/#uni_links_desktop/g' \ + flutter/pubspec.yaml + + set -- + + while read -r _1; do + set -- "$@" "${_1}" + done 0<<.a +$(find flutter/lib/ -type f -name "*dart*") +.a + + sed \ + -i \ + -e 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' \ + "$@" + + set -- + fi + + sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" flutter-sdk/.gclient + + ;; +build) + # build: perform actual build of APK file + + set -e + + # + # Extract required versions for NDK, Rust, Flutter from + # '.github/workflows/flutter-build.yml' + # + + FLUTTER_VERSION="$(yq -r \ + .env.ANDROID_FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then + FLUTTER_VERSION="$(yq -r \ + .env.FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + fi + + NDK_VERSION="$(yq -r \ + .env.NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" + export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + + if ! command -v cargo 1>/dev/null 2>&1; then + . "${HOME}/.cargo/env" + fi + + # Download Flutter dependencies + + pushd flutter + + flutter packages pub get + + popd # flutter + + # Generate FFI bindings + + flutter_rust_bridge_codegen \ + --rust-input ./src/flutter_ffi.rs \ + --dart-output ./flutter/lib/generated_bridge.dart + + # Build host android deps + + bash flutter/build_android_deps.sh "${ANDROID_ABI}" + + # Build rustdesk lib + + cargo ndk \ + --platform 21 \ + --target "${RUST_TARGET}" \ + --bindgen \ + build \ + --release \ + --features "${RUSTDESK_FEATURES}" + + mkdir -p "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}" + + cp "target/${RUST_TARGET}/release/liblibrustdesk.so" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/librustdesk.so" + + cp "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/${NDK_TARGET}/libc++_shared.so" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/" + + "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}"/* + + # Build flutter-jit-release for x86 + + if [ "${ANDROID_ABI}" = "x86" ]; then + pushd flutter-sdk + + echo "## Sync flutter engine sources" + echo "### We need fakeroot because chromium base image is unpacked with weird uid/gid ownership" + + sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" .gclient + + export FAKEROOTDONTTRYCHOWN=1 + + fakeroot gclient sync + + unset FAKEROOTDONTTRYCHOWN + + pushd src + + echo "## Patch away Google Play dependencies" + + rm \ + flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java \ + flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java flutter/shell/platform/android/io/flutter/embedding/android/FlutterPlayStoreSplitApplication.java + + sed \ + -i \ + -e '/PlayStore/d' \ + flutter/tools/android_lint/project.xml \ + flutter/shell/platform/android/BUILD.gn + + sed \ + -i \ + -e '/com.google.android.play/d' \ + flutter/tools/androidx/files.json + + echo "## Configure android engine build" + + flutter/tools/gn \ + --android --android-cpu x86 --runtime-mode=jit_release \ + --no-goma --no-enable-unittests + + echo "## Perform android engine build" + + ninja -C out/android_jit_release_x86 1>/dev/null + + echo "## Configure host engine build" + + flutter/tools/gn \ + --android-cpu x86 --runtime-mode=jit_release \ + --no-goma --no-enable-unittests + + echo "## Perform android engine build" + + ninja -C out/host_jit_release_x86 1>/dev/null + + echo "## Rename host engine" + + mv out/host_jit_release_x86 out/host_jit_release + + echo "## Mimic jit_release engine to debug to use with flutter build apk" + + pushd out/android_jit_release_x86 + + sed \ + -e 's/jit_release/debug/' \ + flutter_embedding_jit_release.maven-metadata.xml \ + 1>flutter_embedding_debug.maven-metadata.xml + + sed \ + -e 's/jit_release/debug/' \ + flutter_embedding_jit_release.pom \ + 1>flutter_embedding_debug.pom + + sed \ + -e 's/jit_release/debug/' \ + x86_jit_release.maven-metadata.xml \ + 1>x86_debug.maven-metadata.xml + + sed \ + -e 's/jit_release/debug/' \ + x86_jit_release.pom \ + 1>x86_debug.pom + + cp -a \ + flutter_embedding_jit_release-sources.jar \ + flutter_embedding_debug-sources.jar + + cp -a \ + flutter_embedding_jit_release.jar \ + flutter_embedding_debug.jar + + cp -a \ + x86_jit_release.jar \ + x86_debug.jar + + popd # out/android_jit_release_x86 + + popd # src + + popd # flutter-sdk + + echo "# Clean up intermediate engine files and show free space" + + rm -rf \ + flutter-sdk/src/out/android_jit_release_x86/obj \ + flutter-sdk/src/out/host_jit_release/obj + + mv flutter-sdk/src/out flutter-out + + rm -rf flutter-sdk + + mkdir -p flutter-sdk/src/ + + mv flutter-out flutter-sdk/src/out + fi + + # Build the apk + + pushd flutter + + if [ "${ANDROID_ABI}" = "x86" ]; then + flutter build apk \ + --local-engine-src-path="$(readlink -mf "../flutter-sdk/src")" \ + --local-engine=android_jit_release_x86 \ + --debug \ + --build-number="${VERCODE}" \ + --build-name="${VERNAME}" \ + --target-platform "${FLUTTER_TARGET}" + else + flutter build apk \ + --release \ + --build-number="${VERCODE}" \ + --build-name="${VERNAME}" \ + --target-platform "${FLUTTER_TARGET}" + fi + + popd # flutter + + rm -rf flutter-sdk + + # Special step for fdroiddata CI builds to remove .gitconfig + + rm -f /home/vagrant/.gitconfig + + ;; +*) + echo "ERROR: Unknown build step '${BUILDSTEP}'!" >&2 + exit 1 + ;; +esac + +# Report success + +echo "All done!" diff --git a/res/fdroid/patches/0000-flutter-android-x86.patch b/res/fdroid/patches/0000-flutter-android-x86.patch new file mode 100644 index 000000000..1bb4436e8 --- /dev/null +++ b/res/fdroid/patches/0000-flutter-android-x86.patch @@ -0,0 +1,16 @@ +diff --git a/flutter-sdk/.gclient b/flutter-sdk/.gclient +new file mode 100644 +index 0000000..fd12886 +--- /dev/null ++++ b/flutter-sdk/.gclient +@@ -0,0 +1,10 @@ ++solutions = [ ++ { ++ "managed": False, ++ "name": "src/flutter", ++ "url": "https://github.com/flutter/engine.git@FLUTTER_VERSION_PLACEHOLDER", ++ "custom_deps": {}, ++ "deps_file": "DEPS", ++ "safesync_url": "", ++ }, ++] diff --git a/res/fdroid/patches/0001-x86-no-debuggable.patch b/res/fdroid/patches/0001-x86-no-debuggable.patch new file mode 100644 index 000000000..c160d60d2 --- /dev/null +++ b/res/fdroid/patches/0001-x86-no-debuggable.patch @@ -0,0 +1,24 @@ +diff --git a/flutter/android/app/build.gradle b/flutter/android/app/build.gradle +index f4dc69e..6b835fd 100644 +--- a/flutter/android/app/build.gradle ++++ b/flutter/android/app/build.gradle +@@ -67,6 +67,19 @@ android { + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules' + } + } ++ ++ applicationVariants.all { variant -> ++ variant.outputs.each { output -> ++ output.processManifest.doLast { task -> ++ def outputDir = multiApkManifestOutputDirectory.asFile.get() ++ File manifestOutFile = new File(outputDir, "AndroidManifest.xml") ++ if (manifestOutFile.exists()) { ++ def newFileContents = manifestOutFile.getText('UTF-8').replace("android:debuggable=\"true\"", "") ++ manifestOutFile.write(newFileContents, 'UTF-8') ++ } ++ } ++ } ++ } + } + + flutter {