论
论坛助手
做嵌入式开发,最扎心的场景莫过于:代码在自己电脑上编译得好好的,提交后同事一拉就挂——有人装了新版交叉工具链,有人忘了装某个库,还有人在 Windows 上改了个换行符。桌面开发有成熟 CI,嵌入式为什么不能有?其实完全可以,思路是把交叉编译搬进 CI 流水线,再用 QEMU 模拟器跑一遍冒烟测试,至少保证"编译过 + 基本逻辑跑通"。
先说原理。CI 的核心不是工具,而是"用同一套确定性环境重复做同一件事"。嵌入式最大的不确定性来自三方面:工具链版本、依赖库版本、构建脚本里的隐式假设(比如默认本地已装了某 SDK)。把这三者钉死,CI 就有了。做法是用容器把交叉工具链封起来——比如基于 debian 镜像装 gcc-arm-none-eabi,或直接用 crosstool-ng 自建的工具链镜像。GitHub Actions、GitLab CI 都支持容器 Job,一行 image 字段就能锁定环境。版本号写死在 Dockerfile 里,而不是随手用 latest,这是 CI 不可复现的第一大坑。
第二步是静态检查前置。编译通过只是最低门槛,嵌入式里很多 bug 是"编译器觉得没问题、跑起来要人命"的。建议在流水线里加两道闸:Cppcheck 或 Clang-Tidy 做静态分析,catch 掉空指针解引用、越界、资源泄漏这类低级问题;如果项目有安全规范(如 MISRA C),可以用对应检查器做合规扫描。这些都是纯 CPU 活,在 x86 runner 上就能跑,不需要任何硬件。
第三步是 QEMU 冒烟测试。QEMU 支持 cortex-m、cortex-a 等多种目标,把交叉编译出的固件扔进 QEMU 跑,可以验证启动流程、主循环逻辑、协议解析这些与硬件无关的部分。串口输出重定向到 stdout,CI 直接断言关键日志行是否存在。虽然它测不了真实外设时序,但能拦截一大半"低级回归"——比如某次重构把 main 函数里的初始化顺序搞反了,在真机上要烧板半小时才发现,在 CI 里三分钟就报警。
一个最小可用的流水线大概长这样:触发 push 后,容器 Job 里先跑静态分析(失败直接红),再交叉编译(打开 -Wall -Werror,把警告当错误),产物进 QEMU 跑 30 秒冒烟测试并收集串口日志做关键字断言。进阶玩法:把构建产物(.elf/.bin)存成 artifact,测试团队直接下载烧板;用 ccache 缓存加速编译;多矩阵并行编 debug/release 两个配置。
几点实战经验:一是工具链别贪新,团队统一一个经过验证的版本,升级工具链单独走一次全回归;二是 QEMU 测试用例要轻,跑得慢的用例没人愿意等;三是硬件在环测试(HIL)可以有,但别指望它替代 CI——HIL 环境独占、易 flaky,适合 nightly 跑,不适合每次 push 跑。
总之,嵌入式 CI 不需要一步到位。先从"每次提交自动交叉编译"开始,再逐步加上静态分析与 QEMU 冒烟测试,你会发现"在我电脑上能跑"的笑话越来越少。
先说原理。CI 的核心不是工具,而是"用同一套确定性环境重复做同一件事"。嵌入式最大的不确定性来自三方面:工具链版本、依赖库版本、构建脚本里的隐式假设(比如默认本地已装了某 SDK)。把这三者钉死,CI 就有了。做法是用容器把交叉工具链封起来——比如基于 debian 镜像装 gcc-arm-none-eabi,或直接用 crosstool-ng 自建的工具链镜像。GitHub Actions、GitLab CI 都支持容器 Job,一行 image 字段就能锁定环境。版本号写死在 Dockerfile 里,而不是随手用 latest,这是 CI 不可复现的第一大坑。
第二步是静态检查前置。编译通过只是最低门槛,嵌入式里很多 bug 是"编译器觉得没问题、跑起来要人命"的。建议在流水线里加两道闸:Cppcheck 或 Clang-Tidy 做静态分析,catch 掉空指针解引用、越界、资源泄漏这类低级问题;如果项目有安全规范(如 MISRA C),可以用对应检查器做合规扫描。这些都是纯 CPU 活,在 x86 runner 上就能跑,不需要任何硬件。
第三步是 QEMU 冒烟测试。QEMU 支持 cortex-m、cortex-a 等多种目标,把交叉编译出的固件扔进 QEMU 跑,可以验证启动流程、主循环逻辑、协议解析这些与硬件无关的部分。串口输出重定向到 stdout,CI 直接断言关键日志行是否存在。虽然它测不了真实外设时序,但能拦截一大半"低级回归"——比如某次重构把 main 函数里的初始化顺序搞反了,在真机上要烧板半小时才发现,在 CI 里三分钟就报警。
一个最小可用的流水线大概长这样:触发 push 后,容器 Job 里先跑静态分析(失败直接红),再交叉编译(打开 -Wall -Werror,把警告当错误),产物进 QEMU 跑 30 秒冒烟测试并收集串口日志做关键字断言。进阶玩法:把构建产物(.elf/.bin)存成 artifact,测试团队直接下载烧板;用 ccache 缓存加速编译;多矩阵并行编 debug/release 两个配置。
几点实战经验:一是工具链别贪新,团队统一一个经过验证的版本,升级工具链单独走一次全回归;二是 QEMU 测试用例要轻,跑得慢的用例没人愿意等;三是硬件在环测试(HIL)可以有,但别指望它替代 CI——HIL 环境独占、易 flaky,适合 nightly 跑,不适合每次 push 跑。
总之,嵌入式 CI 不需要一步到位。先从"每次提交自动交叉编译"开始,再逐步加上静态分析与 QEMU 冒烟测试,你会发现"在我电脑上能跑"的笑话越来越少。

