RustDesk跨平台编译实战:Ubuntu桌面版与Android APK完整构建指南 📅 发布时间:2026/9/17 7:41:28 👁 浏览次数: RustDesk 这种项目拿到源码之后会让人产生一种“必须自己编译一次”的冲动。倒不是官方包不好用而是它现在这个技术栈实在太典型了Rust 写核心Flutter 写界面从 Windows、Linux、macOS 一路排到 Android、iOS。真能亲手把它从源码折腾成可运行的产物你基本上等于把 cargo 构建、Linux 桌面依赖管理、Android NDK 交叉编译、Flutter 打包 APK 这几条常见技术路线都过了一遍。这篇文章是我自己的完整编译记录分 Ubuntu 桌面版和 Android 版两条线来讲。我不会只翻译一遍官方文档而是会把每个依赖为什么需要、每条命令解决什么问题、报错之后从哪里下手排查都讲清楚。无论你是想二次开发 RustDesk、给团队做定制化的远程工具还是单纯想研究一个 Rust Flutter 项目如何跨平台产出这份流程应该都能直接照着走。1. 编译前先把架构和版本看清楚1.1 RustDesk是怎么组成的Rust核心配合Flutter外壳RustDesk 的技术栈理解起来并不复杂核心是 Rust界面是 Flutter。Rust 层面负责的是真正干重活的部分屏幕画面采集、编码压缩、网络传输、键鼠输入模拟、剪贴板同步以及和服务端的协议交互。Flutter 层面负责的则是你肉眼能看到的界面设备列表、连接窗口、设置面板、密码弹窗、菜单栏。用个不太精确的比喻Rust 部分是发动机和底盘Flutter 部分是驾驶舱。这个架构对编译流程的影响非常直接。Ubuntu 桌面版不是一条cargo build就能完事的你得先给 Flutter 准备 Linux 桌面构建环境再让 Rust 主工程去引用 Flutter 生成的产物。Android 版更麻烦一点Rust 核心要交叉编译成 ARM 架构的动态库然后把.so文件送进 Flutter 的 Android 工程里最后由 Gradle 统一打包成 APK。两层之间通过 FFI 桥接通信构建顺序错了就会出各种莫名其妙的问题。如果你只是想改个界面文案或换一套 UI 布局你去改 Flutter 的 dart 代码就行如果你想优化传输协议或者调整画质编码逻辑则要动 Rust 源码。理解了这一层分工后面编译时才不会两眼一抹黑。1.2 版本分支怎么选为什么要避开Sciter老版本RustDesk 的仓库历史上有一个很明显的分水岭。1.1.x 及更早的版本UI 用的是 Sciter一个商业授权的 HTML/CSS 渲染引擎。编译那种老版本时你需要处理 Sciter 的许可证 key构建过程还会额外下载 Sciter 的引擎文件定制和二次开发都受限。当前官方主推的是 Flutter UI 版本也就是 1.2.x 之后的分支。这套 UI 完全开源编译不需要任何额外的商业 key也是目前社区活跃度最高的代码线。另外一个很重要的选择是不要直接编译main分支。main是开发分支依赖的更新频率非常快今天能编过不代表明天还能编过API 可能随时变。我个人的建议是拉一个 release tag 来编比如 v1.3.x 这种正式发布版本或者至少选一个近期稳定的 tag。在仓库里执行git tag -l | sort -V | tail可以看看当前有哪些可用的版本号。1.3 编译产物长什么样Ubuntu的ELF与Android的APKUbuntu 桌面版编译完成后产物是一个 ELF 可执行文件路径在target/release/rustdesk。这个文件不是完全静态的它运行时依赖 GTK、xcb、PAM 等系统共享库所以换一台干净的 Ubuntu 机器直接拷过去不一定能跑需要把依赖装齐或者你自己再做一个打包分发方案。Android 版最终拿到的是一个 APK 文件。APK 里面按 ABI 区分了多个目录例如lib/arm64-v8a、lib/armeabi-v7a、lib/x86_64。ABI 这个问题非常关键因为不同手机的 CPU 指令集不一样Rust 编译出的机器码不能在不同 ABI 之间互换。常见 ABI 和对应设备的对应关系如下ABI说明典型设备arm64-v8a64位 ARM 架构绝大多数现代 Android 手机armeabi-v7a32位 ARM 架构较老的低端手机x86_6464位 Intel/AMD 架构新版安卓模拟器、部分平板x8632位 x86 架构老版安卓模拟器APK 的体积和 ABI 数量直接相关。四个 ABI 全打进去包体自然会大不少但通用性最好。后面我会说怎么按需裁剪。2. Ubuntu编译的前置环境用对工具链等于成功一半2.1 选哪个Ubuntu版本和硬件配置我这边验证过的比较舒服的组合是 Ubuntu 22.04 LTS 或 24.04 LTS64 位系统。Ubuntu 20.04 不是不能编但新版 RustDesk 依赖的一些库版本比较老个别包名对不上你需要额外处理依赖源体验会差一些。硬件方面内存建议至少 16GB。8GB 内存也能编但如果同时在跑 Android 的 Gradle 和 Flutter 构建会很紧张有可能直接卡死或触发 OOM。磁盘空间更要注意Rust 的 target 目录非常能吃硬盘Ubuntu 版一次 release 构建下来target 目录 5GB 到 10GB 很正常加上 Flutter SDK、Android SDK、NDK 和 Gradle 缓存整体占用 30GB 以上是常态。我建议你至少预留 40GB 空间再动手如果你是虚拟机磁盘别只给 20GB编到一半磁盘满是很痛苦的事情。CPU 核心数对编译速度影响最大。Rust 编译大量依赖时是可以并行处理的核数越多越快。四核机器和十六核机器的耗时差距不是几倍的问题是十几分钟和一小时的区别。2.2 系统级依赖逐个说明这些包没有一个是多余的在 Ubuntu 上编译 RustDesk需要先装一批系统级库。完整命令如下sudo apt update sudo apt install -y build-essential cmake ninja-build pkg-config git \ libgtk-3-dev libssl-dev libclang-dev \ libxcb-shape0-dev libxcb-xfixes0-dev libxdo-dev \ libpam0g-dev libvdpau-dev libpipewire-0.3-dev \ libxrandr-dev libxinerama-dev libx11-dev很多人看到这一长串会嫌麻烦但这些包确实没有一个是多余的。build-essential提供 GCC 和 makeRust 编译最终要调用系统的 C 链接器cmake和ninja-build是 Flutter Linux 桌面构建以及部分 Rust crate 里 C/C 子项目构建需要的pkg-config用来定位头文件和库文件路径。libgtk-3-dev是 GTK3 开发库Flutter 在 Linux 上绘制窗口和控件依赖它。libssl-dev是 OpenSSL 头文件Rust 里openssl-sys这个 crate 编译时必须找到 OpenSSL 库才能生成绑定。libxcb-shape0-dev和libxcb-xfixes0-dev属于 X11 协议扩展窗口形状、透明和修复机制会用到。libxdo-dev对应 xdotoolRustDesk 要在远程会话里模拟键鼠输入这个库很关键。libpam0g-dev是系统登录认证模块远程协助解锁屏幕之类的功能需要它。libvdpau-dev是 NVIDIA 的硬解接口视频解码场景会用到。libpipewire-0.3-dev是新一代 Linux 音频框架新版 RustDesk 的音频采集和播放依赖它。libclang-dev则是给 Rust 的 bindgen 工具用的它在编译时解析 C/C 头文件并生成 FFI 绑定缺了它很多依赖会直接报libclang: error。提示不同 Ubuntu 版本里这些包名可能略有差异。尤其是libpipewire-0.3-dev在 20.04 上可能叫别的版本甚至不存在如果你坚持用老版本系统需要自己搜索对应的替代包。2.3 安装Rust工具链并顺手配置cargo加速Rust 的安装方式我强烈建议用 rustup不要用 apt 直接安装 Rust。apt 仓库里的 rustc 版本通常偏旧而且 rustup 后续管理 toolchain、添加交叉编译 target 都非常方便你编完 Ubuntu 版还要编 Android 版rustup 是必须的。安装命令curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env rustup update stableRustDesk 的编译应该基于 stable 工具链不要冒险切 nightly个别 crate 对 nightly 的 API 变化很敏感容易编出奇奇怪怪的编译错误。在国内网络环境下直接去 crates.io 拉依赖经常超时或速度很慢。这种情况下可以给 cargo 配置镜像源编辑~/.cargo/config.toml写入类似下面的内容[source.crates-io] replace-with rsproxy-sparse [source.rsproxy-sparse] registry sparsehttps://rsproxy.cn/index/这是很常规的开发提速手段完全合规而且实测对构建体验的提升非常明显。配置好之后cargo 会自动使用镜像源拉取 crate。2.4 Flutter SDK安装与Linux桌面支持开启由于新版 RustDesk 的 UI 是 Flutter你还需要准备 Flutter SDK。不需要装 Android Studio 里那个 Flutter 插件只需要命令行版本的 SDK。安装方式也很简单直接从 GitHub 拉稳定分支git clone -b stable https://github.com/flutter/flutter.git ~/flutter export PATH$PATH:$HOME/flutter/bin flutter config --enable-linux-desktop flutter doctorflutter doctor会检查当前环境是否满足 Flutter 开发需求。在 Ubuntu 上重点看 Linux Desktop 这一项是否通过它要求 clang、cmake、ninja-build、pkg-config 和 libgtk-3-dev 都可用这些我们其实已经装好了。首次执行flutter命令时会自动下载 Dart SDK 和部分引擎产物耗时取决于网络条件耐心等即可。3. Ubuntu版本完整编译实操3.1 拉取源码并锁定版本源码克隆我推荐用浅克隆仓库历史特别长全量克隆非常消耗时间。命令如下git clone --depth1 https://github.com/rustdesk/rustdesk.git cd rustdesk git fetch --tags git checkout v1.3.8 git submodule update --init --recursivegit fetch --tags是为了拿到 release tag。v1.3.8是我写这篇记录时仓库里一个比较新的稳定 tag你实际操作时请以git tag -l | sort -V | tail看到的实际版本为准选最新的正式版本即可。git submodule update这步经常有人忽略仓库里有些第三方组件是通过 submodule 引用的不更新会导致后续编译时提示文件缺失或者某个模块目录为空。3.2 先构建Flutter UI层新版 RustDesk 的仓库根目录下有一个flutter子目录这个是整套 UI 工程的入口。在构建 Rust 主程序之前先把 Flutter 侧准备好cd flutter flutter pub get flutter build linux --release cd ..flutter pub get会拉取 Flutter 依赖的 dart 包flutter build linux --release会编译出一个 Linux 桌面端的 Flutter runner 以及对应的原生库文件。这一步生成的东西Rust 主程序编译的时候要引用所以顺序一定不能反。如果你这里因为网络原因一直卡住多半是 pub.dev 依赖下载失败可以给 Flutter 配置 PUB_HOSTED_URL 之类的镜像环境变量这也是常规做法。3.3 编译Rust主程序Flutter 层就绪之后回到仓库根目录直接触发 Rust 的 release 构建cargo build --release首次构建的耗时取决于机器性能快的话十分钟左右慢的话半小时甚至更久。Rust 编译慢不是没有原因的大量 crate 的泛型代码需要做单态化实例化release 模式还要经过 LLVM 优化每一步都是实打实的计算量。不过只要不是每次都清空 target 目录增量构建会快很多。编译完成后可执行文件在target/release/rustdesk。如果你机器核心多想进一步利用并行能力可以用cargo build --release -j 16其中的数字根据自己的 CPU 逻辑核心数调整。当然输出的日志会非常长集中在屏幕上反而不利于观察到底有没有报错所以我个人更习惯让它慢慢跑完只在报错时再翻日志。提示如果 cargo 在链接阶段非常慢且你的发行版仓库里有lld装一个并把它配成 cargo 的链接器能显著缩短最终的链接时间。这是 Rust 大型项目常用的优化手段。3.4 运行与验证ldd检查和首启测试编译完成先别急着双击运行先用ldd检查动态库依赖是否完整ldd target/release/rustdesk | grep not found如果没有输出说明依赖库都找到了。如果有缺失项根据缺失的库名回到第 2.2 节补装对应的-dev包。在正常的图形桌面环境里直接运行./target/release/rustdesk即可。如果你是在纯命令行服务器上验证没有 X 环境可以配合xvfb-run跑一下或者直接忽略这种场景毕竟这是桌面软件。首次运行会进入设置向导设置安全密码之后就能作为被控端或控制端使用了。最直接的验证方式就是让另一台机器装上官方版连接这台自编译版本看画面、键鼠、剪贴板是否都正常。4. Android版本编译前准备交叉编译这块硬骨头4.1 理解交叉编译与ABI的关系Android 版本和 Ubuntu 版本的编译逻辑有很大不同。普通电脑的 CPU 是 x86_64 架构而绝大多数 Android 手机是 ARM 架构。你不能直接在 x86 机器上编译出 x86_64 的二进制扔给 ARM 手机用必须使用交叉编译在 x86 主机上调用 ARM 目标的编译器和链接器生成能在 ARM 指令集上运行的机器码。Rust 对交叉编译有很成熟的支持。每个目标平台对应一个 target 三元组名称例如aarch64-linux-android就代表 64 位 ARM Android 平台。RustDesk 在 Android 上要支持的 ABI 和对应 Rust target 的关系如下Android ABIRust target 三元组arm64-v8aaarch64-linux-androidarmeabi-v7aarmv7-linux-androideabix86_64x86_64-linux-androidx86i686-linux-android理解了这层关系之后你就能明白为什么 Android 构建需要 NDK、需要添加多个 Rust target以及为什么 APK 会有 ABI 相关的膨胀问题。4.2 安装JDK、Android SDK和NDKAndroid 构建需要一个完整的 Android 工具链。JDK 版本推荐 17因为新版 Android Gradle Plugin 已经要求 JDK 17 起步。sudo apt install -y openjdk-17-jdk java -versionAndroid SDK 和 NDK 我建议直接用命令行工具安装比打开 Android Studio 点来点去更可控也方便以后在 CI 服务器上复用同一套流程。先下载 commandlinetools 并放到固定目录mkdir -p ~/android-sdk/cmdline-tools cd ~/android-sdk/cmdline-tools wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip unzip commandlinetools-linux-*.zip mv cmdline-tools latest注意目录结构很重要cmdline-tools下必须有一层latest目录sdkmanager 才会正确识别。然后配置环境变量并通过 sdkmanager 安装平台工具、SDK Platform、Build Tools 和 NDK。这里 NDK 版本我建议选择 r25 或 r26 系列不要盲目装最新最新版有时会改变默认的 C 工具链行为导致部分 Rust crate 编译失败。export ANDROID_SDK_ROOT$HOME/android-sdk export ANDROID_NDK_HOME$ANDROID_SDK_ROOT/ndk/26.1.10909125 export ANDROID_NDK_ROOT$ANDROID_NDK_HOME $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager \ --install platform-tools platforms;android-34 \ build-tools;34.0.0 ndk;26.1.10909125 yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --licensesplatforms;android-34里的 34 对应 Android 14 的 API Level。如果你的 Flutter 版本要求更高的 API Level就安装对应版本。最后一步接受所有 SDK 许可证是必须的否则 Gradle 构建时会卡在 license 未接受的报错上。4.3 添加Rust交叉编译target并安装cargo-ndk光有 SDK 和 NDK 还不够Rust 侧也要添加对应的交叉编译 target。执行以下命令rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android如果目前只是为了快速出包可以先只加aarch64-linux-android和x86_64-linux-android后面需要哪个再补。接下来一定要安装cargo-ndk这个工具极大简化了交叉编译时的工具链配置cargo install cargo-ndk理解cargo-ndk的价值你需要先想想没有它你得多麻烦。手动交叉编译时需要为每个 target 设置CC_target、AR_target、CARGO_TARGET_target_LINKER等一堆环境变量每个都要指向 NDK 里对应架构的 clang 交叉编译器写起来又长又容易漏。cargo-ndk把这些细节全部封装了你只需要告诉它要编哪个 ABI、输出到哪里。5. Android版本打包APK实操5.1 编译Rust原生库到JNI目录回到 RustDesk 仓库根目录保证环境变量ANDROID_NDK_HOME和ANDROID_NDK_ROOT都已经设置好然后执行export ANDROID_NDK_HOME$HOME/android-sdk/ndk/26.1.10909125 export ANDROID_NDK_ROOT$ANDROID_NDK_HOME cargo ndk -t armeabi-v7a -t arm64-v8a -t x86 -t x86_64 \ -o flutter/android/app/src/main/jniLibs build --release我来解释一下这个命令做了什么。cargo ndk会启动 NDK 交叉编译环境-t参数指定要构建的 ABI-o flutter/android/app/src/main/jniLibs指定把编译出的.so动态库直接输出到 Flutter Android 工程的jniLibs目录下。这样后续 Gradle 打包时这些动态库会自动打进 APK。如果你只是为了日常调试强烈建议先只编一个 ABI比如cargo ndk -t arm64-v8a -o flutter/android/app/src/main/jniLibs build --release这样不仅编译时间大幅缩短后续 APK 体积也会小很多。等确认整个流程没问题了再补齐所有 ABI 做正式发布包。5.2 用Flutter构建APKRust 动态库就绪之后进入 Flutter 工程目录触发 Android APK 打包cd flutter flutter pub get flutter build apk --release命令结束后默认 APK 会输出到flutter/build/app/outputs/flutter-apk/app-release.apk。这个包里会包含你在cargo ndk时指定的所有 ABI 动态库所以体积会比较大。如果你希望按 ABI 拆分成多个独立 APK使用flutter build apk --release --split-per-abi这种方式会生成app-arm64-v8a-release.apk、app-armeabi-v7a-release.apk和app-x86_64-release.apk三个文件每个只包含对应 ABI 的 so体积明显减小更适合直接分发和安装测试。提示默认构建使用的是 debug 或 release 签名配置。自己编译自己用没问题但如果要对外分发建议在flutter/android里配置正式的签名文件否则安装到别人手机上时会提示签名不安全之类的警告。5.3 产物检查与安装验证打包完成后先不要急着安装用 unzip 看一下 APK 里实际包含哪些动态库确认 Rust 核心确实打进去了unzip -l flutter/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk | grep lib/手机连接电脑并开启 USB 调试后用 adb 安装adb install -r flutter/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk安装成功之后建议做一次真实连接测试。打开 App设置安全密码然后在另一台设备上用官方版本或自编译版本连接这台手机验证画面、音频输入、键鼠转发是否正常。如果中途出现闪退或无法连接重点看adb logcat的日志里面会明确提示是 JNI 初始化失败、FFI symbol 找不到还是权限申请被拒绝。尤其注意 Android 12 以后对输入事件、屏幕捕获、剪贴板的权限要求更严格需要在系统设置里手动授权。6. 常见问题速查与提速技巧6.1 Ubuntu侧编译和运行的典型报错操作系统层面和 Rust 构建层面的坑是最常见的我整理了一份速查表报错现象根本原因解决方案openssl-syscrate build 失败缺少 OpenSSL 头文件安装libssl-devlibclang: error: unable to load library libclang.so缺少 libclang 或系统找不到它安装libclang-dev必要时设置LIBCLANG_PATH环境变量链接时提示undefined reference to gtk_xxxGTK 开发库不完整安装libgtk-3-devcc或collect2报 not found基础工具链缺失安装build-essential磁盘写满构建中断target 目录占用过大清理不需要的中间产物或把 target 放到大分区其中磁盘问题特别值得注意。Rust 的 target 目录清理起来并不直观如果同一份源码你既编过 Linux 又编过 Androidtarget 里会同时存在多个平台的中间产物非常吃硬盘。空间紧张时只保留当前平台对应的release目录其他可以删掉下次需要时重新编就行。6.2 Android侧交叉编译和Gradle构建的典型报错Android 侧的问题往往比 Ubuntu 侧更隐蔽因为错误信息经常藏在 Gradle 的长篇日志里。以下是几个高频问题报错现象根本原因解决方案Could not find NDKNDK 环境变量没配置或路径不对检查ANDROID_NDK_HOME/ANDROID_NDK_ROOT是否指向具体版本目录package android-x86 does not exist之类的 target 错误Rust 目标平台没添加执行rustup target add x86_64-linux-android等命令补齐 targetGradle 下载依赖失败Could not resolve all files网络访问 Maven 仓库不稳定给 Gradle 配置国内镜像仓库或在企业内网使用已有的 Maven 代理缓存clang: error: unknown argument: -static-libstdcNDK 版本与依赖库不匹配换成 RustDesk 官方 CI 使用的 NDK r25/r26 系列而不是最新版Flutter Gradle 构建提示找不到 SDK Platform只装了 NDK 没装 platform回到 sdkmanager 安装platforms;android-34和build-tools遇到 Gradle 报错时不要只看最底下的FAILURE: Build failed with an exception往上面翻日志找到第一个红色或error开头的行那才是真正的根因。Gradle 的包装器有时候也会在首次运行时下载特定版本如果卡在 Gradle 下载阶段八成也是网络问题可以在flutter/android里配置 Gradle 镜像地址或者手动把 Gradle 发行包放到缓存目录。6.3 编译提速的三个实操技巧第一次编译 RustDesk 的耐心消耗是必然的但后面的编译完全可以做到非常快。我自己常用的提速手段有三个。第一个是 sccache。sccache是一个跨平台编译缓存工具可以缓存 Rust 编译的中间产物。安装方式也很简单cargo install sccache然后让 cargo 使用它export RUSTC_WRAPPERsccache配好之后第二次编译时大部分 crate 都可以直接从缓存里取结果速度提升非常明显尤其是多次清理 target 后重新构建区别是“重编一小时”和“几分钟搞定”的差别。第二个是换 lld 链接器。Rust 链接阶段特别是 release 模式非常耗时换成 LLVM 的 lld 后链接时间能降低不少。装好lld后在~/.cargo/config.toml里针对本机 target 指定链接器即可。第三个是按需裁剪编译目标。日常开发调试只编一个arm64-v8a不要每次都在四个 ABI 上来回磨。真正要发版之前再完整编一遍所有 ABI这样可以节省大量重复劳动时间。6.4 我关于稳定复现的几点体会编译这种规模的项目一次成功是运气能稳定复现才是能力。吃了几次亏之后我现在基本遵循几条简单原则。第一每个 tag 对应的构建要求可能不同。不要只看一次 README 就以为永远都会了更新代码之后记得重新读一下仓库根目录的README.md和BUILD.md尤其是 RustDesk 这种迭代很快的项目依赖列表和目录结构随时在变。第二工具的版本组合要固定下来。我的 CI 脚本里会把 Ubuntu 版本、Rust 版本、NDK 版本、Flutter 版本全部写成显式变量每次构建都用同一组版本出现问题才知道往哪个方向排查。如果你今天用 NDK r26、明天换成 r27编译报错就别怪环境不稳定。第三不要频繁 CtrlC 中断编译。cargo 的增量缓存对中断有一定容忍度但反复中断仍可能留下损坏的增量状态最后不得不清空 target 重来。与其中断不如让它在后台跑或者一开始就只编需要的 ABI。第四Android 和 Linux 两条线的环境变量有冲突风险。如果你在同一个终端里既导出了ANDROID_NDK_HOME又编 Linux 版一般情况下没问题但一旦遇到诡异的编译错误先检查环境变量是不是串了。我的做法是给每条线的构建分别写一个独立的小脚本互不污染环境。编译 RustDesk 这件事说实话第一次跑通之后就会觉得它没那么可怕。整条链路里最耗精力的不是具体命令而是让你理解每一层工具之间为什么是这种依赖关系的过程。把 Ubuntu 桌面版和 Android 版都亲手编一遍之后你以后再遇到类似的 Rust Flutter 跨平台项目流程基本就是顺手拈来的事。如果你在某个步骤上卡住了回去对照一下你当前的版本、系统、工具链组合大概率是其中一环和我的环境不同导致的。先锁定版本再逐层排查剩下的就只是时间问题了。