Keil MDK 和 IAR EWARM 的工程文件(uvprojx/.ewp)不直接提供真实头文件依赖,需通过编译器选项(如 Keil 的 --depend、IAR 的 Generate dependency file)生成 .d 文件,再结合宏定义复现与路径过滤提取有效依赖,并辅以统计分析和模块映射来反映实际耦合结构。

Keil MDK 的 uvprojx 文件里没有现成依赖列表,得自己挖
Keil 项目本身不生成标准依赖清单(比如头文件路径、宏定义、源文件树),uvprojx 是 XML 格式,但只存编译配置,不记录预处理实际包含关系。想导出“真实依赖”,不能靠解析工程文件,得走编译器介入路线。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用 Keil 的
--list+--depend编译选项:在Options → C/C++ → Misc Controls中加--list="build\deps.lst" --depend="build\deps.d",会生成 GCC 风格的.d依赖文件(需 Keil v5.36+) -
deps.d是 Makefile 兼容格式,可直接用sed/awk提取所有.h路径,过滤掉系统路径(如ARMCompiler\include) - 注意:该方式仅对 C/C++ 源文件生效,汇编文件(
.s)的INCLUDE不会被捕获,需单独扫描
IAR EWARM 的 .ewp 同样不暴露依赖,但有更稳的替代方案
IAR 的项目文件是二进制或加密 XML,没法直接解析依赖。可靠做法是启用其内置的“生成依赖文件”功能,而不是去逆向工程结构。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 在
Project → Options → C/C++ Compiler → List中勾选Generate dependency file,并指定输出路径(如build/iar_deps.d) - 该文件默认含绝对路径,可用
iarbuild -f your.ewp -make -log all 2>&1 | grep "including"辅助验证是否漏头文件 - 若用命令行构建(推荐自动化),确保传入
-D__IAR_SYSTEM_INCLUDE__等宏与 GUI 一致,否则#include_next行为可能不同,导致依赖偏差
统一提取头文件依赖时,gcc -M 模拟法只适用于部分场景
有人想绕过 IDE,直接用 GCC 的 -M 或 -MM 扫描源码——这在嵌入式 C++ 项目中容易翻车。
原因和规避点:
- Keil/IAR 的预处理器行为与 GCC 不同(比如对
__packed、__align等扩展关键字的处理),gcc -MM可能提前报错退出 - 条件编译宏(如
USE_HAL_DRIVER)若没完整复现,gcc会跳过整块#ifdef,漏掉实际被编译的头文件 - 可行折中:用
arm-none-eabi-gcc -E -dM提前 dump 出 Keil/IAR 实际定义的宏列表,再喂给gcc -MM做二次扫描,但仅适合纯 C 项目;C++ 模板实例化头文件仍无法覆盖
最终交付的依赖清单要区分“构建依赖”和“语义依赖”
自动化脚本吐出来的 .d 文件只是构建系统看到的文件路径,不是开发真正关心的“哪些模块耦合了”。比如 stm32f4xx_hal.h 被 50 个文件包含,但它本身不关键,关键是它间接拉进来的 core_cm4.h 或 device_support.h。
建议在导出后加一层轻量处理:
- 用 Python 脚本读取所有
.d文件,统计每个头文件被引用频次,排除stdint.h、core_*.h等平台基础头 - 把路径映射回模块名(例如
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h→HAL_UART),方便后续做模块解耦分析 - 别忘了检查
__attribute__((section("...")))或#pragma location引用的链接脚本段,这些虽不产头文件依赖,但属于隐性构建约束,需单独归档
真正难的不是生成那张表,而是让这张表反映代码的真实耦合结构——IDE 给的只是编译器视角的快照,不是架构师需要的依赖图谱。


















