Skip to content

Commit 9efa721

Browse files
docs: package templates design v2 — multi-level resolution (#111)
Incorporates the generality + convenience requirements: - 4-level SPEC grammar (NAME | pkg:tmpl | pkg@ver | pkg@ver:tmpl); bare-name resolves builtin registry first (frozen to bin|lib), then the package's DEFAULT template (template.toml default = true, exactly 0/1). - gui builtin becomes a transitional deprecation alias (the imgui hardcode in cmd_new is the anti-example: version already rotted 0.0.2 vs 0.0.5). - [template.inject] self = { features = [...] } ties templates to the features mechanism (docking template injects docking-full) with {{self.version}} eliminating version drift structurally. - pure-data trust boundary (render+copy, no hooks/scripts); placeholder vocabulary deliberately minimal; T1-T8 implementation plan + acceptance. Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
1 parent 9cf039b commit 9efa721

1 file changed

Lines changed: 128 additions & 71 deletions

File tree

Lines changed: 128 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,148 @@
1-
# mcpp 模板系统(package-based templates)— 设计 + TODO
1+
# mcpp 模板系统(package-based templates)— 设计 v2
22

3-
> 2026-06-03 · 状态:builtin 模板已实现;**package 模板为设计/TODO(本轮未实现)**
3+
> 2026-06-04 · 状态:**设计定稿(v2,多级解析)**;builtin `bin|gui` 已实现,package 模板待实现(T1–T8)
44
> 关联:agentdocs/2026-06-03-capability-architecture-rfc.md §9
5+
> .agents/docs/2026-06-04-manifest-schema-ownership.md(语法封闭/词汇开放——本设计是该原则在"脚手架"维度的应用)
56
6-
## 目标
7+
## 0. 目标与现状问题
78

8-
`mcpp new` 的模板不应只有内置几种,而应**复用"库模型":一个库同时携带实现 + 示例 +
9-
可实例化模板**。让库作者把"上手骨架"和库一起分发,消费者一条命令拉起。
9+
`mcpp new` 的模板应**复用库模型:一个库同时携带实现 + 示例 + 可实例化模板**,
10+
让库作者把"上手骨架"随库分发,消费者一条命令拉起。
11+
12+
现状(v0.0.48)的 builtin `--template gui`**反例教材**:imgui 的包名、版本、
13+
示例代码硬编码在 `cmd_new` 里 —— 领域词汇泄漏进机制层,且版本已经腐化
14+
(builtin 钉 `0.0.2`,生态已到 `0.0.5`,docking/viewports 模板全用不上)。
15+
**库每发一版,mcpp 核心就过期一次** —— 这正是要根治的。
16+
17+
## 1. 设计原则
18+
19+
> 机制归 mcpp(解析文法、下载、渲染、注入);词汇归库(模板名、内容、默认选择)。
20+
> —— 语法封闭 / 词汇开放在脚手架维度的投影。
21+
22+
- **架构通用**:mcpp 不认识任何具体库;任何 index 包放个 `templates/` 即获得模板能力。
23+
- **使用方便**:常用路径一个词(`--template imgui`),精确路径全可表达(`pkg@ver:tmpl`)。
24+
- **版本对齐**:模板随包 tarball 分发 ⇒ 模板版本 == 包版本,`{{self.version}}`
25+
注入 ⇒ **版本腐化在结构上不可能发生**
26+
- **纯数据信任边界**:模板 = 渲染 + 拷贝,**无钩子、无脚本执行**(信任面与普通包源码一致,
27+
不引入 install-script 攻击面)。
28+
29+
## 2. 解析文法(多级,核心 UX)
1030

1131
```
12-
mcpp new myapp # builtin: bin(默认)
13-
mcpp new myapp --template gui # builtin: imgui.app 窗口骨架(已实现)
14-
mcpp new myapp --template imgui@0.0.2:window # package 模板(本设计)
15-
mcpp new myapp --template imgui:window # 省略版本 = 最新
32+
SPEC := NAME # L0 裸名(最方便)
33+
| PKG ':' TMPL # L1 指定库的某个模板
34+
| PKG '@' VER # L2 库默认模板 + 钉版本
35+
| PKG '@' VER ':' TMPL # L3 全显式(最精确)
1636
```
1737

18-
## 两层模型
38+
| 写法 | 语义 |
39+
|---|---|
40+
| `--template imgui` | imgui **默认模板**,版本 = 解析到的最新 |
41+
| `--template imgui:docking` | imgui 的 `docking` 模板,最新 |
42+
| `--template imgui@0.0.5` | 默认模板,钉 0.0.5 |
43+
| `--template imgui@0.0.5:docking` | 全显式 |
44+
| `--template imgui:`(空模板名) | 列出该库全部模板(= `--list-templates imgui`) |
45+
46+
**裸名解析顺序**(L0,类似 PATH 查找):
47+
1. **builtin 注册表**(离线标准库,词表冻结:`bin``lib` —— 真正零网络可用的两种);
48+
2. miss → 当作**包名**,经 index 解析,取其**默认模板**
1949

20-
### 1) builtin 模板(已实现)
21-
- `--template bin|gui`,硬编码在 `src/cli.cppm cmd_new`
22-
- 用途:无网络/零依赖即可起步;`gui` 给出 imgui.app Tier-0 骨架。
23-
- 这是 fallback,也是 package 模板的"标准库"等价物。
50+
冲突治理:builtin 词表冻结(不再新增,杜绝未来遮蔽包名);`gui` 保留为
51+
**过渡 alias**(打印 deprecation 提示指向 `--template imgui`,一个 minor 周期后移除)
52+
—— 这是对现存领域泄漏的退场路径,不是新模式。
2453

25-
### 2) package 模板(设计 / TODO)
26-
语法:`--template <pkg>[@<ver>]:<templatename>`
54+
## 3. 库侧约定(词汇层,全部归库)
2755

28-
**库侧目录约定**(库仓库里新增 `templates/`):
2956
```
3057
imgui-m/
31-
├── src/ # 库实现
32-
├── examples/ # 可运行示例
58+
├── src/ # 库实现
59+
├── examples/ # 可运行示例
3360
└── templates/
34-
└── window/ # 模板名 = 目录名
35-
├── template.toml # 模板元数据(见下)
36-
├── mcpp.toml.in # 带占位符的清单
37-
└── src/main.cpp.in # 带占位符的源码
61+
├── window/ # 模板名 = 目录名
62+
│ ├── template.toml # 元数据(下)
63+
│ ├── mcpp.toml.in # .in = 渲染;非 .in = 原样拷贝
64+
│ └── src/main.cpp.in
65+
└── docking/
66+
├── template.toml
67+
├── mcpp.toml.in
68+
└── src/main.cpp.in
3869
```
3970

40-
**template.toml**:
71+
**template.toml**(模板元数据):
72+
4173
```toml
4274
[template]
43-
name = "window"
44-
description = "Minimal imgui.app window app"
45-
# 占位符 → 取值来源
46-
[template.vars]
47-
PROJECT = "{{name}}" # mcpp new 的 name
48-
IMGUI_VER = "{{self.version}}" # 该模板所属包的版本(自动)
49-
# 生成后提示
50-
post_message = "Edit src/main.cpp, then `mcpp run`."
75+
description = "Minimal imgui.app window"
76+
default = true # 库的默认模板(0 或 1 个;多于 1 个 → 解析报错)
77+
post_message = "cd 进项目后 `mcpp run` 即出窗口。"
78+
79+
# 依赖注入:生成的项目自动获得对所属库的依赖(含 features 形态)。
80+
# `self` = 模板所属包,版本自动 = 解析到的包版本 —— 根治版本漂移。
81+
[template.inject]
82+
self = { features = [] } # window 模板:无 feature
83+
# docking 模板则是:self = { features = ["docking-full"] }
5184
```
5285

53-
**占位符渲染**:`{{name}}``{{self.version}}``{{self.name}}` 等;`.in` 后缀文件渲染后去掉 `.in`;非 `.in` 文件原样拷贝。
54-
55-
### 解析与执行流程(core)
56-
1. 解析 `--template` 值:
57-
- 不含 `:` → builtin(`bin`/`gui`)。
58-
-`:``pkg[@ver]:tmpl`
59-
2. 经现有 fetcher/index 解析并下载该 `pkg@ver`(复用 `mcpp.pm` / `fetcher.cppm`)。
60-
3. 读取包内 `templates/<tmpl>/template.toml`;若缺失 → 报错并列出该包可用模板(`templates/*/`)。
61-
4. 渲染:对模板目录递归拷贝,`.in` 文件做占位符替换,写入新项目目录。
62-
5. 若模板 mcpp.toml 未声明对该库的依赖,自动注入 `[dependencies] <pkg> = "<ver>"`(让模板默认依赖它所属的库)。
63-
6. 打印 `template.post_message`
64-
65-
### 代码定位(实现时)
66-
- `src/cli.cppm cmd_new`:解析 `--template`,分流 builtin vs package。
67-
- 新增 `src/scaffold/template.cppm`:模板下载 + 渲染引擎(占位符、`.in` 处理)。
68-
- 复用:`src/fetcher.cppm` / `mcpp.pm.*`(下载包)、`src/manifest.cppm`(注入依赖)。
69-
- index:无需改 schema(模板随源码 tarball 分发,已在 `templates/`)。
70-
71-
### 发现/列举
72-
- `mcpp new --list-templates <pkg>[@ver]`:下载并列出 `templates/*/` 及其 description。
73-
- `mcpp new --template <pkg>:`(空模板名)→ 同上列举提示。
74-
75-
## 为什么这样设计(契合架构不变量)
76-
- I5 复杂度下沉:模板由库作者写一次,消费者一条命令继承。
77-
- I1/I4:`--template gui` builtin 保零配置;package 模板可被 `--list-templates` 解释。
78-
- 与 capability 模型正交:模板只是"起点物料",不改变解析/能力体系。
79-
80-
## TODO(实现顺序)
81-
- [ ] T1 模板字符串解析 `pkg@ver:tmpl`(+ builtin 分流)。
82-
- [ ] T2 `template.cppm` 渲染引擎(`.in` + `{{var}}`)。
83-
- [ ] T3 接 fetcher 下载模板包 + 读取 `templates/<tmpl>/`
84-
- [ ] T4 自动注入依赖 + post_message。
85-
- [ ] T5 `--list-templates`
86-
- [ ] T6 imgui-m 仓增 `templates/window/``templates/headless/` 作为首批样例。
87-
- [ ] T7 文档 + `mcpp new --help` 更新。
88-
89-
## 现状(本轮已落地)
90-
- builtin `--template bin|gui` 已实现并验证(`mcpp new x --template gui` → imgui.app 窗口骨架 → 直接出窗口)。
91-
- package 模板:本文件为设计与 TODO,留待后续实现。
86+
**占位符集**(机制层固定,渲染引擎唯一识别的词表):
87+
88+
| 占位符 | 取值 |
89+
|---|---|
90+
| `{{project.name}}` | `mcpp new <name>` 的 name |
91+
| `{{self.name}}` | 模板所属包名(如 `imgui`) |
92+
| `{{self.version}}` | 解析到的包版本(如 `0.0.5`) |
93+
94+
(刻意最小;模板内容的可变性靠库出多个模板,而非把渲染引擎做成编程语言。)
95+
96+
## 4. 解析与执行流程(机制层,归 mcpp)
97+
98+
1. `cmd_new` 解析 SPEC(§2 文法 + 裸名两级回退);
99+
2. builtin 命中 → 原有路径(离线);
100+
3. 包路径:经现有 fetcher/index 解析下载 `pkg@ver`(**复用包缓存**,与依赖同一套);
101+
4.`templates/`:
102+
- 指定 TMPL → 取该目录;缺失 → 报错并列出可用模板;
103+
- 未指定 → 取 `default = true` 的模板;没有 default → 报错并列出;
104+
5. 渲染:递归拷贝,`.in` 文件做占位符替换(去掉 `.in` 后缀),其余原样;
105+
6. 依赖注入:按 `[template.inject]``self`(以及未来允许的兄弟依赖)写入生成的
106+
`mcpp.toml`(若模板清单已显式声明则模板优先,不重复注入);
107+
7. 打印 `post_message`
108+
109+
**发现**:`mcpp new --list-templates <pkg>[@ver]` → 下载(缓存命中则免)并列出
110+
`templates/*/`:名字、description、是否 default。
111+
112+
### 代码定位
113+
- `src/cli.cppm cmd_new`:SPEC 解析 + 分流 + gui deprecation;
114+
- 新增 `src/scaffold/template.cppm`:渲染引擎(`.in` + 占位符)+ template.toml 解析 + 注入;
115+
- 复用 `src/fetcher.cppm`(下载/缓存)、`src/manifest.cppm`(注入写回);
116+
- index:**无 schema 改动**(模板随源码 tarball 分发)。
117+
118+
## 5. 与既有机制的协同
119+
120+
- **features**:`[template.inject] self.features` 把模板与 feature 体系打通
121+
(docking 模板 ⇒ 注入 `imgui = { version = "<self>", features = ["docking-full"] }`);
122+
- **why/doctor**:生成的项目是普通项目,解析全程可解释(I4),模板不引入任何特殊态;
123+
- **schema 所有权**:占位符词表、文法、注入键归 mcpp(机制);模板名/内容/默认选择/
124+
注入的 feature 词汇归库 —— 通过 §1 判定法审视,无泄漏。
125+
126+
## 6. 实施计划(每步:本地验证 → PR → 三平台 CI 绿 → 合入)
127+
128+
- [ ] **T1** SPEC 解析(四级文法 + 裸名 builtin→package-default 回退)+ `gui` deprecation 提示
129+
- [ ] **T2** `src/scaffold/template.cppm` 渲染引擎(`.in`、占位符、template.toml)
130+
- [ ] **T3** fetcher 集成:下载/缓存 `pkg@ver` → 读 `templates/`、default 选择、错误列举
131+
- [ ] **T4** `[template.inject]` 依赖注入(self + features)+ post_message
132+
- [ ] **T5** `--list-templates <pkg>[@ver]`(+ `pkg:` 空名列举)
133+
- [ ] **T6** imgui-m:`templates/window/`(default)+ `templates/docking/`(inject docking-full)
134+
→ 发版(模板首次随包分发)+ index 收录
135+
- [ ] **T7** 文档(00-getting-started、mcpp new --help、imgui-m README)+ e2e
136+
`69_package_templates.sh`(L0–L3 四级 + 注入 + default + 列举 + 错误路径)
137+
- [ ] **T8** 闭环:fresh 机器 `mcpp new app --template imgui && cd app && mcpp run` 出窗口;
138+
`--template imgui:docking` 出四分屏 + 分离窗口;一个 minor 后移除 builtin `gui`
139+
140+
## 7. 验收标准
141+
142+
- [ ] `--template imgui` 一词出窗口(默认模板,版本自动对齐最新)
143+
- [ ] `--template imgui@0.0.5:docking` 全显式可复现
144+
- [ ] 注入的依赖带正确版本与 features(版本漂移结构性消除)
145+
- [ ] `--list-templates imgui` 列出 window(default)/docking 及描述
146+
- [ ] builtin `bin|lib` 离线可用;`gui` 出 deprecation 提示
147+
- [ ] 模板路径零脚本执行(纯渲染拷贝)
148+
- [ ] e2e 69 全过,三平台 CI 绿

0 commit comments

Comments
 (0)