Skip to content

Commit 214fb2c

Browse files
committed
fix(build): never auto-repair a toolchain the user selected as msvc@system
mcpp never picks msvc@system itself — it cannot install one — so the only way it reaches config.toml is a user running `mcpp toolchain default msvc`. Under the origin rules that landed as GlobalDefault, i.e. repairable, which would have silently moved a user who evidently wants MSVC (and is most likely just missing the SDK component) onto MinGW instead of naming the component to install. Also records the implementation findings in the plan doc — the ones the plan could not have predicted, notably that the raw argv[0] was deliberate, that the Windows branch of command_from_argv is not compiled on the platforms mcpp is developed on, and that Directives is cache-serialized.
1 parent 061314d commit 214fb2c

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

.agents/docs/2026-08-02-windows-usability-implementation-plan.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,20 @@ token 走 `~/.config/gitcode-tool/config.json` 而非环境变量;沙箱 wrapper
10001000

10011001
---
10021002

1003+
## 实施记录(执行中发现、计划里没写的东西)
1004+
1005+
| 发现 | 影响 |
1006+
|---|---|
1007+
| **`command_from_argv` 的裸 argv[0] 是故意的** —— 注释写明引号会被 `cmd /c` 剥掉 | 真修法不在 `command_from_argv` 一处:要按 cmd.exe `/c` 的文档规则给整条命令**再包一层外引号**,让 cmd 吃掉它,内层引号才能抵达。`run_exec` 必须只包不封 stdin(`mcpp run` 要交互) |
1008+
| **`command_from_argv` 只在非 Linux/macOS 分支编译** | Linux 上测不到。把 Windows 的字符串成形抽成宿主无关的 `windows_command_from_argv` / `windows_wrap_for_cmd_c`,Linux CI 也能守住这条规则 —— 这个分支在开发平台上根本不编译,正是裸 argv[0] 活这么久的原因 |
1009+
| **`local_include_flags``msvcDialect` 形参只用于 after-dirs** | 普通 include 硬编码 `-I`。收敛到 `include_token` 后 MSVC 方言下变成 `/I`,`test_ninja_backend.cpp` 里有一条断言编码的正是这个 bug,已更新 |
1010+
| **两层转义,顺序固定** | ninja 的 `$ ` 在内、shell 引号在外。写断言时必须知道文件里是 `'-I/opt/my$ dep/include'`,单测和 e2e 各踩了一次 |
1011+
| **`ensure_built``tc` 已经是宿主工具链** | `prepare.cppm``run_build_program(*m, *root, host->first, host->second, ...)`,host≠target 在调用点就闭合了,组件 D 无需自己解析 |
1012+
| **`Directives` 会被序列化进 build.mcpp 缓存** | 计划里的「产出中立字段」会改缓存格式。改为在 parse 时按方言翻译 —— 缓存键已含 `compiler <hash>`,换工具链自动失效,所以安全且零格式变更 |
1013+
| **`mcpp toolchain default msvc` 也写 `config.toml`** | 与 mcpp 自己持久化的默认同源,会被误判成可改写。收紧:`tc->compiler == MSVC` 一律视为用户显式 —— mcpp 从不自选 msvc@system |
1014+
| **`[build] linkage` 根本不被解析** | `toml.cppm:995` 只在 `[target.<triple>]` 下读。文档三处 + `prepare.cppm` 一处注释都在教一个静默失效的键,已改 |
1015+
| **`no-msvc` 需要是一个显式能力** | 不能由「非 msvc」推出:e2e 182 必须 REQUIRE 它,否则在有 MSVC 的机器上会走普通路径并通过,证明不了任何事 |
1016+
10031017
## Self-Review
10041018

10051019
**Spec coverage**

src/build/prepare.cppm

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ prepare_build(bool print_fingerprint,
14021402
if (windowsGnuFirstRun && tcSpec.has_value()) {
14031403
mcpp::ui::info("First run",
14041404
std::format("no toolchain configured and no Visual Studio found — "
1405-
"installing {} for {} (MinGW-w64, self-contained)",
1405+
"using {} for {} (MinGW-w64, self-contained)",
14061406
*tcSpec, overrides.target_triple));
14071407
if (auto cfgW = get_cfg(); cfgW) {
14081408
if (mcpp::config::write_default_toolchain(**cfgW, *tcSpec))
@@ -1432,8 +1432,24 @@ prepare_build(bool print_fingerprint,
14321432
tc->compiler == mcpp::toolchain::CompilerId::MSVC
14331433
|| mcpp::toolchain::is_msvc_target(*tc);
14341434
if (targetsMsvcAbi && !mcpp::toolchain::msvc::has_usable_msvc()) {
1435+
// Native cl.exe is ALWAYS a deliberate choice: mcpp never selects
1436+
// msvc@system on its own — it cannot install one — so the only way it
1437+
// reaches config.toml is a user typing `mcpp toolchain default msvc`.
1438+
// Without this, that user (who evidently wants MSVC and is probably
1439+
// just missing the SDK component) would be silently moved to MinGW
1440+
// instead of being told which component to install.
1441+
//
1442+
// The residual imprecision is deliberate and bounded: a *global*
1443+
// default of llvm@20.1.7 is indistinguishable from the one mcpp used
1444+
// to write itself, so an explicitly-typed one gets repaired too. The
1445+
// value is identical either way and the machine cannot build with it;
1446+
// a user who wants that failure can pin it in mcpp.toml, which is
1447+
// honoured exactly.
1448+
const bool userChoseMsvcItself =
1449+
tc->compiler == mcpp::toolchain::CompilerId::MSVC;
14351450
const bool mayRepair =
14361451
!tc_origin_is_user_explicit(tcOrigin)
1452+
&& !userChoseMsvcItself
14371453
&& !mcpp::platform::env::offline_mode()
14381454
&& !mcpp::platform::env::no_auto_install()
14391455
&& mcpp::platform::is_windows;

0 commit comments

Comments
 (0)