在 review #387 时发现的既存缺陷,与那个 PR 无关(mcpp build --configure-only 撞上的是同一个洞,已在 #387 内修掉自己那半边;这里记 mcpp test 这半边)。
复现
main(2026.8.10.1,本机 x86_64-linux-gnu):
$ mkdir -p demo/src demo/tests && cd demo
$ printf ' [package]\nname = "tst"\nversion = "0.1.0"\n' > mcpp.toml
$ printf ' int main() { return 0; }\n' > src/main.cpp
$ printf ' int main() { return 0; }\n' > tests/smoke.cpp
$ mcpp build
$ grep ' ^default' target/* /* /build.ninja
default bin/tst
$ mcpp test
$ grep ' ^default' target/* /* /build.ninja
default bin/smoke # <- 同一个 build.ninja 被换成了测试图
$ mcpp build
Finished dev in 0.00s # 快路径命中,问 ninja 要的是 bin/smoke
bin/tst 已经不在这张图里了,但 mcpp build 报成功。
把测试文件改坏,症状就变成硬失败 —— src/ 一个字没动 :
$ printf ' this is not valid C++ at all\n' > tests/smoke.cpp
$ mcpp build
error: build failed
failed: obj/smoke.o
.../tests/smoke.cpp:1:1: error: expected unqualified-id before 'this'
$ echo $?
1
即:一个坏掉的测试文件会让普通 mcpp build 失败。
根因
两件事叠在一起:
mcpp test 用 prepare_build(includeDevDeps=true, extraTargets=tests) 生成 plan,backend 把它写进同一个 target/<fp>/build.ninja(指纹不含 dev-deps/test targets,所以是同一个目录)。emit_ninja_string 的 default 行列的是 plan.linkUnits 全部,测试模式下就只有测试二进制。
try_fast_build 的新鲜度判据是「build.ninja 的 mtime 比源码 新」(src/build/execute.cppm 的 sources_newer_than,只扫 src/** + build.mcpp + glob inputs + resource scripts),从不校验这张图是不是普通构建的图 。target/.build_cache 也没人动。tests/ 不在扫描范围里,所以改测试文件连快路径都不会打断。
于是 mcpp build → mcpp test → mcpp build 就重放了测试图。
(顺带:把产物删掉再 build 反而不会复现 —— 产物缺失让 ninja 以「图过期」的样子失败,快路径回退到完整 prepare,正好把缺陷盖住。写回归测试时别先删产物。)
可选修法
mcpp test 在重写 build.ninja 之后,丢掉指向该 outputDir 的快路径条目。feat: generate compile database without building #387 给 configure-only 加的 forget_build_cache_entry(projectRoot, outputDir)(src/build/execute.cppm)就是这个形状,mcpp test 直接调用即可,代价是 mcpp test 之后的第一次 mcpp build 走完整 prepare。
或者更根本:把 build.ninja 的图形态 (普通 / 含测试)记进 .build_cache 条目,快路径匹配时一并比。这样两种模式可以各自保留增量,但要动缓存格式。
第一种最小、方向保守;第二种更彻底。倾向先做第一种。
在 review #387 时发现的既存缺陷,与那个 PR 无关(
mcpp build --configure-only撞上的是同一个洞,已在 #387 内修掉自己那半边;这里记mcpp test这半边)。复现
main(
2026.8.10.1,本机 x86_64-linux-gnu):bin/tst已经不在这张图里了,但mcpp build报成功。把测试文件改坏,症状就变成硬失败 ——
src/一个字没动:即:一个坏掉的测试文件会让普通
mcpp build失败。根因
两件事叠在一起:
mcpp test用prepare_build(includeDevDeps=true, extraTargets=tests)生成 plan,backend 把它写进同一个target/<fp>/build.ninja(指纹不含 dev-deps/test targets,所以是同一个目录)。emit_ninja_string的default行列的是plan.linkUnits全部,测试模式下就只有测试二进制。try_fast_build的新鲜度判据是「build.ninja的 mtime 比源码新」(src/build/execute.cppm的sources_newer_than,只扫src/**+build.mcpp+ glob inputs + resource scripts),从不校验这张图是不是普通构建的图。target/.build_cache也没人动。tests/不在扫描范围里,所以改测试文件连快路径都不会打断。于是
mcpp build→mcpp test→mcpp build就重放了测试图。(顺带:把产物删掉再 build 反而不会复现 —— 产物缺失让 ninja 以「图过期」的样子失败,快路径回退到完整 prepare,正好把缺陷盖住。写回归测试时别先删产物。)
可选修法
mcpp test在重写build.ninja之后,丢掉指向该 outputDir 的快路径条目。feat: generate compile database without building #387 给 configure-only 加的forget_build_cache_entry(projectRoot, outputDir)(src/build/execute.cppm)就是这个形状,mcpp test直接调用即可,代价是mcpp test之后的第一次mcpp build走完整 prepare。.build_cache条目,快路径匹配时一并比。这样两种模式可以各自保留增量,但要动缓存格式。第一种最小、方向保守;第二种更彻底。倾向先做第一种。