Files
lucky-cat/docs/knowledge vault/versioned-firmware-artifact.zh-CN.md
T

268 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 带版本号固件产物生成说明
## 1. 对应提交
```text
f5b4bd8 build: add versioned firmware artifact
```
该提交在不改变 ESP-IDF 原有应用镜像和烧录流程的前提下,为每次构建额外生成一份带项目版本号的固件文件,方便 OTA 发布、固件归档和版本追踪。
## 2. 问题原因
### 2.1 默认应用镜像名称无法区分版本
ESP-IDF 默认按照项目名称生成应用镜像。本项目的项目名为 `TQ_Cat_Code`,因此默认产物固定为:
```text
build/TQ_Cat_Code.bin
```
无论当前固件版本是 `1.0.1`、`1.0.2` 还是后续版本,文件名都不会变化。连续构建或复制不同版本时,新文件容易覆盖旧文件,单看文件名也无法判断固件版本。
### 2.2 OTA 发布和固件归档依赖人工改名
在增加本次构建逻辑之前,如果需要向 OTA 服务上传带版本标识的固件,只能在构建完成后手工复制或重命名,例如:
```text
TQ_Cat_Code.bin -> TQ_Cat_Code-1.0.2.bin
```
这种方式存在以下问题:
1. 人工填写的版本号可能与固件内部版本不一致。
2. 重命名时可能选错构建目录或上传旧文件。
3. 自动发布脚本无法依赖稳定、可预测的版本化文件名。
4. 多个版本放在同一目录时,固定名称不能提供有效区分。
### 2.3 不能直接替换 ESP-IDF 的默认产物
ESP-IDF 的烧录参数和项目描述仍引用 `TQ_Cat_Code.bin`。如果直接修改默认应用镜像名称,可能影响:
- `idf.py flash` 的默认烧录流程;
- `flasher_args.json` 等构建元数据;
- 已经依赖固定文件名的开发和生产脚本。
因此,本次修改选择保留默认产物,并额外生成版本化副本,而不是改变 ESP-IDF 的标准输出名称。
## 3. 解决方式
### 3.1 从 ESP-IDF 构建属性读取项目版本
提交在 `project()` 完成项目初始化后读取 `PROJECT_VER`:
```cmake
idf_build_get_property(TQ_PROJECT_VERSION PROJECT_VER)
```
本项目通过根目录的 `version.txt` 管理版本。ESP-IDF 配置项目时会解析该文件,并将结果写入 `PROJECT_VER`。提交创建时的项目版本为:
```text
1.0.2
```
构建逻辑直接使用 ESP-IDF 已解析的版本属性,而不是在 CMake 中重复读取或硬编码版本号。这样可以保证文件名中的版本与固件构建版本来自同一数据源。
### 3.2 组合版本化固件路径
版本化产物使用“项目名-项目版本.bin”的命名规则:
```cmake
set(TQ_VERSIONED_FIRMWARE
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${TQ_PROJECT_VERSION}.bin")
```
各变量在当前项目中的值为:
```text
CMAKE_BINARY_DIR = build 目录
CMAKE_PROJECT_NAME = TQ_Cat_Code
TQ_PROJECT_VERSION = 1.0.2
```
因此最终输出路径为:
```text
build/TQ_Cat_Code-1.0.2.bin
```
版本升级后不需要修改构建脚本。例如将 `version.txt` 更新为 `1.0.3`,重新配置并构建后,产物会自动变为:
```text
build/TQ_Cat_Code-1.0.3.bin
```
### 3.3 在应用镜像生成后复制版本化副本
提交新增了一个默认参与构建的自定义目标:
```cmake
add_custom_target(versioned_firmware ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin"
"${TQ_VERSIONED_FIRMWARE}"
DEPENDS app
BYPRODUCTS "${TQ_VERSIONED_FIRMWARE}"
COMMENT "Creating versioned firmware ${CMAKE_PROJECT_NAME}-${TQ_PROJECT_VERSION}.bin"
VERBATIM
)
```
这段代码的行为如下。
#### `versioned_firmware ALL`
定义名为 `versioned_firmware` 的构建目标。`ALL` 表示执行常规项目构建时会自动执行该目标,无需额外运行单独命令。
#### `copy_if_different`
使用 CMake 自带的跨平台复制命令,将默认应用镜像复制为版本化文件:
```text
build/TQ_Cat_Code.bin
-> build/TQ_Cat_Code-1.0.2.bin
```
只有源文件与目标文件内容不同时才会重新写入目标文件,可以避免未发生变化时进行无意义覆盖。该操作只是字节复制,不会再次编译、链接或修改固件内容。
#### `DEPENDS app`
声明版本化目标依赖 ESP-IDF 的 `app` 目标。CMake 必须先完成应用镜像构建,随后才能复制文件,避免在源 `.bin` 尚未生成时执行复制。
#### `BYPRODUCTS`
将版本化 `.bin` 声明为该目标的构建副产物,使 CMake 和底层构建工具能够识别这个文件由构建系统生成,而不是人工放入构建目录的未知文件。
#### `COMMENT`
构建过程中输出明确提示,例如:
```text
Creating versioned firmware TQ_Cat_Code-1.0.2.bin
```
开发人员可以通过构建日志确认版本化产物生成步骤已执行。
#### `VERBATIM`
要求 CMake 按参数原意生成命令,正确处理路径中的空格、引号和平台差异,降低命令转义导致复制失败的风险。
## 4. 完整执行流程
构建过程变为:
```text
读取 version.txt
|
v
ESP-IDF 设置 PROJECT_VER
|
v
编译并链接 app
|
v
生成 build/TQ_Cat_Code.bin
|
v
versioned_firmware 目标执行复制
|
v
生成 build/TQ_Cat_Code-1.0.2.bin
```
最终两个应用镜像同时存在:
```text
build/TQ_Cat_Code.bin
build/TQ_Cat_Code-1.0.2.bin
```
两者内容完全相同,区别仅在文件名。当前工作区的实际构建结果中,两份文件大小一致,SHA-256 也一致:
```text
baa959fda9572c249ceaf6b8937d35940bc4c7dece2814fa2daebe1c616abc6d
```
## 5. 解决成果
### 5.1 固件版本可以直接从文件名识别
版本信息进入构建产物名称后,无需打开固件或查询构建记录,即可识别文件对应的项目版本。
### 5.2 降低 OTA 上传选错版本的概率
OTA 发布人员或自动化脚本可以直接选择带版本号的固件文件,避免手工改名时版本号与实际固件不一致。
### 5.3 支持多个版本并行归档
不同版本使用不同文件名,可以在发布目录、测试目录或制品库中同时保存:
```text
TQ_Cat_Code-1.0.1.bin
TQ_Cat_Code-1.0.2.bin
TQ_Cat_Code-1.0.3.bin
```
这有利于版本回溯、测试对比和问题复现。
### 5.4 保持原有开发与烧录流程兼容
默认的 `TQ_Cat_Code.bin` 仍然生成,ESP-IDF 的烧录参数没有改变。因此原有的构建、串口烧录和调试命令无需调整。
### 5.5 不增加固件运行时开销
修改只发生在构建阶段,没有增加设备端代码、任务、内存、Flash 占用或启动时间。版本化文件也不是另一份不同的固件镜像,只是原应用镜像的副本。
## 6. 后续影响
### 6.1 构建目录磁盘占用增加
构建目录会额外保存一份与应用镜像等大的文件。当前应用镜像约为 2.1 MiB,因此单次当前版本构建会增加约 2.1 MiB 的磁盘占用。
该影响只存在于开发机或构建服务器,不会使设备 Flash 中出现两份应用镜像,也不会增加 OTA 下载大小。
### 6.2 旧版本产物不会被自动删除
当版本从 `1.0.2` 更新为 `1.0.3` 后,新构建会生成新文件,但原来的版本化文件可能继续保留:
```text
TQ_Cat_Code-1.0.2.bin
TQ_Cat_Code-1.0.3.bin
```
这便于本地归档,但也可能让长期复用的构建目录逐渐积累旧产物。发布脚本不应简单选择目录中任意一个带版本号的 `.bin`,而应根据当前 `PROJECT_VER` 计算准确文件名。需要清理时,可以通过完整清理构建目录移除历史副产物。
### 6.3 版本变化后必须重新配置构建系统
版本化路径在 CMake 配置阶段根据 `PROJECT_VER` 生成。修改 `version.txt` 后,应通过正常的 ESP-IDF 构建流程触发重新配置,确保目标文件名使用新版本。持续集成环境使用干净构建目录时不会受到这一问题影响。
### 6.4 该文件只是应用镜像,不是完整烧录包
`TQ_Cat_Code-1.0.2.bin` 只对应应用程序分区。它不包含以下独立镜像或资源:
- Bootloader;
- 分区表;
- OTA data 初始化镜像;
- LittleFS/LCD/音频资源镜像。
该文件适合应用分区 OTA,不能被误认为包含全部分区的整机合并烧录镜像。工厂烧录仍需按照 ESP-IDF 生成的烧录参数写入各个分区。
### 6.5 OTA 服务和发布脚本需要明确选择规则
新增文件不会自动改变 OTA 上传流程,也不会自动把固件发布到服务器。后续自动化应明确:
1. 从当前项目版本计算版本化文件名;
2. 上传前校验文件存在且构建成功;
3. 计算并记录文件大小与 SHA-256;
4. 保证 OTA 元数据中的版本、大小和摘要与该文件一致。
### 6.6 项目名或版本字符串会直接进入文件名
文件名由 `CMAKE_PROJECT_NAME` 和 `PROJECT_VER` 直接组合。当前值只包含适合文件名的字母、数字、下划线、点和连字符,不存在兼容性问题。后续如果引入带斜杠、空格或其他特殊字符的版本规则,需要额外确认制品库、上传脚本和目标文件系统是否支持。
## 7. 结论
该提交以较小的构建配置改动解决了固件产物缺少版本标识的问题。它复用 ESP-IDF 的统一项目版本,保留原始应用镜像和烧录流程,并自动生成内容完全一致的版本化副本。
该方案不会影响设备运行行为,主要后续影响是构建目录增加一份应用镜像、历史版本文件可能累积,以及发布流程必须正确区分“应用 OTA 镜像”和“完整烧录镜像”。