Files
RustDesk/BUILD.md
T

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 xcodebuild and pod install need 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(...) and cast<ffi.Uint8>() 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

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:

  1. The file flutter/macos/Runner/custom.txt must exist (it does in git).
  2. flutter/macos/Runner.xcodeproj/project.pbxproj must list custom.txt in the PBXResourcesBuildPhase's files list. We added it under UUIDs AABBCC000100000000000001 (BuildFile) and AABBCC000200000000000002 (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.com for now)
  • Universal binary (arm64 + x86_64)
  • Code signing with a real developer certificate