Windows 下交叉编译 Rust 为 Linux 可执行文件
前言
日常在 Windows 上写 Rust,上线环境却是 x86_64 Linux 服务器,这是不少 Web 服务与 CLI 工具的常见组合。
若每次改代码都进 Linux 虚拟机或 CI 里编译,反馈链路会明显变长。
本文介绍一种在 Windows 本地完成 Linux 交叉编译 的实用路径:以 x86_64-unknown-linux-musl 为目标,配合 cargo-zigbuild 与 Zig 链接器,产出静态链接的可执行文件,再打成可直接 scp 到服务器的目录包。
下文不展开 Docker 与 WSL 方案,只聚焦「本机一条命令出 Linux 包」。
示例以带静态前端资源的 Axum 服务为场景,纯 CLI 项目可省略前端相关步骤。
依赖
交叉编译链路需要 Rust 工具链、musl 目标、Zig 与 cargo-zigbuild。
下面列出各组件的作用,便于按需安装。
| 组件 | 作用 |
|---|---|
| Rust + rustup | 编译器与 target 管理 |
x86_64-unknown-linux-musl |
musl 静态链接目标 triple |
| Zig | 提供跨平台 C 链接器,替代手写 .cargo/config.toml |
| cargo-zigbuild | 调用 Zig 完成 cargo build 的交叉链接 |
在 PowerShell 中依次执行以下命令,安装 musl 目标与构建插件。
1 | rustup target add x86_64-unknown-linux-musl |
Zig 可通过 pip 安装 ziglang 包,或从 ziglang.org 下载解压后把 zig 加入 PATH。
1 | pip install ziglang |
安装完成后,用下面命令确认三者可用。
1 | rustup target list --installed |
实现
依赖选型
交叉编译的难点往往不在 Rust 本身,而在 C 系原生库 与 OpenSSL 的链接。
在 Cargo.toml 里提前选对 feature,可以少踩「Windows 编过、Linux 缺 .so」的坑。
HTTP 客户端优先用 rustls 而不是 OpenSSL,避免在 Windows 上配置 Linux 版 OpenSSL 交叉链接。
1 | reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] } |
SQLite 等带 C 代码的 crate,优先启用 bundled,让依赖随源码一起编译,而不是链接目标机上的系统库。
1 | rusqlite = { version = "0.31", features = ["bundled"] } |
Release 配置可按需开启体积优化:strip 符号、LTO、偏向体积的优化等级。
1 | [profile.release] |
若项目还依赖 OpenSSL、系统 libpq 等无法 bundled 的库,musl 交叉编译会明显变复杂,需要单独评估是否改用 gnu 目标或容器构建。
核心编译命令
工具链就绪且依赖选型合理后,一条命令即可在 Windows 上产出 Linux 二进制。
1 | cargo zigbuild --release --target x86_64-unknown-linux-musl |
产物位于 target/x86_64-unknown-linux-musl/release/ 下,文件名与 Cargo.toml 里 [package] name 一致。
musl 目标默认静态链接 C 运行时,拷贝到常见 x86_64 Linux 发行版上通常可直接运行,无需额外安装 glibc 兼容包。
组装部署目录
Web 类项目往往除二进制外,还需要静态资源、配置文件与启停脚本。
推荐约定一个输出目录(例如 dist-linux),每次构建后把运行所需文件集中放入,整目录上传到服务器。
典型目录结构如下。
1 | dist-linux/ |
PowerShell 脚本可以按「检查工具链 → 构建前端 → 交叉编译 → 复制文件 → 生成 shell 脚本」分步执行。
下面给出精简版示例,项目名请替换为自己的 crate 名。
1 | $ErrorActionPreference = 'Stop' |
在 Windows 上写出的 start.sh、stop.sh 必须使用 UTF-8 无 BOM 且换行为 LF,否则 Linux 上可能出现 ^M 或解释器找不到。
下面用 PowerShell 辅助函数保证编码正确。
1 | function Write-UnixScript($path, $content) { |
start.sh 可用 nohup 后台运行,并把 PID 写入文件,便于 stop.sh 按 PID 停止。
1 |
|
应用侧建议把静态资源、配置、数据库路径都设为相对可执行文件所在目录,这样 dist-linux 拷到任意路径都能直接跑,无需改环境变量。
验证
将整个 dist-linux 目录上传到 Linux 服务器后,先确认二进制架构与是否静态链接。
1 | file ./my-app |
查看日志与端口是否正常监听。
1 | tail -f my-app.log |
若出现 Exec format error,说明目标 triple 与服务器 CPU 架构不一致(例如在 ARM 服务器上用了 x86_64 产物)。
若提示缺少 .so,多半是依赖仍链到了动态库,回到 依赖选型 检查是否用了 OpenSSL 或未 bundled 的 C 库。
扩展
gnu 目标:x86_64-unknown-linux-gnu 与 glibc 生态兼容更好,但在 Windows 上交叉链接通常比 musl + zigbuild 更折腾;若强依赖 glibc 动态库,可考虑 WSL、Docker 或 Linux CI 构建。
cross 工具:cross 基于容器封装交叉编译环境,适合依赖复杂、本机难以配齐 linker 的项目,代价是需安装 Docker 且构建较慢。
环境变量:脚本里可把 TARGET、DIST_DIR 做成可覆盖项,便于同一套脚本试不同 triple 或输出路径。
1 | $Target = if ($env:TARGET) { $env:TARGET } else { 'x86_64-unknown-linux-musl' } |
增量打包:重新构建时只替换二进制与 web/,不要删除服务器上已有的 data/ 与用户改过的 config.json,避免升级丢数据。
总结
- 安装
x86_64-unknown-linux-musltarget、cargo-zigbuild与 Zig。 - 在
Cargo.toml中尽量使用 rustls、bundled 等减少系统 C 库依赖。 - 用
cargo zigbuild --release --target x86_64-unknown-linux-musl生成 Linux 二进制。 - 将二进制、静态资源、配置与 LF 格式的启停脚本打入
dist-linux,整目录部署。 - 在目标 Linux 上用
file、ldd与一次完整启停验证产物是否可用。
musl 静态二进制体积可能略大于动态链接的 gnu 版,但「Windows 开发、Linux 上线、单文件可拷贝」的链路足够简单,适合多数自研后端与内部工具。