From cb8de0f28abb80566340cf62e60926eecd2a7a06 Mon Sep 17 00:00:00 2001 From: xuwenwei Date: Mon, 8 Jun 2026 21:57:18 +0800 Subject: [PATCH] Add BUILD.md and res/post-build-mac.sh - BUILD.md: complete build instructions for macOS desktop (Xcode 26.5, Flutter 3.44, Dart 3.12, VCPKG, all patches explained) - res/post-build-mac.sh: re-sign + install to /Applications + create dmg, run after every 'flutter build macos --release' Key issues documented in BUILD.md: * Re-signing is mandatory (codesign --force --deep -s -) * Use hdiutil for dmg, not create-dmg (macos-alias is x86_64 broken) * libclang must be arm64 (use Xcode CLT, not homebrew llvm) * EXCLUDED_ARCHS=x86_64 for arm64-only Rust dylib --- BUILD.md | 239 ++++++++++++++++++++++++++++++++++++++++++ res/post-build-mac.sh | 93 ++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 BUILD.md create mode 100755 res/post-build-mac.sh diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..8732635 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,239 @@ +# Building RustDesk for macOS (Jerry's MacBook Air, Apple Silicon) + +This is a fork of [rustdesk/rustdesk](https://github.com/rustdesk/rustdesk) v1.4.7, +patched to build against current macOS tooling (Xcode 26.5, Flutter 3.44, Dart 3.12). + +Last verified build: **2026-06-08**, RustDesk 1.4.7 (arm64), dmg size 27MB. + +--- + +## TL;DR (already-done state) + +If this Mac still has everything set up, you can re-build with: + +```bash +cd ~/rustdesk-master2 +export VCPKG_ROOT=~/vcpkg PATH="$VCPKG_ROOT:$PATH" \ + LIBCLANG_PATH=/Library/Developer/CommandLineTools/usr/lib/libclang.dylib +cargo build --release --features flutter +flutter build macos --release +./res/post-build-mac.sh # re-sign, install to /Applications, create dmg +open /Applications/RustDesk.app +``` + +--- + +## 1. One-time environment setup (if starting fresh) + +### 1.1. Xcode (App Store) + +- Install full **Xcode 26.5** from App Store. CommandLineTools alone is not enough — + Flutter's `xcodebuild` and `pod install` need the full Xcode. +- After install: + ```bash + sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + sudo xcodebuild -license accept + sudo xcodebuild -runFirstLaunch # installs CoreSimulator.framework etc. + ``` + +### 1.2. VCPKG (C++ deps: libvpx, libyuv, opus, aom) + +```bash +git clone https://github.com/microsoft/vcpkg ~/vcpkg +cd ~/vcpkg && ./bootstrap-vcpkg.sh +vcpkg install --triplet=arm64-osx libvpx libyuv opus aom +``` + +Add to `~/.zshrc`: +```sh +export VCPKG_ROOT="$HOME/vcpkg" +export PATH="$VCPKG_ROOT:$PATH" +``` + +### 1.3. Flutter 3.44+ + +Download from https://docs.flutter.dev/get-started/install/macos, then: +```sh +export FLUTTER_ROOT="$HOME/flutter" +export PATH="$FLUTTER_ROOT/bin:$PATH" +flutter doctor # should show green checkmarks for Xcode and Chrome +``` + +### 1.4. CocoaPods (needed by Flutter macOS plugins) + +```bash +brew install cocoapods +``` + +**Note:** On this Mac `homebrew` is **x86_64** (prefix `/usr/local`), not arm64 native. Most +things work but see the libclang note below. + +### 1.5. flutter_rust_bridge_codegen 1.80.1 + +```bash +cargo install flutter_rust_bridge_codegen --version 1.80.1 --features uuid --locked +``` + +--- + +## 2. Project-specific patches (all in git, see `git log`) + +### 2.1. `flutter/pubspec.yaml` + +```yaml + extended_text: ^15.0.2 # was 14.0.0; old missing SelectionHandler methods + +dependency_overrides: + google_fonts: 5.0.0 # 6.x breaks with const Map +``` + +### 2.2. `flutter/lib/generated_bridge.dart` (codegen output, 2 fixes) + +After running `flutter_rust_bridge_codegen` (see step 3), patch: +- `asTypedList(raw.length).setAll(0, raw)` → `cast().asTypedList(...)` and `cast()` in `api2wire_int_32_list` / `api2wire_uint_8_list` +- `typedef DartPort = ffi.Int;` → `typedef DartPort = ffi.Int64;` + +### 2.3. `flutter/lib/common.dart` (4 call sites) + +- `DialogTheme(` → `DialogThemeData(` +- `const TabBarTheme(` → `TabBarThemeData(` + +### 2.4. `flutter/macos/Runner/Configs/Release.xcconfig` + +```xcconfig +EXCLUDED_ARCHS[sdk=macosx*] = x86_64 +``` + +Without this, release builds fail because Xcode 26.5's `ARCHS_STANDARD` includes x86_64 +but our Rust dylib is arm64-only. + +### 2.5. `~/.pub-cache/.../google_fonts-5.0.0/lib/src/google_fonts_variant.dart` + +Dart 3.12 rejects `const Map{...}`. Change `const` to `final` on +`_fontWeightToFilenameWeightParts`. **This patch is in the global pub cache and will +be wiped by `flutter pub cache clean`.** To make it permanent, copy the patched file into +`./local_google_fonts/` and use `dependency_overrides: google_fonts: { path: ... }`. + +--- + +## 3. Build steps (from a clean checkout) + +```bash +cd ~/rustdesk-master2 + +# 3.1. Pull hbb_common submodule (only matters if you switch to submodule setup; +# we currently have it tracked as a regular directory, see [[git-remote-cardsoon]]) +# git submodule update --init --recursive + +# 3.2. Environment +export VCPKG_ROOT=~/vcpkg +export PATH="$VCPKG_ROOT:$PATH" +export LIBCLANG_PATH="/Library/Developer/CommandLineTools/usr/lib/libclang.dylib" +# ^^^ must be the Xcode CLT one (arm64-compatible), NOT /usr/local/opt/llvm/ + +# 3.3. Flutter pub get +cd flutter && flutter pub get && cd .. + +# 3.4. Generate FFI bindings (Flutter <-> Rust) +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-path /Library/Developer/CommandLineTools/usr/ +# Then apply the 2 hand-patches in 2.2 above. + +# 3.5. Rust release build +cargo build --release --features flutter + +# 3.6. Flutter macOS app build +flutter build macos --release + +# 3.7. Re-sign, install, package +./res/post-build-mac.sh +``` + +--- + +## 4. Critical gotchas (learned the hard way) + +### 4.1. Re-sign after every build + +`flutter build macos` produces an `.app` that **does not launch on macOS 26.5**. +The main executable and the embedded Flutter plugin frameworks end up with +**different ad-hoc Team IDs**, and macOS's strict dyld refuses the mismatch. + +**Always run** after every `flutter build macos`: +```bash +codesign --force --deep -s - path/to/RustDesk.app +``` + +The `res/post-build-mac.sh` script does this for you. The `--deep` flag is +essential so that all framework bundles inside the .app get re-signed together. + +### 4.2. Use hdiutil for .dmg, not create-dmg + +`create-dmg` (the official RustDesk build tool) depends on Node's `macos-alias` +native module, which is x86_64-built and won't load on this arm64 Mac. +`npm rebuild` also fails because `macos-alias` uses the outdated `nan@2` N-API +shim that's incompatible with Node 18+. + +Use `hdiutil` instead (see `res/post-build-mac.sh`). + +### 4.3. libclang must be arm64 + +For `flutter_rust_bridge_codegen` to work, `libclang.dylib` must be loadable. +The Homebrew one at `/usr/local/opt/llvm/lib/libclang.dylib` is **x86_64**. +The Xcode CLT one at `/Library/Developer/CommandLineTools/usr/lib/libclang.dylib` +is **universal** (arm64 + x86_64). Use the latter. + +### 4.4. EXCLUDED_ARCHS for release + +Xcode 26.5's default `ARCHS_STANDARD` includes x86_64, but our Rust dylib +is arm64-only. Set `EXCLUDED_ARCHS[sdk=macosx*] = x86_64` in `Release.xcconfig`, +or build a universal dylib with `lipo`. + +### 4.5. Debug config doesn't need EXCLUDED_ARCHS + +`flutter build macos --debug` works without the EXCLUDED_ARCHS hack because +debug builds use `ONLY_ACTIVE_ARCH=YES` which only targets the host arch. + +--- + +## 5. What you get + +After `res/post-build-mac.sh`: + +| File | Location | Size | +|------|----------|------| +| Release `.app` | `flutter/build/macos/Build/Products/Release/RustDesk.app` | 57 MB | +| Installed `.app` | `/Applications/RustDesk.app` | 57 MB | +| `.dmg` installer | `rustdesk-1.4.7-arm64.dmg` | 27 MB | +| Rust lib | `target/release/liblibrustdesk.dylib` | 22.6 MB | + +Bundle ID: `com.carriez.rustdesk` (NOT the official `com.rustdesk.client` — +change in `flutter/macos/Runner/Configs/AppInfo.xcconfig` if needed). + +Architecture: **arm64 only**. For Intel Mac support, build a universal +dylib with `lipo` and drop the EXCLUDED_ARCHS hack. + +--- + +## 6. First-launch permissions + +When you launch the app for the first time, macOS will prompt for: + +- **Screen Recording** (System Settings → Privacy & Security → Screen Recording) — + needed to capture the screen when you are being controlled +- **Microphone** — for audio forwarding +- **Accessibility** — for keyboard/mouse input forwarding + +Grant these or the corresponding features won't work. + +--- + +## 7. NOT covered here (out of scope) + +- iOS / Android builds (different toolchains) +- Apple notarization (would need an Apple Developer ID account) +- Self-hosted rendezvous/relay server (use public `rs-ny.rustdesk.com` for now) +- Universal binary (arm64 + x86_64) +- Code signing with a real developer certificate diff --git a/res/post-build-mac.sh b/res/post-build-mac.sh new file mode 100755 index 0000000..0467c9b --- /dev/null +++ b/res/post-build-mac.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# post-build-mac.sh +# Re-sign and package a release .app into a .dmg for RustDesk (macOS). +# +# Must be run AFTER `flutter build macos --release` finishes. +# The .app will NOT launch on macOS 26.5 without re-signing — see BUILD.md. +# +# Usage: +# ./res/post-build-mac.sh # signs build output, installs to /Applications, creates dmg +# ./res/post-build-mac.sh --no-dmg # just re-sign and install, skip dmg +# ./res/post-build-mac.sh --no-install # just re-sign and create dmg in build dir +# VERSION=1.4.7 ./res/post-build-mac.sh # custom version label in dmg filename + +set -euo pipefail + +# ---------- Config ---------- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +VERSION="${VERSION:-1.4.7}" +ARCH="${ARCH:-arm64}" +APP_REL="flutter/build/macos/Build/Products/Release/RustDesk.app" +APP_PATH="$PROJECT_ROOT/$APP_REL" +DMG_NAME="rustdesk-${VERSION}-${ARCH}.dmg" +STAGING="/tmp/dmg_staging_postbuild" +INSTALL_TARGET="${INSTALL_TARGET:-/Applications/RustDesk.app}" + +SKIP_DMG=0 +SKIP_INSTALL=0 +for arg in "$@"; do + case "$arg" in + --no-dmg) SKIP_DMG=1 ;; + --no-install) SKIP_INSTALL=1 ;; + -h|--help) + grep -E '^#( |!)' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + esac +done + +# ---------- Sanity checks ---------- +if [ ! -d "$APP_PATH" ]; then + echo "ERROR: $APP_PATH does not exist." >&2 + echo "Run 'flutter build macos --release' first." >&2 + exit 1 +fi + +# ---------- Step 1: Re-sign the .app ---------- +echo "==> Re-signing $APP_PATH" +codesign --force --deep -s - "$APP_PATH" +codesign --verify --strict --verbose=2 "$APP_PATH" 2>&1 | head -3 || true +echo " Signature: $(codesign -dvv "$APP_PATH" 2>&1 | grep -E '^Signature=' | head -1)" + +# ---------- Step 2: Install to /Applications ---------- +if [ "$SKIP_INSTALL" -eq 0 ]; then + echo "==> Installing to $INSTALL_TARGET" + pkill -9 -f "RustDesk.app/Contents/MacOS/RustDesk" 2>/dev/null || true + sleep 1 + rm -rf "$INSTALL_TARGET" 2>/dev/null || true + cp -R "$APP_PATH" "$INSTALL_TARGET" + # Re-sign the installed copy (path is now different, so signatures may differ) + codesign --force --deep -s - "$INSTALL_TARGET" + echo " Installed: $INSTALL_TARGET" +fi + +# ---------- Step 3: Build the .dmg ---------- +if [ "$SKIP_DMG" -eq 0 ]; then + echo "==> Building $DMG_NAME" + rm -rf "$STAGING" + mkdir -p "$STAGING" + cp -R "$APP_PATH" "$STAGING/RustDesk.app" + ln -sf /Applications "$STAGING/Applications" + # CRITICAL: re-sign the staging copy too — hdiutil snapshots whatever's there + codesign --force --deep -s - "$STAGING/RustDesk.app" + + DMG_OUT="$PROJECT_ROOT/$DMG_NAME" + rm -f "$DMG_OUT" + hdiutil create -volname "RustDesk Installer" \ + -srcfolder "$STAGING" \ + -ov -format UDZO \ + "$DMG_OUT" + echo " DMG: $DMG_OUT ($(du -h "$DMG_OUT" | awk '{print $1}'))" +fi + +# ---------- Step 4: Cleanup ---------- +rm -rf "$STAGING" + +echo "" +echo "Done. To launch:" +echo " open $INSTALL_TARGET" +if [ "$SKIP_DMG" -eq 0 ]; then + echo "To distribute:" + echo " open $PROJECT_ROOT/$DMG_NAME" +fi