文章目录
- 前言
- 解决过程
-
- 一、问题背景
- 二、问题现象
- 三、关键变更(触发问题)
- 四、问题定位过程
-
- [未启用 RULE_LAUNCH_COMPILE:](#未启用 RULE_LAUNCH_COMPILE:)
- [启用 RULE_LAUNCH_COMPILE:](#启用 RULE_LAUNCH_COMPILE:)
- 五、问题根因分析(核心)
-
- [情况 A(正常)](#情况 A(正常))
- [情况 B(出问题)](#情况 B(出问题))
- 六、递归产生的关键条件
-
- [条件1:CMake 注入 ccache](#条件1:CMake 注入 ccache)
- [条件2:PATH 中存在 ccache wrapper](#条件2:PATH 中存在 ccache wrapper)
- [条件3:启用 distcc](#条件3:启用 distcc)
- 七、递归链条(完整还原)
- 八、为什么以前正常
- 九、解决方案(最终方案)
-
- [✔ CMake 控制 ccache 是否启用](#✔ CMake 控制 ccache 是否启用)
- [✔ 构建方式](#✔ 构建方式)
-
- [distcc 模式](#distcc 模式)
- [ccache 本地模式](#ccache 本地模式)
- 十、核心原则(非常重要)
-
- [⚠️ 原则1:ccache 只能有一个入口](#⚠️ 原则1:ccache 只能有一个入口)
- [⚠️ 原则2:不要混用三种注入方式](#⚠️ 原则2:不要混用三种注入方式)
- [⚠️ 原则3:distcc worker 必须是"干净 gcc"](#⚠️ 原则3:distcc worker 必须是“干净 gcc”)
- 十一、调试方法总结
- 十二、经验总结
- 十三、推荐最佳实践(最终建议)
-
- [✔ 推荐结构](#✔ 推荐结构)
-
- [distcc 模式](#distcc 模式)
- [ccache 模式](#ccache 模式)
- [❌ 避免](#❌ 避免)
- 十四、附加优化(可选)
- 补充说明(关键补充)
-
- [✔ 补充1:PATH + CCACHE_PREFIX 组合是安全的](#✔ 补充1:PATH + CCACHE_PREFIX 组合是安全的)
- [✔ 补充2:调试日志方法(非常重要)](#✔ 补充2:调试日志方法(非常重要))
- [✔ 补充3:distcc和ccache的在成功失败的日志对比](#✔ 补充3:distcc和ccache的在成功失败的日志对比)
-
- [distcc 日志对比](#distcc 日志对比)
- 失败情况
- 成功情况
- 结论(非常重要)
- [ccache 日志对比](#ccache 日志对比)
- 失败链路(关键片段)
- 失败链路总结
- 成功链路(关键片段)
- 成功链路总结
- 最终结论
-
- [✔ 正确解法](#✔ 正确解法)
- [✔ 关键经验](#✔ 关键经验)
- 总结
前言
打不过就加入,既然AI强那就不能一味地排斥,可以让它为我所用,这个问题解决过程中它也是一直误导我,最终我找到证据了告诉它,AI才分析的有点道理,情况就是我一直用着ccache+distcc的组合没问题,但是今天突然就编译不成功了,经过反复实验发现是更新代码后,其他人在CMakeLists.txt中增加了 RULE_LAUNCH_COMPILE 来控制ccache,和我本地的distcc冲突了,这还是我翻日志发现的,在我发现之前AI就说我本地的配置问题,不过最后关于这个问题的总结还是交给AI来做,毕竟这是它擅长的领域。
解决过程
一、问题背景
在一个 CMake + Make 的 C++ 项目中,引入了:
- ccache:用于加速本地编译
- distcc:用于分布式编译
编译脚本如下:
bash
cmake ../../../.. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DWITH_COV=OFF
CCACHE_PREFIX=distcc make -j12
同时系统环境中存在:
bash
PATH="/usr/lib/ccache:$PATH"
即 gcc/g++ 被 ccache wrapper 接管。
二、问题现象
编译过程中出现:
text
distcc ... failed with exit code 111
CRITICAL! distcc seems to have invoked itself recursively!
特点:
- 远端编译失败
- 本地 fallback 后仍失败
- 出现 distcc 递归调用警告
三、关键变更(触发问题)
在 CMakeLists.txt 中新增:
cmake
FIND_PROGRAM(CCACHE_PROGRAM ccache)
IF(CCACHE_PROGRAM)
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
ENDIF()
四、问题定位过程
对比cmake生成的 build.make 编译命令发现:
未启用 RULE_LAUNCH_COMPILE:
bash
/usr/lib/ccache/c++
启用 RULE_LAUNCH_COMPILE:
bash
/usr/bin/ccache /usr/lib/ccache/c++
👉 差异:多了一层 ccache
五、问题根因分析(核心)
梳理下编译链结构:
情况 A(正常)
text
gcc(ccache wrapper)
→ ccache
→ distcc(通过 CCACHE_PREFIX)
→ remote gcc
👉 单层 ccache ✔
情况 B(出问题)
text
ccache(CMake 注入)
→ ccache(PATH wrapper)
→ distcc
→ remote
→ ccache(远端)
→ distcc
→ ...
👉 双层 ccache ❌ → 递归
六、递归产生的关键条件
必须同时满足:
条件1:CMake 注入 ccache
cmake
RULE_LAUNCH_COMPILE = ccache
条件2:PATH 中存在 ccache wrapper
bash
/usr/lib/ccache/g++
条件3:启用 distcc
bash
CCACHE_PREFIX=distcc
七、递归链条(完整还原)
text
ccache(外层)
→ distcc
→ ccache(内层)
→ distcc
→ ccache
→ ...
👉 💥 无限递归
八、为什么以前正常
可能原因:
- ccache 命中率高(未触发 distcc)
- distcc 未 fallback
- 未使用 CMake launcher(只有一层 ccache)
九、解决方案(最终方案)
✔ CMake 控制 ccache 是否启用
cmake
option(USE_DISTCC "Enable distcc build" OFF)
FIND_PROGRAM(CCACHE_PROGRAM ccache)
IF(CCACHE_PROGRAM)
if(NOT USE_DISTCC)
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
ENDIF()
ENDIF()
✔ 构建方式
distcc 模式
bash
cmake ... -DUSE_DISTCC=ON
CCACHE_PREFIX=distcc make -j12
ccache 本地模式
bash
cmake ... -DUSE_DISTCC=OFF
make -j12
十、核心原则(非常重要)
⚠️ 原则1:ccache 只能有一个入口
合法:
text
ccache → distcc → gcc
非法:
text
ccache → ccache → distcc → gcc
⚠️ 原则2:不要混用三种注入方式
| 方式 | 是否推荐 |
|---|---|
PATH /usr/lib/ccache |
✔ 可用 |
| RULE_LAUNCH_COMPILE | ✔ 可用 |
| CCACHE_PREFIX=distcc | ✔ 可用 |
| 三者同时用 | ❌ 禁止 |
⚠️ 原则3:distcc worker 必须是"干净 gcc"
远端必须:
bash
which gcc → /usr/bin/gcc
不能是:
bash
/usr/lib/ccache/gcc
十一、调试方法总结
查看真实编译命令
bash
make VERBOSE=1
查看执行链(最强)
bash
strace -f -e execve make -j1
查看 distcc 行为
bash
DISTCC_VERBOSE=1 make -j1
查看 ccache 状态
bash
ccache -s
十二、经验总结
本次问题本质
❗ 不是 distcc 或 ccache bug
❗ 而是"编译链多层 wrapper 冲突"
一句话结论
👉 编译链中只能有一个"入口控制点"
十三、推荐最佳实践(最终建议)
✔ 推荐结构
distcc 模式
bash
PATH=/usr/bin:/bin
CCACHE_PREFIX=distcc
ccache 模式
cmake
RULE_LAUNCH_COMPILE=ccache
❌ 避免
bash
PATH=/usr/lib/ccache
+ RULE_LAUNCH_COMPILE=ccache
+ CCACHE_PREFIX=distcc
十四、附加优化(可选)
可以进一步优化为:
- 用 CMake 控制 distcc / ccache 模式
- 不依赖 PATH 劫持
- 使用:
cmake
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
替代 RULE_LAUNCH_COMPILE(更标准)
补充说明(关键补充)
✔ 补充1:PATH + CCACHE_PREFIX 组合是安全的
bash
PATH="/usr/lib/ccache:$PATH"
CCACHE_PREFIX=distcc
👉 这一组合本身是没有问题的
执行链为:
text
gcc(ccache wrapper)
→ ccache
→ distcc
→ remote gcc
✔ 单层 ccache
✔ 不会递归
👉 问题只在于叠加了 CMake 的 ccache launcher
✔ 补充2:调试日志方法(非常重要)
可以通过以下环境变量查看完整调用链:
bash
export CCACHE_DEBUG=1
export CCACHE_LOGFILE=/tmp/ccache.log
export DISTCC_VERBOSE=1
各变量作用
CCACHE_DEBUG
text
开启 ccache 调试模式
CCACHE_LOGFILE
text
输出 ccache 执行日志(包含命中、调用链等)
DISTCC_VERBOSE
text
输出 distcc 分发、fallback、远端执行信息
实际用途
可以观察:
- ccache 是否命中
- 是否调用 distcc
- distcc 是否 fallback
- 是否发生递归调用
✔ 补充3:distcc和ccache的在成功失败的日志对比
这一部分是最终确认问题根因的关键证据。
distcc 日志对比
失败的日志:
distcc[134131] (dcc_scan_args) scanning arguments: /usr/lib/ccache/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
distcc[134131] (dcc_scan_args) found object/output file "CMakeFiles/kwgame_logicplugin.dir/kwboardmodule.cpp.o"
distcc[134131] (dcc_scan_args) found input file "xxx"
成功的日志
distcc[135925] (dcc_scan_args) scanning arguments: /usr/bin/c++ -fPIC -O0 -Wall -g -ggdb -DDEBUG -g -shared -Wl,-soname,kwgame_logicplugin.so -o xxx
distcc[135925] (dcc_scan_args) found object/output file "../../../../../../../debug/kwgame_logicplugin.so"
distcc[135925] (dcc_scan_args) found object/output file "CMakeFiles/kwgame_logicplugin.dir/xxx.cpp.o"
失败情况
text
distcc ... scanning arguments: /usr/lib/ccache/c++
👉 关键点:
❗ distcc 收到的是
/usr/lib/ccache/c++
成功情况
text
distcc ... scanning arguments: /usr/bin/c++
👉 关键点:
✔ distcc 收到的是"真实编译器"
结论(非常重要)
❗ distcc 是否递归,取决于它收到的是:
/usr/lib/ccache/c++→ ❌ 会递归/usr/bin/c++→ ✔ 正常
ccache 日志对比
失败的日志:
[2026-04-17T11:00:38.365957 134110] === CCACHE 3.7.7 STARTED =========================================
...
[2026-04-17T11:00:38.366080 134110] Command line: /usr/bin/ccache /usr/lib/ccache/c++ -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:00:38.369547 134110] No such manifest file
[2026-04-17T11:00:38.369554 134110] Did not find object file hash in manifest
[2026-04-17T11:00:38.371333 134110] Running preprocessor
[2026-04-17T11:00:38.371355 134110] Executing /usr/lib/ccache/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
[2026-04-17T11:00:38.371968 134111] === CCACHE 3.7.7 STARTED =========================================
...
[2026-04-17T11:00:38.372098 134111] Command line: /usr/lib/ccache/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:00:38.372250 134111] Using command-line prefix distcc
[2026-04-17T11:00:38.372258 134111] Failed; falling back to running the real compiler
[2026-04-17T11:00:38.372263 134111] Executing /usr/bin/distcc /usr/bin/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
[2026-04-17T11:00:38.372284 134111] Result: called for preprocessing
...
2026-04-17T11:00:38.912259 134110] Using command-line prefix distcc
[2026-04-17T11:00:38.912272 134110] Running real compiler
[2026-04-17T11:00:38.912855 134110] Executing /usr/bin/distcc /usr/lib/ccache/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
[2026-04-17T11:00:38.915592 134132] === CCACHE 3.7.7 STARTED =========================================
...
[2026-04-17T11:00:38.915718 134132] Command line: /usr/lib/ccache/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:00:38.918396 134132] Did not find object file hash in manifest
[2026-04-17T11:00:38.919801 134132] Running preprocessor
[2026-04-17T11:00:38.919811 134132] Executing /usr/bin/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
[2026-04-17T11:00:39.490500 134132] Got object file hash from preprocessor
[2026-04-17T11:00:39.490542 134132] Object file /home/shz/.ccache/5/2/e126701e9f4e861edf1abfdd9a15d7-11562023.o not in cache
[2026-04-17T11:00:39.490591 134132] Using command-line prefix distcc
[2026-04-17T11:00:39.490608 134132] Running real compiler
[2026-04-17T11:00:39.490651 134132] Executing /usr/bin/distcc /usr/bin/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:00:39.498410 134132] Result: compile failed
成功的日志:
[2026-04-17T11:04:58.767050 135920] === CCACHE 3.7.7 STARTED =========================================
[2026-04-17T11:04:58.767220 135920] Command line: /usr/lib/ccache/c++ -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:30:25.416455 138404] No such manifest file
[2026-04-17T11:30:25.416463 138404] Did not find object file hash in manifest
[2026-04-17T11:30:25.417699 138404] Running preprocessor
[2026-04-17T11:30:25.417715 138404] Executing /usr/bin/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:30:25.989594 138404] Using command-line prefix distcc
[2026-04-17T11:30:25.989603 138404] Running real compiler
[2026-04-17T11:30:25.989642 138404] Executing /usr/bin/distcc /usr/bin/c++ -O0 -Wall -g -ggdb -g -fPIC -std=c++2a -DELPP_NO_DEFAULT_LOG_FILE -DRECORD_FAST_QUERY_MODE -Dkwgame_logicplugin_EXPORTS -I/xxx.cpp
...
[2026-04-17T11:30:43.583089 138505] === CCACHE 3.7.7 STARTED =========================================
...
[2026-04-17T11:30:43.583222 138505] Command line: /usr/lib/ccache/c++ -fPIC -O0 -Wall -g -ggdb -DDEBUG -g -shared -Wl,-soname,kwgame_logicplugin.so -o ../../../../../../../debug/kwgame_logicplugin.so xxx
...
[2026-04-17T11:30:43.583393 138505] Using command-line prefix distcc
[2026-04-17T11:30:43.583404 138505] Failed; falling back to running the real compiler
[2026-04-17T11:30:43.583408 138505] Executing /usr/bin/distcc /usr/bin/c++ -fPIC -O0 -Wall -g -ggdb -DDEBUG -g -shared -Wl,-soname,kwgame_logicplugin.so -o ../../../../../../../debug/kwgame_logicplugin.so xxx
[2026-04-17T11:30:43.583447 138505] Result: called for link
失败链路(关键片段)
text
Command line:
/usr/bin/ccache /usr/lib/ccache/c++
👉 第一层 ccache(CMake 注入)
text
Executing /usr/lib/ccache/c++
👉 第二层 ccache(PATH wrapper)
text
Using command-line prefix distcc
Executing /usr/bin/distcc /usr/lib/ccache/c++
👉 distcc 收到 ccache wrapper ❌
text
=== CCACHE STARTED AGAIN
Command line: /usr/lib/ccache/c++
👉 💥 第二次进入 ccache
失败链路总结
text
ccache
→ ccache
→ distcc
→ ccache
→ distcc
→ ...
👉 无限递归成立
成功链路(关键片段)
text
Executing /usr/bin/c++
👉 ✔ 直接进入真实编译器
text
Executing /usr/bin/distcc /usr/bin/c++
👉 ✔ distcc 使用干净编译器
成功链路总结
text
ccache
→ distcc
→ gcc
👉 单层结构,无递归
最终结论
本次问题的根本原因是:
ccache 被调用了两次(CMake + PATH),再叠加 distcc,形成递归闭环
✔ 正确解法
👉 保证 ccache 只出现一次(唯一入口)
✔ 关键经验
⚠️ 多层 wrapper(CMake + PATH + distcc)是编译系统中最隐蔽、最危险的问题之一
总结
- 原来好好的东西突然不好了,那一定是环境发生了变化,可能是外部环境,可能是内部环境
- 硅基生物的问题往往能够重现,而碳基生物的不确定性实在让人无所适从,往往就是试试看吧
时间都去哪了,还没好好感受年轻就老了~