8.8 KiB
Building RustDesk for macOS (Jerry's MacBook Air, Apple Silicon)
This is a fork of 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:
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
xcodebuildandpod installneed the full Xcode. - After install:
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)
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:
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:
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)
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
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
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<FontWeight, ...>
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<ffi.Int32>().asTypedList(...)andcast<ffi.Uint8>()inapi2wire_int_32_list/api2wire_uint_8_listtypedef 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
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<FontWeight, String>{...}. 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: ... }.
2.6. flutter/macos/Runner/custom.txt (default settings via Rust's load_custom_client)
RustDesk's Rust core reads Contents/Resources/custom.txt on startup
(see src/common.rs::load_custom_client + read_custom_client). It accepts two
keys: default-settings (fills DEFAULT_SETTINGS; user config still wins) and
override-settings (fills OVERWRITE_SETTINGS; user can't override).
Current contents:
{
"app-name": "RustDesk",
"default-settings": {
"enable-audio": "N"
}
}
Effect: the "Enable audio" option in Settings is off by default for new
installs. Users can still flip it on. To add more defaults, follow the
KEYS_SETTINGS whitelist in libs/hbb_common/src/config.rs:3130.
Two parts to make this work:
- The file
flutter/macos/Runner/custom.txtmust exist (it does in git). flutter/macos/Runner.xcodeproj/project.pbxprojmust listcustom.txtin thePBXResourcesBuildPhase'sfileslist. We added it under UUIDsAABBCC000100000000000001(BuildFile) andAABBCC000200000000000002(FileReference). If you regenerate the Xcode project, re-add it.
3. Build steps (from a clean checkout)
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:
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.comfor now) - Universal binary (arm64 + x86_64)
- Code signing with a real developer certificate