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

9.5 KiB
Raw Blame History

带版本号固件产物生成说明

1. 对应提交

f5b4bd8 build: add versioned firmware artifact

该提交在不改变 ESP-IDF 原有应用镜像和烧录流程的前提下,为每次构建额外生成一份带项目版本号的固件文件,方便 OTA 发布、固件归档和版本追踪。

2. 问题原因

2.1 默认应用镜像名称无法区分版本

ESP-IDF 默认按照项目名称生成应用镜像。本项目的项目名为 TQ_Cat_Code,因此默认产物固定为:

build/TQ_Cat_Code.bin

无论当前固件版本是 1.0.1、1.0.2 还是后续版本,文件名都不会变化。连续构建或复制不同版本时,新文件容易覆盖旧文件,单看文件名也无法判断固件版本。

2.2 OTA 发布和固件归档依赖人工改名

在增加本次构建逻辑之前,如果需要向 OTA 服务上传带版本标识的固件,只能在构建完成后手工复制或重命名,例如:

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:

idf_build_get_property(TQ_PROJECT_VERSION PROJECT_VER)

本项目通过根目录的 version.txt 管理版本。ESP-IDF 配置项目时会解析该文件,并将结果写入 PROJECT_VER。提交创建时的项目版本为:

1.0.2

构建逻辑直接使用 ESP-IDF 已解析的版本属性,而不是在 CMake 中重复读取或硬编码版本号。这样可以保证文件名中的版本与固件构建版本来自同一数据源。

3.2 组合版本化固件路径

版本化产物使用“项目名-项目版本.bin”的命名规则:

set(TQ_VERSIONED_FIRMWARE
    "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${TQ_PROJECT_VERSION}.bin")

各变量在当前项目中的值为:

CMAKE_BINARY_DIR   = build 目录
CMAKE_PROJECT_NAME = TQ_Cat_Code
TQ_PROJECT_VERSION = 1.0.2

因此最终输出路径为:

build/TQ_Cat_Code-1.0.2.bin

版本升级后不需要修改构建脚本。例如将 version.txt 更新为 1.0.3,重新配置并构建后,产物会自动变为:

build/TQ_Cat_Code-1.0.3.bin

3.3 在应用镜像生成后复制版本化副本

提交新增了一个默认参与构建的自定义目标:

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 自带的跨平台复制命令,将默认应用镜像复制为版本化文件:

build/TQ_Cat_Code.bin
    -> build/TQ_Cat_Code-1.0.2.bin

只有源文件与目标文件内容不同时才会重新写入目标文件,可以避免未发生变化时进行无意义覆盖。该操作只是字节复制,不会再次编译、链接或修改固件内容。

DEPENDS app

声明版本化目标依赖 ESP-IDF 的 app 目标。CMake 必须先完成应用镜像构建,随后才能复制文件,避免在源 .bin 尚未生成时执行复制。

BYPRODUCTS

将版本化 .bin 声明为该目标的构建副产物,使 CMake 和底层构建工具能够识别这个文件由构建系统生成,而不是人工放入构建目录的未知文件。

COMMENT

构建过程中输出明确提示,例如:

Creating versioned firmware TQ_Cat_Code-1.0.2.bin

开发人员可以通过构建日志确认版本化产物生成步骤已执行。

VERBATIM

要求 CMake 按参数原意生成命令,正确处理路径中的空格、引号和平台差异,降低命令转义导致复制失败的风险。

4. 完整执行流程

构建过程变为:

读取 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

最终两个应用镜像同时存在:

build/TQ_Cat_Code.bin
build/TQ_Cat_Code-1.0.2.bin

两者内容完全相同,区别仅在文件名。当前工作区的实际构建结果中,两份文件大小一致,SHA-256 也一致:

baa959fda9572c249ceaf6b8937d35940bc4c7dece2814fa2daebe1c616abc6d

5. 解决成果

5.1 固件版本可以直接从文件名识别

版本信息进入构建产物名称后,无需打开固件或查询构建记录,即可识别文件对应的项目版本。

5.2 降低 OTA 上传选错版本的概率

OTA 发布人员或自动化脚本可以直接选择带版本号的固件文件,避免手工改名时版本号与实际固件不一致。

5.3 支持多个版本并行归档

不同版本使用不同文件名,可以在发布目录、测试目录或制品库中同时保存:

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 后,新构建会生成新文件,但原来的版本化文件可能继续保留:

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 镜像”和“完整烧录镜像”。