第五章 现在,你是建筑师
5.1 eng-lite项目概述——嵌入式工程工具链
打开那个目录
凌晨两点,你被拉进一个紧急问题群。生产线下线检测时,三台ECU的CAN通信偶发超时。群里的消息刷得飞快,有人怀疑是PHY芯片批次问题,有人说是PCB走线阻抗不匹配,还有人说“看门狗复位了“。你打开终端,cd进项目目录,敲下tree -L 2。
输出很安静,没有两千行的Makefile怪物,没有嵌套八层的头文件依赖地狱。你看到的是这样一个目录结构:
eng-lite/
├── Makefile
├── README.md
├── project.yml
├── .cppcheck-suppress
├── include/
│ └── sensor.h
├── src/
│ ├── main.c
│ └── sensor.c
├── tests/
│ ├── test_sensor.c
│ └── test_main.c
└── scripts/
├── check.sh
└── trace.py
配套的 CI 流水线配置位于仓库根目录的 .github/workflows/ci.yml——GitHub Actions 只识别仓库根目录的 workflow 文件,它会在每次 push 时自动执行 make ci 的完整流水线。
全部加起来700多行代码。但就是这700多行,覆盖了一个嵌入式软件项目工程基础设施的全部关键环节:模块化构建系统、静态分析门禁、单元测试框架、覆盖率报告生成、CI流水线、需求追溯矩阵。
你花了三分钟浏览了一遍,心里有了底。这个项目的工程骨架是健康的,不是那种“能编译就行“的裸奔代码,也不是那种过度工程化的配置地狱。它恰好在那个甜点上:够用,够清晰,够可靠。
这不是巧合,是我们在造车时学到的一课:好的工程基础设施,应该像一栋楼的承重墙,平时你感觉不到它,但风暴来临时,你知道它可以信赖。
核心洞察:健康的工程基础设施像承重墙——平时感觉不到,风暴来临时才知道它可靠。 700多行的eng-lite覆盖了构建、静态分析、测试、覆盖率、CI、追溯全部关键环节,它不是功能堆砌,而是落在“裸奔代码“与“配置地狱“之间的甜点上:够用、够清晰、够可靠。这正是区分项目工程骨架是否健康的试金石。
什么是eng-lite
eng-lite是一个教学级的嵌入式软件工程工具链。它不依赖任何真实ECU硬件,不要求你有一块开发板,甚至不需要ARM交叉编译工具链,一切都在你的Linux笔记本上运行。它的目标是让你在不被硬件调试器折磨的情况下,完整体验一个嵌入式项目从编码到CI的完整工程闭环。
这个名字的由来:「eng」取自engineering,「lite」表示它是轻量级教学版本。全称可以理解为“工程基础设施精简版“。就像学建筑的学生先做小比例模型,而不是直接盖一栋真楼,但模型里的每一根梁、每一处节点,都和真实建筑遵循相同的力学原理。
eng-lite覆盖了什么
| 工程环节 | eng-lite中的实现 | 对应生产实践 |
|---|---|---|
| 构建系统 | GNU Make,多目标,自动依赖 | Vector GlobalMake、CMake |
| 静态分析 | Cppcheck + 自定义规则 | PRQA QA-C、Polyspace |
| 单元测试 | Ceedling/Unity,Mock框架 | VectorCAST、Tessy |
| 覆盖率 | gcov → gcovr → HTML报告 | 同款工具链,ISO 26262要求 |
| CI流水线 | GitHub Actions YAML | Jenkins、GitLab CI、Bitbucket Pipelines |
| 需求追溯 | trace.py自定义脚本 | DOORS、Polarion、Reqtify |
eng-lite特意省略了什么
这是教学项目,不是产品。它刻意省略了以下内容:
- 真实ECU硬件:没有AUTOSAR RTE,没有CAN驱动,没有NVM。sensor模块用硬编码的静态数组模拟ADC读数:/* TEACHING: 生产代码中这里是MCAL层的ADC驱动调用 */
- HIL测试环境:没有硬件在环仿真。但CI配置中预留了HIL阶段的接口注释。
- ISO 26262认证文档:trace.py生成的是教学级追溯矩阵,不是认证级安全档案。
- 多核/多分区:不涉及AUTOSAR的OS-Application隔离或内存保护。
- 功能安全:没有安全状态机、没有E2E保护、没有看门狗管理。
知道省略了什么,比知道包含了什么更重要。正如一个建筑师必须清楚他的结构计算做了哪些简化假设。
核心洞察:知道省略了什么,比知道包含了什么更重要。 eng-lite刻意省略真实ECU、HIL、ISO 26262认证文档、多核与功能安全,并非偷工减料,而是教学项目的刻意简化——这些都不是工程基础设施的本质,而是基础设施要管理的对象。像建筑师必须清楚结构计算的简化假设一样,理解了省略的边界,才算真正理解一套工具链。
使用方式
前置条件
一台运行Linux的电脑(物理机、虚拟机、WSL均可),安装以下工具:
# 编译器
sudo apt install build-essential gcc
# 静态分析
sudo apt install cppcheck
# 覆盖率工具(gcovr,通过pip安装)
pip3 install --user gcovr
# 测试框架(Ruby gem)
sudo apt install ruby
# 注意:必须锁定 0.31.1。最新版 ceedling (>= 1.0) 要求 Ruby >= 3.2,
# 在 Ubuntu 20.04/22.04 自带的 Ruby 2.7 上无法安装。
sudo gem install ceedling -v 0.31.1
# Python(用于追溯脚本)
python3 --version # 应该已安装
快速开始
在开始之前,理解一个关键心态:eng-lite不是用来“学习Makefile语法“或“学习Cppcheck命令“的。这些可以通过搜索引擎在5分钟内找到。eng-lite是用来让你建立“工程基础设施在整体上应该如何运转“的直觉的。
这种直觉很难通过阅读文档获得。它和学骑自行车一样:看再多理论都没有亲自骑上去的体验来得深刻。当你亲手跑通make ci看到五个绿色通过提示时,你的大脑会将这种体验与“正确的工程实践“建立连接。将来你在真实项目中面对一个只做了编译检查的CI流水线时,你会自然地觉得“少了点什么“,因为你的大脑已经记住了完整的五环结构。
cd eng-lite
# 1. 编译项目
make all
# 2. 运行可执行文件(验证sensor模块行为)
./build/bin/sensor_app
# 3. 运行静态分析
make check
# 4. 运行单元测试
make test
# 5. 生成覆盖率报告
make coverage
# 打开 build/coverage/index.html 查看
# 6. 运行需求追溯
make trace
# 查看 build/traceability.csv
# 7. 一键全流程(模拟CI)
make ci
构建产物
所有构建产物都放在build/目录下,确保make clean可以一键清空。这是嵌入式工程的基本纪律:不要让构建产物污染源码树。见过太多项目,.o文件和.c文件混在一起,版本库里还提交了.elf文件。这不是风格问题,这是工程素养问题。
核心洞察:eng-lite不是用来学工具语法的,而是用来建立“工程基础设施整体如何运转“的直觉。 亲手跑通
make ci的五环结构,比读任何文档都更能把“正确的工程实践“写进大脑;与此同时,所有产物统一收进build/、make clean一键清空,守住了“不要让构建产物污染源码树“这一基本工程纪律。
为什么“教学级“不是降级
很多人听到“教学项目“四个字,第一反应是“简化版、阉割版、不实用“。这是一个根深蒂固的误解。
教学项目的价值恰恰在于它的简化。简化是剥离噪音,让核心架构显现出来。eng-lite剥离的是什么?剥离的是芯片厂商SDK里那8000行你永远用不到的HAL代码,剥离的是AUTOSAR配置工具生成的30000行宏展开,剥离的是特定编译器的非标准扩展和链接脚本的特殊语法。这些在生产项目中必须存在,但它们不是工程基础设施的本质,它们是基础设施要管理的对象。
留下的这700多行代码,每一行都服务于一个核心教学目的:让你理解嵌入式软件工程的五个核心环节为什么存在,它们如何协同工作,以及如果你从零开始设计一个工程的质控流程,你会怎么组织它们。
这就好比建筑系的第一个设计作业:盖狗窝。狗窝不是真实建筑,但它的每一个立面、每一处节点、每一次荷载计算,和你未来设计一百层高楼时使用的是同样的力学原理。你在狗窝上学会判断一个结构是否合理的直觉,会伴你走过整个职业生涯。
同样的道理,你在eng-lite上学到的“构建系统应该是新人15分钟能读懂的“,“静态分析门禁是自动化纪律而非惩罚”,“单元测试的价值在三个月后体现”。这些洞察,无论你将来用的是Vector的工具链还是ETAS的工具链,是ASIL A的灯光控制器还是ASIL D的制动控制器,都同样成立。
eng-lite设计中的五个刻意取舍
1. 本地编译,不交叉编译。 这意味着你不需要安装ARM-GCC、不需要配置调试器、不需要操心JTAG连接。但CC ?= gcc那行代码的设计哲学是通用的:容器化的构建系统、Yocto/BitBake的交叉编译SDK、Vitis的统一工具链配置,本质上都在解决同一个问题:“如何在不修改Makefile的前提下切换编译器?”
2. 硬编码静态数组模拟ADC,不接真实硬件。 sensor模块不从硬件寄存器读取模拟量,而是使用静态数组。在一个完整的产品代码中,这层抽象应该由AUTOSAR MCAL提供。eng-lite省略MCAL层级是因为它对理解“传感器滤波状态机“这个核心逻辑并不关键。好代码的标志之一就是:算法逻辑和硬件访问是隔离的。 eng-lite通过刻意简化恰好示范了这一点。
3. Ceedling + Unity而非VectorCAST+Tessy。 Unity的单头文件设计让它可以在没有操作系统、没有标准库的裸机核心上运行,这正是嵌入式单元测试的硬需求。商业工具的附加价值在于ISO 26262工具鉴定、需求追溯集成、认证报告生成,这些都是eng-lite省略的内容,但你可以清晰地看到它们应该在何处插入。
4. GitHub Actions而非Jenkins+Bitbucket Pipelines。 CI的本质是“让构建、检查、测试这三个动作在每次代码变更后自动执行“。GitHub Actions的YAML是最干净的表态方式:你可以一眼看到整个流水线的结构。换成其他平台,语义一致,语法不同。
5. trace.py而非DOORS。 一条不到100行的Python脚本不可能替代完整的ALM(Application Lifecycle Management)系统。但它示范了一个核心原理:追溯矩阵的本质是一张“需求ID→实现位置→测试位置“的映射表。无论用DOORS的拖拽链接还是用Reqtify的自动解析,底层数据结构都是这张表。理解底层数据结构的人,不会被上层工具的复杂性吓倒。
核心洞察:简化是剥离噪音、让核心架构显现,不是降级。 eng-lite的五个刻意取舍——本地编译、静态数组模拟ADC、Unity替代VectorCAST、GitHub Actions、trace.py替代DOORS——每一个都在示范放之四海皆准的原理:编译器可切换、算法与硬件隔离、裸机可测、CI的本质是自动执行、追溯矩阵的本质是一张映射表。理解这些底层原理,不会因上层工具的更替而迷失。
六个命令,跑一遍
现在,打开你的终端,cd进eng-lite目录。什么都别改,先把那六个命令跑一遍:make all,make check,make test,make coverage,make trace,make ci。感受一下,当一个嵌入式项目的工程基础设施被完整地建立起来时,是什么样的体验。
你会注意到两件事:第一,你不需要看任何文档就知道该怎么做,make ci的名字已经告诉了你要做的事情。第二,每一个阶段都有清晰的输出,成功或失败一目了然。这不是巧合,这是刻意为之的“自文档化设计“。一个好的工程基础设施,就像一栋楼的紧急出口指示牌,在紧急状况下(凌晨三点、交付前夜),任何人都能在十秒内找到正确的操作。
然后问自己一个问题:你现在正在做的嵌入式项目,如果删掉所有源代码只保留工程基础设施,它的基础设施是否值得保留? 如果你的答案是“不值得“,那这本书就是为你写的。
如果你已经在运行某些工程基础设施环节,比如有Makefile但没静态分析,或有CI但没需求追溯,选一个缺失的环节,用eng-lite的模板直接添加到你的项目中。不需要一步到位全部完善,工程化的过程本身是渐进式的。 今天加一个步骤,下个月加另一个。一年后回头看,你会发现你的项目已经从“能编译就行“进化到了“每次提交都是安全的“。
核心洞察:好的工程基础设施是自文档化的,工程化本身是渐进式的。
make ci的名字已说明用途,每个阶段输出成功或失败一目了然,让任何人在凌晨三点或交付前夜都能十秒内找到正确操作;不必一步到位,今天补一个缺失环节,下个月再补一个,一年后项目就从“能编译就行“进化到“每次提交都是安全的“。
自我评估清单
在进入下一节之前,用这张表格做一个快速自我审视:
| 维度 | eng-lite | 你的项目 |
|---|---|---|
| 构建时间(从零到可执行) | <5秒 | ? |
| Makefile行数 | ~60 | ? |
| 新人理解构建系统时间 | 15分钟 | ? |
| 是否有编译警告即错误 | 是 | ? |
| 是否有自动化静态分析 | 有(Cppcheck) | ? |
| 是否有单元测试框架 | 有(Ceedling+Unity) | ? |
| 测试覆盖率是否可见 | 是(HTML报告) | ? |
| 是否有CI门禁 | 有(GitHub Actions) | ? |
| 是否有需求追溯矩阵 | 有(trace.py→CSV) | ? |
| 逐项打勾。每一行没有勾的地方,就是你下一步可以改进的方向。不要试图一次性把所有缺失项都补上,那会让人崩溃。选一项,花一个下午,做完。工程化不是冲刺,是持续改善。 |
本篇小结
- 全景视图:本章建立了eng-lite项目的全景视图,你看到了一个教学级嵌入式工程工具链的完整目录结构。
- 范围边界:理解了它的范围边界:覆盖什么、省略什么、为什么这么设计。
- 命令清单:拿到了快速上手的命令清单。
- 思维转折:从现在开始,你不只是一个写C代码的程序员,你是这个嵌入式软件工程项目的建造者。
- 五个环节缺一不可:你写的每一行代码,都要经过构建系统的编译检查、静态分析的安全审查、单元测试的逻辑验证、CI流水线的自动验收,以及需求追溯矩阵的完整性确认。
【下集预告】:第5.2章,我们将拆开eng-lite的Makefile。你会看到一个精心设计的模块化构建系统。它的重点在于让你理解:为什么一个好的构建系统,应该让新人花15分钟就能看懂,而不是花三天才能改一行? 我们将把Makefile当成“施工图纸“来读,每一条规则都是一道工序。