# 项目文件引用关系图(tools/refgraph) > 生成并维护 STM32F4-Base 的**文件级引用关系图**(`#include` 引用)。 > 工具:`tools/refgraph/gen_refgraph.py`(纯标准库)+ 成熟渲染引擎 Graphviz。 > 产物:`docs/images/引用关系图.*`(本目录图 + `.dot` + `.json`)。 ## 1. 为什么自写工具(调研结论) | 成熟工具 | 能力 | 对本需求的差距 | |---|---|---| | **Doxygen**(开源,C 文档标准工具) | 每文件的 include 依赖图、每函数的调用图、类协作图 | 输出按文件/函数**分散**,无单一整工程文件引用图;需配置解析全量源码 | | **Graphviz**(开源,图布局渲染引擎 `dot`) | 渲染任意 DOT 图(分层布局) | 只做布局渲染、不解析源码 —— **本工具用它出图** | | **cflow**(binutils) | 函数级调用图 | 粒度是函数不是文件;依赖汇编/编译产物 | | **SciTools Understand**(商业) | 依赖图/调用树/引用映射 | 重量级付费 IDE,不可脚本化、不入构建流程 | 结论:**没有现成成熟工具能直接输出"单一、整工程、可重跑更新"的文件引用关系图**, 故按"没有则自写"原则自写解析/建模工具;布局渲染这一难点交给成熟引擎 Graphviz。 本机已通过 winget 安装 Graphviz 16.1.0(安装后 `dot` 可从 **PATH** 找到;非标准安装位置用 `DTU_GRAPHVIZ_DOT` 指向 `dot.exe`,见 `docs/环境与部署/本地工具路径环境变量方案.md`)。 ## 2. 生成 / 更新(一条命令) ```powershell # 方式一:bat(任意位置可运行) & 'tools\refgraph\gen_refgraph.bat' # 方式二:python 直跑(等效) & '.venv\Scripts\python.exe' tools/refgraph/gen_refgraph.py ``` **项目文件有增删改后,重跑上式即可**:产物全量重新生成并覆盖 `docs/images/引用关系图.*`,对照 `引用关系图.json` 的文本 diff 即可看出引用面变化, 然后按仓库惯例把(工具 + 产物)一并提交。 可选参数: | 参数 | 默认 | 说明 | |---|---|---| | `--views full,module` | 两者 | 只出文件级 / 只出模块级 | | `--edge-scope project\|all` | `project` | project=仅第一方代码发起的引用边(清晰);all=含第三方内部引用(完整但密集) | | `--no-render` | 关 | 只出 `.dot`/`.json`,不调 Graphviz | | `--quiet` | 关 | 精简控制台输出 | ## 3. 产物与图例 | 文件 | 说明 | 是否提交 | |---|---|---| | `引用关系图.svg` / `.dot` | **文件级引用图**(项目代码视图) | 是 | | `引用关系图_模块.svg` / `.dot` | **模块级概览图**(目录/库聚合,边上 ×n=引用条数) | 是 | | `引用关系图.json` | 结构化数据:全部节点/边/未解析 include 明细 | 是 | | `引用关系图.png` | 文件级图位图预览(大,~5 MB) | 否(本地预览,可随时重出) | 图例(文件级): - 节点:**蓝=第一方**(App/、Inc/、Src/、test/、simulator/〔除 SDL2、build〕、 Drivers/BSP/〔除 NET/lftpd〕);**灰=第三方**(Lib/、Middlewares/、 Drivers/CMSIS、HAL、lftpd、SDL2);**橙=外部/系统头文件**聚合节点。 - 边:`蓝实线` 第一方→第一方;`灰实线` 第一方→第三方; `橙虚线` →外部/系统头文件(stdio.h 等编译器提供); `all` 模式下另有淡点线=第三方内部引用。 模块级图把第一方按目录模块聚合(App、App/task、BSP/UI、Src/Inc (CubeMX)…)、 第三方按库聚合(LVGL、FreeRTOS、CMSIS、HAL、FatFs、dhara、lftpd、SDL2)。 ## 4. 解析原理(与编译器行为对齐) 1. 扫描仓库全部 `.c/.h`(跳过 `.git`/`.venv`/`MDK-ARM`/`docs`/`simulator/build`); 2. 提取每条 `#include`(处理行尾反斜杠续行;编码按 UTF-8→GBK→Latin-1 兜底); 3. 解析顺序与编译器一致:**所在文件目录 → Keil 工程包含路径(解析自 `MDK-ARM/STM32F407-Demo.uvprojx`,自动跟随)→ 模拟器 CMake 包含路径 (`simulator/CMakeLists.txt`,脚本内 `SIM_INCLUDE_DIRS` 需手动同步)**; 4. 解析到仓库内文件 → 成边;解析不到 → 归"外部/系统头文件"(明细在 JSON `unresolved`),与编译器查系统目录的行为一致。 5. **刻意不做"基名全库兜底"**:仓库内存在同名系统头(rapidjson 自带 `msinttypes/stdint.h`),兜底会把各处 `` 误连到库内部文件产生假边 (曾产生 100+ 条假边,已验证并移除)。 ## 5. 语义与已知限制 - **文本层引用**:`#if 0` 内、MCU/模拟器平台条件下的引用均计入(完整呈现项目引用面); 不评估 `#if` 条件 —— 未激活条件内的引用(如 `LV_USE_DRAW_VG_LITE=0` 时的 `vg_lite.h`)会显示为"未解析→外部",属预期。 - 仅**文件级 `#include` 引用**;不含函数级调用关系(如需,可另行用 Doxygen/cflow 补充)。 - 双端(Keil MCU / CMake 模拟器)包含路径合并解析,个别引用是"另一端视角" (如 LVGL 的 SDL2 后端引用),属项目级引用面的如实呈现。 - 第一方/第三方划分规则见脚本头部 `classify()`;lftpd 为移植的第三方组件 (与 AGENTS.md pre-commit 排除名单一致)。 ## 6. 维护 - 新增/移动第一方目录 → 改脚本 `classify()`、`FP_MODULES`; 新增第三方库 → 改 `TP_LIBS`(模块级聚合用)。 - Keil 工程包含路径变化:**自动跟随**(每次运行解析 uvprojx)。 - 模拟器包含路径变化:同步脚本 `SIM_INCLUDE_DIRS` 常量。 - 换机:`winget install Graphviz.Graphviz`(渲染依赖;缺失时工具自动跳过渲染, 仍产出 `.dot`)。 ## 7. 当前数据(2026-10-14 刷新) 索引 1495 个 `.c/.h`;解析 include 5972 条(解析成功 4821,外部/系统 1151,351 种); 第一方文件 215 个;节点 = 第一方 215 + 第三方 1266 + 外部聚合 1;去重边 5176 条。 被引用最多的第一方文件:`Drivers/BSP/dbg_log.h`(48)、`Inc/main.h`(33)、 `Drivers/BSP/NET/net_socket.h`(18)……(完整 Top10 见运行报告)。