冗余头文件检查(七):使用 Docker 构建可复现的 IWYU 环境
冗余头文件检查(七):使用 Docker 构建可复现的 IWYU 环境
为什么使用 Docker
在开发 IWYU 的扩展功能时,环境一致性是一个重要问题。不同的开发者可能在不同的操作系统、不同的 Clang 版本、不同的依赖库环境下工作。这种差异可能导致:
- 编译失败
- 运行时行为不一致
- CI 环境与本地开发环境不匹配
- 新成员加入项目时需要繁琐的环境配置
Docker 提供了一种轻量级的虚拟化方案,通过容器技术确保开发、测试和生产环境的一致性。
IWYU Docker镜像构建
基础镜像选择
IWYU 依赖 Clang/LLVM,因此我们需要一个包含基本构建工具和 Clang 的基础镜像:
1
2
3
4
5
# 使用 Ubuntu 20.04 作为基础镜像
FROM ubuntu:20.04
# 设置非交互式安装
ENV DEBIAN_FRONTEND=noninteractive
完整的 Dockerfile
以下是完整的 IWYU 开发环境 Dockerfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Dockerfile for IWYU Development Environment
FROM ubuntu:20.04
# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive
ENV LLVM_VERSION=10
ENV PATH="/usr/lib/llvm-${LLVM_VERSION}/bin:${PATH}"
ENV LD_LIBRARY_PATH="/usr/lib/llvm-${LLVM_VERSION}/lib:${LD_LIBRARY_PATH}"
# 安装基础依赖
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
ninja-build \
ccache \
wget \
curl \
vim \
&& rm -rf /var/lib/apt/lists/*
# 安装 Clang/LLVM 10
RUN apt-get update && apt-get install -y \
llvm-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} \
libclang-${LLVM_VERSION}-dev \
libllvm${LLVM_VERSION} \
&& rm -rf /var/lib/apt/lists/*
# 创建工作目录
WORKDIR /workspace
# 克隆 IWYU 仓库
RUN git clone https://github.com/include-what-you-use/include-what-you-use.git
# 构建 Debug 版本的 IWYU
WORKDIR /workspace/include-what-you-use
RUN mkdir -p build-debug
WORKDIR /workspace/include-what-you-use/build-debug
RUN cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DLLVM_DIR=/usr/lib/llvm-${LLVM_VERSION}/lib/cmake/llvm \
.. && \
ninja -j$(nproc)
# 设置默认工作目录
WORKDIR /workspace
# 默认命令:显示 IWYU 版本
CMD ["/workspace/include-what-you-use/build-debug/bin/include-what-you-use", "--version"]
构建镜像
1
2
3
4
5
# 构建镜像
docker build -t iwyu-dev:latest .
# 查看构建的镜像
docker images | grep iwyu
使用 Docker 容器
基本运行
1
2
3
4
5
# 运行容器并进入 shell
docker run -it --rm iwyu-dev:latest bash
# 在容器中运行 IWYU
/workspace/include-what-you-use/build-debug/bin/include-what-you-use --help
挂载本地代码目录
为了能够使用容器中的 IWYU 分析本地代码,我们需要挂载本地目录:
1
2
3
4
5
6
7
8
9
# 运行容器并挂载本地项目目录
docker run -it --rm \
-v /path/to/your/project:/workspace/project \
iwyu-dev:latest bash
# 在容器中分析项目代码
/workspace/include-what-you-use/build-debug/bin/include-what-you-use \
-I/workspace/project/include \
/workspace/project/src/*.cpp
使用 docker run 的便捷方式
为了避免每次输入完整路径,可以在宿主机上创建一个 shell 脚本:
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#!/bin/bash
# iwyu-docker.sh - 使用 Docker 运行 IWYU
PROJECT_DIR=$(pwd)
IWYU_BINARY="/workspace/include-what-you-use/build-debug/bin/include-what-you-use"
docker run --rm \
-v "${PROJECT_DIR}:/workspace/project" \
-w /workspace/project \
iwyu-dev:latest \
$IWYU_BINARY "$@"
使用方式:
1
2
3
4
5
6
7
chmod +x iwyu-docker.sh
# 分析当前目录下的文件
./iwyu-docker.sh src/*.cpp -I./include
# 带调试输出
./iwyu-docker.sh src/main.cpp --verbose | fix_includes.py
Docker Compose 配置
对于更复杂的项目,可以使用 Docker Compose 管理多个服务或配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# docker-compose.yml
version: '3.8'
services:
iwyu:
build:
context: .
dockerfile: Dockerfile
image: iwyu-dev:latest
volumes:
- ./project:/workspace/project
- ./.config/iwyu:/root/.config/iwyu
working_dir: /workspace/project
command: ["/workspace/include-what-you-use/build-debug/bin/include-what-you-use", "--version"]
使用方式:
1
2
3
4
5
6
# 构建并启动服务
docker-compose up --build
# 执行 IWYU 分析
docker-compose exec iwyu /workspace/include-what-you-use/build-debug/bin/include-what-you-use \
src/*.cpp -I include
VSCode 与 Docker 集成
安装 Dev Containers 扩展
在 VSCode 中,安装 Dev Containers (ms-vscode-remote.remote-containers) 扩展。
创建 .devcontainer 配置
在项目根目录下创建 .devcontainer/devcontainer.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "IWYU Development",
"image": "iwyu-dev:latest",
"features": {
"ghcr.io/devcontainers/features/git:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-vscode.cmake-tools"
],
"settings": {
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}
}
},
"mounts": [
"source=${localWorkspaceFolder},target=/workspace/project,type=bind,consistency=cached"
],
"workspaceFolder": "/workspace/project",
"remoteUser": "root"
}
在容器内进行开发
- 打开项目文件夹
- 按
F1,选择Dev Containers: Reopen in Container - VSCode 会在容器内部启动,并挂载本地代码
- 可以像在本地开发一样使用 VSCode 的调试功能
调试环境中的 IWYU
使用 VSCode Remote SSH 调试
当使用 Dev Containers 时,VSCode 的调试配置 (.vscode/launch.json) 需要调整路径:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug IWYU (Container)",
"type": "cppdbg",
"request": "launch",
"program": "/workspace/include-what-you-use/build-debug/bin/include-what-you-use",
"args": [
"-I/workspace/project/include",
"/workspace/project/src/main.cpp",
"--verbose"
],
"stopAtEntry": false,
"cwd": "/workspace/project",
"environment": [],
"externalConsole": false,
"MIMode": "lldb",
"setupCommands": [
{
"description": "Enable pretty-printing for lldb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
CI/CD 集成
GitHub Actions 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# .github/workflows/iwyu-check.yml
name: IWYU Check
on: [push, pull_request]
jobs:
iwyu:
runs-on: ubuntu-latest
container:
image: iwyu-dev:latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run IWYU
run: |
/workspace/include-what-you-use/build-debug/bin/include-what-you-use \
-I./include \
src/*.cpp || true
- name: Check for IWYU suggestions
run: |
# 如果 IWYU 有任何输出,则构建失败
/workspace/include-what-you-use/build-debug/bin/include-what-you-use \
-I./include --no_comments \
src/*.cpp 2>&1 | tee /tmp/iwyu_output.txt
if [ -s /tmp/iwyu_output.txt ]; then
echo "IWYU found issues:"
cat /tmp/iwyu_output.txt
exit 1
fi
优势总结
使用 Docker 构建 IWYU 环境的优势:
- 环境一致性:所有开发者使用相同的编译环境和依赖版本
- 快速上手:新成员只需要安装 Docker,无需手动配置复杂的开发环境
- 隔离性:容器与宿主机环境隔离,避免环境污染
- 可复现性:CI 环境与本地开发环境完全一致
- 版本切换方便:通过多标签镜像可以轻松切换不同版本的 IWYU 或 Clang
常见问题
容器启动失败
问题:容器启动时出现库找不到的错误。
解决:确保 Dockerfile 中正确设置了 LD_LIBRARY_PATH 环境变量。
性能慢
问题:在容器中编译 IWYU 速度较慢。
解决:
- 挂载本地目录时使用
consistency=cached选项 - 使用本地构建版本,减少容器镜像大小
1
2
3
4
5
6
# 在 Dockerfile 中
FROM iwyu-dev:latest as builder
RUN cd /workspace/include-what-you-use/build-debug && ninja
FROM ubuntu:20.04
COPY --from=builder /workspace /workspace
总结
通过 Docker,我们构建了一个完全可复现的 IWYU 开发环境。这不仅确保了不同开发者之间的一致性,也为 CI/CD 流程提供了可靠的基础。在接下来的文章中,我们将利用这个环境来实现 IWYU 的功能扩展。
本文由作者按照 CC BY 4.0 进行授权