# 带版本号固件产物生成说明 ## 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 初始化镜像; - 独立模型分区镜像。 当前 LCD 图片和提示音已包含在应用镜像内,会随应用 OTA 一起更新。 该文件适合应用分区 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 镜像”和“完整烧录镜像”。