Skip to content

Commit 6e79919

Browse files
committed
feat: mcpp new --template gui (imgui.app starter) + package-template design
Adds a builtin gui scaffold to `mcpp new` (Tier-0 imgui.app window starter, deps imgui 0.0.2, no toolchain pin). Verified: new --template gui -> build -> window renders. Also documents the long-term package-based template model (mcpp new name --template pkg@ver:tmpl, libraries ship templates/) as TODO.
1 parent af91e99 commit 6e79919

2 files changed

Lines changed: 123 additions & 2 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# mcpp 模板系统(package-based templates)— 设计 + TODO
2+
3+
> 2026-06-03 · 状态:builtin 模板已实现;**package 模板为设计/TODO(本轮未实现)**
4+
> 关联:agentdocs/2026-06-03-capability-architecture-rfc.md §9
5+
6+
## 目标
7+
8+
`mcpp new` 的模板不应只有内置几种,而应**复用"库模型":一个库同时携带实现 + 示例 +
9+
可实例化模板**。让库作者把"上手骨架"和库一起分发,消费者一条命令拉起。
10+
11+
```
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 # 省略版本 = 最新
16+
```
17+
18+
## 两层模型
19+
20+
### 1) builtin 模板(已实现)
21+
- `--template bin|gui`,硬编码在 `src/cli.cppm cmd_new`
22+
- 用途:无网络/零依赖即可起步;`gui` 给出 imgui.app Tier-0 骨架。
23+
- 这是 fallback,也是 package 模板的"标准库"等价物。
24+
25+
### 2) package 模板(设计 / TODO)
26+
语法:`--template <pkg>[@<ver>]:<templatename>`
27+
28+
**库侧目录约定**(库仓库里新增 `templates/`):
29+
```
30+
imgui-m/
31+
├── src/ # 库实现
32+
├── examples/ # 可运行示例
33+
└── templates/
34+
└── window/ # 模板名 = 目录名
35+
├── template.toml # 模板元数据(见下)
36+
├── mcpp.toml.in # 带占位符的清单
37+
└── src/main.cpp.in # 带占位符的源码
38+
```
39+
40+
**template.toml**:
41+
```toml
42+
[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`."
51+
```
52+
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,留待后续实现。

src/cli.cppm

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,16 @@ int cmd_new(const mcpplibs::cmdline::ParsedArgs& parsed) {
10351035
return 2;
10361036
}
10371037

1038+
// `--template` selects the project skeleton: "bin" (default) or "gui"
1039+
// (an imgui.app starter — Tier-0 zero-boilerplate window).
1040+
std::string tmpl = "bin";
1041+
if (auto t = parsed.value("template")) tmpl = *t;
1042+
if (tmpl != "bin" && tmpl != "gui") {
1043+
std::println(stderr, "error: unknown --template '{}' (expected: bin | gui)", tmpl);
1044+
return 2;
1045+
}
1046+
const bool gui = (tmpl == "gui");
1047+
10381048
std::filesystem::path root = std::filesystem::current_path() / name;
10391049
if (std::filesystem::exists(root)) {
10401050
std::println(stderr, "error: '{}' already exists", root.string());
@@ -1051,10 +1061,28 @@ int cmd_new(const mcpplibs::cmdline::ParsedArgs& parsed) {
10511061
{
10521062
std::ofstream os(root / "mcpp.toml");
10531063
os << mcpp::manifest::default_template(name);
1064+
if (gui) {
1065+
// The GUI template depends on the imgui module package. It does not
1066+
// pin a toolchain — mcpp resolves the environment/default toolchain
1067+
// and the GL runtime is closed by the ecosystem (compat.glx-runtime).
1068+
os << "\n[dependencies]\nimgui = \"0.0.2\"\n";
1069+
}
10541070
}
10551071
// src/main.cpp — template with PROJECT placeholder, replaced with `name`.
10561072
{
1057-
std::string body = R"(// PROJECT — generated by `mcpp new`
1073+
std::string body = gui ? R"GUI(// PROJECT — generated by `mcpp new --template gui`
1074+
// Tier-0 zero-boilerplate window via the imgui.app facade. No #include.
1075+
import imgui.core;
1076+
import imgui.app;
1077+
1078+
int main() {
1079+
return ImGui::App::run([] {
1080+
ImGui::Begin("PROJECT");
1081+
ImGui::TextUnformatted("Hello from mcpp + imgui (imgui.app facade)");
1082+
ImGui::End();
1083+
});
1084+
}
1085+
)GUI" : R"(// PROJECT — generated by `mcpp new`
10581086
import std;
10591087
10601088
int main(int argc, char* argv[]) {
@@ -1094,7 +1122,7 @@ int main() {
10941122
os << "target/\n";
10951123
}
10961124

1097-
std::println("Created package '{}' at {}", name, root.string());
1125+
std::println("Created {} package '{}' at {}", gui ? "gui" : "bin", name, root.string());
10981126
std::println("Next: cd {} && mcpp build && mcpp run (or `mcpp test`)", name);
10991127
return 0;
11001128
}
@@ -5367,6 +5395,8 @@ int run(int argc, char** argv) {
53675395
.subcommand(cl::App("new")
53685396
.description("Create a new mcpp package skeleton")
53695397
.arg(cl::Arg("name").help("Package directory name").required())
5398+
.option(cl::Option("template").short_name('t').takes_value().value_name("KIND")
5399+
.help("Project template: bin (default) | gui (imgui.app window starter)"))
53705400
.action(wrap_rc(cmd_new)))
53715401
.subcommand(cl::App("build")
53725402
.description("Build the current package")

0 commit comments

Comments
 (0)