【AI开发之Rust】第 22 课:一键多平台与工程收尾 —— CI、发布检查与结课

22.1 这节课解决什么问题

第 21 课的两条管线能手工跑通,但"手工"不可发布------人总会漏命令、漏版本。本课把它变成可复现、可审计、一键执行的工程:

复制代码
① scripts/build-all.sh:一条命令出 Android AAR + iOS xcframework(+ 桌面库)
② CI(GitHub Actions macOS runner):push 即自动出全平台包 + 跑测试
③ release 构建调优与发布前 checklist
④ 知识回顾图 + 结课作业

💡 本课反复出现一个词:可复现(reproducible)。发布物能由"仓库 + 固定版本 + 一条命令"重建,才谈得上灰度、回滚与多人协作。所有版本号(NDK、Xcode、Rust toolchain、uniffi)都要钉死并写进文档/脚本。

22.2 一条命令出全平台:build-all.sh

把第 21 课的手工命令固化成脚本(放在仓库根 scripts/):

bash 复制代码
#!/usr/bin/env bash
set -euo pipefail                    # 任何一步失败立刻停

# 0) 版本与工具链钉死(可复现的关键)
RUST_VERSION="1.85.0"                # 或 rust-toolchain.toml 钉版本
UNIFFI_VERSION="0.29.0"
ANDROID_TARGETS=("aarch64-linux-android" "x86_64-linux-android")
IOS_TARGETS=("aarch64-apple-ios" "aarch64-apple-ios-sim")

echo "==> [1/6] 测试 core"
cargo test -p my-ai-core

echo "==> [2/6] Android .so"
cargo ndk -t arm64-v8a -t x86_64 -o android-out/jniLibs build -p my_ai_ffi --release

echo "==> [3/6] iOS 静态库 + xcframework"
rustup target add "${IOS_TARGETS[@]}"
for t in "${IOS_TARGETS[@]}"; do
  cargo build -p my_ai_ffi --release --target "$t"
done
rm -rf ios-out/MyAiCore.xcframework
xcrun xcframework create \
  -library target/aarch64-apple-ios/release/libmy_ai_ffi.a \
  -library target/aarch64-apple-ios-sim/release/libmy_ai_ffi.a \
  -output ios-out/MyAiCore.xcframework

echo "==> [4/6] 生成绑定"
uniffi-bindgen generate src/lib.rs \
  --library target/aarch64-linux-android/release/libmy_ai_ffi.so \
  --language kotlin --out-dir android-out/kotlin
uniffi-bindgen generate src/lib.rs \
  --library target/aarch64-apple-ios/release/libmy_ai_ffi.a \
  --language swift --out-dir ios-out/swift

echo "==> [5/6] 冒烟(Python 通道,21 课保留的回归脚本)"
python3 ../../scripts/smoke.py

echo "==> [6/6] 产物清单"
find android-out ios-out -type f | sort

echo "全部完成 ✅"

配套约定:

复制代码
脚本幂等:重复执行结果一致(先删旧产物再生成)
产物不进 git:.gitignore 加 android-out/ ios-out/ target/
文档同步:docs/build-notes.md 记录工具版本与踩坑(21 课练习 7)

💡 iOS 步骤在非 macOS 上直接报错(xcrun 不存在)------这正是 CI 要解决的:用 macOS runner 跑全流程 ,开发者本地跑 cargo test 即可,出包交给 CI。

22.3 CI:push 即出全平台包

GitHub Actions 用 macOS runner(能同时编 iOS/Android/桌面):

yaml 复制代码
# .github/workflows/release.yml
name: build-all
on:
  push:
    tags: ["v*"]            # 打 tag 才触发正式出包

jobs:
  build:
    runs-on: macos-latest    # ★ 必须 macOS:iOS 交叉编译需要 Xcode

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: aarch64-linux-android,x86_64-linux-android,
                   aarch64-apple-ios,aarch64-apple-ios-sim

      - name: Install NDK + cargo-ndk
        run: |
          brew install --cask android-commandlinetools  # 或 actions/setup-android
          cargo install cargo-ndk

      - name: Test core
        run: cargo test --workspace

      - name: Build all
        run: bash scripts/build-all.sh

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: my-ai-${GITHUB_REF_NAME}
          path: |
            android-out/
            ios-out/

CI 三要义:

复制代码
① 环境与本地一致:toolchain/NDK/Xcode 版本都钉死(否则本地绿、CI 红)
② 双保险:先 cargo test,再 build-all(把"测试"与"出包"同一次跑完)
③ 产物可下载:artifacts/Release 挂包,方便测试同学与双端 Demo 直接拿

💡 版本演进纪律:tag v0.1.0 出的包即对外契约。共享核心要遵守 semver------任何对 UI 可见的 API 变更都 bump 大版本,并在 release notes 里写迁移说明(17 课"导出=冻结签名"的工程版)。

22.4 release 构建调优与产物体检

22.4.1 profile.release 全套

toml 复制代码
[profile.release]
opt-level = 3              # 速度优先(网络/解析类代码建议)
lto = true                 # 全程序链接优化(跨 crate 内联,减体积)
codegen-units = 1          # 单编译单元,配合 LTO 效果最好
panic = "abort"            # FFI/发布:abort 比 unwind 更省且不会把栈 unwind 漏出去
strip = "symbols"          # 去掉符号表,显著减体积(注意:栈回溯会没名字)

⚠️ 权衡:panic = "abort" 会让 catch_unwind(16 课 FFI 兜底)失效------发布版通常可接受(core 已用错误返回代替 panic),但要在测试环境用默认 unwind 跑一遍全测试。strip 同理:想保留崩溃符号就换成 strip = "debuginfo" 或单独出符号文件。

22.4.2 出包后体检清单(发布前逐项过)

bash 复制代码
# 大小
du -sh android-out ios-out
# 架构(每片都要对)
file ios-out/MyAiCore.xcframework/ios-arm64/libmy_ai_ffi.a
# 版本/符号(可选)
strings libmy_ai_ffi.so | grep -i version
检查项 通过标准
cargo test --workspace 全绿
Python 冒烟 通过(19 课保留的回归通道)
debug 与 release 各测一次核心 行为一致
产物架构正确 file/readelf 显示目标平台
包体在预期范围 记录基线,后续版本比较涨幅
无明文 http 端点 ATS/网络安全配置就位
隐私清单 网络/用户数据声明齐全
日志与崩溃上报 tracing 落盘或接入平台上报;RUST_BACKTRACE 仅 debug

22.5 桌面 / Web 的快速扩展(同核心,再附赠平台)

同一 core 顺手出桌面与 Web,让"一次编写"兑现到更多平台:

bash 复制代码
# 桌面:主机库 + 各语言绑定(第 16 课 ctypes / UniFFI 都有现成通道)
cargo build -p my_ai_ffi --release          # libmy_ai_ffi.dylib/.dll/.so
uniffi-bindgen generate src/lib.rs --library target/release/libmy_ai_ffi.dylib \
  --language python --out-dir desktop-out/python   # 或 swift/kotlin

# 桌面整包方案:Tauri(Rust 主进程 + Web UI)可整包复用 core ------ 介绍即可
# Web 子集:wasm32 可复用"模型 + 校验 + 存储接口",网络/SSE 走浏览器原生
rustup target add wasm32-unknown-unknown

💡 范围声明:本课不展开 wasm 的线程与 IO 适配(那是另一个话题)。重点是展示架构红利------UI 换壳,core 不动:移动(21 课)、桌面(本课)、Web 全部消费同一份 17-19 课资产。

22.6 发布前内存 / 安全 / 质量手册(精简版)

内存与线程

  • core 单例持有;raw 对象不跨线程共享;锁只在 SQL 语句内;
  • 所有跨 FFI 的分配/释放走 UniFFI 成对生成,禁止手写 free;
  • 长连接/轮询在页面退出时取消(未来可 drop 即停)。

安全

  • API key / token 只存系统钥匙串 / Android Keystore,不进 core 也不进 git;
  • 所有出网走 https;参数化 SQL(18 课);不信任上游 JSON(14 课缺字段即报错);
  • 会话删除要连消息一起删(数据库 CASCADE + service 双保险)。

质量

  • 提交前链条:fmt → clippy -D warnings → test(10 课)+ 打 tag 前跑 build-all;
  • 每次升级 uniffi/NDK/工具链都记录在 docs/,回滚有据。

常见坑清单(终极版)

坑 一句话解法
NDK 版本不一致 钉版本:ANDROID_NDK_HOME + CI 一致
iOS bitcode 新 Xcode 已默认关;别手动开
回调线程 壳侧统一回主线程(20 课)
release 优化缺失 [profile.release] 四件套
忘 strip 包体虚胖;上线对比基线
忘记版本对齐 uniffi crate == uniffi_bindgen 版本
debug/release 不一致 两档都跑核心测试

22.7 🗺️ 全课程知识地图(回顾)

复制代码
基础篇 01--10:语言地基
  01 环境 → 02 语法 → 03 字符串/复合类型 → 04 所有权 → 05 生命周期
  → 06 struct/enum/方法 → 07 错误处理 → 08 trait/泛型 → 09 迭代器/闭包 → 10 工程化

高级篇 11--16:并发与跨平台零件
  11 智能指针 → 12 线程并发 → 13 async/tokio → 14 网络+JSON
  → 15 SSE 流式 → 16 FFI 手写原理

实战篇 17--22:AI 助手共享核心(小灵)
  17 架构(core 分层)→ 18 SQLite 存储 → 19 UniFFI 导出 → 20 壳封装
  → 21 Android/iOS 出包 → 22 CI 与发布收尾

能力闭环:
  建模 → 持久化 → 网络/流式 → 跨端导出 → 出包发布

22.8 📝 结课作业

给你自己的一个 App 场景(天气、记账、笔记、或继续打磨小灵)按本架构完整交付:

复制代码
1. 规划:一句话业务 + FFI 边界(哪些进 Rust、哪些留 UI)
2. 建模:核心类型 + 两个 trait(存储/外部服务)+ 一个用例层
3. 实现:core 用假实现让 service 测试全绿
4. 持久化:换 sqlite 实现,测试零改动
5. 网络:真/假客户端 + 超时重试 + 流式(如业务需要)
6. 绑定:UniFFI 导出 + Python 冒烟通过
7. 壳:一端 UI(SwiftUI/Kotlin/TS 任一)完整跑通
8. 收尾:build-all.sh + CI 出包 + release checklist 过一遍
9. 提交一份 README:架构图、命令、版本、已知限制

验收门禁(课程毕业标准):

  • 能独立讲 5 分钟"所有权如何保障我的共享核心安全";
  • 能向队友解释"为什么 UI 层不该碰网络与 DB";
  • 一份核心,能在 ≥2 个平台出包并被调用;
  • 遇到 panic/锁/链接错误时,能定位到 04/12/16/21 的对应知识并给出解法。

✅ 本节小结(课程收官)

  • 可复现:脚本 + CI 把双端出包变成"一条命令 + 固定版本";
  • release 调优 :opt-level/LTO/codegen-units/panic/strip 权衡取舍;出包前体检清单;
  • 多平台红利:移动(21 课)+桌面+Web 消费同一 core,壳随便换;
  • 工程纪律:semver 管理导出契约、文档记录工具版本、上线 checklist;
  • 你的收获:从"Hello World"到"一份 Rust 核心打进 Android/iOS 双端包"全流程;
  • 接下来 :可以继续深入的方向------Rust 网络性能调优(hyper/axum 服务端)、flutter_rust_bridge(Flutter 生态)、共享核心的增量发布与热更新、或者回头用本课程的知识重构手头另一个项目。

祝贺你完成《Rust 跨平台业务核心开发》22 课! 现在你不仅"会写 Rust",而且能用它交付真实的多端产品。保持练习,把结课作业做成一个能拿得出手的作品,就是最好的下一课。🚀

相关推荐
一条小小yu8 小时前
Spring IoC的理解
java·后端·spring
落魄实习生8 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
茉莉玫瑰花茶9 小时前
GO [ 方法 ]
开发语言·后端·golang
Wx-bishekaifayuan9 小时前
django个性化旅游路线推荐平台49005-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
Eric_见嘉9 小时前
在职前端 Skill 和 MCP 分享
前端·后端·agent
狗凯之家源码网9 小时前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php
孟健11 小时前
出海开发者资金合规:从港卡结汇到完税申报实操
后端·架构
乌暮11 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
架构技术专栏11 小时前
Skill 有效性与稳定性评估:用对照、重复与证据判断价值
后端