From 96c3617906f775ff483a811ec1f3c1d5775839b5 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 11:16:49 +0800 Subject: [PATCH 1/6] =?UTF-8?q?docs+ci:=20=E6=9C=AC=E5=9C=B0=E4=B8=8A?= =?UTF-8?q?=E6=89=8B=E4=B8=89=E6=AD=A5=E8=B5=B0=20+=20=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E7=9F=A9=E9=98=B5=E6=8B=86=E5=88=86(linux/macos/win10/win11/wi?= =?UTF-8?q?n=20=E6=97=A0=20MSVC=20STL)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README(三语 + book 两语)重写「本地练习环境」段,改成明确的三步: 1. 装工具 —— `xlings install d2x mcpp -y`,两个工具名都链到各自仓库 2. 取课程并验证环境 —— 默认 `d2x install d2mcpp` → `cd d2mcpp` → `mcpp test`; clone 源码那条路收进
,作为可选项而不是与默认路径并列的「二选一」 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 均全过。 --- .github/workflows/dslings-ref-ci.yml | 168 ++++++++++++++++++++++++--- .xlings.json | 8 +- README.md | 28 +++-- README.zh.hant.md | 26 +++-- README.zh.md | 26 +++-- book/en/src/README.md | 45 ++++++- book/src/README.md | 43 ++++++- d2x/buildtools/tests/checker-e2e.sh | 130 +++++++++++++++++++++ 8 files changed, 426 insertions(+), 48 deletions(-) create mode 100755 d2x/buildtools/tests/checker-e2e.sh diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 7cbd45b..e124b97 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -7,12 +7,25 @@ name: Validate exercises and reference solutions # 练习即测试:e2e 走 `mcpp test`,与 `d2x checker` 内部的 Provider 是同一条 # 链路,所以 CI 绿灯等价于学习者本地能跑通,而不是另一条平行的构建路径。 # +# 两层都要验,因为它们的失效方式不同: +# - `mcpp test` —— 编译/运行这一层(e2e.sh) +# - `d2x checker` —— 在它之上还叠了 Provider 的 NDJSON 协议、进度持久化、 +# 文件监听三层(checker-e2e.sh)。这三层断掉时 mcpp test +# 照样全绿,Windows 上就出过这样的静默回归(d2mcpp#87)。 +# +# 平台矩阵拆成五档,因为它们各有各的踩法,合成一个 ubuntu job 等于没测: +# linux / macos / windows-10 线 / windows-11 线 / windows 且无 MSVC STL +# 最后一档见下方 mask_msvc 的注释。 +# # 依赖 mcpp >= 0.0.104(测试能力批次:逐测试隔离 / 过滤 / --list / --timeout / # --message-format json / tests 吃 [build].flags)。 # -# 但真正的下限已被上游抬高:mcpplibs 依赖索引现在要求 mcpp >= 0.0.109,更低的 -# 版本一律 E0006「index requires mcpp >= 0.0.109」而拒绝解析依赖 —— 原先钉的 -# 0.0.104 自此装不出来。pin 见 .xlings.json,当前为 2026.8.1.1。 +# 但真正的下限已被上游抬高两次: +# - mcpplibs 依赖索引要求 mcpp >= 0.0.109,更低版本一律 E0006 拒绝解析依赖 +# - 「无 MSVC STL 的 Windows」这一档要求 mcpp >= 2026.8.2.1:自动回落到 +# winlibs MinGW(x86_64-windows-gnu)是那个版本才有的能力,再早的版本在 +# 裸 Windows 上直接硬失败 +# pin 见 .xlings.json,当前为 2026.8.2.1。 on: push: @@ -23,6 +36,7 @@ on: - "d2x/**" - "mcpp.toml" - ".d2x.json" + - ".xlings.json" - ".github/workflows/dslings-ref-ci.yml" pull_request: paths: @@ -31,52 +45,172 @@ on: - "d2x/**" - "mcpp.toml" - ".d2x.json" + - ".xlings.json" - ".github/workflows/dslings-ref-ci.yml" workflow_dispatch: +env: + XLINGS_VERSION: "0.4.51" + XLINGS_NON_INTERACTIVE: "1" + jobs: validate-exercises: - runs-on: ubuntu-latest + name: validate (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 120 - env: - XLINGS_VERSION: "0.4.51" - XLINGS_NON_INTERACTIVE: "1" + # GitHub 不提供 Windows 客户端版的 x64 托管 runner,只有 Server 镜像;这里 + # 用与各自客户端版共享内核基线的 Server 镜像代表两条产品线: + # windows-2022 = Server 2022,与 Windows 10 22H2 同基线(10.0.20348) + # windows-2025 = Server 2025,与 Windows 11 24H2 同基线(10.0.26100) + # 这是托管 runner 上能覆盖到的最接近真实 Win10/Win11 的组合。 + strategy: + fail-fast: false + matrix: + include: + - name: linux + os: ubuntu-latest + langs: all # zh + en 两套课程都验 + - name: macos + os: macos-14 + langs: zh + - name: windows-10 baseline + os: windows-2022 + langs: zh + - name: windows-11 baseline + os: windows-2025 + langs: zh + # 学习者的机器上装了 Visual Studio 才是少数情况。这一档把 VS 从 + # runner 上藏掉,验证「一台干净的 Windows」同样能跑完整门课 —— + # mcpp 会自动回落到自带的 winlibs MinGW(x86_64-windows-gnu)。 + - name: windows-11, no MSVC STL + os: windows-2025 + langs: zh + mask_msvc: true + + defaults: + run: + shell: bash steps: - uses: actions/checkout@v4 + # 必须排在装工具链之前:此后的任何一步都不该看见 Visual Studio。 + - name: Mask Visual Studio (simulate a bare Windows box) + if: matrix.mask_msvc + shell: pwsh + run: | + $ErrorActionPreference = 'Continue' + + # mcpp 的三条 MSVC 发现路径(vswhere / 环境变量 / 已知路径扫描)最终 + # 都落到 \VC\Tools\MSVC,所以藏的是 VC 目录而不是 VS 根目录: + # 根目录在 runner 上被进程占着改不了名,低一层的 VC 可以。 + $vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" + if (Test-Path $vswhere) { Rename-Item $vswhere "vswhere.exe.masked" } + + Resolve-Path "C:\Program Files*\Microsoft Visual Studio\*\*\VC" ` + -ErrorAction SilentlyContinue | ForEach-Object { + $p = $_.Path + Write-Host "masking $p" + try { Rename-Item -LiteralPath $p -NewName "VC.masked" -ErrorAction Stop } + catch { Write-Host " rename failed: $($_.Exception.Message)" } + } + + foreach ($v in @('VSINSTALLDIR','VCINSTALLDIR','VCToolsInstallDir', + 'VS170COMNTOOLS','VS160COMNTOOLS','VS150COMNTOOLS')) { + "$v=" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8 + } + + # 就地检查遮蔽的后置条件。漏遮的后果是这一档静悄悄变成第二个 + # windows-11 baseline —— 绿灯,但什么都没验到。 + $left = Get-ChildItem "C:\Program Files*\Microsoft Visual Studio\*\*\VC\Tools\MSVC" ` + -Directory -ErrorAction SilentlyContinue + if ($left) { + Write-Host "FAIL: VC tools still present after masking:" + $left | ForEach-Object { Write-Host " $($_.FullName)" } + exit 1 + } + Write-Host "Visual Studio masked: no VC\Tools\MSVC remains." + # 官方一键安装脚本自带镜像回退。手动 curl GitHub release 包会被 # release CDN 的间歇性 504 打断。 - - name: Install xlings ${{ env.XLINGS_VERSION }} + - name: Install xlings ${{ env.XLINGS_VERSION }} (Linux/macOS) + if: runner.os != 'Windows' run: | set -eu curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ | bash -s "v${XLINGS_VERSION}" echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - # 按 .xlings.json 安装 mcpp。mcpp 自带工具链沙箱,无需另装 gcc。 - - name: Install mcpp + - name: Install xlings (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex + "$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 + + # 按 .xlings.json 安装 mcpp + d2x。mcpp 自带工具链沙箱,无需另装 gcc。 + - name: Install pinned toolchain (.xlings.json) run: | set -eu xlings update xlings install -y + mcpp --version + d2x --version + + - name: Report runner OS + if: runner.os == 'Windows' + shell: pwsh + run: | + $os = Get-CimInstance Win32_OperatingSystem + Write-Host "runner=${{ matrix.os }} caption=$($os.Caption) build=$($os.Version)" - name: Build d2x Provider run: mcpp build -p d2x/buildtools - - name: Validate exercises and solutions (zh + en) - run: bash d2x/buildtools/tests/e2e.sh all + # mcpp 把产物按 target triple 分目录,所以目录名就是「实际用了哪条 + # 工具链」最直接的证据。遮蔽生效时它必须是 gnu 而不是 msvc。 + - name: Assert MinGW fallback took effect (no MSVC STL) + if: matrix.mask_msvc + run: | + set -eu + ls d2x/buildtools/target/ + if [ ! -d d2x/buildtools/target/x86_64-windows-gnu ]; then + echo "FAIL: 期望回落到 x86_64-windows-gnu,实际产物 target 目录如上" + exit 1 + fi + echo "OK: 无 MSVC STL 时回落到 winlibs MinGW (x86_64-windows-gnu)" + + # 第一层:`mcpp test` —— pristine 全部不通过 + 覆盖答案后全部通过 + - name: mcpp test (pristine fails, solutions all pass) + run: bash d2x/buildtools/tests/e2e.sh ${{ matrix.langs }} + + # 第二层:`d2x checker` —— 答案就位后 checker 必须自己走完全程并退 0 + - name: d2x checker (solutions drive it to completion) + run: bash d2x/buildtools/tests/checker-e2e.sh zh + + - name: d2x checker — en course + if: matrix.langs == 'all' + run: bash d2x/buildtools/tests/checker-e2e.sh en + + # 纯文件名核对,与工具链无关,跑一个平台就够。 + solutions-parity: + name: exercises ↔ solutions filename parity + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 - name: Verify exercises ↔ solutions filename parity run: | set -eu # en/ 镜像跳过 —— 参考答案与语言无关,两边共用同一份 missing=0 - for f in $(find intro/tests cpp*/tests -name '*.cpp'); do - std="${f%%/*}" - rel="${f#*/tests/}" - if [ ! -f "solutions/$std/$rel" ]; then - echo "::warning::missing solutions/$std/$rel (paired with $f)" + for f in $(find src/intro/tests src/cpp*/tests -name '*.cpp'); do + rel="${f#src/}" + std="${rel%%/*}" + rest="${rel#*/tests/}" + if [ ! -f "solutions/$std/$rest" ]; then + echo "::warning::missing solutions/$std/$rest (paired with $f)" missing=$((missing+1)) fi done diff --git a/.xlings.json b/.xlings.json index 83b5d00..62f7a82 100644 --- a/.xlings.json +++ b/.xlings.json @@ -1,12 +1,12 @@ { "workspace": { - "d2x": "2026.08.02.1", + "d2x": "2026.08.02.2", "mdbook": "0.4.43", "code": "", "mcpp": { - "linux": "2026.8.1.1", - "macosx": "2026.8.1.1", - "windows": "2026.8.1.1" + "linux": "2026.8.2.1", + "macosx": "2026.8.2.1", + "windows": "2026.8.2.1" } } } diff --git a/README.md b/README.md index 48b2e99..6c6819d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ ### Interactive Code Practice (Local) -**Step 1 - Install the d2x tool** +**Step 1 - Install the [d2x](https://github.com/d2learn/d2x) and [mcpp](https://github.com/mcpp-community/mcpp) tools**
click to view xlings installation command @@ -69,26 +69,40 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i
```bash -xlings install d2x -y # exercise framework CLI +xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & test tool ``` -**Step 2 - Get the course (choose one)** +**Step 2 - Get the course, then configure and verify the environment** ```bash -# Option A - one command: download the course, toolchain auto-configured -d2x install d2mcpp +d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp +mcpp test # verify the environment: every exercise compiles and runs ``` +> The exercises **are** the tests. On a fresh checkout `mcpp test` reports them all as +> failing — that is the expected starting point, and it already proves your toolchain +> works. Drop the reference answers from `solutions/` in and the very same command goes +> all green. + +
+ optional - start from the git source instead + +--- + ```bash -# Option B - git clone the source git clone https://github.com/mcpp-community/d2mcpp.git # or your fork cd d2mcpp xlings install -y # toolchain pinned by .xlings.json +mcpp test # same environment check ``` > Tip: fork this repo and clone your fork — `git commit / push` keeps your practice progress in your own repository. +--- + +
+ **Step 3 - Start practicing** ```bash @@ -96,7 +110,7 @@ d2x checker # practice loop: edit -> save -> auto-check -> adva d2x status # progress overview ``` -> Exercises are real mcpp tests — `mcpp test -p src/cpp11` works too (no d2x involved). +> Want to run a single chapter without d2x? `mcpp test -p src/cpp11` works too. **👉 [more details...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/README.zh.hant.md b/README.zh.hant.md index f3b1aee..d89a8a9 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -43,7 +43,7 @@ ### 建立本地練習環境 -**第一步 - 安裝 d2x 工具** +**第一步 - 安裝 [d2x](https://github.com/d2learn/d2x) 和 [mcpp](https://github.com/mcpp-community/mcpp) 工具**
點選查看xlings安裝指令 @@ -69,26 +69,38 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i
```bash -xlings install d2x -y # 練習框架 CLI +xlings install d2x mcpp -y # d2x: 練習框架 CLI | mcpp: C++ 建置與測試工具 ``` -**第二步 - 獲取課程(二選一)** +**第二步 - 獲取課程, 並配置和驗證環境** ```bash -# 方式 A - 一鍵安裝: 下載課程,環境自動配置 -d2x install d2mcpp +d2x install d2mcpp # 下載課程,環境自動配置 cd d2mcpp +mcpp test # 驗證環境: 每道練習都能編譯並執行 ``` +> 練習**就是**測試。剛拿到課程時 `mcpp test` 會報告全部未通過 —— 這正是練習的起點, +> 同時也說明工具鏈已經跑通了。把 `solutions/` 裡的參考答案覆蓋進去,同一條指令就會全綠。 + +
+ 可選 - 改用 clone 專案原始碼的方式 + +--- + ```bash -# 方式 B - git clone 原始碼 git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 後的倉庫 cd d2mcpp xlings install -y # 按 .xlings.json 安裝固定版本工具鏈 +mcpp test # 同樣的環境驗證 ``` > 建議: 先 fork 本倉庫再 clone 自己的 fork —— 通過 `git commit / push` 把練習進度保存到自己的倉庫裡。 +--- + +
+ **第三步 - 開始練習** ```bash @@ -96,7 +108,7 @@ d2x checker # 練習循環: 編輯 -> 保存 -> 自動偵測 -> d2x status # 進度總覽 ``` -> 練習就是真實的 mcpp 測試 —— 不經過 d2x,直接 `mcpp test -p src/cpp11` 同樣可用。 +> 想單獨跑某一章而不經過 d2x? 直接 `mcpp test -p src/cpp11` 同樣可用。 **👉 [更多細節...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/README.zh.md b/README.zh.md index 0994e7d..13280dc 100644 --- a/README.zh.md +++ b/README.zh.md @@ -43,7 +43,7 @@ ### 搭建本地练习环境 -**第一步 - 安装 d2x 工具** +**第一步 - 安装 [d2x](https://github.com/d2learn/d2x) 和 [mcpp](https://github.com/mcpp-community/mcpp) 工具**
点击查看xlings安装命令 @@ -69,26 +69,38 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i
```bash -xlings install d2x -y # 练习框架 CLI +xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试工具 ``` -**第二步 - 获取课程(二选一)** +**第二步 - 获取课程, 并配置和验证环境** ```bash -# 方式 A - 一键安装: 下载课程,环境自动配置 -d2x install d2mcpp +d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp +mcpp test # 验证环境: 每道练习都能编译并运行 ``` +> 练习**就是**测试。刚拿到课程时 `mcpp test` 会报告全部未通过 —— 这正是练习的起点, +> 同时也说明工具链已经跑通了。把 `solutions/` 里的参考答案覆盖进去,同一条命令就会全绿。 + +
+ 可选 - 改用 clone 项目源码的方式 + +--- + ```bash -# 方式 B - git clone 源码 git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 后的仓库 cd d2mcpp xlings install -y # 按 .xlings.json 安装固定版本工具链 +mcpp test # 同样的环境验证 ``` > 建议: 先 fork 本仓库再 clone 自己的 fork —— 通过 `git commit / push` 把练习进度保存到自己的仓库里。 +--- + +
+ **第三步 - 开始练习** ```bash @@ -96,7 +108,7 @@ d2x checker # 练习循环: 编辑 -> 保存 -> 自动检测 -> d2x status # 进度总览 ``` -> 练习就是真实的 mcpp 测试 —— 不经过 d2x,直接 `mcpp test -p src/cpp11` 同样可用。 +> 想单独跑某一章而不经过 d2x? 直接 `mcpp test -p src/cpp11` 同样可用。 **👉 [更多细节...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/book/en/src/README.md b/book/en/src/README.md index 00dc449..4aae72b 100644 --- a/book/en/src/README.md +++ b/book/en/src/README.md @@ -39,6 +39,8 @@ ### Interactive Code Practice (Local) +**Step 1 - Install the [d2x](https://github.com/d2learn/d2x) and [mcpp](https://github.com/mcpp-community/mcpp) tools** +
click to view xlings installation command @@ -63,12 +65,49 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i
```bash -xlings install d2x -y -d2x install d2mcpp +xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & test tool +``` + +**Step 2 - Get the course, then configure and verify the environment** + +```bash +d2x install d2mcpp # download the course, toolchain auto-configured +cd d2mcpp +mcpp test # verify the environment: every exercise compiles and runs +``` + +> The exercises **are** the tests. On a fresh checkout `mcpp test` reports them all as +> failing — that is the expected starting point, and it already proves your toolchain +> works. Drop the reference answers from `solutions/` in and the very same command goes +> all green. + +
+ optional - start from the git source instead + +--- + +```bash +git clone https://github.com/mcpp-community/d2mcpp.git # or your fork cd d2mcpp -d2x checker +xlings install -y # toolchain pinned by .xlings.json +mcpp test # same environment check +``` + +> Tip: fork this repo and clone your fork — `git commit / push` keeps your practice progress in your own repository. + +--- + +
+ +**Step 3 - Start practicing** + +```bash +d2x checker # practice loop: edit -> save -> auto-check -> advance +d2x status # progress overview ``` +> Want to run a single chapter without d2x? `mcpp test -p src/cpp11` works too. + **👉 [more details...](https://mcpp-community.github.io/d2mcpp/en/base/chapter_1.html)** ## Community diff --git a/book/src/README.md b/book/src/README.md index 831d3fd..b43dc2d 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -39,6 +39,8 @@ > 尝试 `Code -> Book -> Video -> X -> Code` +**第一步 - 安装 [d2x](https://github.com/d2learn/d2x) 和 [mcpp](https://github.com/mcpp-community/mcpp) 工具** +
点击查看xlings安装命令 @@ -63,12 +65,47 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i
```bash -xlings install d2x -y -d2x install d2mcpp +xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试工具 +``` + +**第二步 - 获取课程, 并配置和验证环境** + +```bash +d2x install d2mcpp # 下载课程,环境自动配置 +cd d2mcpp +mcpp test # 验证环境: 每道练习都能编译并运行 +``` + +> 练习**就是**测试。刚拿到课程时 `mcpp test` 会报告全部未通过 —— 这正是练习的起点, +> 同时也说明工具链已经跑通了。把 `solutions/` 里的参考答案覆盖进去,同一条命令就会全绿。 + +
+ 可选 - 改用 clone 项目源码的方式 + +--- + +```bash +git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 后的仓库 cd d2mcpp -d2x checker +xlings install -y # 按 .xlings.json 安装固定版本工具链 +mcpp test # 同样的环境验证 +``` + +> 建议: 先 fork 本仓库再 clone 自己的 fork —— 通过 `git commit / push` 把练习进度保存到自己的仓库里。 + +--- + +
+ +**第三步 - 开始练习** + +```bash +d2x checker # 练习循环: 编辑 -> 保存 -> 自动检测 -> 推进 +d2x status # 进度总览 ``` +> 想单独跑某一章而不经过 d2x? 直接 `mcpp test -p src/cpp11` 同样可用。 + **👉 [更多细节...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** ## 社区 diff --git a/d2x/buildtools/tests/checker-e2e.sh b/d2x/buildtools/tests/checker-e2e.sh new file mode 100755 index 0000000..dac35bd --- /dev/null +++ b/d2x/buildtools/tests/checker-e2e.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# d2x checker 端到端验证 —— 一条硬断言: +# +# 把 solutions/ 全部覆盖到练习上之后, `d2x checker` 必须自己走完全部练习 +# 并主动退出(退出码 0)。checker 只在「全部练习已完成」时才退出,平时它 +# 会一直等文件改动,所以「正常退出」本身就是「全过」的判据。 +# +# 为什么值得和 e2e.sh 分开:e2e.sh 只验 `mcpp test` 那一层。checker 在它上面 +# 还叠了三层 —— Provider 的 NDJSON 协议、进度持久化(.d2x/state.json)、文件 +# 监听循环。任何一层断掉,`mcpp test` 都照样全绿而 checker 已经废了;Windows +# 上就真出过整条链路静默失效、CI 却是绿的回归(见 d2x#35 / d2mcpp#87)。 +# +# 判据(缺一不可): +# 1) 退出码 0 —— checker 主动跑完退出 +# 2) 渲染出的 passed 条目数 >= 参考答案数 +# —— 防空转:陈旧的 .d2x/state.json 会让 checker 一道题都不编译就直接 +# 宣告完成并退 0。脚本会先删掉状态,这条是第二道保险。 +# +# 用法: bash d2x/buildtools/tests/checker-e2e.sh [zh|en] (默认 zh) +# 环境: D2X= (默认取 PATH 里的 d2x) +set -uo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +cd "$ROOT" + +LANG_SEL="${1:-zh}" +D2X="${D2X:-d2x}" +LIMIT="${CHECKER_TIMEOUT:-1800}" # 冷机第一次要下工具链 + 编译 52 道题 + +case "$LANG_SEL" in + zh) PREFIX="src/" ;; + en) PREFIX="src/en/" ;; + *) echo "用法: $0 [zh|en]"; exit 2 ;; +esac + +MEMBERS=("${PREFIX}intro" "${PREFIX}cpp11" "${PREFIX}cpp14") +TEST_DIRS=() +for m in "${MEMBERS[@]}"; do TEST_DIRS+=("$m/tests"); done + +# —— 脏树检查 —— 与 e2e.sh 同理:脚本要把答案覆盖到练习上再用 git 还原, +# 未提交的改动会被直接覆盖丢失。 +dirty=$(git status --porcelain -- "${TEST_DIRS[@]}" solutions/ .d2x.json | head -20) +if [[ -n "$dirty" ]]; then + echo "CHECKER-E2E($LANG_SEL) 拒绝运行: 练习/答案目录存在未提交改动(覆盖-还原流程会将其丢失):" + echo "$dirty" + exit 1 +fi + +restore() { + git checkout -q -- "${TEST_DIRS[@]}" .d2x.json 2>/dev/null + rm -rf .d2x/state.json .d2x/result.ndjson .d2x/last-output.log .d2x/checker.lock +} +trap restore EXIT + +# —— 覆盖参考答案(zh/en 共用同一份 solutions)—— +n_expected=0 +while IFS= read -r sol; do + rel="${sol#solutions/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp + std="${rel%%/*}"; rest="${rel#*/}" + dst="${PREFIX}${std}/tests/${rest}" + if [[ ! -f "$dst" ]]; then + echo "CHECKER-E2E($LANG_SEL) FAIL: 答案 $sol 没有对应的练习文件 $dst" + exit 1 + fi + cp "$sol" "$dst" + n_expected=$((n_expected + 1)) +done < <(find solutions -name '*.cpp' | sort) + +if [[ "$n_expected" -eq 0 ]]; then + echo "CHECKER-E2E($LANG_SEL) FAIL: 一份参考答案都没找到" + exit 1 +fi + +# Provider 按 .d2x.json 的 "lang" 决定扫 src/ 还是 src/en/(d2x --lang 只切界面 +# 语言,不切课程目录),所以这里改的是配置而不是命令行。 +if [[ "$LANG_SEL" == "en" ]]; then + sed -i.bak 's/"lang": *"zh"/"lang": "en"/' .d2x.json && rm -f .d2x.json.bak +fi + +# 必须从零开始:陈旧的进度会让 checker 一道题都不跑就宣告完成。 +rm -rf .d2x/state.json .d2x/result.ndjson .d2x/last-output.log .d2x/checker.lock + +OUT="$ROOT/.d2x/checker-e2e.out" +mkdir -p "$ROOT/.d2x" + +# 看门狗用 shell 自己实现,不用 timeout(1):macOS 默认没有这个命令。 +# +# 逐秒轮询而不是 `sleep $LIMIT`:后者即使被 kill 掉父 subshell,sleep 自己 +# 也会作为孤儿活到时限,一直攥着继承来的 stdout —— 调用方一旦把本脚本接进 +# 管道(CI 里常见),管道就要等满整个 LIMIT 才关,本来几分钟的任务看起来 +# 像挂了半小时。轮询版在 d2x 退出后 1 秒内自己收摊。 +watchdog() { + local target="$1" limit="$2" waited=0 + while kill -0 "$target" 2>/dev/null; do + if [[ "$waited" -ge "$limit" ]]; then + kill -9 "$target" 2>/dev/null + return + fi + sleep 1; waited=$((waited + 1)) + done +} + +"$D2X" checker --ui print --lang en > "$OUT" 2>&1 & +d2x_pid=$! +watchdog "$d2x_pid" "$LIMIT" >/dev/null 2>&1 & +watchdog_pid=$! +wait "$d2x_pid"; code=$? +kill "$watchdog_pid" 2>/dev/null; wait "$watchdog_pid" 2>/dev/null + +echo "------------------ checker output (tail) ------------------" +tail -n 30 "$OUT" 2>/dev/null +echo "-----------------------------------------------------------" + +# 判据 1:退出码 +if [[ "$code" != 0 ]]; then + echo "CHECKER-E2E($LANG_SEL) FAIL: checker 退出码 $code —— 答案已全部覆盖,它本应走完全部练习后退 0" + echo "(退出码 137 = 被看门狗按 ${LIMIT}s 上限杀掉,通常意味着卡在某道题上不推进)" + exit 1 +fi + +# 判据 2:防空转 +n_passed=$(grep -c "Status:.*passed" "$OUT" 2>/dev/null) +n_passed="${n_passed:-0}" +if [[ "$n_passed" -lt "$n_expected" ]]; then + echo "CHECKER-E2E($LANG_SEL) FAIL: 防空转 —— 覆盖了 $n_expected 份答案,但 checker 只渲染出 $n_passed 条通过记录" + echo "(checker 很可能读到了陈旧进度直接退出,而没有真的编译练习)" + exit 1 +fi + +echo "CHECKER-E2E($LANG_SEL): checker 自主走完 $n_passed/$n_expected 道练习并正常退出 ✓" From 5aa32f86c771b4bb3dff08ad9001af4eaf21deed Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 11:19:33 +0800 Subject: [PATCH 2/6] =?UTF-8?q?docs(book):=20chapter=5F1=20=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A=E5=B7=A5=E5=85=B7=E5=AE=89=E8=A3=85=E4=B8=8E=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E9=AA=8C=E8=AF=81=EF=BC=8C=E4=B8=8E=20README=20?= =?UTF-8?q?=E4=B8=89=E6=AD=A5=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chapter_1 是三份 README 的「更多细节」落点,但它的 §0 只装 xlings, 接着就直接 `d2x install d2mcpp` —— 一台干净的机器照着做会在第二条命令 上失败,因为 d2x 这个二进制根本还没装。 补两处,与 README 新的三步保持同一套说法: - §0 结尾加 `xlings install d2x mcpp -y`,两个工具名链到各自仓库 - §1 加「验证环境」小节:`cd d2mcpp` + `mcpp test`,并说明「刚拿到课程 时全部未通过」既是起点也是工具链已通的证据 --- book/en/src/base/chapter_1.md | 19 +++++++++++++++++++ book/src/base/chapter_1.md | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 4354f9f..369f1ef 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -32,6 +32,14 @@ wget https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_ Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1' -UseBasicParsing).Content ``` +**With xlings in place, install the two tools this course needs** + +```bash +xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & test tool +``` + +> [d2x](https://github.com/d2learn/d2x) drives exercise checking and progression, [mcpp](https://github.com/mcpp-community/mcpp) builds and runs them — the exercises *are* mcpp tests + ## 1. Get Project and Auto-configure Environment > Download the project to current directory and automatically configure local environment @@ -40,6 +48,17 @@ Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/openxlin d2x install d2mcpp ``` +### Verify the environment + +> Run the full test suite once inside the project to confirm the toolchain works + +```bash +cd d2mcpp +mcpp test +``` + +> The exercises **are** the tests. On a fresh checkout this reports them all as failing — that is the expected starting point, and it already proves the compile-and-run path works. Drop the reference answers from `solutions/` in and the very same command goes all green. + ### Local E-book > Execute `d2x book` command in the project directory to open local documentation (includes usage guide and e-book) diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index fc7a251..5d53021 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -32,6 +32,14 @@ wget https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_ Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1' -UseBasicParsing).Content ``` +**装好 xlings 后, 用它装本教程需要的两个工具** + +```bash +xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试工具 +``` + +> [d2x](https://github.com/d2learn/d2x) 负责练习的检测与推进, [mcpp](https://github.com/mcpp-community/mcpp) 负责构建与运行 —— 练习本身就是 mcpp 的测试 + ## 1.获取项目及自动配置环境 > 下载项目到当前目录并自动配置本地环境 @@ -40,6 +48,17 @@ Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/openxlin d2x install d2mcpp ``` +### 验证环境 + +> 进入项目目录后跑一遍全量测试, 确认工具链是通的 + +```bash +cd d2mcpp +mcpp test +``` + +> 练习**就是**测试。刚拿到课程时这里会报告全部未通过 —— 这正是练习的起点, 同时也说明编译运行这条链路已经跑通了。把 `solutions/` 里的参考答案覆盖进去, 同一条命令就会全绿。 + ### 本地电子书 > 可以在项目目录执行`d2x book`命令, 打开本地文档(包含使用说明和电子书) From eb5ce5e8793100b91105f530f4139f37b52aa81a Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 11:42:36 +0800 Subject: [PATCH 3/6] =?UTF-8?q?docs:=20=E6=94=B9=E6=8E=89=20step=202=20?= =?UTF-8?q?=E9=87=8C=E3=80=8C=E6=AF=8F=E9=81=93=E7=BB=83=E4=B9=A0=E9=83=BD?= =?UTF-8?q?=E8=83=BD=E7=BC=96=E8=AF=91=E5=B9=B6=E8=BF=90=E8=A1=8C=E3=80=8D?= =?UTF-8?q?=E8=BF=99=E5=8F=A5=E9=94=99=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原话把 `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 处同步。 --- README.md | 11 ++++++----- README.zh.hant.md | 7 ++++--- README.zh.md | 7 ++++--- book/en/src/README.md | 11 ++++++----- book/en/src/base/chapter_1.md | 2 +- book/src/README.md | 7 ++++--- book/src/base/chapter_1.md | 2 +- 7 files changed, 26 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 6c6819d..4838df3 100644 --- a/README.md +++ b/README.md @@ -77,13 +77,14 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t ```bash d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp -mcpp test # verify the environment: every exercise compiles and runs +mcpp test # environment self-check — prints your progress table ``` -> The exercises **are** the tests. On a fresh checkout `mcpp test` reports them all as -> failing — that is the expected starting point, and it already proves your toolchain -> works. Drop the reference answers from `solutions/` in and the very same command goes -> all green. +> The exercises **are** the tests, and they ship unfinished — so a fresh `mcpp test` is +> *supposed* to come out all red. What it verifies is everything around them: the +> toolchain resolved, the `d2x` library built, and the runner reached every exercise. +> Copy `solutions/` over the exercise files and the very same command goes all green +> (that is exactly what CI does).
optional - start from the git source instead diff --git a/README.zh.hant.md b/README.zh.hant.md index d89a8a9..6042107 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -77,11 +77,12 @@ xlings install d2x mcpp -y # d2x: 練習框架 CLI | mcpp: C++ 建置與測試 ```bash d2x install d2mcpp # 下載課程,環境自動配置 cd d2mcpp -mcpp test # 驗證環境: 每道練習都能編譯並執行 +mcpp test # 環境自檢 —— 印出你的進度表 ``` -> 練習**就是**測試。剛拿到課程時 `mcpp test` 會報告全部未通過 —— 這正是練習的起點, -> 同時也說明工具鏈已經跑通了。把 `solutions/` 裡的參考答案覆蓋進去,同一條指令就會全綠。 +> 練習**就是**測試, 而且發下來時是沒做完的 —— 所以剛拿到課程時 `mcpp test` **本來就該全紅**。 +> 它驗的是練習之外的一切: 工具鏈解析成功、`d2x` 函式庫編譯通過、測試執行器逐題都跑到了。 +> 把 `solutions/` 覆蓋到練習檔案上, 同一條指令就會全綠(CI 做的正是這件事)。
可選 - 改用 clone 專案原始碼的方式 diff --git a/README.zh.md b/README.zh.md index 13280dc..a41eaf4 100644 --- a/README.zh.md +++ b/README.zh.md @@ -77,11 +77,12 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 ```bash d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp -mcpp test # 验证环境: 每道练习都能编译并运行 +mcpp test # 环境自检 —— 打印你的进度表 ``` -> 练习**就是**测试。刚拿到课程时 `mcpp test` 会报告全部未通过 —— 这正是练习的起点, -> 同时也说明工具链已经跑通了。把 `solutions/` 里的参考答案覆盖进去,同一条命令就会全绿。 +> 练习**就是**测试, 而且发下来时是没做完的 —— 所以刚拿到课程时 `mcpp test` **本来就该全红**。 +> 它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。 +> 把 `solutions/` 覆盖到练习文件上, 同一条命令就会全绿(CI 做的正是这件事)。
可选 - 改用 clone 项目源码的方式 diff --git a/book/en/src/README.md b/book/en/src/README.md index 4aae72b..14bbafa 100644 --- a/book/en/src/README.md +++ b/book/en/src/README.md @@ -73,13 +73,14 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t ```bash d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp -mcpp test # verify the environment: every exercise compiles and runs +mcpp test # environment self-check — prints your progress table ``` -> The exercises **are** the tests. On a fresh checkout `mcpp test` reports them all as -> failing — that is the expected starting point, and it already proves your toolchain -> works. Drop the reference answers from `solutions/` in and the very same command goes -> all green. +> The exercises **are** the tests, and they ship unfinished — so a fresh `mcpp test` is +> *supposed* to come out all red. What it verifies is everything around them: the +> toolchain resolved, the `d2x` library built, and the runner reached every exercise. +> Copy `solutions/` over the exercise files and the very same command goes all green +> (that is exactly what CI does).
optional - start from the git source instead diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 369f1ef..23b97c1 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -57,7 +57,7 @@ cd d2mcpp mcpp test ``` -> The exercises **are** the tests. On a fresh checkout this reports them all as failing — that is the expected starting point, and it already proves the compile-and-run path works. Drop the reference answers from `solutions/` in and the very same command goes all green. +> The exercises **are** the tests, and they ship unfinished — so this is *supposed* to come out all red. What it verifies is everything around them: the toolchain resolved, the `d2x` library built, and the runner reached every exercise. Note that `solutions/` is not a workspace member, so `mcpp test` never runs it; copy it over the exercise files and only then does the same command go all green (that is exactly what CI does). ### Local E-book diff --git a/book/src/README.md b/book/src/README.md index b43dc2d..27fa7e1 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -73,11 +73,12 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 ```bash d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp -mcpp test # 验证环境: 每道练习都能编译并运行 +mcpp test # 环境自检 —— 打印你的进度表 ``` -> 练习**就是**测试。刚拿到课程时 `mcpp test` 会报告全部未通过 —— 这正是练习的起点, -> 同时也说明工具链已经跑通了。把 `solutions/` 里的参考答案覆盖进去,同一条命令就会全绿。 +> 练习**就是**测试, 而且发下来时是没做完的 —— 所以刚拿到课程时 `mcpp test` **本来就该全红**。 +> 它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。 +> 把 `solutions/` 覆盖到练习文件上, 同一条命令就会全绿(CI 做的正是这件事)。
可选 - 改用 clone 项目源码的方式 diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 5d53021..8856241 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -57,7 +57,7 @@ cd d2mcpp mcpp test ``` -> 练习**就是**测试。刚拿到课程时这里会报告全部未通过 —— 这正是练习的起点, 同时也说明编译运行这条链路已经跑通了。把 `solutions/` 里的参考答案覆盖进去, 同一条命令就会全绿。 +> 练习**就是**测试, 而且发下来时是没做完的 —— 所以这里**本来就该全红**。它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。注意 `solutions/` 不是工作区成员, `mcpp test` 不会去跑它; 把它覆盖到练习文件上, 同一条命令才会全绿(CI 做的正是这件事)。 ### 本地电子书 From 7aee9c30e8e8a986639b09e6eda0313e7e03cded Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 11:47:04 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat(solutions):=20=E5=8F=82=E8=80=83?= =?UTF-8?q?=E7=AD=94=E6=A1=88=E8=87=AA=E5=B7=B1=E6=88=90=E4=B8=BA=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=20mcpp=20=E5=B7=A5=E7=A8=8B=EF=BC=8C`mcpp=20test=20-p?= =?UTF-8?q?=20solutions`=20=E7=9B=B4=E6=8E=A5=E8=B7=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在此之前 solutions/ 只是一堆躺在磁盘上的 .cpp:不是工作区成员,mcpp 从头到 尾不认识它。想验证答案对不对,唯一的办法是把 52 个文件覆盖到 src/*/tests/ 上、跑一遍、再 git 还原 —— 一套带副作用、要求工作树干净、只有 CI 脚本会用 的流程。学习者或贡献者想确认「答案是好的」,没有一条命令可以跑。 现在 solutions/ 是一个正经工程: solutions/mcpp.toml name=solutions, c++23, 依赖 d2x solutions/tests//... 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/ --- .github/workflows/dslings-ref-ci.yml | 12 +++++- d2x/buildtools/tests/checker-e2e.sh | 4 +- d2x/buildtools/tests/e2e.sh | 41 ++++++++++++++++++- mcpp.toml | 5 +++ solutions/mcpp.toml | 22 ++++++++++ .../cpp11/00-auto-and-decltype/0.cpp | 0 .../cpp11/00-auto-and-decltype/1.cpp | 0 .../cpp11/00-auto-and-decltype/2.cpp | 0 .../cpp11/00-auto-and-decltype/3.cpp | 0 .../cpp11/00-auto-and-decltype/4.cpp | 0 .../cpp11/00-auto-and-decltype/5.cpp | 0 .../cpp11/01-default-and-delete/0.cpp | 0 .../cpp11/01-default-and-delete/1.cpp | 0 .../cpp11/01-default-and-delete/2.cpp | 0 .../cpp11/02-final-and-override/0.cpp | 0 .../cpp11/02-final-and-override/1.cpp | 0 .../cpp11/02-final-and-override/2.cpp | 0 .../cpp11/03-trailing-return-type/0.cpp | 0 .../cpp11/04-rvalue-references/0.cpp | 0 .../{ => tests}/cpp11/05-move-semantics/0.cpp | 0 .../{ => tests}/cpp11/05-move-semantics/1.cpp | 0 .../{ => tests}/cpp11/05-move-semantics/2.cpp | 0 .../{ => tests}/cpp11/06-scoped-enums/0.cpp | 0 .../{ => tests}/cpp11/06-scoped-enums/1.cpp | 0 .../{ => tests}/cpp11/07-constexpr/0.cpp | 0 .../{ => tests}/cpp11/07-constexpr/1.cpp | 0 .../{ => tests}/cpp11/08-literal-type/0.cpp | 0 .../{ => tests}/cpp11/08-literal-type/1.cpp | 0 .../cpp11/09-list-initialization/0.cpp | 0 .../cpp11/09-list-initialization/1.cpp | 0 .../cpp11/09-list-initialization/2.cpp | 0 .../cpp11/09-list-initialization/3.cpp | 0 .../cpp11/10-delegating-constructors/0.cpp | 0 .../cpp11/10-delegating-constructors/1.cpp | 0 .../cpp11/11-inherited-constructors/0.cpp | 0 .../cpp11/11-inherited-constructors/1.cpp | 0 .../cpp11/11-inherited-constructors/2.cpp | 0 solutions/{ => tests}/cpp11/12-nullptr/0.cpp | 0 solutions/{ => tests}/cpp11/12-nullptr/1.cpp | 0 solutions/{ => tests}/cpp11/12-nullptr/2.cpp | 0 .../{ => tests}/cpp11/13-long-long/0.cpp | 0 .../{ => tests}/cpp11/13-long-long/1.cpp | 0 .../{ => tests}/cpp11/14-type-alias/0.cpp | 0 .../{ => tests}/cpp11/14-type-alias/1.cpp | 0 .../{ => tests}/cpp11/14-type-alias/2.cpp | 0 .../{ => tests}/cpp11/14-type-alias/3.cpp | 0 .../cpp11/15-variadic-templates/0.cpp | 0 .../cpp11/15-variadic-templates/1.cpp | 0 .../cpp11/16-generalized-unions/0.cpp | 0 .../cpp11/16-generalized-unions/1.cpp | 0 .../cpp11/16-generalized-unions/2.cpp | 0 solutions/{ => tests}/cpp11/17-pod-type/0.cpp | 0 solutions/{ => tests}/cpp11/17-pod-type/1.cpp | 0 solutions/{ => tests}/cpp11/17-pod-type/2.cpp | 0 .../cpp14/00-generic-lambdas/0.cpp | 0 .../cpp14/00-generic-lambdas/1.cpp | 0 solutions/{ => tests}/intro/hello-mcpp.cpp | 0 57 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 solutions/mcpp.toml rename solutions/{ => tests}/cpp11/00-auto-and-decltype/0.cpp (100%) rename solutions/{ => tests}/cpp11/00-auto-and-decltype/1.cpp (100%) rename solutions/{ => tests}/cpp11/00-auto-and-decltype/2.cpp (100%) rename solutions/{ => tests}/cpp11/00-auto-and-decltype/3.cpp (100%) rename solutions/{ => tests}/cpp11/00-auto-and-decltype/4.cpp (100%) rename solutions/{ => tests}/cpp11/00-auto-and-decltype/5.cpp (100%) rename solutions/{ => tests}/cpp11/01-default-and-delete/0.cpp (100%) rename solutions/{ => tests}/cpp11/01-default-and-delete/1.cpp (100%) rename solutions/{ => tests}/cpp11/01-default-and-delete/2.cpp (100%) rename solutions/{ => tests}/cpp11/02-final-and-override/0.cpp (100%) rename solutions/{ => tests}/cpp11/02-final-and-override/1.cpp (100%) rename solutions/{ => tests}/cpp11/02-final-and-override/2.cpp (100%) rename solutions/{ => tests}/cpp11/03-trailing-return-type/0.cpp (100%) rename solutions/{ => tests}/cpp11/04-rvalue-references/0.cpp (100%) rename solutions/{ => tests}/cpp11/05-move-semantics/0.cpp (100%) rename solutions/{ => tests}/cpp11/05-move-semantics/1.cpp (100%) rename solutions/{ => tests}/cpp11/05-move-semantics/2.cpp (100%) rename solutions/{ => tests}/cpp11/06-scoped-enums/0.cpp (100%) rename solutions/{ => tests}/cpp11/06-scoped-enums/1.cpp (100%) rename solutions/{ => tests}/cpp11/07-constexpr/0.cpp (100%) rename solutions/{ => tests}/cpp11/07-constexpr/1.cpp (100%) rename solutions/{ => tests}/cpp11/08-literal-type/0.cpp (100%) rename solutions/{ => tests}/cpp11/08-literal-type/1.cpp (100%) rename solutions/{ => tests}/cpp11/09-list-initialization/0.cpp (100%) rename solutions/{ => tests}/cpp11/09-list-initialization/1.cpp (100%) rename solutions/{ => tests}/cpp11/09-list-initialization/2.cpp (100%) rename solutions/{ => tests}/cpp11/09-list-initialization/3.cpp (100%) rename solutions/{ => tests}/cpp11/10-delegating-constructors/0.cpp (100%) rename solutions/{ => tests}/cpp11/10-delegating-constructors/1.cpp (100%) rename solutions/{ => tests}/cpp11/11-inherited-constructors/0.cpp (100%) rename solutions/{ => tests}/cpp11/11-inherited-constructors/1.cpp (100%) rename solutions/{ => tests}/cpp11/11-inherited-constructors/2.cpp (100%) rename solutions/{ => tests}/cpp11/12-nullptr/0.cpp (100%) rename solutions/{ => tests}/cpp11/12-nullptr/1.cpp (100%) rename solutions/{ => tests}/cpp11/12-nullptr/2.cpp (100%) rename solutions/{ => tests}/cpp11/13-long-long/0.cpp (100%) rename solutions/{ => tests}/cpp11/13-long-long/1.cpp (100%) rename solutions/{ => tests}/cpp11/14-type-alias/0.cpp (100%) rename solutions/{ => tests}/cpp11/14-type-alias/1.cpp (100%) rename solutions/{ => tests}/cpp11/14-type-alias/2.cpp (100%) rename solutions/{ => tests}/cpp11/14-type-alias/3.cpp (100%) rename solutions/{ => tests}/cpp11/15-variadic-templates/0.cpp (100%) rename solutions/{ => tests}/cpp11/15-variadic-templates/1.cpp (100%) rename solutions/{ => tests}/cpp11/16-generalized-unions/0.cpp (100%) rename solutions/{ => tests}/cpp11/16-generalized-unions/1.cpp (100%) rename solutions/{ => tests}/cpp11/16-generalized-unions/2.cpp (100%) rename solutions/{ => tests}/cpp11/17-pod-type/0.cpp (100%) rename solutions/{ => tests}/cpp11/17-pod-type/1.cpp (100%) rename solutions/{ => tests}/cpp11/17-pod-type/2.cpp (100%) rename solutions/{ => tests}/cpp14/00-generic-lambdas/0.cpp (100%) rename solutions/{ => tests}/cpp14/00-generic-lambdas/1.cpp (100%) rename solutions/{ => tests}/intro/hello-mcpp.cpp (100%) diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index e124b97..25ef1e2 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -181,7 +181,15 @@ jobs: fi echo "OK: 无 MSVC STL 时回落到 winlibs MinGW (x86_64-windows-gnu)" + # 参考答案自检 —— 单独一段,因为它是最便宜也最直白的一条:solutions/ 自己 + # 就是一个 mcpp 工程,一条命令、零副作用,红了就说明答案本身编不过或跑不过, + # 跟练习目录、跟 d2x 都没关系。放在覆盖-还原那套之前,失败定位最短。 + - name: mcpp test -p solutions (reference answers must be all green) + run: mcpp test -p solutions + # 第一层:`mcpp test` —— pristine 全部不通过 + 覆盖答案后全部通过 + # (e2e.sh 内部还会再跑一遍上面那条自检并核对测试条数,防止布局改动后 + # tests/**/*.cpp 一个都没扫到却依然 "test result ok") - name: mcpp test (pristine fails, solutions all pass) run: bash d2x/buildtools/tests/e2e.sh ${{ matrix.langs }} @@ -209,8 +217,8 @@ jobs: rel="${f#src/}" std="${rel%%/*}" rest="${rel#*/tests/}" - if [ ! -f "solutions/$std/$rest" ]; then - echo "::warning::missing solutions/$std/$rest (paired with $f)" + if [ ! -f "solutions/tests/$std/$rest" ]; then + echo "::warning::missing solutions/tests/$std/$rest (paired with $f)" missing=$((missing+1)) fi done diff --git a/d2x/buildtools/tests/checker-e2e.sh b/d2x/buildtools/tests/checker-e2e.sh index dac35bd..f2dafad 100755 --- a/d2x/buildtools/tests/checker-e2e.sh +++ b/d2x/buildtools/tests/checker-e2e.sh @@ -55,7 +55,7 @@ trap restore EXIT # —— 覆盖参考答案(zh/en 共用同一份 solutions)—— n_expected=0 while IFS= read -r sol; do - rel="${sol#solutions/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp + rel="${sol#solutions/tests/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp std="${rel%%/*}"; rest="${rel#*/}" dst="${PREFIX}${std}/tests/${rest}" if [[ ! -f "$dst" ]]; then @@ -64,7 +64,7 @@ while IFS= read -r sol; do fi cp "$sol" "$dst" n_expected=$((n_expected + 1)) -done < <(find solutions -name '*.cpp' | sort) +done < <(find solutions/tests -name '*.cpp' | sort) if [[ "$n_expected" -eq 0 ]]; then echo "CHECKER-E2E($LANG_SEL) FAIL: 一份参考答案都没找到" diff --git a/d2x/buildtools/tests/e2e.sh b/d2x/buildtools/tests/e2e.sh index 14f1c5a..38969b0 100755 --- a/d2x/buildtools/tests/e2e.sh +++ b/d2x/buildtools/tests/e2e.sh @@ -4,6 +4,9 @@ # 1) 每个练习未完成时不通过 (pristine 全量 mcpp test 必须 0 passed) # 2) 每份参考答案放进去后通过 (overlay 后全量 mcpp test 必须全绿) # +# 外加一条独立的答案自检:solutions/ 自己是一个 mcpp 工程, +# `mcpp test -p solutions` 直接编译运行全部答案,零副作用(见 solutions_selftest)。 +# # 这是 rustlings `cargo dev check --require-solutions` 的等价物。 # # 两道防线(均源自历史缺陷,背景见 .agents/docs 参考文档): @@ -54,7 +57,7 @@ run_lang() { # —— 覆盖参考答案 (zh/en 共用同一份 solutions) —— local n_overlaid=0 sol rel std rest dst while IFS= read -r sol; do - rel="${sol#solutions/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp + rel="${sol#solutions/tests/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp std="${rel%%/*}"; rest="${rel#*/}" dst="${prefix}${std}/tests/${rest}" if [[ ! -f "$dst" ]]; then @@ -64,7 +67,7 @@ run_lang() { fi cp "$sol" "$dst" n_overlaid=$((n_overlaid + 1)) - done < <(find solutions -name '*.cpp' | sort) + done < <(find solutions/tests -name '*.cpp' | sort) # —— 断言 2 + 防线 2: overlay 后全绿,且 passed 总数与答案数一致 —— local total_passed=0 ok=1 @@ -92,6 +95,39 @@ run_lang() { echo "E2E($label) solutions: $total_passed/$n_overlaid 参考答案全部通过 ✓" } +# 参考答案自检:solutions/ 自己就是一个 mcpp 工程,直接编译运行,不碰练习目录。 +# +# 这是最便宜也最直白的一条 —— 一条命令、零副作用、谁都能在本地复现: +# mcpp test -p solutions +# 底下 run_lang 的覆盖-还原流程验的是另一件事(答案「放进练习的位置」也成立, +# 且练习在未完成时确实不通过),两者互补:这条挂了说明答案本身就是错的,那条 +# 挂了说明答案与练习对不上。 +solutions_selftest() { + local n_files out passed + n_files=$(find solutions/tests -name '*.cpp' | wc -l | tr -d ' ') + if [[ "$n_files" -eq 0 ]]; then + echo "E2E(solutions) FAIL: solutions/tests 下一份答案都没有" + return 1 + fi + + out=$(mcpp test -p solutions 2>&1) + if ! echo "$out" | grep -q "test result ok"; then + echo "E2E(solutions) FAIL: mcpp test -p solutions 未全绿:" + echo "$out" | grep -E "FAIL|failures" -A10 | head -20 + return 1 + fi + + passed=$(echo "$out" | grep -oE '[0-9]+ passed' | grep -oE '^[0-9]+' | tail -1) + passed="${passed:-0}" + # 防空转:测试发现是按 tests/**/*.cpp 自动扫的,布局一改就可能一个都没扫到, + # 而「0 个测试」在 mcpp 眼里同样是 "test result ok"。 + if [[ "$passed" -ne "$n_files" ]]; then + echo "E2E(solutions) FAIL: solutions/tests 下有 $n_files 份答案,但只跑了 $passed 个测试" + return 1 + fi + echo "E2E(solutions) 自检: mcpp test -p solutions —— $passed/$n_files 全部通过 ✓" +} + provider_smoke() { # Provider 协议冒烟:枚举数量、check 的关键事件 local n @@ -110,6 +146,7 @@ provider_smoke() { } rc=0 +solutions_selftest || rc=1 provider_smoke || rc=1 if [[ "$MODE" == "zh" || "$MODE" == "all" ]]; then run_lang "src/" zh || rc=1; fi if [[ "$MODE" == "en" || "$MODE" == "all" ]]; then run_lang "src/en/" en || rc=1; fi diff --git a/mcpp.toml b/mcpp.toml index 0635035..66a1602 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,10 +1,15 @@ # d2mcpp 根 workspace。练习即测试:每个 C++ 标准对应 src/ 下的一个 mcpp 工程, # 练习即该工程的 tests/,mcpp test 的测试报告即学习进度;d2x checker 复用同一条链路。 # d2x/ 目录包含练习库(import d2x)与 Provider(buildtools/)。 +# +# solutions/ 同理,是参考答案自己的工程:`mcpp test -p solutions` 直接编译运行 +# 全部答案,应当全绿。它和练习工程的关系是「同一份代码的两种状态」—— +# src/ 下是挖空的,solutions/ 下是填好的,两边配置一致才可比。 [workspace] members = [ "d2x", "src/intro", "src/cpp11", "src/cpp14", "src/en/intro", "src/en/cpp11", "src/en/cpp14", + "solutions", "d2x/buildtools", ] diff --git a/solutions/mcpp.toml b/solutions/mcpp.toml new file mode 100644 index 0000000..5f95c9a --- /dev/null +++ b/solutions/mcpp.toml @@ -0,0 +1,22 @@ +# 参考答案工程。每份答案就是这里的一个测试: +# mcpp test -p solutions 全部参考答案(应当全绿) +# mcpp test -p solutions <子串> 只跑匹配的答案 +# +# 为什么答案自己也是一个 mcpp 工程,而不只是一堆躺着的 .cpp: +# 答案要能被验证,而验证的唯一可信方式就是真的编译并运行它。做成工程之后 +# `mcpp test -p solutions` 就是一条谁都能跑的命令 —— CI 跑它,贡献者提 PR 前 +# 也跑它,不需要先把文件覆盖到练习目录里再 git 还原那一套。 +# +# 布局与练习一一对应,tests/ 下的相对路径就是两边的对应关系: +# solutions/tests//<章节>/<序号>.cpp ←→ src[/en]//tests/<章节>/<序号>.cpp +# 所以测试名(intro/hello-mcpp、cpp11/00-auto-and-decltype/0)一眼就能对回练习。 +# +# 与练习工程共用同一套配置(c++23 + d2x),否则「答案能过」证明不了「答案放进 +# 练习里也能过」—— 两边的编译条件必须是同一个。 +[package] +name = "solutions" +version = "0.1.0" +standard = "c++23" + +[dependencies] +d2x = { path = "../d2x" } diff --git a/solutions/cpp11/00-auto-and-decltype/0.cpp b/solutions/tests/cpp11/00-auto-and-decltype/0.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/0.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/0.cpp diff --git a/solutions/cpp11/00-auto-and-decltype/1.cpp b/solutions/tests/cpp11/00-auto-and-decltype/1.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/1.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/1.cpp diff --git a/solutions/cpp11/00-auto-and-decltype/2.cpp b/solutions/tests/cpp11/00-auto-and-decltype/2.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/2.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/2.cpp diff --git a/solutions/cpp11/00-auto-and-decltype/3.cpp b/solutions/tests/cpp11/00-auto-and-decltype/3.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/3.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/3.cpp diff --git a/solutions/cpp11/00-auto-and-decltype/4.cpp b/solutions/tests/cpp11/00-auto-and-decltype/4.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/4.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/4.cpp diff --git a/solutions/cpp11/00-auto-and-decltype/5.cpp b/solutions/tests/cpp11/00-auto-and-decltype/5.cpp similarity index 100% rename from solutions/cpp11/00-auto-and-decltype/5.cpp rename to solutions/tests/cpp11/00-auto-and-decltype/5.cpp diff --git a/solutions/cpp11/01-default-and-delete/0.cpp b/solutions/tests/cpp11/01-default-and-delete/0.cpp similarity index 100% rename from solutions/cpp11/01-default-and-delete/0.cpp rename to solutions/tests/cpp11/01-default-and-delete/0.cpp diff --git a/solutions/cpp11/01-default-and-delete/1.cpp b/solutions/tests/cpp11/01-default-and-delete/1.cpp similarity index 100% rename from solutions/cpp11/01-default-and-delete/1.cpp rename to solutions/tests/cpp11/01-default-and-delete/1.cpp diff --git a/solutions/cpp11/01-default-and-delete/2.cpp b/solutions/tests/cpp11/01-default-and-delete/2.cpp similarity index 100% rename from solutions/cpp11/01-default-and-delete/2.cpp rename to solutions/tests/cpp11/01-default-and-delete/2.cpp diff --git a/solutions/cpp11/02-final-and-override/0.cpp b/solutions/tests/cpp11/02-final-and-override/0.cpp similarity index 100% rename from solutions/cpp11/02-final-and-override/0.cpp rename to solutions/tests/cpp11/02-final-and-override/0.cpp diff --git a/solutions/cpp11/02-final-and-override/1.cpp b/solutions/tests/cpp11/02-final-and-override/1.cpp similarity index 100% rename from solutions/cpp11/02-final-and-override/1.cpp rename to solutions/tests/cpp11/02-final-and-override/1.cpp diff --git a/solutions/cpp11/02-final-and-override/2.cpp b/solutions/tests/cpp11/02-final-and-override/2.cpp similarity index 100% rename from solutions/cpp11/02-final-and-override/2.cpp rename to solutions/tests/cpp11/02-final-and-override/2.cpp diff --git a/solutions/cpp11/03-trailing-return-type/0.cpp b/solutions/tests/cpp11/03-trailing-return-type/0.cpp similarity index 100% rename from solutions/cpp11/03-trailing-return-type/0.cpp rename to solutions/tests/cpp11/03-trailing-return-type/0.cpp diff --git a/solutions/cpp11/04-rvalue-references/0.cpp b/solutions/tests/cpp11/04-rvalue-references/0.cpp similarity index 100% rename from solutions/cpp11/04-rvalue-references/0.cpp rename to solutions/tests/cpp11/04-rvalue-references/0.cpp diff --git a/solutions/cpp11/05-move-semantics/0.cpp b/solutions/tests/cpp11/05-move-semantics/0.cpp similarity index 100% rename from solutions/cpp11/05-move-semantics/0.cpp rename to solutions/tests/cpp11/05-move-semantics/0.cpp diff --git a/solutions/cpp11/05-move-semantics/1.cpp b/solutions/tests/cpp11/05-move-semantics/1.cpp similarity index 100% rename from solutions/cpp11/05-move-semantics/1.cpp rename to solutions/tests/cpp11/05-move-semantics/1.cpp diff --git a/solutions/cpp11/05-move-semantics/2.cpp b/solutions/tests/cpp11/05-move-semantics/2.cpp similarity index 100% rename from solutions/cpp11/05-move-semantics/2.cpp rename to solutions/tests/cpp11/05-move-semantics/2.cpp diff --git a/solutions/cpp11/06-scoped-enums/0.cpp b/solutions/tests/cpp11/06-scoped-enums/0.cpp similarity index 100% rename from solutions/cpp11/06-scoped-enums/0.cpp rename to solutions/tests/cpp11/06-scoped-enums/0.cpp diff --git a/solutions/cpp11/06-scoped-enums/1.cpp b/solutions/tests/cpp11/06-scoped-enums/1.cpp similarity index 100% rename from solutions/cpp11/06-scoped-enums/1.cpp rename to solutions/tests/cpp11/06-scoped-enums/1.cpp diff --git a/solutions/cpp11/07-constexpr/0.cpp b/solutions/tests/cpp11/07-constexpr/0.cpp similarity index 100% rename from solutions/cpp11/07-constexpr/0.cpp rename to solutions/tests/cpp11/07-constexpr/0.cpp diff --git a/solutions/cpp11/07-constexpr/1.cpp b/solutions/tests/cpp11/07-constexpr/1.cpp similarity index 100% rename from solutions/cpp11/07-constexpr/1.cpp rename to solutions/tests/cpp11/07-constexpr/1.cpp diff --git a/solutions/cpp11/08-literal-type/0.cpp b/solutions/tests/cpp11/08-literal-type/0.cpp similarity index 100% rename from solutions/cpp11/08-literal-type/0.cpp rename to solutions/tests/cpp11/08-literal-type/0.cpp diff --git a/solutions/cpp11/08-literal-type/1.cpp b/solutions/tests/cpp11/08-literal-type/1.cpp similarity index 100% rename from solutions/cpp11/08-literal-type/1.cpp rename to solutions/tests/cpp11/08-literal-type/1.cpp diff --git a/solutions/cpp11/09-list-initialization/0.cpp b/solutions/tests/cpp11/09-list-initialization/0.cpp similarity index 100% rename from solutions/cpp11/09-list-initialization/0.cpp rename to solutions/tests/cpp11/09-list-initialization/0.cpp diff --git a/solutions/cpp11/09-list-initialization/1.cpp b/solutions/tests/cpp11/09-list-initialization/1.cpp similarity index 100% rename from solutions/cpp11/09-list-initialization/1.cpp rename to solutions/tests/cpp11/09-list-initialization/1.cpp diff --git a/solutions/cpp11/09-list-initialization/2.cpp b/solutions/tests/cpp11/09-list-initialization/2.cpp similarity index 100% rename from solutions/cpp11/09-list-initialization/2.cpp rename to solutions/tests/cpp11/09-list-initialization/2.cpp diff --git a/solutions/cpp11/09-list-initialization/3.cpp b/solutions/tests/cpp11/09-list-initialization/3.cpp similarity index 100% rename from solutions/cpp11/09-list-initialization/3.cpp rename to solutions/tests/cpp11/09-list-initialization/3.cpp diff --git a/solutions/cpp11/10-delegating-constructors/0.cpp b/solutions/tests/cpp11/10-delegating-constructors/0.cpp similarity index 100% rename from solutions/cpp11/10-delegating-constructors/0.cpp rename to solutions/tests/cpp11/10-delegating-constructors/0.cpp diff --git a/solutions/cpp11/10-delegating-constructors/1.cpp b/solutions/tests/cpp11/10-delegating-constructors/1.cpp similarity index 100% rename from solutions/cpp11/10-delegating-constructors/1.cpp rename to solutions/tests/cpp11/10-delegating-constructors/1.cpp diff --git a/solutions/cpp11/11-inherited-constructors/0.cpp b/solutions/tests/cpp11/11-inherited-constructors/0.cpp similarity index 100% rename from solutions/cpp11/11-inherited-constructors/0.cpp rename to solutions/tests/cpp11/11-inherited-constructors/0.cpp diff --git a/solutions/cpp11/11-inherited-constructors/1.cpp b/solutions/tests/cpp11/11-inherited-constructors/1.cpp similarity index 100% rename from solutions/cpp11/11-inherited-constructors/1.cpp rename to solutions/tests/cpp11/11-inherited-constructors/1.cpp diff --git a/solutions/cpp11/11-inherited-constructors/2.cpp b/solutions/tests/cpp11/11-inherited-constructors/2.cpp similarity index 100% rename from solutions/cpp11/11-inherited-constructors/2.cpp rename to solutions/tests/cpp11/11-inherited-constructors/2.cpp diff --git a/solutions/cpp11/12-nullptr/0.cpp b/solutions/tests/cpp11/12-nullptr/0.cpp similarity index 100% rename from solutions/cpp11/12-nullptr/0.cpp rename to solutions/tests/cpp11/12-nullptr/0.cpp diff --git a/solutions/cpp11/12-nullptr/1.cpp b/solutions/tests/cpp11/12-nullptr/1.cpp similarity index 100% rename from solutions/cpp11/12-nullptr/1.cpp rename to solutions/tests/cpp11/12-nullptr/1.cpp diff --git a/solutions/cpp11/12-nullptr/2.cpp b/solutions/tests/cpp11/12-nullptr/2.cpp similarity index 100% rename from solutions/cpp11/12-nullptr/2.cpp rename to solutions/tests/cpp11/12-nullptr/2.cpp diff --git a/solutions/cpp11/13-long-long/0.cpp b/solutions/tests/cpp11/13-long-long/0.cpp similarity index 100% rename from solutions/cpp11/13-long-long/0.cpp rename to solutions/tests/cpp11/13-long-long/0.cpp diff --git a/solutions/cpp11/13-long-long/1.cpp b/solutions/tests/cpp11/13-long-long/1.cpp similarity index 100% rename from solutions/cpp11/13-long-long/1.cpp rename to solutions/tests/cpp11/13-long-long/1.cpp diff --git a/solutions/cpp11/14-type-alias/0.cpp b/solutions/tests/cpp11/14-type-alias/0.cpp similarity index 100% rename from solutions/cpp11/14-type-alias/0.cpp rename to solutions/tests/cpp11/14-type-alias/0.cpp diff --git a/solutions/cpp11/14-type-alias/1.cpp b/solutions/tests/cpp11/14-type-alias/1.cpp similarity index 100% rename from solutions/cpp11/14-type-alias/1.cpp rename to solutions/tests/cpp11/14-type-alias/1.cpp diff --git a/solutions/cpp11/14-type-alias/2.cpp b/solutions/tests/cpp11/14-type-alias/2.cpp similarity index 100% rename from solutions/cpp11/14-type-alias/2.cpp rename to solutions/tests/cpp11/14-type-alias/2.cpp diff --git a/solutions/cpp11/14-type-alias/3.cpp b/solutions/tests/cpp11/14-type-alias/3.cpp similarity index 100% rename from solutions/cpp11/14-type-alias/3.cpp rename to solutions/tests/cpp11/14-type-alias/3.cpp diff --git a/solutions/cpp11/15-variadic-templates/0.cpp b/solutions/tests/cpp11/15-variadic-templates/0.cpp similarity index 100% rename from solutions/cpp11/15-variadic-templates/0.cpp rename to solutions/tests/cpp11/15-variadic-templates/0.cpp diff --git a/solutions/cpp11/15-variadic-templates/1.cpp b/solutions/tests/cpp11/15-variadic-templates/1.cpp similarity index 100% rename from solutions/cpp11/15-variadic-templates/1.cpp rename to solutions/tests/cpp11/15-variadic-templates/1.cpp diff --git a/solutions/cpp11/16-generalized-unions/0.cpp b/solutions/tests/cpp11/16-generalized-unions/0.cpp similarity index 100% rename from solutions/cpp11/16-generalized-unions/0.cpp rename to solutions/tests/cpp11/16-generalized-unions/0.cpp diff --git a/solutions/cpp11/16-generalized-unions/1.cpp b/solutions/tests/cpp11/16-generalized-unions/1.cpp similarity index 100% rename from solutions/cpp11/16-generalized-unions/1.cpp rename to solutions/tests/cpp11/16-generalized-unions/1.cpp diff --git a/solutions/cpp11/16-generalized-unions/2.cpp b/solutions/tests/cpp11/16-generalized-unions/2.cpp similarity index 100% rename from solutions/cpp11/16-generalized-unions/2.cpp rename to solutions/tests/cpp11/16-generalized-unions/2.cpp diff --git a/solutions/cpp11/17-pod-type/0.cpp b/solutions/tests/cpp11/17-pod-type/0.cpp similarity index 100% rename from solutions/cpp11/17-pod-type/0.cpp rename to solutions/tests/cpp11/17-pod-type/0.cpp diff --git a/solutions/cpp11/17-pod-type/1.cpp b/solutions/tests/cpp11/17-pod-type/1.cpp similarity index 100% rename from solutions/cpp11/17-pod-type/1.cpp rename to solutions/tests/cpp11/17-pod-type/1.cpp diff --git a/solutions/cpp11/17-pod-type/2.cpp b/solutions/tests/cpp11/17-pod-type/2.cpp similarity index 100% rename from solutions/cpp11/17-pod-type/2.cpp rename to solutions/tests/cpp11/17-pod-type/2.cpp diff --git a/solutions/cpp14/00-generic-lambdas/0.cpp b/solutions/tests/cpp14/00-generic-lambdas/0.cpp similarity index 100% rename from solutions/cpp14/00-generic-lambdas/0.cpp rename to solutions/tests/cpp14/00-generic-lambdas/0.cpp diff --git a/solutions/cpp14/00-generic-lambdas/1.cpp b/solutions/tests/cpp14/00-generic-lambdas/1.cpp similarity index 100% rename from solutions/cpp14/00-generic-lambdas/1.cpp rename to solutions/tests/cpp14/00-generic-lambdas/1.cpp diff --git a/solutions/intro/hello-mcpp.cpp b/solutions/tests/intro/hello-mcpp.cpp similarity index 100% rename from solutions/intro/hello-mcpp.cpp rename to solutions/tests/intro/hello-mcpp.cpp From 97e004133ed36e6575942927099a92b3c6981e52 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 11:49:18 +0800 Subject: [PATCH 5/6] =?UTF-8?q?docs:=20step=202=20=E7=9A=84=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E9=AA=8C=E8=AF=81=E6=94=B9=E7=94=A8=20`mcpp=20test=20?= =?UTF-8?q?-p=20solutions`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一版让 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 ` 自查,不必再走覆盖流程。 --- .agents/skills/d2mcpp-authoring/SKILL.md | 2 +- .../skills/d2mcpp-authoring/references/anatomy.md | 12 +++++++----- README.md | 12 ++++++------ README.zh.hant.md | 9 +++++---- README.zh.md | 9 +++++---- book/en/src/README.md | 12 ++++++------ book/en/src/base/chapter_1.md | 9 +++++++-- book/src/README.md | 9 +++++---- book/src/base/chapter_1.md | 9 +++++++-- 9 files changed, 49 insertions(+), 34 deletions(-) diff --git a/.agents/skills/d2mcpp-authoring/SKILL.md b/.agents/skills/d2mcpp-authoring/SKILL.md index c40ddaa..453df4e 100644 --- a/.agents/skills/d2mcpp-authoring/SKILL.md +++ b/.agents/skills/d2mcpp-authoring/SKILL.md @@ -71,7 +71,7 @@ When adding a feature numbered `NN` with slug `topic` (e.g. `06-scoped-enums`): 2. `book/en/src//NN-topic.md` — en chapter (translate prose, keep code identical) 3. `src//tests/NN-topic/K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments 4. `src/en//tests/NN-topic/K.cpp` — en exercises (translate comments only) -5. `solutions//NN-topic/K.cpp` — reference solution per exercise (zh/en 共用) +5. `solutions/tests//NN-topic/K.cpp` — reference solution per exercise (zh/en 共用) 6. (nothing to register — exercises are tests, discovered by directory convention) 8. Add the chapter line to **both** `book/src/SUMMARY.md` and `book/en/src/SUMMARY.md` 9. Add a changelog entry to **both** `book/src/changelog.md` and `book/en/src/changelog.md` diff --git a/.agents/skills/d2mcpp-authoring/references/anatomy.md b/.agents/skills/d2mcpp-authoring/references/anatomy.md index e40daa0..e174fb3 100644 --- a/.agents/skills/d2mcpp-authoring/references/anatomy.md +++ b/.agents/skills/d2mcpp-authoring/references/anatomy.md @@ -19,7 +19,7 @@ book/en/src/changelog.md # en changelog (add entry) src//tests/NN-topic/K.cpp # zh exercise(s) — no registration needed src/en//tests/NN-topic/K.cpp # en exercise(s) — no registration needed -solutions//NN-topic/K.cpp # reference solution(s), zh/en 共用 +solutions/tests//NN-topic/K.cpp # reference solution(s), zh/en 共用 ``` Repo-level shared basis (NOT per-lesson, created once, never edited by hand): @@ -37,7 +37,7 @@ snapshot via `msvc-stl/SOURCE.md` when needed. A new `` section that does not exist yet needs: a `src//mcpp.toml` (copy `src/cpp14/mcpp.toml`, change the name), `src//tests/` + `src/en//tests/` + -`solutions//` directories, the member added to the root `mcpp.toml` +`solutions/tests//` directories, the member added to the root `mcpp.toml` workspace list (both `src/` and `src/en/`), and the section heading in both `SUMMARY.md` files (e.g. `# C++14核心语言特性` / `# C++14 Core Language Features`). @@ -79,9 +79,11 @@ change compile flags. ## Solution registration — there is none either -Drop the file at `solutions//NN-topic/K.cpp` (zh/en share one solution). -CI swaps it over the exercise and asserts it passes; see -`d2x/buildtools/tests/e2e.sh`. +Drop the file at `solutions/tests//NN-topic/K.cpp` (zh/en share one +solution). `solutions/` is itself an mcpp package, so the file becomes a test by +directory convention — verify it with `mcpp test -p solutions `, no copying +involved. CI additionally swaps it over the exercise and asserts it passes there +too; see `d2x/buildtools/tests/e2e.sh`. ## SUMMARY registration diff --git a/README.md b/README.md index 4838df3..c9877cd 100644 --- a/README.md +++ b/README.md @@ -77,14 +77,14 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t ```bash d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp -mcpp test # environment self-check — prints your progress table +mcpp test -p solutions # verify the environment: 52 reference answers, all green +mcpp test # your progress table: the exercises, all red for now ``` -> The exercises **are** the tests, and they ship unfinished — so a fresh `mcpp test` is -> *supposed* to come out all red. What it verifies is everything around them: the -> toolchain resolved, the `d2x` library built, and the runner reached every exercise. -> Copy `solutions/` over the exercise files and the very same command goes all green -> (that is exactly what CI does). +> `solutions/` is a real mcpp package, so the first command compiles and runs all 52 +> reference answers — an unambiguous green tick that your toolchain works. The second +> one is your own progress table: the exercises **are** the tests and they ship +> unfinished, so red is exactly where you start.
optional - start from the git source instead diff --git a/README.zh.hant.md b/README.zh.hant.md index 6042107..2782bbe 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -77,12 +77,13 @@ xlings install d2x mcpp -y # d2x: 練習框架 CLI | mcpp: C++ 建置與測試 ```bash d2x install d2mcpp # 下載課程,環境自動配置 cd d2mcpp -mcpp test # 環境自檢 —— 印出你的進度表 +mcpp test -p solutions # 驗證環境: 52 份參考答案, 應當全綠 +mcpp test # 你的進度表: 練習還沒做, 此刻全紅 ``` -> 練習**就是**測試, 而且發下來時是沒做完的 —— 所以剛拿到課程時 `mcpp test` **本來就該全紅**。 -> 它驗的是練習之外的一切: 工具鏈解析成功、`d2x` 函式庫編譯通過、測試執行器逐題都跑到了。 -> 把 `solutions/` 覆蓋到練習檔案上, 同一條指令就會全綠(CI 做的正是這件事)。 +> `solutions/` 本身就是一個 mcpp 工程, 所以第一條指令會真的編譯並執行全部 52 份參考答案 —— +> 全綠即工具鏈沒問題, 這是個不含糊的訊號。第二條才是你自己的進度表: 練習**就是**測試, +> 而且發下來時是沒做完的, 全紅正是起點。
可選 - 改用 clone 專案原始碼的方式 diff --git a/README.zh.md b/README.zh.md index a41eaf4..36979b7 100644 --- a/README.zh.md +++ b/README.zh.md @@ -77,12 +77,13 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 ```bash d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp -mcpp test # 环境自检 —— 打印你的进度表 +mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 +mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> 练习**就是**测试, 而且发下来时是没做完的 —— 所以刚拿到课程时 `mcpp test` **本来就该全红**。 -> 它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。 -> 把 `solutions/` 覆盖到练习文件上, 同一条命令就会全绿(CI 做的正是这件事)。 +> `solutions/` 本身就是一个 mcpp 工程, 所以第一条命令会真的编译并运行全部 52 份参考答案 —— +> 全绿即工具链没问题, 这是个不含糊的信号。第二条才是你自己的进度表: 练习**就是**测试, +> 而且发下来时是没做完的, 全红正是起点。
可选 - 改用 clone 项目源码的方式 diff --git a/book/en/src/README.md b/book/en/src/README.md index 14bbafa..8b360bb 100644 --- a/book/en/src/README.md +++ b/book/en/src/README.md @@ -73,14 +73,14 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t ```bash d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp -mcpp test # environment self-check — prints your progress table +mcpp test -p solutions # verify the environment: 52 reference answers, all green +mcpp test # your progress table: the exercises, all red for now ``` -> The exercises **are** the tests, and they ship unfinished — so a fresh `mcpp test` is -> *supposed* to come out all red. What it verifies is everything around them: the -> toolchain resolved, the `d2x` library built, and the runner reached every exercise. -> Copy `solutions/` over the exercise files and the very same command goes all green -> (that is exactly what CI does). +> `solutions/` is a real mcpp package, so the first command compiles and runs all 52 +> reference answers — an unambiguous green tick that your toolchain works. The second +> one is your own progress table: the exercises **are** the tests and they ship +> unfinished, so red is exactly where you start.
optional - start from the git source instead diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 23b97c1..507c5ce 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -54,10 +54,15 @@ d2x install d2mcpp ```bash cd d2mcpp -mcpp test +mcpp test -p solutions # verify the environment: 52 reference answers, all green +mcpp test # your progress table: the exercises, all red for now ``` -> The exercises **are** the tests, and they ship unfinished — so this is *supposed* to come out all red. What it verifies is everything around them: the toolchain resolved, the `d2x` library built, and the runner reached every exercise. Note that `solutions/` is not a workspace member, so `mcpp test` never runs it; copy it over the exercise files and only then does the same command go all green (that is exactly what CI does). +> `solutions/` is a real mcpp package (`solutions/mcpp.toml` + `solutions/tests/`), so the first command actually compiles and runs all 52 reference answers — green means your toolchain is fine. The second is your own progress table: the exercises **are** the tests and they ship unfinished, so red is exactly where you start. +> +> An answer and its exercise are paired by their `tests/`-relative path: +> `solutions/tests/cpp11/00-auto-and-decltype/0.cpp` ←→ `src/cpp11/tests/00-auto-and-decltype/0.cpp`. +> To run just one slice: `mcpp test -p solutions cpp11`. ### Local E-book diff --git a/book/src/README.md b/book/src/README.md index 27fa7e1..10b7f40 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -73,12 +73,13 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 ```bash d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp -mcpp test # 环境自检 —— 打印你的进度表 +mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 +mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> 练习**就是**测试, 而且发下来时是没做完的 —— 所以刚拿到课程时 `mcpp test` **本来就该全红**。 -> 它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。 -> 把 `solutions/` 覆盖到练习文件上, 同一条命令就会全绿(CI 做的正是这件事)。 +> `solutions/` 本身就是一个 mcpp 工程, 所以第一条命令会真的编译并运行全部 52 份参考答案 —— +> 全绿即工具链没问题, 这是个不含糊的信号。第二条才是你自己的进度表: 练习**就是**测试, +> 而且发下来时是没做完的, 全红正是起点。
可选 - 改用 clone 项目源码的方式 diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 8856241..032c5c4 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -54,10 +54,15 @@ d2x install d2mcpp ```bash cd d2mcpp -mcpp test +mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 +mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> 练习**就是**测试, 而且发下来时是没做完的 —— 所以这里**本来就该全红**。它验的是练习之外的一切: 工具链解析成功、`d2x` 库编译通过、测试运行器逐题都跑到了。注意 `solutions/` 不是工作区成员, `mcpp test` 不会去跑它; 把它覆盖到练习文件上, 同一条命令才会全绿(CI 做的正是这件事)。 +> `solutions/` 本身就是一个 mcpp 工程(`solutions/mcpp.toml` + `solutions/tests/`), 所以第一条命令会真的编译并运行全部 52 份参考答案 —— 全绿即工具链没问题。第二条才是你自己的进度表: 练习**就是**测试, 而且发下来时是没做完的, 全红正是起点。 +> +> 答案与练习的对应关系就是 `tests/` 下的相对路径: +> `solutions/tests/cpp11/00-auto-and-decltype/0.cpp` ←→ `src/cpp11/tests/00-auto-and-decltype/0.cpp`。 +> 想只看某一部分: `mcpp test -p solutions cpp11`。 ### 本地电子书 From f7a57c81247c818ad545017fed49364127e217bf Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 2 Aug 2026 12:21:51 +0800 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20step=202=20=E5=8F=AA=E7=95=99?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E9=AA=8C=E8=AF=81=EF=BC=8C=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E8=A1=A8=E6=8C=AA=E5=88=B0=20step=203=20=E7=9A=84=E5=8F=AF?= =?UTF-8?q?=E9=80=89=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 收敛为一条命令;
里 clone 那条 路的环境验证也跟着改成 `mcpp test -p solutions`(原来还是裸 mcpp test) - chapter_1 两语:「验证环境」节同样只留参考答案那条;裸 `mcpp test` 落到 已有的「原生模式(可选)」节,作为该节三条命令里的第一条 --- README.md | 13 ++++++------- README.zh.hant.md | 11 +++++------ README.zh.md | 11 +++++------ book/en/src/README.md | 13 ++++++------- book/en/src/base/chapter_1.md | 6 +++--- book/src/README.md | 11 +++++------ book/src/base/chapter_1.md | 6 +++--- 7 files changed, 33 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c9877cd..5d4924c 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,10 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp mcpp test -p solutions # verify the environment: 52 reference answers, all green -mcpp test # your progress table: the exercises, all red for now ``` -> `solutions/` is a real mcpp package, so the first command compiles and runs all 52 -> reference answers — an unambiguous green tick that your toolchain works. The second -> one is your own progress table: the exercises **are** the tests and they ship -> unfinished, so red is exactly where you start. +> `solutions/` is a real mcpp package, so this compiles and runs all 52 reference +> answers — an unambiguous green tick that your toolchain is ready.
optional - start from the git source instead @@ -95,7 +92,7 @@ mcpp test # your progress table: the exercises, all red for n git clone https://github.com/mcpp-community/d2mcpp.git # or your fork cd d2mcpp xlings install -y # toolchain pinned by .xlings.json -mcpp test # same environment check +mcpp test -p solutions # same environment check ``` > Tip: fork this repo and clone your fork — `git commit / push` keeps your practice progress in your own repository. @@ -111,7 +108,9 @@ d2x checker # practice loop: edit -> save -> auto-check -> adva d2x status # progress overview ``` -> Want to run a single chapter without d2x? `mcpp test -p src/cpp11` works too. +> Prefer plain mcpp? The exercises **are** the tests — `mcpp test` prints your whole +> progress table (all red until you start), and `mcpp test -p src/cpp11` runs a single +> chapter. **👉 [more details...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/README.zh.hant.md b/README.zh.hant.md index 2782bbe..a8cf222 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -78,12 +78,10 @@ xlings install d2x mcpp -y # d2x: 練習框架 CLI | mcpp: C++ 建置與測試 d2x install d2mcpp # 下載課程,環境自動配置 cd d2mcpp mcpp test -p solutions # 驗證環境: 52 份參考答案, 應當全綠 -mcpp test # 你的進度表: 練習還沒做, 此刻全紅 ``` -> `solutions/` 本身就是一個 mcpp 工程, 所以第一條指令會真的編譯並執行全部 52 份參考答案 —— -> 全綠即工具鏈沒問題, 這是個不含糊的訊號。第二條才是你自己的進度表: 練習**就是**測試, -> 而且發下來時是沒做完的, 全紅正是起點。 +> `solutions/` 本身就是一個 mcpp 工程, 所以這條指令會真的編譯並執行全部 52 份參考答案 —— +> 全綠即工具鏈就緒, 這是個不含糊的訊號。
可選 - 改用 clone 專案原始碼的方式 @@ -94,7 +92,7 @@ mcpp test # 你的進度表: 練習還沒做, 此刻全紅 git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 後的倉庫 cd d2mcpp xlings install -y # 按 .xlings.json 安裝固定版本工具鏈 -mcpp test # 同樣的環境驗證 +mcpp test -p solutions # 同樣的環境驗證 ``` > 建議: 先 fork 本倉庫再 clone 自己的 fork —— 通過 `git commit / push` 把練習進度保存到自己的倉庫裡。 @@ -110,7 +108,8 @@ d2x checker # 練習循環: 編輯 -> 保存 -> 自動偵測 -> d2x status # 進度總覽 ``` -> 想單獨跑某一章而不經過 d2x? 直接 `mcpp test -p src/cpp11` 同樣可用。 +> 不想經過 d2x? 練習**就是**測試 —— `mcpp test` 印出你的完整進度表(動手前全紅), +> `mcpp test -p src/cpp11` 只跑某一章。 **👉 [更多細節...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/README.zh.md b/README.zh.md index 36979b7..ee077df 100644 --- a/README.zh.md +++ b/README.zh.md @@ -78,12 +78,10 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 -mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> `solutions/` 本身就是一个 mcpp 工程, 所以第一条命令会真的编译并运行全部 52 份参考答案 —— -> 全绿即工具链没问题, 这是个不含糊的信号。第二条才是你自己的进度表: 练习**就是**测试, -> 而且发下来时是没做完的, 全红正是起点。 +> `solutions/` 本身就是一个 mcpp 工程, 所以这条命令会真的编译并运行全部 52 份参考答案 —— +> 全绿即工具链就绪, 这是个不含糊的信号。
可选 - 改用 clone 项目源码的方式 @@ -94,7 +92,7 @@ mcpp test # 你的进度表: 练习还没做, 此刻全红 git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 后的仓库 cd d2mcpp xlings install -y # 按 .xlings.json 安装固定版本工具链 -mcpp test # 同样的环境验证 +mcpp test -p solutions # 同样的环境验证 ``` > 建议: 先 fork 本仓库再 clone 自己的 fork —— 通过 `git commit / push` 把练习进度保存到自己的仓库里。 @@ -110,7 +108,8 @@ d2x checker # 练习循环: 编辑 -> 保存 -> 自动检测 -> d2x status # 进度总览 ``` -> 想单独跑某一章而不经过 d2x? 直接 `mcpp test -p src/cpp11` 同样可用。 +> 不想经过 d2x? 练习**就是**测试 —— `mcpp test` 打印你的完整进度表(动手前全红), +> `mcpp test -p src/cpp11` 只跑某一章。 **👉 [更多细节...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/book/en/src/README.md b/book/en/src/README.md index 8b360bb..4fefd9a 100644 --- a/book/en/src/README.md +++ b/book/en/src/README.md @@ -74,13 +74,10 @@ xlings install d2x mcpp -y # d2x: exercise framework CLI | mcpp: C++ build & t d2x install d2mcpp # download the course, toolchain auto-configured cd d2mcpp mcpp test -p solutions # verify the environment: 52 reference answers, all green -mcpp test # your progress table: the exercises, all red for now ``` -> `solutions/` is a real mcpp package, so the first command compiles and runs all 52 -> reference answers — an unambiguous green tick that your toolchain works. The second -> one is your own progress table: the exercises **are** the tests and they ship -> unfinished, so red is exactly where you start. +> `solutions/` is a real mcpp package, so this compiles and runs all 52 reference +> answers — an unambiguous green tick that your toolchain is ready.
optional - start from the git source instead @@ -91,7 +88,7 @@ mcpp test # your progress table: the exercises, all red for n git clone https://github.com/mcpp-community/d2mcpp.git # or your fork cd d2mcpp xlings install -y # toolchain pinned by .xlings.json -mcpp test # same environment check +mcpp test -p solutions # same environment check ``` > Tip: fork this repo and clone your fork — `git commit / push` keeps your practice progress in your own repository. @@ -107,7 +104,9 @@ d2x checker # practice loop: edit -> save -> auto-check -> adva d2x status # progress overview ``` -> Want to run a single chapter without d2x? `mcpp test -p src/cpp11` works too. +> Prefer plain mcpp? The exercises **are** the tests — `mcpp test` prints your whole +> progress table (all red until you start), and `mcpp test -p src/cpp11` runs a single +> chapter. **👉 [more details...](https://mcpp-community.github.io/d2mcpp/en/base/chapter_1.html)** diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 507c5ce..1a689b5 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -50,15 +50,14 @@ d2x install d2mcpp ### Verify the environment -> Run the full test suite once inside the project to confirm the toolchain works +> Run the reference answers once inside the project to confirm the toolchain works ```bash cd d2mcpp mcpp test -p solutions # verify the environment: 52 reference answers, all green -mcpp test # your progress table: the exercises, all red for now ``` -> `solutions/` is a real mcpp package (`solutions/mcpp.toml` + `solutions/tests/`), so the first command actually compiles and runs all 52 reference answers — green means your toolchain is fine. The second is your own progress table: the exercises **are** the tests and they ship unfinished, so red is exactly where you start. +> `solutions/` is a real mcpp package (`solutions/mcpp.toml` + `solutions/tests/`), so this command actually compiles and runs all 52 reference answers — green means your toolchain is ready. > > An answer and its exercise are paired by their `tests/`-relative path: > `solutions/tests/cpp11/00-auto-and-decltype/0.cpp` ←→ `src/cpp11/tests/00-auto-and-decltype/0.cpp`. @@ -101,6 +100,7 @@ d2x status > Every C++ standard directory is a real mcpp project and the exercises are its tests/ — you can practice with plain mcpp, no d2x involved: ```bash +mcpp test # your whole progress table (all red before you start) mcpp test -p src/en/cpp11 # progress table for the whole cpp11 section mcpp test -p src/en/cpp11 03-trailing # run only exercises matching the name ``` diff --git a/book/src/README.md b/book/src/README.md index 10b7f40..eb2254a 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -74,12 +74,10 @@ xlings install d2x mcpp -y # d2x: 练习框架 CLI | mcpp: C++ 构建与测试 d2x install d2mcpp # 下载课程,环境自动配置 cd d2mcpp mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 -mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> `solutions/` 本身就是一个 mcpp 工程, 所以第一条命令会真的编译并运行全部 52 份参考答案 —— -> 全绿即工具链没问题, 这是个不含糊的信号。第二条才是你自己的进度表: 练习**就是**测试, -> 而且发下来时是没做完的, 全红正是起点。 +> `solutions/` 本身就是一个 mcpp 工程, 所以这条命令会真的编译并运行全部 52 份参考答案 —— +> 全绿即工具链就绪, 这是个不含糊的信号。
可选 - 改用 clone 项目源码的方式 @@ -90,7 +88,7 @@ mcpp test # 你的进度表: 练习还没做, 此刻全红 git clone https://github.com/mcpp-community/d2mcpp.git # 或你 fork 后的仓库 cd d2mcpp xlings install -y # 按 .xlings.json 安装固定版本工具链 -mcpp test # 同样的环境验证 +mcpp test -p solutions # 同样的环境验证 ``` > 建议: 先 fork 本仓库再 clone 自己的 fork —— 通过 `git commit / push` 把练习进度保存到自己的仓库里。 @@ -106,7 +104,8 @@ d2x checker # 练习循环: 编辑 -> 保存 -> 自动检测 -> d2x status # 进度总览 ``` -> 想单独跑某一章而不经过 d2x? 直接 `mcpp test -p src/cpp11` 同样可用。 +> 不想经过 d2x? 练习**就是**测试 —— `mcpp test` 打印你的完整进度表(动手前全红), +> `mcpp test -p src/cpp11` 只跑某一章。 **👉 [更多细节...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 032c5c4..4ba9107 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -50,15 +50,14 @@ d2x install d2mcpp ### 验证环境 -> 进入项目目录后跑一遍全量测试, 确认工具链是通的 +> 进入项目目录后跑一遍参考答案, 确认工具链是通的 ```bash cd d2mcpp mcpp test -p solutions # 验证环境: 52 份参考答案, 应当全绿 -mcpp test # 你的进度表: 练习还没做, 此刻全红 ``` -> `solutions/` 本身就是一个 mcpp 工程(`solutions/mcpp.toml` + `solutions/tests/`), 所以第一条命令会真的编译并运行全部 52 份参考答案 —— 全绿即工具链没问题。第二条才是你自己的进度表: 练习**就是**测试, 而且发下来时是没做完的, 全红正是起点。 +> `solutions/` 本身就是一个 mcpp 工程(`solutions/mcpp.toml` + `solutions/tests/`), 所以这条命令会真的编译并运行全部 52 份参考答案 —— 全绿即工具链就绪。 > > 答案与练习的对应关系就是 `tests/` 下的相对路径: > `solutions/tests/cpp11/00-auto-and-decltype/0.cpp` ←→ `src/cpp11/tests/00-auto-and-decltype/0.cpp`。 @@ -101,6 +100,7 @@ d2x status > 每个 C++ 标准目录都是真实的 mcpp 工程,练习就是它的 tests/——不经过 d2x,直接用 mcpp 也能练: ```bash +mcpp test # 你的完整进度表(动手前全红, 那就是起点) mcpp test -p src/cpp11 # 整个 cpp11 的进度表(哪题绿哪题红) mcpp test -p src/cpp11 03-trailing # 只跑名字匹配的练习 ```