From beafdb3676e01cb1ed76b90118616ffbf8c84a10 Mon Sep 17 00:00:00 2001 From: sunrisepeak <38786181+sunrisepeak@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:03:19 +0800 Subject: [PATCH] feat: package-based project templates (design v2 T1-T5,T7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcpp new --template now accepts a multi-level SPEC: a bare name resolves the builtin registry first (frozen: bin; gui = transitional deprecation alias), then falls through to a package template — pkg | pkg:tmpl | pkg@ver | pkg@ver:tmpl. Packages opt in by shipping templates// (template.toml metadata: description / default / post_message / [template.inject] self features). New mcpp.scaffold module: SPEC parsing, {{project.name}}/{{self.name}}/ {{self.version}} rendering (.in files; everything else verbatim; pure data — no hooks/scripts), self-dependency injection (template's own declaration wins), template listing. new --list-templates prints a package's templates with the default marked. Fetch reuses the dependency machinery (index lua -> semver latest -> install cache). Builtin gui keeps working with a deprecation pointer; its pinned imgui version bumps to current as a transitional stopgap (structural fix is the package template itself: {{self.version}} cannot rot). e2e 69 (hermetic: pre-seeded install cache + local index lua) covers L0/L3 resolution, rendering, inject-vs-declared, listing, error paths, gui deprecation. --- docs/00-getting-started.md | 3 +- src/cli.cppm | 192 +++++++++++++++++++++++-- src/scaffold/template.cppm | 231 ++++++++++++++++++++++++++++++ tests/e2e/69_package_templates.sh | 136 ++++++++++++++++++ 4 files changed, 553 insertions(+), 9 deletions(-) create mode 100644 src/scaffold/template.cppm create mode 100755 tests/e2e/69_package_templates.sh diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index 79b8845d..13c48c07 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -122,6 +122,7 @@ mcpp pack --mode bundle-all # 全自包含,含 libc 与 ld-linux ## 更多入口 -- GUI 起步:`mcpp new myapp --template gui`(imgui.app 窗口骨架,构建后 `mcpp run` 直接出窗口)。 +- GUI 起步:`mcpp new myapp --template imgui`(模板随 imgui 库分发、版本自动对齐; + `mcpp new --list-templates imgui` 查看库提供的全部模板,`--template imgui:docking` 选指定模板)。 - 解释默认决策:`mcpp why [toolchain|runtime|deps]`;主机能力体检:`mcpp self doctor`; 机器可读解析清单:构建产物 `target///resolution.json`。 diff --git a/src/cli.cppm b/src/cli.cppm index 03a83e01..346fb8e8 100644 --- a/src/cli.cppm +++ b/src/cli.cppm @@ -38,6 +38,7 @@ import mcpp.fetcher; import mcpp.pm.resolver; // PR-R4: extracted from cli.cppm import mcpp.pm.commands; // PR-R5: cmd_add / cmd_remove / cmd_update live here now import mcpp.pm.index_spec; // IndexSpec for [indices] support +import mcpp.scaffold; // package-based project templates import mcpp.pm.mangle; // Level 1 multi-version fallback (cross-major coexistence) import mcpp.pm.compat; // 0.0.6: namespace field + dotted-name compat shims import mcpp.pm.dep_spec; @@ -1093,20 +1094,191 @@ void gcc_post_install_fixup(const mcpp::config::GlobalConfig& cfg, // --- Commands --- +// ─── Package-based templates (design v2: multi-level --template) ────── +// +// Resolve SPEC's package@version through the index, ensure the package +// sources are installed (same cache as dependencies), and return the +// package root (the directory containing mcpp.toml). +struct FetchedTemplatePackage { + std::filesystem::path root; + std::string name; // short package name (e.g. "imgui") + std::string version; // resolved exact version +}; + +std::expected +fetch_template_package(const mcpp::scaffold::TemplateSpec& spec) { + auto cfg = mcpp::config::load_or_init(/*quiet=*/false, + make_bootstrap_progress_callback()); + if (!cfg) return std::unexpected(cfg.error().message); + mcpp::pm::Fetcher fetcher(*cfg); + + // Namespace candidates mirror dependency lookup: index root first, + // then the compat namespace. + std::string ns; + std::optional lua; + for (std::string cand : {std::string{}, std::string{"compat"}}) { + if (auto l = fetcher.read_xpkg_lua(cand, spec.pkg)) { + ns = cand; + lua = std::move(*l); + break; + } + } + if (!lua) { + return std::unexpected(std::format( + "template package '{}' not found in the index " + "(check the name, or run `mcpp index update`)", spec.pkg)); + } + + std::string version = spec.version; + if (version.empty()) { + auto v = mcpp::pm::resolve_semver(ns, spec.pkg, "*", fetcher); + if (!v) return std::unexpected(v.error()); + version = *v; + } + + auto installed = fetcher.install_path(ns, spec.pkg, version); + if (!installed) { + auto fq = ns.empty() ? spec.pkg : std::format("{}.{}", ns, spec.pkg); + mcpp::ui::info("Downloading", std::format("{} v{}", fq, version)); + CliInstallProgress progress; + std::vector targets{ std::format("{}@{}", fq, version) }; + auto r = fetcher.install(targets, &progress); + if (!r) return std::unexpected(std::format( + "fetch '{}@{}': {}", fq, version, r.error().message)); + if (r->exitCode != 0) return std::unexpected(std::format( + "fetch '{}@{}' failed (exit {})", fq, version, r->exitCode)); + installed = fetcher.install_path(ns, spec.pkg, version); + if (!installed) return std::unexpected(std::format( + "package '{}@{}' install path missing after fetch", fq, version)); + } + + // Package root = the directory holding mcpp.toml (tarballs usually wrap + // everything in a single top-level directory). + std::filesystem::path root = *installed; + if (!std::filesystem::exists(root / "mcpp.toml")) { + std::error_code ec; + for (auto& e : std::filesystem::directory_iterator(root, ec)) { + if (e.is_directory() + && std::filesystem::exists(e.path() / "mcpp.toml")) { + root = e.path(); + break; + } + } + } + if (!std::filesystem::exists(root / "mcpp.toml")) { + return std::unexpected(std::format( + "package '{}@{}' has no mcpp.toml", spec.pkg, version)); + } + return FetchedTemplatePackage{root, spec.pkg, version}; +} + +void print_template_listing(const FetchedTemplatePackage& pkg, + const std::vector& entries) { + std::println("Templates in {}@{}:", pkg.name, pkg.version); + for (auto& t : entries) { + std::println(" {:<14}{}{}", t.name, + t.meta.isDefault ? "(default) " : " ", + t.meta.description); + } + std::println(""); + std::println("usage: mcpp new --template {}[@ver][: