CMake 项目切换 Ninja 构建问题排查记录
1. 背景
在 CMake 项目编译时,原先经常需要手动使用:
bash
make -j
来启用并行编译。后来希望通过配置 CMakePresets.json,改用 Ninja 构建系统,从而避免每次手动指定 make -j。
需要注意的是,这里应当是 Ninja ,不是 Jinja。
- Ninja:C/C++ 项目常用的构建工具,CMake 可以生成 Ninja 构建文件。
- Jinja:Python 模板引擎,和 CMake 并行构建无直接关系。
2. 目标
希望通过 CMakePresets.json 固定使用 Ninja:
json
{
"version": 8,
"configurePresets": [
{
"name": "x64",
"displayName": "GCC 13.3.0 x86_64-linux-gnu",
"description": "正在使用编译器: C = /usr/bin/gcc, CXX = /usr/bin/g++",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "/usr/bin/gcc",
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
"CMAKE_BUILD_TYPE": "Debug",
"FETCHCONTENT_BASE_DIR": "${sourceDir}/.deps"
}
}
]
}
核心配置是:
json
"generator": "Ninja"
这样 CMake 会生成 Ninja 构建文件,而不是 Unix Makefiles。
3. 出现的问题
配置项目时报错:
text
CMake Error: Error: generator : Ninja
Does not match the generator used previously: Unix Makefiles
Either remove the CMakeCache.txt file and CMakeFiles directory or choose a different binary directory.
同时日志中还出现:
text
CMake Error at /usr/share/cmake-4.2/Modules/FetchContent.cmake:1916 (message):
CMake step for libhv failed: 1
这说明不仅主项目可能存在旧缓存,FetchContent 下载和构建的依赖项目也可能存在旧缓存。
4. 根因分析
CMake 的构建目录中会保存生成器信息。
如果某个目录之前用:
text
Unix Makefiles
配置过,之后又在同一个目录里改成:
text
Ninja
CMake 会拒绝继续配置,因为同一个构建目录不能混用不同 generator。
本次问题的关键点在于:
json
"FETCHCONTENT_BASE_DIR": "${sourceDir}/.deps"
项目把 FetchContent 的依赖缓存目录指定到了:
bash
.deps
因此,虽然主构建目录可能已经清理过,但 .deps 里面的 libhv 子项目构建缓存仍然记录着旧的:
text
Unix Makefiles
当主项目切换到 Ninja 后,FetchContent 重新配置 libhv 时就发生 generator 冲突。
5. 最终解决方法
删除 .deps 后重新生成即可:
bash
rm -rf .deps
cmake --preset x64
如果希望彻底清理,也可以同时删除主构建目录:
bash
rm -rf out/build/x64
rm -rf .deps
cmake --preset x64
然后编译:
bash
cmake --build out/build/x64
如果配置了 buildPresets,则可以使用:
bash
cmake --build --preset x64
6. 推荐补充 buildPresets
原来的配置只包含 configurePresets,这是可以正常使用的。
但为了让命令更统一,推荐补充 buildPresets:
json
{
"version": 8,
"configurePresets": [
{
"name": "x64",
"displayName": "GCC 13.3.0 x86_64-linux-gnu",
"description": "正在使用编译器: C = /usr/bin/gcc, CXX = /usr/bin/g++",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "/usr/bin/gcc",
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
"CMAKE_BUILD_TYPE": "Debug",
"FETCHCONTENT_BASE_DIR": "${sourceDir}/.deps"
}
}
],
"buildPresets": [
{
"name": "x64",
"configurePreset": "x64"
}
]
}
这样后续构建流程可以统一为:
bash
cmake --preset x64
cmake --build --preset x64
7. 是否还需要 make -j
如果使用的是:
json
"generator": "Ninja"
通常不需要再手动写:
bash
make -j
因为 Ninja 默认会自动并行构建。
如果仍然使用默认的 Unix Makefiles,则通常需要:
bash
cmake --build build -j
或者:
bash
make -j$(nproc)
也可以使用 CMake 的通用并行参数:
bash
cmake --build build --parallel
或者设置环境变量:
bash
export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc)
8. 后续排查建议
以后遇到类似错误:
text
Does not match the generator used previously
优先检查并清理以下目录:
bash
rm -rf out/build/x64
rm -rf .deps
如果明确只是 FetchContent 子项目出问题,通常只清理:
bash
rm -rf .deps
即可。
9. 本次结论
本次问题不是 CMakePresets.json 配置错误,而是 .deps 中的 FetchContent 子项目缓存仍然保留了旧的 Unix Makefiles generator 信息。
删除 .deps 后,CMake 重新下载并配置依赖,所有子项目都统一使用 Ninja,问题解决。
最终推荐使用:
bash
cmake --preset x64
cmake --build --preset x64
并在项目中固定:
json
"generator": "Ninja"
这样后续不需要再手动执行 make -j。
10. 致谢
致谢土区GPT5.5 : )