# 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