4. 开发环境搭建:基于Docker的嵌入式测试环境构建
做嵌入式单元测试,最头疼的问题是什么?
环境不一致。你在Ubuntu 18.04上编译通过,同事的20.04就报错。更别说不同芯片厂商的工具链版本冲突了。
我几年前带一个项目时,光帮团队统一开发环境就花了两周。后来改用Docker,一天搞定。说白了,Docker就是给你的测试环境拍个快照——谁拉下来都一样。
4.1 为什么选Docker?
嵌入式测试环境有三大痛点:
- 工具链依赖复杂——GCC版本、交叉编译器、调试器,一个不对就翻车
- 多项目并行——A项目用ARM GCC 9,B项目用ARM GCC 11,本地装两套容易冲突
- 新人上手慢——光配环境就能劝退一半实习生
Docker把整个环境打包成一个镜像。你拉下来,跑起来,就能编译、测试、出报告。我在团队里推这个方案后,新人从「配环境三天」变成了「docker run五分钟」。
核心思路:把工具链、测试框架、依赖库全部写进Dockerfile,用docker-compose编排多容器(比如一个跑编译,一个跑测试,一个跑覆盖率)。
4.2 整体架构
先看一张图,理解我们最终要搭成什么样:
三个容器通过共享卷交换数据。编译容器产出的二进制,测试容器拿去跑用例,报告容器解析覆盖率。互不干扰,各司其职。
4.3 Dockerfile编写实战
直接上代码。这是我个人习惯的写法——分层构建,把不变的部分放前面,变的部分放后面,充分利用Docker缓存。
# 基础镜像:Ubuntu 20.04 LTS
FROM ubuntu:20.04 AS base
# 避免交互式安装卡住
ENV DEBIAN_FRONTEND=noninteractive
# 安装基础工具
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
wget \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# ---------- 工具链层 ----------
FROM base AS toolchain
# 安装ARM交叉编译器(以ARM Cortex-M为例)
RUN apt-get update && apt-get install -y \
gcc-arm-none-eabi \
gdb-multiarch \
&& rm -rf /var/lib/apt/lists/*
# 验证工具链
RUN arm-none-eabi-gcc --version
# ---------- 测试框架层 ----------
FROM toolchain AS test-framework
# 安装CUnit
RUN apt-get update && apt-get install -y libcunit1-dev && rm -rf /var/lib/apt/lists/*
# 安装Ceedling(Ruby依赖)
RUN apt-get update && apt-get install -y ruby-full && rm -rf /var/lib/apt/lists/*
RUN gem install ceedling
# 安装lcov
RUN apt-get update && apt-get install -y lcov && rm -rf /var/lib/apt/lists/*
# ---------- 工作目录 ----------
WORKDIR /workspace
COPY . .
# 默认命令
CMD ["/bin/bash"]
小技巧:用多阶段构建(FROM ... AS ...)可以减小最终镜像体积。我有个项目从1.2GB压到了380MB。
4.4 docker-compose编排
单容器不够用?那就上docker-compose。下面这个配置,我一直在用:
version: '3.8'
services:
builder:
build:
context: .
target: toolchain
volumes:
- ./src:/workspace/src
- ./build:/workspace/build
command: >
sh -c "cd /workspace/build && cmake .. && make -j$(nproc)"
tester:
build:
context: .
target: test-framework
volumes:
- ./build:/workspace/build
- ./test:/workspace/test
- ./reports:/workspace/reports
depends_on:
- builder
command: >
sh -c "cd /workspace && ctest --output-on-failure"
reporter:
build:
context: .
target: test-framework
volumes:
- ./build:/workspace/build
- ./reports:/workspace/reports
depends_on:
- tester
command: >
sh -c "lcov --capture --directory /workspace/build \
--output-file /workspace/reports/coverage.info \
&& genhtml /workspace/reports/coverage.info \
--output-directory /workspace/reports/html"
执行顺序:builder → tester → reporter。一步到位。
4.5 工具链安装与验证
工具链装好了,怎么确认没问题?我一般跑三个检查:
| 检查项 | 命令 | 预期输出 |
|---|---|---|
| 编译器版本 | arm-none-eabi-gcc --version |
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors) 9.3.1 |
| 编译测试 | echo 'int main(){return 0;}' | arm-none-eabi-gcc -x c - -o /tmp/test.elf |
无报错,生成ELF文件 |
| 测试框架 | ceedling version |
Ceedling 0.31.1 |
注意:我曾经遇到过arm-none-eabi-gcc装好了,但gdb-multiarch版本不匹配,导致调试时断点打不上。建议工具链版本统一从官方源或ARM官网下载,别混用。
4.6 避坑指南
这些年踩过的坑,列几个典型的:
- 权限问题:容器内生成的文件属主是root,宿主机上删不掉。解决方案:创建容器时指定用户ID,或者用
docker run --user。 - 时区不一致:默认UTC,日志时间对不上。加一行
ENV TZ=Asia/Shanghai。 - apt源慢:换国内镜像。在Dockerfile里加
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list。 - 镜像体积膨胀:每层RUN都会产生新层。用
&&合并命令,最后rm -rf /var/lib/apt/lists/*清理缓存。
一句话总结:Dockerfile写清楚,docker-compose编排好,工具链验证通过。你的嵌入式测试环境就稳了。团队里谁拉下来都能跑,再也不用「我机器上明明可以啊」。