Commit 247f7d6
authored
feat: 练习即测试——d2mcpp 全面重构(mcpp Provider/d2x 库/双入口) (#84)
* feat(buildtools): 新增 mcpp Provider(C++26),替代 xmake 插件
d2x 改为通用练习框架后,构建工具由具体课程实现。本 Provider 用
C++26 + mcpp 编写——教 C++ 的仓库用 C++ 写自己的工具,且 mcpp 本就是
必需品,零新增运行时依赖。
发现方式刻意不引入声明文件:真相只有目录结构和练习文件自己的头部注释
(// d2x:cxxflags:)。rustlings PR #1355 的教训是 edition 同时写在 rustc
参数和 rust-project.json 里、两边漂移酿成 bug——任何独立声明文件都是
第二套真相源。
双 member 布局解决一个硬约束:dslings 的练习默认就编译不过,而 mcpp 的
build/run 都会先全量构建整个包,一个坏兄弟拖垮全部且零产物。于是
.d2x/build/<std>/ 持全量 target 供 clangd 拿到完整 compile_commands.json,
.d2x/build/_current/ 每次只写当前一题供 checker。清单写入前做内容比对,
不推进 mtime,mcpp 的快速路径得以保留。
练习源文件原地不动:main 用 ../ 逃逸出包根。
引导零脚本、Windows 安全:.d2x.json 指向
`mcpp run -q -p d2x/buildtools/mcpp --`,从仓库根执行无需 cd,首次自动
构建 Provider。
runner 在每次 spawn 前清 LD_LIBRARY_PATH:mcpp run 会把它指向自己私有的
glibc 并注入子进程,嵌套的 mcpp 被迫加载错配运行时后会在动态链接器里
段错误(冷启动稳定复现)。d2x 侧对同一问题早有相同处理。
tests/e2e.sh 断言每个参考答案通过、每个练习不通过——rustlings
`dev check --require-solutions` 的等价物。实测 51/51 通过。
* chore: 彻底移除 xmake,构建链路全部走 mcpp
d2x 改为通用练习框架、构建由 mcpp Provider 承担后,xmake 已无调用方。
删除 11 个 xmake.lua(根 / dslings / dslings/en / solutions 各级)、
d2x/buildtools/xmake/ 插件、d2x/cpp/common.lua(xmake 侧的语言探测辅助,
早已被 main.lua 读 .d2x.json 取代)、以及只写着 TODO 的 cmake 适配器占位。
d2x/buildtools/README.md 一并删除——它描述的 list/build/run 契约已被
Provider 协议(describe/exercises/check)取代,留着会误导。
.xlings.json 去掉 xmake 3.0.7 的 pin(该版本已从 registry 移除,是 d2x CI
里那条 "用真 xmake 而非 xlings shim" workaround 的根因),同时去掉 gcc 与
mingw-w64 —— mcpp 自带工具链沙箱,Windows 侧由它的 MinGW/MSVC 覆盖。
CI 重写:旧流水线只挑 `-ref` 结尾的 xmake target,而 solutions/ 在
xmake.lua 里被注释掉,grep 返回空、循环全跳过、job 退出 0 —— 实际校验零个
目标,且第 60 行的 wc -l 对空串仍打印 "Found 1 reference targets" 把空转
掩盖了。新流水线跑 e2e.sh,断言每个参考答案通过、每个练习不通过,走的是
`d2x checker` 内部同一条 Provider 路径。
撰稿 skill 同步更新:练习不再需要注册,放进 dslings/<std>/ 即可被目录约定
发现;per-exercise 编译选项改为练习文件头部的 // d2x:cxxflags: 指令。
复验:Provider 枚举 52 题、端到端 51/51 参考答案通过 0 失败、d2x checker
全链路正常。
* feat(harness): 判定信号改走侧信道,练习脚手架独立成库
判定原先 100% 是带内信号:断言失败并不改变退出码(实测退出码仍为 0),
所以只能扫 stdout 找 ❌。这会误判——一个断言全过的正确解答,只要在说明
文字里打了个 ❌ 就被判失败(已复现)。反过来,输出截断或 Windows 控制台
代码页不对,判定同样失效。
改为:stdout 回归「给人看」,判定走侧信道 NDJSON。
harness 独立成 mcpp 库包 dslings/harness/,同时提供两条路径:
- #include <d2x/cpp/common.hpp> 宏路径。可见输出与旧版逐字节一致,
✅/❌ 的逐条对照是教学的一部分,不动。
- import d2x.harness; 模块路径。用 std::source_location 自动带
file/line,比 __LINE__ 更准,正好是 Verdict.diagnostics 需要的东西。
两条路径不等价,这是 C++ 的硬约束而非疏漏:宏无法跨模块导出,所以
D2X_YOUR_ANSWER 没有模块等价物——它必须展开为空才能制造编译错误。
模块化章节需要另一套填空约定。
include/ 下保持 d2x/cpp/common.hpp 这个既有路径,104 个练习和书本一行
不用改;路径改名可以以后单独做,不必和机制改造捆在一起。
生成的 member 清单改用 [dependencies] path 依赖,干掉了
include_dirs = ["../../.."] 那个把整个仓库根塞进搜索路径的 hack——
原先练习能 #include 仓库里任何文件。
侧信道逐条追加而非退出时统一写:练习段错误时崩溃前的断言结果照样保留。
D2X_RESULT_FILE 未设置时 harness 只打印不写文件,学员直接跑二进制零摩擦。
Provider 判定顺序:有 ok:false → Fail 且每条失败转成一个 Diagnostic;
无失败但有 wait → Blocked;侧信道文件不存在 → 退回「编译通过 + 退出 0」。
最后一条让 harness 自动变成可选的——纯观察型练习(现有 18 个只用
D2X_WAIT)可以写成零依赖的纯 C++ 文件,学员能原样拷进 Compiler Explorer。
顺带删掉死代码 d2x_is_invocable(104 个练习中 0 处使用)和坏掉的模块桩
d2x/mcpp/common.cppm(宏不能跨模块导出,import 进去什么也拿不到)。
e2e.sh 的清理钩子收窄为逐个练习目录:harness 现在也住在 dslings/ 下,
原先的 `git checkout -- dslings/` 会把开发中的改动一起抹掉(已踩过)。
实测:51/51 参考答案通过;四态判定正确(未完成→fail、参考答案→pass、
答案对且输出含❌→pass、答案对留路障→blocked);真实断言失败产出带
行号与期望/实际值的 diagnostics;模块路径与「无 harness 退回退出码」均通过。
* fix: 堵住 id 注入、修复英文答案空转、让教学漂移可被 CI 检测
三个真缺陷,都不是理论风险:
1. 练习 id 注入。id 直接取自文件名,有两个危险去向:d2x 把它拼进 shell
命令,我们把它写进生成的 TOML([targets.<id>])。带反引号、`]` 或引号
的文件名在任一处都能越界 —— 对社区课程仓库来说,一个恶意 PR 文件名就
足以在任何跑 checker 的人机器上执行命令。
在 discovery 源头做白名单校验并拒绝,而不是在两个下游各自转义:这类
文件名本就是笔误或恶意,与其想办法安全地传递,不如让作者改名。
实测:`99-evil`touch pwned_marker`.cpp` 被拒绝,命令未执行。
2. e2e.sh 把所有英文参考答案静默 SKIP。前缀剥离顺序错了 —— `${sol#en/}`
执行时 sol 已经以 "solutions/" 开头,匹配不到任何东西,是个静默 no-op,
于是每道英文练习都因找不到 solutions/en/... 而跳过。
这正是本脚本头部注释里说要防的那种空转,和旧 CI 一模一样的毛病。
除了修顺序,另加一道防线:pass==0 时直接判失败,杜绝「0 失败」蒙混。
实测修复后 en 也是 51/51 真验证(此前是 0 通过 / 52 跳过)。
3. d2x_assert_eq 的日志分支仍用裸 std::to_string,而上报分支已改用
SFINAE 安全的 show()。std::to_string 没有 std::string / const char* /
scoped enum 的重载,下一个比较字符串或强类型枚举的练习会直接编译失败 ——
show() 存在的意义就是避免这个,却只用了一半。
教学漂移(04-rvalue-references):
C++17 起 prvalue 直接初始化目标(保证复制省略),-fno-elide-constructors
再也无法让 `Object obj = Object();` 产生移动构造。全仓库改按 c++23 编译后,
这节课的核心观测点被静默抹掉。
改成从具名对象 std::move —— 这在任何标准下都必然调用移动构造。更重要的是
补了一条 d2x_assert(move_ctor_calls >= 1):漂移之所以能静默发生,正是因为
从前没有任何断言检查它,输出少一行没人发现。现在它是 CI 可检测的事实。
* fix(dslings): 04-rvalue-references 练习侧同步修复,并给 e2e 加脏树防护
上一次提交只带上了 solutions/ 侧 —— 练习侧的补丁被 e2e.sh 的清理钩子
还原掉了,我没复查就提交了。
清理钩子会把参考答案覆盖到练习上再还原,所以它天然会吃掉练习目录里未提交
的改动。这个陷阱已经咬过两次(一次丢了脚手架,一次丢了刚修好的练习),
所以加一道前置检查:练习目录不干净就拒绝运行,并列出是哪些文件。
宁可拒绝,也不能悄悄丢掉别人的工作。
* fix(provider): 报错路径改为相对仓库根,协议里仍给绝对路径
学员看到的断言报错原先顶着一长串 /home/... 前缀 —— mcpp 传给编译器的是
绝对路径,__FILE__ 便是绝对的。生成的清单加上 -fmacro-prefix-map 让它
相对仓库根,噪声消失。
但协议要求绝对路径:d2x 靠 diagnostics.file 打开编辑器、监听文件变更。
所以在协议边界上还原成绝对。展示归展示,定位归定位。
* docs: 添加 Provider 与练习脚手架参考(目录约定、判定机制、已知缺口)
* docs: 练习即测试重设计稿(双入口、无宏 harness、mcpp 上游三改动)
* docs: 练习即测试迁移计划(试点先行)
* feat(harness+pilot): 无宏 harness 包 + cpp14 试点迁移(练习即测试链路验证通过)
* feat(provider): 瘦身为 mcpp test JSON 薄壳——清单生成/自建判定/unsetenv workaround 全部删除
* feat: dslings 全量迁移到「练习即测试」布局(104 练习 + 51 答案 + 7 成员工程)
* fix: 迁移清尾——hello-mcpp 教学文本重写+答案、NULL/int8_t/dont_delete_this、harness formattable 探测
* test(e2e): 重写为 mcpp test 驱动——pristine 0-pass + 52/52 答案全绿(zh/en),防线保留
* docs: book/撰稿技能/CI/gitignore 全面同步「练习即测试」布局
* docs(skill): chapter 模板路径同步
* docs: 迁移后参考文档 + 旧文档标记取代 + 计划勾选
* refactor: harness→d2x 库(import d2x)、课程工程收进 src/、日志标识去 HONLY 重设计
* docs: 术语统一(学员→学习者) + 判定顺序注释与实现对齐
* fix: hello-mcpp 恢复未完成态(体验时的通关改动被误提交,pristine 断言抓出) + 参考文档同步新布局
* refactor: Provider 拍平到 d2x/buildtools(去掉冗余的 mcpp 目录层)
* docs: 注释与教学文案专业化(消除口语化表达)
* chore: mcpp pin 0.0.99 -> 0.0.104(测试能力批次已发布,CI 依赖满足)
* chore: d2x pin 0.1.5 -> 2026.07.24.1(协议层分库/稳定性批次已发布)
* docs: 获取与使用对齐 d2x 2026.07.24.1(status/原生模式双入口;d2x update 幽灵命令改为 git 说明;三语 README 快速开始)1 parent 1c37ddd commit 247f7d6
258 files changed
Lines changed: 3883 additions & 2669 deletions
File tree
- .agents
- docs
- skills/d2mcpp-authoring
- assets
- references
- .github/workflows
- .vscode
- book
- en/src
- base
- cpp11
- cpp14
- src
- base
- cpp11
- cpp14
- d2x
- buildtools
- cmake
- src
- tests
- xmake
- cpp
- mcpp
- src
- dslings
- cpp11
- cpp14
- cpp17
- cpp20
- cpp23
- en
- cpp11
- cpp14
- solutions
- cpp11
- 00-auto-and-decltype
- 01-default-and-delete
- 02-final-and-override
- 03-trailing-return-type
- 04-rvalue-references
- 05-move-semantics
- 06-scoped-enums
- 07-constexpr
- 08-literal-type
- 09-list-initialization
- 10-delegating-constructors
- 11-inherited-constructors
- 12-nullptr
- 13-long-long
- 14-type-alias
- 15-variadic-templates
- 16-generalized-unions
- 17-pod-type
- cpp14
- 00-generic-lambdas
- intro
- src
- cpp11
- tests
- 00-auto-and-decltype
- 01-default-and-delete
- 02-final-and-override
- 03-trailing-return-type
- 04-rvalue-references
- 05-move-semantics
- 06-scoped-enums
- 07-constexpr
- 08-literal-type
- 09-list-initialization
- 10-delegating-constructors
- 11-inherited-constructors
- 12-nullptr
- 13-long-long
- 14-type-alias
- 15-variadic-templates
- 16-generalized-unions
- 17-pod-type
- cpp14
- tests/00-generic-lambdas
- en
- cpp11
- tests
- 00-auto-and-decltype
- 01-default-and-delete
- 02-final-and-override
- 03-trailing-return-type
- 04-rvalue-references
- 05-move-semantics
- 06-scoped-enums
- 07-constexpr
- 08-literal-type
- 09-list-initialization
- 10-delegating-constructors
- 11-inherited-constructors
- 12-nullptr
- 13-long-long
- 14-type-alias
- 15-variadic-templates
- 16-generalized-unions
- 17-pod-type
- cpp14
- tests/00-generic-lambdas
- intro
- tests
- intro
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
0 commit comments