Skip to content

docs+ci: 本地上手三步走 + 平台矩阵拆分(linux/macos/win10/win11/win 无 MSVC STL) - #89

Merged
Sunrisepeak merged 6 commits into
mainfrom
ci/platform-matrix-and-local-quickstart
Aug 2, 2026
Merged

docs+ci: 本地上手三步走 + 平台矩阵拆分(linux/macos/win10/win11/win 无 MSVC STL)#89
Sunrisepeak merged 6 commits into
mainfrom
ci/platform-matrix-and-local-quickstart

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

1. README:「本地练习环境」改成明确的三步

三份顶层 README(zh / zh-hant / en)与 book 两语 README 同步。

第一步 xlings install d2x mcpp -y —— 两个工具名分别链到 d2xmcpp 仓库
第二步 取课程 + 配置并验证环境:d2x install d2mcppcd d2mcppmcpp test
第三步 d2x checker / d2x status

原来的「方式 A / 方式 B 二选一」把 clone 源码摆成了和一键安装并列的选项,新手在第一屏就要做一个他还没有依据去做的决定。现在默认路径只有一条,clone 源码收进 <details> 作为可选项。

第二步把 mcpp test 摆到台面上是有意的:练习就是测试。新环境跑一遍会看到「全部不通过」——这既是练习的起点,也已经证明工具链是通的。把参考答案覆盖进去,同一条命令就全绿。

2. CI:单个 ubuntu job → 五档平台矩阵

linux           ubuntu-latest    (zh + en 两套课程都验)
macos           macos-14
windows-10 线   windows-2022     (Server 2022,与 Win10 22H2 同内核基线)
windows-11 线   windows-2025     (Server 2025,与 Win11 24H2 同内核基线)
windows 无 MSVC STL  windows-2025 + 遮蔽 Visual Studio

最后一档是重点。 GitHub 的 Windows 镜像预装 VS 2022 Enterprise,所以在托管 runner 上「Windows 能跑」测到的其实是「装了 Visual Studio 的 Windows 能跑」——而学习者的机器多数不是这样。这一档把 VS 藏掉(改名 vswhere.exeVC 目录、清空 VS* 环境变量),遮蔽手法取自 mcpp 自己的 ci-windows.yml,并就地检查后置条件:漏遮的后果是这一档静悄悄退化成第二个 windows-11 baseline,绿灯但什么都没验到。

mcpp 应当自动回落到自带的 winlibs MinGW,产物 target 目录名就是断言依据:

test -d d2x/buildtools/target/x86_64-windows-gnu

3. 每档都跑两层验证

脚本 断言
mcpp test e2e.sh(已有) pristine 全部不通过 + 覆盖答案后全部通过
d2x checker checker-e2e.sh(新增) 答案就位后 checker 自己走完 52 道题并退 0

加第二层的理由:checker 在 mcpp test 之上还叠了 Provider 的 NDJSON 协议、进度持久化、文件监听三层。这三层断掉时 mcpp test 照样全绿 —— Windows 上就出过这种静默回归(#87)。

checker-e2e.sh 的两条判据缺一不可:

  1. 退出码 0 —— checker 平时会一直等文件改动,只在「全部练习已完成」时才主动退出,所以「正常退出」本身就是「全过」
  2. 渲染出的 passed 条目数 ≥ 参考答案数 —— 防空转。陈旧的 .d2x/state.json 会让 checker 一道题都不编译就宣告完成并退 0

看门狗用 shell 逐秒轮询自己实现,不依赖 timeout(1)(macOS 默认没有这个命令)。

4. 顺带修的

  • .xlings.json pin 上抬:mcpp 2026.8.1.12026.8.2.1,d2x 2026.08.02.12026.08.02.2不是可选的:裸 Windows 自动回落 MinGW 是 mcpp 2026.8.2.1 才有的能力(mcpp#332),更早的版本在无 MSVC 的机器上直接硬失败,「无 MSVC STL」那一档没有它就跑不起来。
  • 文件名核对 job 之前是假绿:它还在按迁移前的布局找 intro/tests cpp*/tests,而练习早已搬进 src/,于是一个文件都没扫到、恒绿。现在真的核对 52 对。
  • 工作流触发路径补上 .xlings.json

验证情况

Linux 本地全部实跑通过(mcpp 2026.8.2.1 + d2x 2026.08.02.2):

E2E(provider) 协议冒烟: 52 个练习,check 事件流完整 ✓
E2E(zh) pristine: 全部练习保持未通过 ✓
E2E(zh) solutions: 52/52 参考答案全部通过 ✓
E2E(en) pristine: 全部练习保持未通过 ✓
E2E(en) solutions: 52/52 参考答案全部通过 ✓
E2E: ALL GREEN

CHECKER-E2E(zh): checker 自主走完 52/52 道练习并正常退出 ✓
CHECKER-E2E(en): checker 自主走完 52/52 道练习并正常退出 ✓

macOS 与三档 Windows 只能靠本 PR 的 CI 来验 —— 手上没有对应机器,而这四档此前从未跑过完整课程(旧 CI 只有 ubuntu 一档;d2x 侧的 Windows 冒烟只断言 checker 能报出第一题的编译错误,没验过全部答案能过)。所以这几档首次运行有可能红,那正是这个 PR 想要暴露的信息。合并前请看 CI 结果。

README(三语 + book 两语)重写「本地练习环境」段,改成明确的三步:

  1. 装工具 —— `xlings install d2x mcpp -y`,两个工具名都链到各自仓库
  2. 取课程并验证环境 —— 默认 `d2x install d2mcpp` → `cd d2mcpp` → `mcpp test`;
     clone 源码那条路收进 <details>,作为可选项而不是与默认路径并列的「二选一」
  3. 开始练习 —— `d2x checker` / `d2x status`

第 2 步把 `mcpp test` 摆到台面上是有意的:练习就是测试,新环境跑一遍
「全部不通过」既是练习的起点,也已经证明工具链是通的。

CI 从单个 ubuntu job 拆成五档平台矩阵,每档都跑完整两层验证:

  linux / macos / windows-10 线(windows-2022)/ windows-11 线(windows-2025)
  / windows 且无 MSVC STL

最后一档把 runner 上的 Visual Studio 藏掉(改名 vswhere.exe 与 VC 目录 +
清空 VS* 环境变量,并就地检查后置条件),模拟学习者的干净 Windows —— 这
才是多数人的机器。mcpp 应当自动回落到自带的 winlibs MinGW,产物 target
目录名(x86_64-windows-gnu)就是断言依据。

两层验证:
  - `mcpp test`(已有 e2e.sh)—— pristine 全不过 + 覆盖答案后全过
  - `d2x checker`(新增 checker-e2e.sh)—— 答案就位后 checker 必须自己走完
    52 道题并退 0

加第二层是因为 checker 在 mcpp test 之上还叠了 Provider 的 NDJSON 协议、
进度持久化、文件监听三层;这三层断掉时 mcpp test 照样全绿,Windows 上就
出过这种静默回归(#87)。checker-e2e.sh 的看门狗自己用 shell 轮询实现,
不依赖 timeout(1)(macOS 默认没有)。

顺带:
- .xlings.json pin 上抬 —— mcpp 2026.8.1.1 → 2026.8.2.1(裸 Windows 自动
  回落 MinGW 是这个版本才有的能力,无 MSVC STL 那一档依赖它),
  d2x 2026.08.02.1 → 2026.08.02.2
- 修复文件名核对 job:它还在按迁移前的布局找 `intro/tests cpp*/tests`,
  练习早已搬进 src/,于是一个文件都没扫到、永远绿。现在真的核对 52 对。
- 工作流触发路径补上 .xlings.json

本地已验:e2e.sh all(zh/en 各 52/52)、checker-e2e.sh zh 与 en 均全过。
chapter_1 是三份 README 的「更多细节」落点,但它的 §0 只装 xlings,
接着就直接 `d2x install d2mcpp` —— 一台干净的机器照着做会在第二条命令
上失败,因为 d2x 这个二进制根本还没装。

补两处,与 README 新的三步保持同一套说法:
- §0 结尾加 `xlings install d2x mcpp -y`,两个工具名链到各自仓库
- §1 加「验证环境」小节:`cd d2mcpp` + `mcpp test`,并说明「刚拿到课程
  时全部未通过」既是起点也是工具链已通的证据
@Sunrisepeak

Copy link
Copy Markdown
Member Author

CI 结果:五档全绿(run 30730372646)。macOS 与三档 Windows 是首次跑完整门课,全部一次通过。

runner 用时 mcpp test d2x checker
linux ubuntu-latest 5m22s zh 52/52 + en 52/52 zh 52/52 + en 52/52
macos macos-14 3m34s 52/52 52/52
windows-10 线 windows-2022 (Server 2022, 10.0.20348) 5m38s 52/52 52/52
windows-11 线 windows-2025 (Server 2025, 10.0.26100) 7m5s 52/52 52/52
windows 无 MSVC STL windows-2025 + 遮蔽 VS 8m12s 52/52 52/52

无 MSVC STL 那一档确认不是空过 —— 遮蔽真的生效了,回落也真的发生了:

masking C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC
Visual Studio masked: no VC\Tools\MSVC remains.

First run no toolchain configured and no Visual Studio found
  — using gcc@16.1.0 for x86_64-windows-gnu (MinGW-w64, self-contained)
Resolved gcc@16.1.0 → x86_64-windows-gnu → xim-x-mingw-gcc/16.1.0/bin/g++.exe

OK: 无 MSVC STL 时回落到 winlibs MinGW (x86_64-windows-gnu)
E2E(zh) solutions: 52/52 参考答案全部通过 ✓
CHECKER-E2E(zh): checker 自主走完 52/52 道练习并正常退出 ✓

两档 baseline 的日志里没有这段回落公告(mcpp 只在回落时才打),说明它们走的确实是 MSVC —— 三档 Windows 覆盖到的是三条不同的路径,不是同一条跑三遍。

另外补了一个 commit:chapter_1(三份 README 的「更多细节」落点)的 §0 只装 xlings 就直接 d2x install d2mcpp,干净机器上第二条命令必失败(d2x 还没装)。已补上工具安装与「验证环境」小节,与 README 三步同一套说法。

原话把 `mcpp test` 说成「验证每道练习都能编译并运行」,正好说反了:练习发
下来就是没做完的,`std:endl`、`D2X_YOUR_ANSWER` 这类东西**必然编不过**,
pristine 状态下 0 passed 才是对的(e2e.sh 的断言 1 就是在守这条)。

顺带澄清一个容易误会的点:`solutions/` 不是工作区成员(mcpp.toml 的
members 里没有它),`mcpp test` 从头到尾不碰它。要它变绿得先覆盖到练习
文件上——那是 CI 的动作,不是学习者跑一条命令就会发生的事。

改成说清楚它到底验了什么:工具链解析成功、d2x 库编译通过、运行器逐题都
跑到了;练习本身红着是设计如此。README 三语 + book 两语 + chapter_1 两语
共 7 处同步。
在此之前 solutions/ 只是一堆躺在磁盘上的 .cpp:不是工作区成员,mcpp 从头到
尾不认识它。想验证答案对不对,唯一的办法是把 52 个文件覆盖到 src/*/tests/
上、跑一遍、再 git 还原 —— 一套带副作用、要求工作树干净、只有 CI 脚本会用
的流程。学习者或贡献者想确认「答案是好的」,没有一条命令可以跑。

现在 solutions/ 是一个正经工程:

    solutions/mcpp.toml            name=solutions, c++23, 依赖 d2x
    solutions/tests/<std>/...      52 份答案,就是它的测试

于是:

    mcpp test -p solutions          # 52 passed; 0 failed
    mcpp test -p solutions cpp11    # 按子串筛

零副作用、不碰练习目录、任何人随手可跑。布局与练习一一对应,测试名
(intro/hello-mcpp、cpp11/00-auto-and-decltype/0) 直接对回练习文件。
配置刻意与练习工程一致(c++23 + d2x),否则「答案能过」推不出「答案放进
练习里也能过」。

覆盖-还原那套 e2e 保留 —— 它验的是另一件事:答案放到练习的位置上也成立,
且练习在未完成时确实不通过。两条互补:新的这条挂了说明答案本身是错的,
旧的那条挂了说明答案与练习对不上。

- e2e.sh 新增 solutions_selftest(),并核对测试条数 == 答案文件数:测试发现
  是按 tests/**/*.cpp 自动扫的,布局一改就可能一个都没扫到,而「0 个测试」
  在 mcpp 眼里同样是 "test result ok"
- CI 每档平台新增独立一段 `mcpp test -p solutions`,排在覆盖-还原之前,
  失败定位最短
- e2e.sh / checker-e2e.sh / 文件名核对 job 的路径映射跟随改为 solutions/tests/
上一版让 step 2 跑裸 `mcpp test`,然后解释「全红是对的」。这在逻辑上站不住:
工具链坏掉时也是全红,学习者看着同样的输出,分辨不出是自己还没做题还是环境
没装好——一个只会红的命令没法用来验证环境。

solutions/ 成为工程之后就有了确定的绿:

    mcpp test -p solutions       # 验证环境: 52 份参考答案, 应当全绿
    mcpp test                    # 你的进度表: 练习还没做, 此刻全红

两条命令各自含义明确:第一条绿 = 工具链没问题;第二条红 = 你的起点。

README 三语 + book 两语 + chapter_1 两语同步。chapter_1 里另外写清了答案与
练习靠 tests/ 相对路径配对,以及 `mcpp test -p solutions cpp11` 的筛选用法。

撰稿技能(.agents/skills/d2mcpp-authoring)的路径与「答案怎么验」也跟随更新:
新增答案后可以直接 `mcpp test -p solutions <slug>` 自查,不必再走覆盖流程。
@Sunrisepeak

Copy link
Copy Markdown
Member Author

补充:solutions/ 现在是一个真正的 mcpp 工程

此前 solutions/ 只是一堆躺在磁盘上的 .cpp —— 不是工作区成员,mcpp 从头到尾不认识它。想确认「答案是好的」,唯一的办法是把 52 个文件覆盖到 src/*/tests/ 上、跑一遍、再 git checkout 还原:带副作用、要求工作树干净、只有 CI 脚本会用。学习者和贡献者没有一条命令可跑。

现在它是一个正经工程:

solutions/mcpp.toml            name=solutions, c++23, 依赖 d2x
solutions/tests/<std>/...      52 份答案,就是它的测试
$ mcpp test -p solutions
 test result ok. 52 passed; 0 failed; finished in 5.18s

$ mcpp test -p solutions cpp14        # 按子串筛
 test result ok. 2 passed; 0 failed

零副作用,不碰练习目录。测试名(intro/hello-mcppcpp11/00-auto-and-decltype/0)与练习文件一一对应。配置刻意与练习工程一致(c++23 + d2x)——否则「答案能过」推不出「答案放进练习里也能过」。

覆盖-还原那套 e2e 保留,因为两条验的不是一件事:新的这条挂了说明答案本身编不过/跑不过;旧的那条挂了说明答案与练习对不上,或者练习在未完成时居然通过了。

作为 CI 测试段

每档平台新增独立一步,排在覆盖-还原之前(失败定位最短):

- name: mcpp test -p solutions (reference answers must be all green)
  run: mcpp test -p solutions

e2e.sh 里也加了 solutions_selftest(),额外核对测试条数 == 答案文件数 —— 测试发现是按 tests/**/*.cpp 自动扫的,布局一改就可能一个都没扫到,而「0 个测试」在 mcpp 眼里同样是 test result ok

五档平台实测(run 30731287264,全绿):

mcpp test -p solutions
linux 52 passed; 0 failed (29.6s)
macos 52 passed; 0 failed (22.0s)
windows-10 线 52 passed; 0 failed (19.4s)
windows-11 线 52 passed; 0 failed (23.9s)
windows 无 MSVC STL 52 passed; 0 failed (56.7s)

顺带解决了 README step 2 的一个逻辑问题

原来 step 2 让学习者跑裸 mcpp test 然后解释「全红是对的」—— 但工具链坏掉时也是全红,两种情况输出一样,这个命令根本没法用来验证环境。现在有了确定的绿:

mcpp test -p solutions       # 验证环境: 52 份参考答案, 应当全绿
mcpp test                    # 你的进度表: 练习还没做, 此刻全红

第一条绿 = 工具链没问题;第二条红 = 你的起点。README 三语 + book 两语 + chapter_1 两语已同步,撰稿技能也更新为「新增答案后直接 mcpp test -p solutions <slug> 自查」。

step 2 摆两条命令、再解释「第一条绿第二条红」,等于在验证环境这一步塞了
一个当下用不上的概念。step 2 现在只做一件事:

    mcpp test -p solutions       # 验证环境: 52 份参考答案, 应当全绿

裸 `mcpp test`(你自己的进度表)本来就属于「不经过 d2x 怎么练」这一类,
归到 step 3 已有的那条可选 note 里,和 `mcpp test -p src/cpp11` 放一起:

    > 不想经过 d2x? 练习就是测试 —— `mcpp test` 打印你的完整进度表(动手前
    > 全红), `mcpp test -p src/cpp11` 只跑某一章。

同步的地方:
- README 三语 + book 两语:step 2 收敛为一条命令;<details> 里 clone 那条
  路的环境验证也跟着改成 `mcpp test -p solutions`(原来还是裸 mcpp test)
- chapter_1 两语:「验证环境」节同样只留参考答案那条;裸 `mcpp test` 落到
  已有的「原生模式(可选)」节,作为该节三条命令里的第一条
@Sunrisepeak
Sunrisepeak merged commit 6bcf5db into main Aug 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant