冗余头文件检查(三):include-what-you-use实战使用指南
冗余头文件检查(三):include-what-you-use实战使用指南
编译 IWYU
从源码编译
IWYU 推荐从源码编译,这样可以确保与你的 Clang 版本匹配。以下是官方推荐的编译步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 克隆仓库
git clone https://github.com/include-what-you-use/include-what-you-use.git
cd include-what-you-use
# 2. 创建构建目录
mkdir build
cd build
# 3. 配置 CMake
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
# 4. 编译
make -j$(nproc)
# 5. 安装(可选)
sudo make install
常见编译问题
Clang 版本不匹配:IWYU 需要与 Clang 的版本匹配。常见的版本名为 libclang-XX,如 libclang-10。
1
2
3
4
5
# 检查 Clang 版本
clang --version
# 如果版本不匹配,需要安装对应的 Clang 开发包
sudo apt-get install libclang-10-dev # Ubuntu/Debian
缺少依赖:
1
2
3
4
5
# Ubuntu/Debian
sudo apt-get install clang-10 gcc-c++
# macOS (使用 Homebrew)
brew install llvm@10
基本使用
最简单的用法
1
2
3
4
5
# 分析单个文件
include-what-you-use main.cpp
# 分析多个文件
include-what-you-use src/*.cpp
IWYU 会输出两个部分:
- 给人类的可读建议
- 给
fix_includes.py脚本使用的机器可读格式
典型输出示例
1
$ include-what-you-use example.cpp
输出:
1
2
3
4
5
6
7
8
9
example.cpp should add these lines:
#include <string>
example.cpp should remove these lines:
- #include <vector> // lines 3-3
The full include-list for example.cpp:
#include <iostream>
#include <string>
使用 fix_includes.py 自动修复
IWYU 输出的第二部分可以直接通过管道传输给 fix_includes.py 脚本:
1
include-what-you-use src/*.cpp | fix_includes.py
这个脚本会:
- 读取 IWYU 的标准输出
- 解析机器可读格式
- 修改源文件,添加或删除
#include指令
常用参数
控制输出格式
| 参数 | 说明 |
|---|---|
--verbose | 显示详细的分析信息,有助于调试 |
--no_comments | 不输出人类可读的建议,只输出机器可读格式 |
--transitive_includes | 显示传递性包含关系 |
1
2
3
4
5
# 完整模式:显示所有细节
include-what-you-use --verbose example.cpp
# 纯机器格式:用于管道传输
include-what-you-use --no_comments example.cpp | fix_includes.py
指定包含路径
如果你的项目使用非标准的包含路径:
1
include-what-you-use -I./include -I./third_party/include src/*.cpp
定义宏
1
include-what-you-use -DPROJECT_EXPORTS src/*.cpp
排除特定文件
1
include-what-you-use --exclude_files '*_test.cpp' src/*.cpp
集成到构建系统
与 Make 集成
1
2
3
4
5
6
IWYU = include-what-you-use
LIBCLANG_INC = -I/usr/lib/llvm-10/include
iwyu_check:
$(IWYU) $(CXXFLAGS) $(LIBCLANG_INC) $(SOURCES) 2>&1 | \
fix_includes.py
与 CMake 集成
CMake 官方提供了与 IWYU 的集成支持。在你的 CMakeLists.txt 中添加:
1
2
3
4
5
6
7
# 启用 IWYU 支持
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use")
# 设置 IWYU 参数
set(CMAKE_CXX_IWYU_OPTIONS
"--mapping_file=${CMAKE_SOURCE_DIR}/iwyu_mappings.imp"
"--verbose=1")
运行时,CMake 会自动调用 IWYU 分析每个源文件。
与 Bazel 集成
使用 bazel-compile-commands-extractor 生成 compile_commands.json,然后:
1
2
3
4
5
6
7
8
9
# 1. 生成 compile_commands.json
bazel query 'kind("cc_.*", //...)' \
--output=xml > bazel_compile_commands.xml
# 2. 转换格式
python3 convert_compile_commands.py bazel_compile_commands.json
# 3. 运行 IWYU
iwyu_tool -j$(nproc) -p compile_commands.json
高级功能
映射文件(Mapping File)
IWYU 可以使用映射文件来覆盖其默认的头文件选择规则:
# mapping.imp
// 指定某个符号应该从哪个头文件获取
{ symbol: ["std::shared_ptr"], include: ["<memory>"] }
// 指定某个头文件应该被哪些类型包含
{ include: ["<boost/shared_ptr.hpp>"], private: ["boost::shared_ptr"] }
使用方式:
1
include-what-you-use --mapping_file=mapping.imp src/*.cpp
前向声明
IWYU 懂得”何时使用前向声明”:
1
2
3
4
5
6
7
8
9
// 原始代码
#include "ComplexClass.h"
void process(ComplexClass* obj);
// IWYU 建议
class ComplexClass; // 前向声明就足够了
void process(ComplexClass* obj);
这是 IWYU 的一个重要特性,因为它可以减少不必要的编译依赖。
常见问题与解决方案
1. “IWYU 报告了很多误报”
原因:IWYU 的分析假设头文件自包含,或者某些依赖关系通过宏间接引入。
解决方案:
- 使用映射文件修正
- 检查是否有不自包含的头文件
2. “IWYU 运行太慢”
原因:大型项目的完整 AST 分析耗时较长。
解决方案:
- 使用
iwyu_tool并行处理文件 - 分批次分析特定模块
- 考虑使用
--no_comments减少输出开销
3. “修复后编译错误”
原因:某些头文件虽然看似未被直接引用,但实际上提供了必要的宏定义或内联函数。
解决方案:
- 检查 IWYU 的详细输出,看看是否遗漏了间接依赖
- 使用映射文件强制保留某些
#include
总结
最后总结一下,iwyu本身是可以先在小型模块上试验,确认效果后再推广到整个项目,同时将 IWYU 检查加入 CI 流程,防止引入新的冗余包含,并且在代码合并前运行,而不是等到问题积累到很多的时候再解决(PS.实际情况正好相反,使用iwyu的人一般是像批量解决问题的)
本文由作者按照 CC BY 4.0 进行授权