RustDesk编译实战:Ubuntu与Android全流程解析

发布时间:2026/9/17 1:48:27
RustDesk编译实战:Ubuntu与Android全流程解析 RustDesk 大概是目前唯一一个让普通开发者愿意花一下午去折腾源码编译的远程桌面项目——它是开源的客户端核心是 Rust 写的界面已经全面迁移到 Flutter编译完的产物不仅能自己用还能顺手改包名、改图标、接上自己的中继服务器。这篇文章就把我在 Ubuntu 上编译 RustDesk 桌面版和 Android 版的全过程拆开讲清楚重点说清楚工具链之间怎么配合、哪些坑最容易让新手卡住一整天以及怎么把常常超过一小时的编译时间压下来。如果你已经在用 RustDesk、想定制一个专属版本或者单纯想研究远程桌面的实现方式这篇可以当作一份可以直接上手操作的手册。1. 动手前先搞清楚 RustDesk 的编译架构1.1 Rust 核心加 Flutter 界面的分工很多第一次编译 RustDesk 的人会习惯性把它当成一个“普通 Rust 项目”上来就cargo build。这样确实能编出一个可执行文件但大概率是旧版 Sciter 界面或者不完整的壳而且会和现在主线版的编译流程对不上。RustDesk 的实际结构是“Rust 核心 Flutter 前端”的混合工程。核心的 NAT 穿透、音视频编码解码、输入控制、剪贴板同步这些逻辑全部在 Rust 代码里位于仓库根目录下的src/而你在屏幕上看到的界面、窗口管理、托盘菜单、手机端的触控交互则是由flutter/目录下的 Flutter 工程负责。两个部分通过一个叫rust_lib_rustdesk的 Flutter 插件桥接起来这个插件在构建阶段会自动调用 cargo 把 Rust 代码编译成动态库再打包进 Flutter 的产物里。这个架构带来的直接影响是编译桌面版和 Android 版的入口其实都是 Flutter 侧而不是直接敲 cargo。你可以理解为整个构建流程分了两层Flutter 是外层包装Rust 是内层引擎。理解这一点后面所有命令你就能对号入座了不会出现“明明 cargo 成功了为什么 APK 还是旧的”这种困惑。1.2 编译工具链的全景图三路并进RustDesk 的构建不是一个工具链就能搞定的至少需要三套环境协同工作第一套是 Rust 工具链负责编译核心引擎包括rustup、cargo以及对应目标平台的交叉编译 target。第二套是 Flutter SDK负责编译界面和打包整体应用它需要配套的 Dart SDK 和平台构建工具在桌面端是 GTK 依赖加 CMake/Ninja在 Android 端是 Gradle。第三套是平台原生工具链Ubuntu 桌面版需要一系列 GTK/X11 开发库Android 版则需要 JDK、Android SDK 和 NDK。这三套环境并不是装好就能直接配合它们之间通过环境变量和配置文件发生关系。比如 Flutter 在构建 Android 时需要在gradle.properties里知道 NDK 的路径同时 Rust 的交叉编译器也需要通过ANDROID_NDK_HOME找到对应平台的链接器。任何一个环节没对上构建就会在某个你完全没预料的地方崩掉。1.3 版本选择不要一上来就编最新 commitRustDesk 主干分支的开发节奏很快UI 层有时会引入新依赖Rust 侧也在持续重构。我个人的习惯是编译之前先看一下官方 Release 的 tag挑一个相对稳定的版本去编译比如 1.2.x 或者 1.3.x 系列避免直接编最新的 main 分支时被一个刚提交的 bug 卡住。仓库克隆时还有一个容易忽略的点这个工程带了子模块必须用--recurse-submodules参数一次性拉完整否则后面编译会报缺文件排查起来很麻烦。如果已经用普通方式克隆了也可以在仓库根目录执行一次git submodule update --init --recursive补上。git clone --recurse-submodules https://github.com/rustdesk/rustdesk.git我建议你在动手前先理清自己要哪个版本、要哪条平台线然后用不超过十分钟把工具链装好。不要边编边装那种体验非常糟糕。2. Ubuntu 版编译从裸机到能跑起远程桌面2.1 系统依赖安装与不同 Ubuntu 版本间的名字差异Ubuntu 桌面版编译 RustDesk第一步就是把系统依赖补齐。这一步用到的包比较多而且不同 Ubuntu 版本之间某些依赖的包名会不一样这是新手最容易出错的地方。sudo apt update sudo apt upgrade -y sudo apt install -y git curl wget unzip zip \ ninja-build cmake pkg-config \ libgtk-3-dev \ libayatana-appindicator3-dev \ libxdo-dev \ libxcb-shape0-dev libxcb-xfixes0-dev \ libclang-dev逐个解释一下这些包为什么会出现libgtk-3-devFlutter Linux 桌面端的基础窗口、按钮、输入框都靠它这个几乎绕不开。libayatana-appindicator3-dev系统托盘的实现依赖。RustDesk 在桌面端把窗口最小化时会收进系统托盘没有这个库编译可能能过但运行起来托盘会消失。libxdo-dev远程桌面最关键的一环——模拟键盘和鼠标输入。RustDesk 在 Linux 上通过 XTest 相关接口模拟远端输入这个库就是提供这套接口的。libxcb-shape0-dev和libxcb-xfixes0-dev窗口形状和区域刷新相关的 X11 库Flutter Linux 插件层有时会直接引用。libclang-devRust 项目里有些 crate 需要在编译时解析 C/C 头文件生成绑定比如某些音视频库没有 clang 库会报libclang: couldnt find any valid shared libraries这类错误。我这里特别提醒一下libayatana-appindicator3-dev这个名字。在 Ubuntu 20.04 上这个包可能叫做libappindicator3-dev到了 22.04 和 24.04 才全面改成libayatana-appindicator3-dev。如果你 apt 安装时报找不到包先搜一下实际名字再装不用死磕。apt search appindicator2.2 Rust 和 Flutter 工具链的安装细节Rust 工具链我建议直接用 rustup 安装不要用 apt 装 rustc因为 apt 里的版本通常偏旧而 RustDesk 这种项目对 Rust 版本有最低要求缺了某个新语法特性就编译不过。curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env rustup default stableFlutter SDK 也是同样思路直接从 GitHub 拉稳定分支不要用 apt 装。git clone -b stable https://github.com/flutter/flutter.git $HOME/flutter export PATH$PATH:$HOME/flutter/bin flutter --version首次执行 flutter 命令时SDK 会做一次自举需要下载 Dart SDK 和各类组件这个过程比较慢。如果网络不太好可以先配置 Flutter 官方提供的国内镜像export PUB_HOSTED_URLhttps://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn配置完之后运行flutter doctor确认环境没问题。正常情况下它会列出 Linux 工具链的状态只要没有红色叉号就可以继续。2.3 克隆源码与两条构建路线工具链就位后回到 RustDesk 仓库目录进入flutter/子目录先执行flutter pub get把 Dart 依赖拉齐然后就可以构建了。cd rustdesk/flutter flutter pub get flutter build linux --release这条命令实际会做两件事先通过插件机制调用 cargo 把 Rust 核心编译成动态库再用 Flutter 的 Linux 桌面构建流程打包整个应用。整个过程第一次会比较漫长因为 Rust 侧有很多第三方 C/C 依赖需要编译比如音视频编解码相关库耗时通常在二十分钟到一小时之间。如果你只想快速验证 Rust 核心逻辑或者在做某个 Rust 模块的修改调试也可以直接回到仓库根目录执行cargo build --release但这样编出来的是不带 Flutter UI 的早期版本现在主线分支已经不太推荐这种用法了。我的建议是调试界面和整体功能走 Flutter 路线调试纯 Rust 逻辑才用 cargo 路线两者不要混着来。2.4 运行验证与产物位置构建成功后产物在flutter/build/linux/x64/release/bundle/目录下里面是一个完整的应用程序包包含可执行文件和相关资源。你可以直接运行./build/linux/x64/release/bundle/rustdesk如果想调试不跑 release而是用开发模式启动还能享受 Flutter 的热重载flutter run -d linux运行起来之后建议做几个最基本的验证窗口能否正常打开系统托盘图标能不能显示能否通过 ID 连接到另一台设备。如果只是窗口起来了但连不上那问题多半不在编译而在网络和配置层面。顺手提一句flutter/config/下有一些客户端设置文件桌面端的配置和日志通常存在~/.local/share/rustdesk/和~/.config/rustdesk/排查问题时可以去翻日志。3. Android 版编译交叉编译才是重头戏3.1 JDK、Android SDK 和 NDK 的准备Android 版比桌面版多了一道交叉编译的工序而且对 JDK、SDK、NDK 的版本组合很敏感。先说结论JDK 用 17SDK 平台用编译仓库里要求的版本NDK 强烈建议用 r23b也就是版本号23.2.8568313。sudo apt install -y openjdk-17-jdkAndroid SDK 建议用 Android Studio 的安装器装或者自己下载 command-line tools 命令行安装关键是确认ANDROID_HOME这个环境变量被正确设置。如果你已经装了 Android StudioSDK 通常在~/Android/Sdkexport ANDROID_HOME$HOME/Android/Sdk export ANDROID_NDK_HOME$ANDROID_HOME/ndk/23.2.8568313NDK 版本这里我特意强调因为很多人会顺手装最新版 NDK结果 Rust 的交叉编译链和最新版 LLVM 之间存在兼容性问题链接阶段报各种奇怪的undefined reference。我自己实测下来 r23b 配合 RustDesk 主线的 Rust 版本是没问题的。如果你在构建时发现 ndk 目录下没有 23.2.8568313就去 SDK Manager 里勾选安装。$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --list | grep ndk;23 $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install ndk;23.2.85683133.2 Rust 交叉编译 target 与 NDK 工具的配合Android 设备的 CPU 架构并不统一RustDesk 至少要支持 arm64-v8a 和 armeabi-v7a 两种主流架构所以你得让 Rust 工具链具备交叉编译能力。先添加对应 targetrustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android这里顺带解释一下为什么要在 Rust 侧加 target。Rust 核心代码需要编译成运行在 Android 系统上的.so动态库而你的编译宿主是 Ubuntu两个平台的系统接口完全不一样所以必须用 Android NDK 里的交叉编译工具链来链接。Flutter 的插件层通过cargokit在 Gradle 构建时自动完成这一步它会读取ANDROID_NDK_HOME环境变量找到对应的 clang 编译器和链接器。很多人在这一步卡住是因为忘了设置环境变量导致构建过程报unable to find utility clang这类错误。确认一下你的 environmentecho $ANDROID_HOME echo $ANDROID_NDK_HOME如果输出为空就在~/.bashrc里把这几个变量加上再source ~/.bashrc。3.3 flutter build apk 的完整构建流程进入flutter/目录和桌面版一样先拉依赖然后执行 Android 构建cd rustdesk/flutter flutter pub get flutter build apk --release这条命令默认会打出包含所有支持 ABIs 的 fat APK体积比较大。如果你只需要 arm64 的设备可以用--split-per-abi参数按架构拆包flutter build apk --release --split-per-abi构建过程中Gradle 会先解析插件依赖然后调用 cargokit 编译 Rust 核心。这一阶段会占满 CPU耗时比桌面版更长。在 8 核 16G 内存的机器上单编 arm64 大概需要三十分钟到一小时全 ABIs 可能需要两小时以上请做好准备。3.4 产物说明与安装验证构建完成后APK 在flutter/build/app/outputs/flutter-apk/目录下。用--split-per-abi的话你会看到app-arm64-v8a-release.apk、app-armeabi-v7a-release.apk、app-x86_64-release.apk三个文件。从体积可以看出差别arm64 的包通常比 armv7 大不少这也是正常的。安装到手机可以直接用 adbadb install build/app/outputs/flutter-apk/app-arm64-v8a-release.apk装好后打开应用看到 ID 列表能正常刷新说明网络穿透部分工作正常。如果打开即闪退先查日志Android 上的 logcat 会明确告诉你崩溃发生在哪个 so 库百分之八十的情况还是 Rust 核心和 NDK 版本不匹配导致的。4. 编译过程中最常见的坑与排查链路4.1 依赖下载慢或失败镜像配置与超时处理RustDesk 的依赖来源横跨 crates.io、pub.dev、Gradle Plugin Portal 和服务器的多个 CDN。对网络状态不理想的用户来说构建失败的第一大原因就是下载超时而不是代码问题。crates.io 的下载慢可以通过配置镜像解决在~/.cargo/config.toml里写入[source.crates-io] replace-with rsproxy [source.rsproxy] registry https://rsproxy.cn/crates.io-index同时设置环境变量让 rustup 也走加速export RUSTUP_DIST_SERVERhttps://mirrors.tuna.tsinghua.edu.cn/rustup export RUSTUP_UPDATE_ROOThttps://mirrors.tuna.tsinghua.edu.cn/rustup/rustupFlutter 侧在之前已经配置过PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URLGradle 则要在flutter/android/build.gradle.kts或对应 gradle 文件里添加阿里云镜像或者在gradle-wrapper.properties里把distributionUrl换成腾讯镜像。4.2 内存不足与编译超时ninja 被杀是典型信号RustDesk 的音视频相关依赖编译时非常吃内存。我在编译时遇到过rustc进程被系统直接 kill报错信息是signal: 9, SIGKILL这种情况十有八九是 OOM。ninja 也会给出ninja: build stopped: subcommand failed这样的提示但根本原因在系统内存不够。解决办法是加 swap 空间或者限制并行编译任务数。我的经验是 4G 内存的云主机至少要加 8G swap 才稳16G 内存的本机不限制并行任务数也有可能被吃满。# 限制 Rust 编译并行数 export CARGO_BUILD_JOBS2同时修改flutter/android/gradle.properties把 Gradle 的内存限制调大一点org.gradle.jvmargs-Xmx4096m -XX:MaxMetaspaceSize1024m4.3 NDK 版本和 Rust 工具链的链接错误如果你使用的 NDK 版本过新在链接阶段经常会遇到下面这类错误error: undefined reference to std::__1::mutex::lock()这种问题跟你的代码没有关系纯粹是 NDK 提供的 libc 版本和 Rust 使用的 Android target 之间的 ABI 不兼容。遇到这种情况最直接的排查方法是切回官方文档推荐的 NDK 版本也就是 r23b。如果你不想重装整套 SDK可以直接下载 NDK 的 zip 包手动放到$ANDROID_HOME/ndk/目录下然后重新指定ANDROID_NDK_HOME。wget https://dl.google.com/android/repository/android-ndk-r23b-linux.zip unzip android-ndk-r23b-linux.zip -d $ANDROID_HOME/ndk/这里我再补一句经验换 NDK 版本后最好把 Rust 的构建缓存清理干净否则旧的编译缓存可能还被继续引用cargo clean cd flutter flutter clean4.4 GTK 头文件和系统库缺失的定位方法桌面版编译时经常出现这种错误fatal error: gtk/gtk.h: No such file or directory这里的问题非常直接就是系统缺了 GTK 开发头文件。按我之前给的 apt 命令把libgtk-3-dev装好就行。如果没有appindicator会报找不到 appindicator/app-indicator.h。这种问题不要慌先确认包里有没有dpkg -L libayatana-appindicator3-dev | grep \.h$然后看一下关键运行库是否齐全用ldd查编译出的可执行文件即可ldd build/linux/x64/release/bundle/rustdesk | grep not found那个not found就是你缺的运行时依赖按图索骥装回来就行。这里我把常见的几个症状整理成一张表遇到问题时可以直接对号入座报错特征可能原因处理方式gtk/gtk.h: No such file缺 GTK3 开发头文件安装libgtk-3-devapp-indicator.h找不到缺系统托盘开发库安装libayatana-appindicator3-devlibclang相关报错缺 C/C 绑定解析库安装libclang-devSIGKILL或 ninja 中断内存不足加 swap 或限制并行数Android 链接期undefined referenceNDK 版本不兼容换 NDK r23b下载超时或找不到依赖网络源不通配置国内镜像5. 我的编译提速方案与后续定制方向5.1 二次编译提速sccache 是最大的功臣RustDesk 最让人头疼的就是首次编译时间。但如果你要反复修改代码、重新编译那么安装一个 sccache 是非常值得的。sccache 是一个编译缓存工具可以把同一份编译器中间结果缓存下来第二次编译能快很多。cargo install sccache export RUSTC_WRAPPERsccache把这个环境变量写入~/.bashrc之后再编译 RustDesk 的时候增量构建时间能从十几二十分钟缩短到几分钟。尤其对于 Android 这种多个架构挨个编译的场景sccache 的收益非常明显因为四个 ABI 编译的是同一份 Rust 源码只不过目标平台不同缓存命中率相当可观。另外不要轻易删除$HOME/.cargo和target目录这些都是构建缓存。很多新手编译失败后第一反应就是删掉重来反而把之前下载的依赖缓存也删了陷入每次都是全量编译的恶性循环。5.2 编译完成后还能做什么定制与自建服务编译成功只是开始。RustDesk 的开源属性让你可以在产物上做很多定制首先是改包名和图标。Android 的applicationId在flutter/android/app/build.gradle.kts里修改图标在对应的mipmap目录替换。注意改了包名之后自己是没法通过 OTA 覆盖安装官方版本的需要先卸载旧版。其次是接入自己的 ID 服务器和中继服务器。RustDesk 本身区分 ID 服务器hbbs和中继服务器hbbr这部分同样是开源的。客户端默认连接官方公共服务器但你在编译时可以或运行前通过设置填入你的服务器地址。如果需要在公网部署自建服务器可以额外克隆 rustdesk-server 仓库编译那是在另一条构建链路上的事和客户端编译是独立的。最后RustDesk 的flutter/目录里也有大量 UI 文案和主题配置改起来比想象中容易。如果你只是想把主界面的语言、按钮文本或者默认配置改掉不需要深入 Rust 代码直接编辑 Dart 端的资源配置就能重新打包。我自己实际编译时还有一个体会如果既编 Linux 桌面版又编 Android 版最好准备两个独立的源码副本一个专门用于桌面版 cargo/flutter 编译另一个专门用于 Android 交叉编译因为两套构建的缓存目录混在一起时偶尔会因为 NDK 和系统链接器的选择冲突出现难以排查的怪问题。分开后两个副本各用各的缓存互不干扰省去很多心智负担。编译这种东西环境干净比什么都重要。