# 可重复构建

[English]

## 简介

ESP-IDF 构建系统支持 可重复构建。

启用可重复构建后，使用 ESP-IDF 构建的应用程序不再依赖构建环境。即使以下变量发生变化，应用程序的 `.elf` 文件和 `.bin` 文件仍然保持不变：

- 项目所在目录

- ESP-IDF 所在目录 (`IDF_PATH`)

- 构建时间

- 工具链安装路径

## 构建不可重复的原因

即便使用相同的源代码和工具版本，以下几个原因依旧可能导致应用程序依赖构建环境：

- 在 C 代码中， `__FILE__` 预处理器宏展开为源文件的完整路径。

- `__DATE__` 和 `__TIME__` 预处理器宏展开为编译日期和时间。

- 当编译器生成目标文件时，会添加调试信息。这些信息能帮助 GDB 等调试器定位与机器代码中特定位置相对应的源代码，且通常包含相关源文件的路径。这些路径可能是绝对路径，并且包括前往 ESP-IDF 或项目的路径。

此外，输入顺序的不稳定性、构建系统的不确定性等因素也可能导致应用程序依赖构建环境。

## 在 ESP-IDF 中启用可重复构建

启用 CONFIG_APP_REPRODUCIBLE_BUILD 选项，可在 ESP-IDF 中实现可重复构建。

此选项默认处于禁用状态，可在 `menuconfig` 中启用。

也可以将该选项添加到 `sdkconfig.defaults` 中，并删除 `sdkconfig` 文件、重新运行构建。更多信息，请参阅 自定义 sdkconfig 的默认值。

## 如何实现可重复构建

ESP-IDF 可通过以下方式实现可重复构建：

- 在启用可重复构建时，ESP-IDF 的源代码不使用 `__DATE__` 和 `__TIME__` 宏。请注意，如果应用程序源代码使用了这些宏，则构建将无法重复。

- ESP-IDF 构建系统传递一组 `-fmacro-prefix-map` 和 `-fdebug-prefix-map` 标志，并用占位符替换基本路径：

  > - 替换 ESP-IDF 的路径为 `/IDF`

  > - 替换项目路径为 `/IDF_PROJECT`

  > - 替换构建目录的路径为 `/IDF_BUILD`

  > - 替换组件路径为 `/COMPONENT_NAME_DIR` （其中 `NAME` 指的是组件的名称）

  > - 替换工具链的路径为 `/TOOLCHAIN`

- 如果启用 CONFIG_APP_REPRODUCIBLE_BUILD，则不会将构建日期和时间包括在 应用程序元数据结构 和 引导加载程序元数据结构 中。

- ESP-IDF 构建系统在将源文件列表、组件列表和其他序列传递给 CMake 之前会对其进行排序。构建系统的其他各个部分，如链接器脚本生成器，也会先排序，从而确保无论环境如何，输出都一致。

## 可重复构建与调试

如前文所述，启用可重复构建时，调试信息部分中包含的文件名会被更改。因此，调试器 (GDB) 无法定位给定代码位置的源文件。

可以使用 GDB 的 `set substitute-path` 命令来解决这个问题。例如，添加以下命令到 GDB 初始化脚本中，可以将更改后的路径恢复为原始路径。

    set substitute-path /COMPONENT_FREERTOS_DIR /home/user/esp/esp-idf/components/freertos

ESP-IDF 构建系统在构建过程中会自动生成一个包含 `set substitute-path` 命令列表的文件。该文件名为 `prefix_map_gdbinit`，位于项目 `build` 目录中。

当使用 idf.py gdb 开始调试时，此额外的 `gdbinit` 文件会自动传递给 GDB。当手动启动 GDB 或从 IDE 启动 GDB 时，请使用 `-x build/prefix_map_gdbinit` 参数将此额外的 `gdbinit` 脚本传递给 GDB。

## 仍可能影响可重复构建的因素

请注意，构建应用程序还依赖于：

- ESP-IDF 版本

- 构建工具 (CMake, Ninja) 及交叉编译器的版本

IDF Docker 镜像 可避免这些因素影响构建。

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。