CMake无法生成头文件级依赖图,必须用clang++ -M系列命令提取;清洗需过滤系统头、抽关系、转相对路径并补dot语法;iwyu仅提供诊断建议,不能替代-M生成真实包含边。

CMake 本身不生成依赖关系图,它只管理构建目标间的依赖顺序;要得到头文件级的依赖图,必须绕过 CMake,用编译器原生命令提取。
clang++ -M 是唯一靠谱的头文件依赖提取方式
别指望 add_dependencies 或 CMake 的 target_link_libraries 能画出头文件包含关系图——它们只管目标(如 libA → libB),不管 A.h 里 #include "B.h" 这种细节。
真正能反映实际 #include 路径的,只有编译器的 -M 系列开关:
-
clang++ -M输出所有被#include拉进来的头文件路径(含系统头),纯文本、无宏干扰 -
-MG允许缺失头文件(比如自动生成的config.h),避免因找不到而中断 -
-MP为每个依赖项生成空规则,防止 Make 在头删掉后报错 -
-MT "obj/main.o"显式指定目标名,否则默认用源文件名,和构建系统对不上 -
-MF deps/main.d必须指定输出文件,否则依赖信息直接刷屏丢弃
典型命令:clang++ -M -MG -MP -MT "obj/main.o" -MF deps/main.d main.cpp
立即学习“C++免费学习笔记(深入)”;
从 .d 文件转成 Graphviz dot 图的关键清洗步骤
原始 .d 输出不能直接喂给 dot:含绝对路径、系统头(如 /usr/include/c++/11/string)、重复项、构建目标名(如 obj/main.o:),图会密得没法看。
清洗必须分步做:
- 先过滤系统头:用
gcc -E -dM /dev/null | grep __VERSION__查系统头位置,再用grep -v "/usr/include\|/opt/homebrew/include"删掉匹配行 - 抽“目标 → 依赖”关系:
awk '{print $1, $3}' deps/main.d | sort -u($1是目标,$3是第一个直接依赖) - 路径统一转相对:假设项目根目录是
/home/user/proj,用sed 's|/home/user/proj/||'替换前缀 - 补 dot 语法:每行转成
"main.cpp" -> "helper.h";,再包进digraph { ... }
不要误用 include-what-you-use(iwyu)当依赖图工具
include-what-you-use 的输出不是依赖边,而是诊断建议:
- 它只告诉你“该删
foo.h”或“该加bar.h”,没有A.h → B.h这样的边数据 - 它不跟踪间接包含(
A.cpp → A.h → B.h → C.h中,iwyu只看到A.cpp直接用了A.h,漏掉B.h和C.h) - 强行解析它的 JSON 或文本输出去拼图,结果必然是断连、缺边、节点孤立
它唯一合理用途是:先用 clang++ -M 生成图,再跑 iwyu 扫一遍,把标红的冗余 #include 从图里人工删掉。
真正难的不是生成图,是让图有用:系统头要不要留、第三方库要不要折叠、路径要不要按模块分组——这些决策没标准答案,得看你想用图解决什么问题。图一旦生成,别急着导出 PNG,先用 grep 和 sort 手动筛两遍,比自动美化重要得多。


















