news 2026/7/11 21:29:58

Cargo 多平台 CI:用 GitHub Actions 同时跑 Linux、macOS、Windows 构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cargo 多平台 CI:用 GitHub Actions 同时跑 Linux、macOS、Windows 构建

Cargo 多平台 CI:用 GitHub Actions 同时跑 Linux、macOS、Windows 构建

标签:技术、Cargo、CI/CD、GitHub Actions、跨平台

一、"我只在 macOS 上编译了,应该都能跑吧?"

结果第二天就收到 Issue:Windows 用户说"双击没反应",Linux 用户说"缺少 GLIBC_2.29"。

那是第一次被现实教育:Rust 的"跨平台"不等于"一次编译到处运行"。Rust 提供的是源码层面的跨平台能力,二进制文件仍然和操作系统、CPU 架构、系统库绑定。

这件事之后,我才开始认真搭建跨平台的 CI 流水线。这篇文章就记录一下我的 GitHub Actions 配置文件是怎么一步步进化到能同时构建 Linux、macOS、Windows 三个平台 6 种目标(每个平台 x86_64 + ARM64)的。

二、CI 流水线的整体设计

动手写 YAML 之前,先理清楚整个流程。GitHub Actions 一个 Workflow 可以包含多个 Job,每个 Job 跑在一个独立的虚拟机上。

flowchart TB Push["git push / PR"] --> Trigger["触发 GitHub Actions"] Trigger --> Lint["Job 1: 代码检查(并行)"] Trigger --> Test["Job 2: 测试(并行)"] Trigger --> Build["Job 3: 编译(矩阵)"] Lint --> LintSteps["cargo fmt --check\ncargo clippy"] Test --> TestSteps["cargo test\n多平台测试"] Build --> Matrix["矩阵策略"] Matrix --> Linux_x86["Ubuntu + x86_64"] Matrix --> Linux_arm["Ubuntu + ARM64"] Matrix --> Mac_x86["macOS + x86_64"] Matrix --> Mac_arm["macOS + ARM64"] Matrix --> Win_x86["Windows + x86_64"] Matrix --> Win_arm["Windows + ARM64"] Linux_x86 --> Collect["收集产物"] Linux_arm --> Collect Mac_x86 --> Collect Mac_arm --> Collect Win_x86 --> Collect Win_arm --> Collect Collect --> Release["Job 4: 创建 GitHub Release"] Release --> Upload["上传 6 个平台的二进制文件"] style Build fill:#e3f2fd style Release fill:#c8e6c9 style Matrix fill:#fff3e0

整体分四个阶段,有依赖关系:

  • Lint 和 Test并行跑,先确认代码质量没问题再编译。
  • Build用矩阵策略自动生成 6 个编译任务,各平台互不影响。
  • Release必须等 Build 完成,收集所有产物后统一发布。

三、完整的 GitHub Actions 配置

下面是我的.github/workflows/release.yml完整配置,每一步都加了注释:

# ============================================================ # 多平台 CI/CD 工作流 # 触发条件:push 到 main 分支或创建 tag(如 v1.0.0) # 目标平台:Linux/macOS/Windows x x86_64/ARM64 # ============================================================ name: 多平台构建与发布 on: push: branches: [main] tags: ['v*'] # v1.0.0 这种 tag 会触发正式发布 pull_request: branches: [main] # PR 到 main 也触发,但不会发布 env: CARGO_TERM_COLOR: always # Cargo 输出保持彩色,便于阅读日志 # 二进制文件的名称,根据 Cargo.toml 里的 name 设置 BINARY_NAME: my-rust-cli # ============================================================ # Job 1:代码质量检查(不使用 cache 加速) # ============================================================ jobs: lint: name: 代码检查 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 安装 Rust 工具链 uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy # 需要这两个组件做格式检查和静态分析 - name: 运行 cargo fmt(格式检查) run: cargo fmt --all -- --check # --check:只检查不修改,格式不对就报错 - name: 运行 cargo clippy(代码检查) run: cargo clippy --all-targets --all-features -- -D warnings # -D warnings:把所有 clippy 警告当作错误处理 # ========================================================== # Job 2:测试 # ========================================================== test: name: 测试 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 安装 Rust 工具链 uses: dtolnay/rust-toolchain@stable - name: 缓存 Cargo 依赖和编译产物 uses: actions/cache@v4 with: path: | ~/.cargo/registry/ # 下载的 crate ~/.cargo/git/ # git 依赖 target/ # 编译产物 key: ${{ runner.os }}-cargo-test-${{ hashFiles('Cargo.lock') }} # key 基于 Cargo.lock 的哈希,依赖变化时自动重建缓存 - name: 运行测试 run: cargo test --all-features # ========================================================== # Job 3:多平台编译(矩阵策略) # ========================================================== build: name: 编译 ${{ matrix.target }} needs: [lint, test] # 等 lint 和 test 通过才编译,节省资源 runs-on: ${{ matrix.os }} strategy: # fail-fast: false —— 一个平台编译失败不影响其他平台继续 fail-fast: false matrix: include: # ------------------ Linux 平台 ------------------ - os: ubuntu-latest target: x86_64-unknown-linux-gnu # 使用 glibc 编译,兼容大多数 Linux 发行版 artifact_name: linux-amd64 - os: ubuntu-latest target: aarch64-unknown-linux-gnu artifact_name: linux-arm64 # ARM64 交叉编译需要额外安装工具链 setup_cross: true # ------------------ macOS 平台 ------------------ - os: macos-latest target: x86_64-apple-darwin artifact_name: macos-amd64 - os: macos-latest target: aarch64-apple-darwin artifact_name: macos-arm64 # ------------------ Windows 平台 ------------------ - os: windows-latest target: x86_64-pc-windows-msvc artifact_name: windows-amd64 # Windows ARM64 交叉编译在 GitHub Actions 上还不成熟 # 暂时跳过,等环境支持后再加回来 # - os: windows-latest # target: aarch64-pc-windows-msvc # artifact_name: windows-arm64 steps: - uses: actions/checkout@v4 - name: 安装 Rust 工具链 uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} # 安装目标平台的编译目标 # Linux ARM64 交叉编译需要安装 gcc-aarch64 等交叉编译工具 - name: 安装 Linux ARM64 交叉编译依赖 if: matrix.setup_cross == true run: | sudo apt-get update sudo apt-get install -y \ gcc-aarch64-linux-gnu \ g++-aarch64-linux-gnu \ libc6-dev-arm64-cross - name: 缓存 Cargo 依赖 uses: actions/cache@v4 with: path: | ~/.cargo/registry/ ~/.cargo/git/ target/ # 缓存 key 包含平台和目标,不同平台/目标的缓存独立 key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('Cargo.lock') }} - name: 编译(Release 模式) run: cargo build --release --target ${{ matrix.target }} # 配置交叉编译的目标链接器(仅 Linux ARM64 需要) - name: 配置交叉编译链接器 if: matrix.setup_cross == true env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc run: cargo build --release --target ${{ matrix.target }} # Windows 的可执行文件需要 .exe 后缀 - name: 确定产物文件名 id: artifact_name shell: bash run: | if [[ "${{ matrix.os }}" == "windows-latest" ]]; then echo "binary_name=${{ env.BINARY_NAME }}.exe" >> $GITHUB_OUTPUT else echo "binary_name=${{ env.BINARY_NAME }}" >> $GITHUB_OUTPUT fi - name: 打包产物(Linux/macOS 用 tar.gz,Windows 用 zip) shell: bash run: | cd target/${{ matrix.target }}/release if [[ "${{ matrix.os }}" == "windows-latest" ]]; then # Windows 用 zip 格式,Windows 用户解压更方便 7z a ../../../${{ matrix.artifact_name }}.zip \ ${{ steps.artifact_name.outputs.binary_name }} else # Linux/macOS 用 tar.gz tar -czf ../../../${{ matrix.artifact_name }}.tar.gz \ ${{ steps.artifact_name.outputs.binary_name }} fi cd ../../.. - name: 上传构件(用于后续 Release Job) uses: actions/upload-artifact@v4 with: name: ${{ matrix.artifact_name }} path: | ${{ matrix.artifact_name }}.tar.gz ${{ matrix.artifact_name }}.zip # 保留 7 天,Release 完成后可以删掉 retention-days: 7 # ========================================================== # Job 4:创建 GitHub Release 并上传产物 # 只在创建 tag 时触发,普通 push 不执行 # ========================================================== release: name: 创建 Release needs: [build] if: startsWith(github.ref, 'refs/tags/') # 只有 tag push 才执行 runs-on: ubuntu-latest # 需要写入 Release 的权限 permissions: contents: write steps: # 下载所有 build job 产出的构件 - name: 下载所有平台产物 uses: actions/download-artifact@v4 with: path: artifacts # artifacts/ 下会按名称分类: # artifacts/linux-amd64/ # artifacts/macos-arm64/ # ... - name: 显示产物结构 run: find artifacts -type f - name: 生成 Release 说明 id: release_body run: | # 从 git tag 中提取版本号(去掉 v 前缀) VERSION=${GITHUB_REF#refs/tags/v} # 生成 Release 正文 cat > release_body.md << EOF ## 下载 根据你的操作系统和架构选择对应文件: | 平台 | 架构 | 文件 | |------|------|------| | Linux | x86_64 | linux-amd64.tar.gz | | Linux | ARM64 | linux-arm64.tar.gz | | macOS | x86_64 | macos-amd64.tar.gz | | macOS | ARM64 | macos-arm64.tar.gz | | Windows | x86_64 | windows-amd64.zip | ## 安装 \`\`\`bash # 下载对应平台的压缩包 # Linux/macOS: tar -xzf 平台名.tar.gz chmod +x ${{ env.BINARY_NAME }} ./${{ env.BINARY_NAME }} --help # Windows: # 解压 zip 文件,双击运行或从命令行执行 \`\`\` EOF echo "body_path=release_body.md" >> $GITHUB_OUTPUT - name: 创建 GitHub Release uses: softprops/action-gh-release@v2 with: # 使用 release_body.md 的内容作为 Release 说明 body_path: ${{ steps.release_body.outputs.body_path }} # 自动生成 Release 标题 name: ${{ github.ref_name }} # 标记为非预发布 prerelease: false # 上传 artifacts 目录下所有压缩文件 files: artifacts/**/*.tar.gz artifacts/**/*.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

四、几个踩过的坑

坑 1:GLIBC 版本不兼容

Rust 在 Ubuntu 最新版上编译出来的二进制,可能依赖高版本的 GLIBC,在 CentOS 7 这些老系统上就跑不了。

解决方法:用x86_64-unknown-linux-musl编译目标代替gnu,musl 是静态链接的,不依赖系统的 glibc 版本。或者在较老版本的 Ubuntu(如 ubuntu-20.04)上编译,向后兼容更好。

# 用 musl 静态编译的配置 - os: ubuntu-latest target: x86_64-unknown-linux-musl artifact_name: linux-amd64-static # 需要先安装 musl 工具链 - name: 安装 musl 编译工具 run: | sudo apt-get update sudo apt-get install -y musl-tools

坑 2:ARM64 macOS 交叉编译不需要额外配置

好消息:macOS 的 ARM64 编译不需要交叉编译工具链。GitHub Actions 的macos-latest跑的是 ARM64 虚拟机,直接编译就行。

坑 3:Windows 编译的 OpenSSL 依赖

如果你的 Rust 项目依赖了 OpenSSL(很多网络库都会),Windows 上编译会找不到头文件。要么用rustls代替native-tls,要么在 CI 里安装 OpenSSL。

// Cargo.toml 里用 rustls 代替默认的 native-tls # 不用这个: # reqwest = { version = "0.12", features = ["native-tls"] } # 用这个,不依赖系统 OpenSSL,跨平台编译更省心: reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
flowchart LR A["项目依赖 TLS"] --> B{"tls 后端选哪个?"} B -->|"native-tls"| C["依赖系统 OpenSSL"] C --> C1["Linux: 装 libssl-dev"] C --> C2["macOS: 系统自带"] C --> C3["Windows: 需要手动装"] B -->|"rustls"| D["纯 Rust 实现"] D --> D1["所有平台零配置"] D --> D2["跨平台 CI 省心"] style C fill:#ffcdd2 style D fill:#c8e6c9

坑 4:缓存策略设计

GitHub Actions 的缓存有 10GB 总容量上限(免费版)。如果你的矩阵生成 6 个平台,每个平台缓存 1-2GB,轻松爆容量。

我的做法是:最频繁变动的 target/ 目录不缓存,只缓存 ~/.cargo/registry/ 和 ~/.cargo/git/。依赖下载是编译里最慢的部分,缓存它们就能拿到最大收益。

我在项目里就遇到过一次:缓存 key 没区分平台,macOS 和 Linux 的 target/ 缓存互相污染,编译报了一堆找不到系统库的错。后来把runner.os加进缓存 key,每个平台的缓存完全隔离,再没出过问题。

还有一点:macOS 的 GitHub Actions runner 默认 shell 是 zsh,但很多脚本默认写 bash 语法。直接run: cargo build没问题,一旦加了if或数组操作就可能报错。建议关键 step 显式指定shell: bash

五、总结

这个 CI 配置我从最开始的单平台 30 行 YAML,慢慢迭代到了现在的多平台 200 行。每一次改动都是因为踩了一个具体的坑,然后去翻文档、查 StackOverflow、看别人的配置学来的。

几点最重要的心得:

  1. 矩阵策略是 GitHub Actions 最强大的功能。不需要 6 份重复的配置,一份matrix.include搞定。
  2. fail-fast: false 很重要。一个平台编译失败不等于你的代码有问题,可能只是 CI 环境抽风。
  3. 缓存策略需要精心设计。缓存 key 的设计、缓存什么不缓存什么,直接影响 CI 速度。
  4. 尽可能用 rustls 代替 native-tls。对于需要跨平台编译的 Rust 项目,这能省掉至少 30% 的环境配置工作。
  5. GLIBC 版本问题是 Linux 分发的头号陷阱。用 musl 静态编译或者老版本 Ubuntu 编译来解决。

我还是自学编程的新手,CI/CD 这块是最近才开始认真搞的。上面的配置肯定还有不少可以优化的地方,如果你有更好的实践,欢迎在评论区分享。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/11 21:29:37

大数据转大模型:把边界和取舍讲清楚

这篇不先堆名词。我们把《大数据转大模型&#xff1a;一次新的项目切入》拆成几级台阶&#xff0c;看完至少知道下一步该学什么、该练什么。摘要先把这篇文章的目标说清楚&#xff1a;看完之后&#xff0c;你应该能判断这件事值不值得做&#xff0c;以及从哪里动手。最近跟几个…

作者头像 李华
网站建设 2026/7/11 21:29:09

GTA V终极防护增强:YimMenu完全指南

GTA V终极防护增强&#xff1a;YimMenu完全指南 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu 你是否厌…

作者头像 李华
网站建设 2026/7/11 21:29:06

DeepMind COrigami:神经符号计算实现AI自动化折纸设计

在传统折纸艺术与人工智能的交叉领域&#xff0c;设计师们长期面临一个核心挑战&#xff1a;如何将抽象的创意概念转化为既美观又符合物理折叠规律的复杂三维结构。Google DeepMind最新发布的COrigami框架通过神经符号计算架构&#xff0c;将Gemini多模态大模型与强化学习&…

作者头像 李华
网站建设 2026/7/11 21:27:23

万级TPS金融信贷实时风控架构优化:从串行阻塞到20ms极速风控落地

前言金融互联网信贷、支付系统是对延迟、稳定性、准确性要求最严苛的业务场景&#xff0c;没有之一。不同于互联网普通业务&#xff0c;金融风控有一个硬性铁律&#xff1a;每一笔借贷、每一次支付交易&#xff0c;必须在毫秒级完成风险判定&#xff0c;不允许重试、不允许超时…

作者头像 李华
网站建设 2026/7/11 21:27:18

SCADA 系统架构演进解析:从单体到网络化的3代技术变迁与安全挑战

SCADA系统架构演进解析&#xff1a;从单体到网络化的3代技术变迁与安全挑战工业自动化领域的心脏——SCADA&#xff08;数据采集与监视控制&#xff09;系统&#xff0c;在过去半个世纪中经历了从封闭式控制到开放互联的范式转变。本文将深入剖析三代SCADA架构的技术特征、典型…

作者头像 李华
网站建设 2026/7/11 21:26:05

工业级负载控制方案:TPD2015FN与PIC18F47J53应用解析

1. 项目概述&#xff1a;工业级负载控制方案设计在工业自动化、机器人控制等高需求环境中&#xff0c;电感和电阻负载的精确控制一直是工程师面临的挑战。TPD2015FN智能高侧开关与PIC18F47J53微控制器的组合&#xff0c;为解决这一问题提供了可靠的技术方案。这套系统特别适用于…

作者头像 李华