From 3c50fa599d1eed80a01969f92512e51ce07eadd4 Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Thu, 6 Aug 2026 16:39:55 +0800 Subject: [PATCH 1/2] Add package descriptor for CLI11 library This file provides a package descriptor for the CLI11 library, detailing its specifications, dependencies, and source information. --- pkgs/c/compat.CLI11.lua | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 pkgs/c/compat.CLI11.lua diff --git a/pkgs/c/compat.CLI11.lua b/pkgs/c/compat.CLI11.lua new file mode 100644 index 0000000..0723891 --- /dev/null +++ b/pkgs/c/compat.CLI11.lua @@ -0,0 +1,85 @@ +-- Form B inline descriptor for CLI11 — a modern C++ command line parser. +-- +-- CLI11 is a HEADER-ONLY library. All functionality is implemented in headers +-- under `include/CLI/`; there are no library sources to compile. Therefore the +-- package simply exports the `include/` directory and provides a tiny generated +-- translation unit so mcpp always has a buildable `lib` target. +-- +-- Consumers simply write: +-- +-- #include +-- +-- No additional configuration or features are required. + +package = { + spec = "1", + namespace = "compat", + name = "CLI11", + description = "Modern C++ command line parser (header-only)", + licenses = { "BSD-3-Clause" }, + repo = "https://github.com/CLIUtils/CLI11", + type = "package", + + xpm = { + linux = { + ["2.7.2"] = { + url = { + GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + }, + sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", + }, + }, + + macosx = { + ["2.7.2"] = { + url = { + GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + }, + sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", + }, + }, + + windows = { + ["2.7.2"] = { + url = { + GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + }, + sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", + }, + }, + }, + + mcpp = { + language = "c++17", + import_std = false, + + -- Upstream headers live in include/CLI/ + include_dirs = { + "include", + }, + + -- Header-only anchor. + generated_files = { + ["mcpp_generated/cli11_anchor.cpp"] = [==[ +int mcpp_compat_cli11_headers_anchor(void) { + return 0; +} +]==], + }, + + sources = { + "mcpp_generated/cli11_anchor.cpp", + }, + + targets = { + ["CLI11"] = { + kind = "lib", + }, + }, + + deps = {}, + }, +} From c9d3cac8e184efcb7cf4c9c076a6d4056c9f5488 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 6 Aug 2026 17:05:25 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(CLI11):=20=E8=A1=A5=E4=B8=8A=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E4=B8=8E=E6=88=90=E5=91=98,=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A5=BD=E6=8C=87=E4=B8=8D=E5=88=B0=E5=A4=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=20include=20=E6=A0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 描述符原样是构建不了的:`mcpp` 段里的路径都是相对 verdir 的 glob, `include_dirs = { "include" }` 少了吸收 `CLI11-2.7.2/` wrap 层的前导 `*`, 消费者的 `#include ` 落不到任何目录 —— 改成 `*/include`。 `language` 跟仓内其余 61 个描述符一起用 c++23(它只作用于本包那个 anchor TU, CLI11 自身只要 C++11,消费者按自己声明的标准编头文件)。 CN 那两个键原本复制的是 GLOBAL 的 github 地址,lint(check_mirror_urls.lua) 要求 CN 必须在 gitcode mcpp-res 之下,照原样是红的。现建 mcpp-res/cli11 并 发布 2.7.2:CN http=200,与 GLOBAL 字节一致(sha256 46eef310…)。 成员 tests/examples/cli11 断言一次完整 parse(取值/默认值/flag/多值/子命令) 与两条失败路径(未声明选项抛 ParseError、Range 越界抛 ValidationError), 外加 CLI11_VERSION 与包自身的 anchor 符号 —— header-only 包的 lib 里就这一个 TU,引用它才能证明包真的被编译并链接,而不是只有头文件被 include。 本地(CI 同版本 mcpp 2026.8.6.1、冷启动)`mcpp test -p cli11` 通过, 两个 obj:parse.o 与 compat_CLI11/mcpp_generated/cli11_anchor.o。 lint 全套(含 mcpp xpkg parse)本地复现通过。 --- .agents/docs/2026-08-06-add-cli11-plan.md | 106 ++++++++++++++++++++++ README.md | 1 + README.zh-CN.md | 1 + mcpp.toml | 1 + pkgs/c/compat.CLI11.lua | 75 ++++++++------- tests/examples/cli11/mcpp.toml | 17 ++++ tests/examples/cli11/tests/parse.cpp | 93 +++++++++++++++++++ 7 files changed, 263 insertions(+), 31 deletions(-) create mode 100644 .agents/docs/2026-08-06-add-cli11-plan.md create mode 100644 tests/examples/cli11/mcpp.toml create mode 100644 tests/examples/cli11/tests/parse.cpp diff --git a/.agents/docs/2026-08-06-add-cli11-plan.md b/.agents/docs/2026-08-06-add-cli11-plan.md new file mode 100644 index 0000000..53d801c --- /dev/null +++ b/.agents/docs/2026-08-06-add-cli11-plan.md @@ -0,0 +1,106 @@ +# 新增 CLI11 收录方案 + +**日期**: 2026-08-06 +**本仓**: `mcpp-community/mcpp-index`(github 别名 `mcpplibs/mcpp-index`) +**来源**: 社区 PR [#169](https://github.com/mcpplibs/mcpp-index/pull/169)(@tz12323 提供描述符初稿), +本文档记录补齐部分:CN 镜像、workspace 成员、描述符修正与验证结论。 + +**目标**: +1. 收录 [`CLIUtils/CLI11`](https://github.com/CLIUtils/CLI11) 2.7.2 —— header-only 命令行解析器, + `compat` 形态,文件 `pkgs/c/compat.CLI11.lua`。 +2. 建立 CN 镜像 `gitcode.com/mcpp-res/cli11`。 +3. 添加 `tests/examples/cli11/` 测试工程并登记为 workspace 成员。 + +--- + +## 1. 形态判定 + +| 库 | 形态 | 最新 tag | 收录版本 | License | +|---|---|---|---|---| +| CLI11 | B. header-only | `v2.7.2` | `2.7.2` | BSD-3-Clause | + +上游 tarball 顶层是 `CLI11-2.7.2/` wrap 层,头文件在 `include/CLI/` 下,实现头在 +`include/CLI/impl/*_inl.hpp`。默认模式下所有定义带 `CLI11_INLINE`,`CLI/CLI.hpp` 把 +impl 头一并拉进来 —— **没有需要编译的库源码**,所以走 shape B:`include_dirs` 暴露头 +文件根,再配一个 anchor TU 让 mcpp 有可构建的 `lib` target。 + +原 PR 写的是 `include_dirs = { "include" }`,这是错的:`mcpp` 段里的路径都是**相对 +verdir 的 glob**,必须由前导 `*` 吸收 `CLI11-/` 这一层,即 `*/include`。按原样 +构建时消费者的 `#include ` 找不到头文件。 + +### 1.1 两个不收的上游额外件 + +- `src/Precompile.cpp` —— 上游的预编译模式。只有在 `CLI11_COMPILE` 同时到达**消费者** + 的 TU 时才有意义(否则头文件里的 inline 定义照常生效,编出来的那份是死重量)。 + 那是 interface define,不是 sources 门控,feature 表达不了完整语义,故不做半吊子实现。 +- `src/modules/CLI11.cppm` —— 上游自带的 C++20 模块接口。模块层是另一种包形态 + (参见 `pkgs/n/nlohmann.json.lua`),不是 compat 包的 feature。日后要 `import CLI11;` + 应另开一个模块层描述符。 + +因此本包**没有 feature**,也就没有需要做负向验证的门控。 + +## 2. CN 镜像 + +slug 取包名去掉 `compat.` 前缀后小写:`cli11`(与既有 `mcpp-res` 仓库的小写风格一致)。 + +``` +GLOBAL: https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz +CN: https://gitcode.com/mcpp-res/cli11/releases/download/2.7.2/cli11-2.7.2.tar.gz +sha256: 46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da +``` + +执行:`gtc repo create` → 先推 init commit(新仓无分支,release 无法 target main) +→ `gtc release publish --tag 2.7.2 --asset cli11-2.7.2.tar.gz`。 + +闭环验证:CN url `http=200`,`size=1446996`,sha256 与 GLOBAL **字节一致**。 +纯头文件包,三平台共用同一 url 与 sha。 + +## 3. 测试工程 + +`tests/examples/cli11/`,依赖 `[dependencies.compat] CLI11 = "2.7.2"`。 +`compat` 的 `[indices]` 重定向由 workspace 根继承,不在成员里重复声明(单条是硬约束)。 +依赖写**限定形式**:索引表按请求的 namespace 取键,裸名会走默认命名空间、从线上索引 +解析,被测的就不再是本 checkout。 + +`tests/parse.cpp` 断言四件事加两条失败路径: + +- 取值 / 默认值保持 / flag / 多值选项(`expected(3)`)/ 子命令的一次完整 parse; +- 未声明的选项必须抛 `CLI::ParseError`(证明 parse 真的跑了,而不是静默无动作); +- `check(CLI::Range(1,10))` 越界必须抛 `CLI::ValidationError`(带到 Validators 那一组头文件); +- `CLI11_VERSION == "2.7.2"`(证明解开的是这个归档); +- 调用包自身的 anchor 符号 `mcpp_compat_cli11_headers_anchor()`。 + +最后一条是有意的:header-only 包的 lib target 里就只有这一个 TU,引用它能证明包**确实 +被编译并链接**了,而不是只有头文件被 include(绿 CI 不等于包被编译)。 + +## 4. 验证结果 + +本地用 CI 同版本 mcpp `2026.8.6.1`,`MCPP_INDEX_MIRROR=CN`(CI 侧走 GLOBAL), +清掉 `target/` 与 `.mcpp/` 冷启动: + +``` + Downloading compat.CLI11 v2.7.2 + Compiling cli11-tests v0.1.0 (.) + Compiling compat.CLI11 v2.7.2 + Compiling parse (test) + Running bin/parse +parse ... ok (0.02s) + + test result ok. 1 passed; 0 failed; finished in 87.28s (build 3.06s + run 0.02s) +``` + +产出 obj 两个:`obj/parse.o` 与 `obj/compat_CLI11/mcpp_generated/cli11_anchor.o`。 + +本地复现 `validate.yml` 的 lint,全部通过:lua 语法、必填字段、无前导 `v`、 +`check_mirror_urls.lua`、`check_package_name.lua`、`check_cross_package_refs.lua`、 +`mcpp xpkg parse`(解析器语法,严格模式)。 + +## 5. 文件清单 + +1. `pkgs/c/compat.CLI11.lua` — 包描述符(修正 `include_dirs` glob、CN 镜像 url、 + `language` 对齐仓内其余描述符的 `c++23`) +2. `tests/examples/cli11/mcpp.toml` — 测试工程配置 +3. `tests/examples/cli11/tests/parse.cpp` — 测试代码 +4. `mcpp.toml` — 登记 workspace 成员 +5. `README.md` / `README.zh-CN.md` — 参考样例表各加一行 +6. `.agents/docs/2026-08-06-add-cli11-plan.md` — 本文档 diff --git a/README.md b/README.md index 429508a..e4f0081 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Two kinds of packages live here: | C++-source compat, one depending on the other | [`compat.abseil`](pkgs/c/compat.abseil.lua) (151 TUs; a wildcard over `absl/**` trimmed by upstream's test/benchmark naming conventions) · [`compat.protobuf`](pkgs/c/compat.protobuf.lua) (the libprotobuf runtime, 79 TUs transcribed from upstream's own `src/file_lists.cmake`; declares `compat.abseil` as a dependency because protobuf's public headers include `absl/…`, and its `gzip` feature defines `HAVE_ZLIB` and pulls `compat.zlib`, while `upb` adds protobuf's 64-TU C runtime out of the same tarball. It also exposes **`protoc`** as a `kind = "bin"` target, so a consumer writing `tools = ["protoc"]` gets the compiler built for its own machine out of the same package it links — making a generator/runtime version mismatch inexpressible) · [`compat.re2`](pkgs/c/compat.re2.lua) (22 TUs, upstream's own `RE2_SOURCES`) | | C++-source compat, zero-dep client + optional components | [`compat.websocket`](pkgs/c/compat.websocket.lua) (IXWebSocket 12.0.1 — a pure RFC 6455 client compiled from upstream's `IXWEBSOCKET_SOURCES` minus the four server TUs, so the **base build has zero external dependencies**: TLS off (the OpenSSL/MbedTLS/AppleSSL TUs aren't built) and `IXWEBSOCKET_USE_ZLIB` unset, so the gzip codec compiles to a no-op. Two optional features add on top: `server` (the four server TUs — `IXWebSocketServer`, `IXSocketServer`, `IXHttpServer`, `IXWebSocketProxyServer` — needing nothing external, and it **implies `zlib`** because upstream's server advertises permessage-deflate by default, which the transport negotiates regardless of the define) and `zlib` (deps `compat.zlib` and turns the codec into real per-message-deflate compression). The default-feature test brings its own minimal RFC 6455 echo server on loopback sockets (handshake, masking, fragmentation and close all exercised offline); a second member, `websocket-features`, runs a real `ix::WebSocketServer` and asserts the compression is observable on the wire — a 64 KiB repeated payload round-trips with `wireSize` = 80) | | header-only (with `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) | +| header-only, nothing to gate | [`compat.CLI11`](pkgs/c/compat.CLI11.lua) (a command line parser whose every definition is `CLI11_INLINE`, so the package is `*/include` plus an anchor TU. Upstream's two extras stay out: `src/Precompile.cpp` only means anything when `CLI11_COMPILE` also reaches the CONSUMER's translation units — an interface define, not a sources-only gate — and `src/modules/CLI11.cppm` is a module layer, which is a package shape of its own rather than a feature of the compat package) | | Runtime loader compat (pure sources, sidestepping upstream codegen/asm) | [`compat.vulkan`](pkgs/c/compat.vulkan.lua) (the Khronos loader: `loader/generated/` is checked in, and the assembly path degrades to plain C through `UNKNOWN_FUNCTIONS_SUPPORTED`, so no CMake/Python/assembler is needed; windows deferred) · [`compat.vulkan-headers`](pkgs/c/compat.vulkan-headers.lua) | | Whole-source direct build + generated config (only where a platform lacks one) | [`compat.curl`](pkgs/c/compat.curl.lua) (win32 uses upstream's checked-in config, unix generates one) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua) (win/mac use upstream's checked-in config; linux generates one and enables X11 by hand) · [`compat.c-ares`](pkgs/c/compat.c-ares.lua) (91 TUs; the release tarball already ships `ares_build.h` and a Windows config, so only `ares_config.h` is snapshotted per OS) | | Upstream codegen frozen into the mirror archive | [`compat.godot-cpp`](pkgs/c/compat.godot-cpp.lua) (two versions: `4.5.0` = the `godot-4.5-stable` bindings, `10.0.0-rc1` = godot-cpp's own 10.x line, whose bindings target Godot 4.6. The ~1000 GDExtension classes under `gen/` exist in no upstream tag archive — upstream's `binding_generator.py` emits them at build time. Running it once offline and publishing upstream's tree byte-for-byte **plus** `gen/` keeps Python off the consumer side entirely; `tools/godot-cpp/repack.sh` reproduces the archive deterministically and refuses to publish if any upstream file differs) | diff --git a/README.zh-CN.md b/README.zh-CN.md index 97cfe12..3a93444 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -39,6 +39,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 | C++ 源码 compat(彼此依赖) | [`compat.abseil`](pkgs/c/compat.abseil.lua)(151 TU;对 `absl/**` 取通配后,按上游自身的 test/benchmark 命名约定裁剪) · [`compat.protobuf`](pkgs/c/compat.protobuf.lua)(libprotobuf 运行时,79 TU 逐条转录自上游 `src/file_lists.cmake`;因 protobuf 公开头文件 include 了 `absl/…`,故显式依赖 `compat.abseil`;`gzip` feature 定义 `HAVE_ZLIB` 并拉入 `compat.zlib`,`upb` feature 则从同一个 tarball 里再编出 protobuf 的 64 TU C 运行时;还以 `kind = "bin"` target 暴露 **`protoc`**,消费者写 `tools = ["protoc"]` 即可从「自己链接的那个包」拿到为本机构建的编译器,使生成器与运行时的版本错配无法表达) · [`compat.re2`](pkgs/c/compat.re2.lua)(22 TU,取自上游自身的 `RE2_SOURCES`) | | C++ 源码 compat(零依赖客户端 + 可选组件) | [`compat.websocket`](pkgs/c/compat.websocket.lua)(IXWebSocket 12.0.1 —— 从上游 `IXWEBSOCKET_SOURCES` 剔掉 4 个 server TU 后直编的纯 RFC 6455 客户端,**基座零外部依赖**:TLS 关闭(OpenSSL/MbedTLS/AppleSSL 三组 TU 均不编),`IXWEBSOCKET_USE_ZLIB` 不定义(gzip codec 编译为 no-op)。两个可选 feature 在基座上叠加:`server`(4 个 server TU —— `IXWebSocketServer`/`IXSocketServer`/`IXHttpServer`/`IXWebSocketProxyServer`,零新增外部依赖,且 **implies `zlib`** —— 因为上游 server 默认就宣称 permessage-deflate,而 transport 的协商不受宏门控)与 `zlib`(依赖 `compat.zlib`,把 codec 变成真正的 permessage-deflate 压缩)。默认构建的测试自带基于 loopback 原始 socket 的最小 RFC 6455 echo server(握手/掩码/分片/关闭全部离线实测);第二个成员 `websocket-features` 跑真实的 `ix::WebSocketServer`,并断言压缩在线路上可观测 —— 64 KiB 重复载荷往返,`wireSize` = 80) | | header-only(含 `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) | +| header-only(无可门控组件) | [`compat.CLI11`](pkgs/c/compat.CLI11.lua)(命令行解析器,全部定义都是 `CLI11_INLINE`,故整包就是 `*/include` 加一个 anchor TU。上游两个额外件都不收:`src/Precompile.cpp` 只有在 `CLI11_COMPILE` 同时到达**消费者** TU 时才有意义 —— 那是 interface define,不是 sources 门控;`src/modules/CLI11.cppm` 属于模块层,是另一种包形态,而非 compat 包的 feature) | | 运行时 loader compat(纯源码,绕开上游 codegen/asm) | [`compat.vulkan`](pkgs/c/compat.vulkan.lua)(Khronos loader:`loader/generated/` 已签入,汇编路径经 `UNKNOWN_FUNCTIONS_SUPPORTED` 降级为纯 C,故无需 CMake/Python/汇编器;windows 延后)· [`compat.vulkan-headers`](pkgs/c/compat.vulkan-headers.lua) | | 全源码直编 + 生成 config(仅缺口平台) | [`compat.curl`](pkgs/c/compat.curl.lua)(win32 用上游签入 config,unix 生成) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua)(win/mac 用上游签入 config,linux 生成 + 手工开 X11) · [`compat.c-ares`](pkgs/c/compat.c-ares.lua)(91 TU;release tarball 已自带 `ares_build.h` 与 Windows 配置,故只需按 OS 冻结 `ares_config.h`) | | 上游 codegen 前置冻结进镜像归档 | [`compat.godot-cpp`](pkgs/c/compat.godot-cpp.lua)(两个版本:`4.5.0` 是 `godot-4.5-stable` 的绑定,`10.0.0-rc1` 是 godot-cpp 自己的 10.x 线、对应 Godot 4.6。`gen/` 下约 1000 个 GDExtension 类不在任何上游 tag 归档里,由上游 `binding_generator.py` 在构建时生成。改为离线跑一次,把上游源码树逐字节原样 **加上** `gen/` 一起发布,消费侧就完全不需要 Python;`tools/godot-cpp/repack.sh` 可确定性复现该归档,且上游文件一旦有出入即拒绝打包) | diff --git a/mcpp.toml b/mcpp.toml index f192b49..570c917 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -19,6 +19,7 @@ members = [ "tests/examples/catch2-v2", "tests/examples/catch2-v2-main", "tests/examples/cjson", + "tests/examples/cli11", "tests/examples/core", "tests/examples/curl", "tests/examples/eigen", diff --git a/pkgs/c/compat.CLI11.lua b/pkgs/c/compat.CLI11.lua index 0723891..cc5941c 100644 --- a/pkgs/c/compat.CLI11.lua +++ b/pkgs/c/compat.CLI11.lua @@ -1,16 +1,34 @@ -- Form B inline descriptor for CLI11 — a modern C++ command line parser. -- --- CLI11 is a HEADER-ONLY library. All functionality is implemented in headers --- under `include/CLI/`; there are no library sources to compile. Therefore the --- package simply exports the `include/` directory and provides a tiny generated --- translation unit so mcpp always has a buildable `lib` target. +-- CLI11 is HEADER-ONLY in its default mode: every definition under +-- `include/CLI/` is marked `CLI11_INLINE`, and `CLI/CLI.hpp` pulls the +-- implementation headers under `include/CLI/impl/` in behind it. There is +-- nothing to compile for normal use, so this package exposes the header root +-- on the include path and carries a tiny anchor translation unit so mcpp +-- always has a buildable `lib` target (same shape as compat.eigen / +-- compat.opengl). -- --- Consumers simply write: +-- Consumers write: -- -- #include -- --- No additional configuration or features are required. - +-- and need no feature or extra configuration. +-- +-- All `mcpp` paths are GLOBS relative to the verdir, so the leading `*` +-- absorbs the GitHub archive's `CLI11-/` wrap layer: the header root is +-- `*/include`, NOT `include`. +-- +-- Two upstream extras are deliberately NOT packaged here: +-- +-- * `src/Precompile.cpp` — upstream's precompiled mode. It only does +-- anything when `CLI11_COMPILE` is defined, and that define has to reach +-- the CONSUMER's translation units as well (otherwise the headers keep +-- their inline definitions and the compiled ones are dead weight). That +-- makes it an interface define, not a sources-only gate, so it is left +-- out rather than half-expressed. +-- * `src/modules/CLI11.cppm` — upstream's C++20 module interface. A module +-- layer is a separate package shape (see pkgs/n/nlohmann.json.lua), not a +-- feature of the compat package. package = { spec = "1", namespace = "compat", @@ -20,12 +38,13 @@ package = { repo = "https://github.com/CLIUtils/CLI11", type = "package", + -- Pure headers: one archive and one sha256 serve all three platforms. xpm = { linux = { ["2.7.2"] = { url = { GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", - CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://gitcode.com/mcpp-res/cli11/releases/download/2.7.2/cli11-2.7.2.tar.gz", }, sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", }, @@ -35,7 +54,7 @@ package = { ["2.7.2"] = { url = { GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", - CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://gitcode.com/mcpp-res/cli11/releases/download/2.7.2/cli11-2.7.2.tar.gz", }, sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", }, @@ -45,7 +64,7 @@ package = { ["2.7.2"] = { url = { GLOBAL = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", - CN = "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.7.2.tar.gz", + CN = "https://gitcode.com/mcpp-res/cli11/releases/download/2.7.2/cli11-2.7.2.tar.gz", }, sha256 = "46eef3101da70852ec7af026e09d485ccee81813331c8c6052d39344443b83da", }, @@ -53,15 +72,18 @@ package = { }, mcpp = { - language = "c++17", - import_std = false, - - -- Upstream headers live in include/CLI/ - include_dirs = { - "include", - }, - - -- Header-only anchor. + -- Applies to this package's OWN translation unit (the anchor below); + -- a consumer compiles the headers at whatever standard it declares, + -- and CLI11 itself needs only C++11. Kept at c++23 like every other + -- descriptor in this index. + language = "c++23", + import_std = false, + -- Upstream headers live in `include/CLI/`, so the include ROOT is + -- `include/` — that is what makes `#include ` resolve. + include_dirs = { "*/include" }, + -- Header-only: a trivial anchor TU gives mcpp a buildable lib target. + -- The basename is unique across the index on purpose — mcpp's obj/ + -- directory is flat and keyed by basename. generated_files = { ["mcpp_generated/cli11_anchor.cpp"] = [==[ int mcpp_compat_cli11_headers_anchor(void) { @@ -69,17 +91,8 @@ int mcpp_compat_cli11_headers_anchor(void) { } ]==], }, - - sources = { - "mcpp_generated/cli11_anchor.cpp", - }, - - targets = { - ["CLI11"] = { - kind = "lib", - }, - }, - - deps = {}, + sources = { "mcpp_generated/cli11_anchor.cpp" }, + targets = { ["CLI11"] = { kind = "lib" } }, + deps = { }, }, } diff --git a/tests/examples/cli11/mcpp.toml b/tests/examples/cli11/mcpp.toml new file mode 100644 index 0000000..1ddacfc --- /dev/null +++ b/tests/examples/cli11/mcpp.toml @@ -0,0 +1,17 @@ +# CLI11 test project: consumes compat.CLI11 (header-only command line parser) +# from this repo's own index and asserts real argv parsing under `mcpp test`. +# Part of the mcpp-index self-referential workspace: the `compat` redirect is +# declared at the workspace root and inherited here, so the dependency +# resolves to the checked-in recipe (pkgs/c/compat.CLI11.lua) rather than to +# the published remote index. +# +# The dependency is spelled QUALIFIED (`[dependencies.compat]`) on purpose: +# the index redirect is keyed by the REQUEST's namespace, so a bare +# `CLI11 = "2.7.2"` would ask under the default namespace, resolve from the +# published index, and quietly stop testing this checkout. +[package] +name = "cli11-tests" +version = "0.1.0" + +[dependencies.compat] +CLI11 = "2.7.2" diff --git a/tests/examples/cli11/tests/parse.cpp b/tests/examples/cli11/tests/parse.cpp new file mode 100644 index 0000000..90be561 --- /dev/null +++ b/tests/examples/cli11/tests/parse.cpp @@ -0,0 +1,93 @@ +// Behavioral test: parse synthetic argv with CLI11 and assert what came out. +// Covers the four things a parser has to get right — values and defaults, +// flags, a multi-value option, a subcommand — plus the two failure paths +// (unknown option, failed validator), which is what proves the parse actually +// ran rather than silently doing nothing. Returns non-zero on any mismatch. +// +// It also calls the package's anchor symbol. compat.CLI11 is header-only, so +// its lib target holds exactly that one translation unit: if the reference +// resolves, the package really was built and linked, not just included. +#include + +#include +#include + +int mcpp_compat_cli11_headers_anchor(void); + +namespace { + +// Values, default, flag, multi-value option and a subcommand, in one parse. +bool parses_a_full_command_line() { + CLI::App app{"mcpp-index CLI11 member"}; + + std::string name; + app.add_option("-n,--name", name, "a name")->required(); + + int count = 1; // left alone by the argv below: asserts the default holds + app.add_option("-c,--count", count, "how many"); + + bool verbose = false; + app.add_flag("-v,--verbose", verbose, "chatty"); + + std::vector nums; + app.add_option("--nums", nums, "numbers")->expected(3); + + auto *sub = app.add_subcommand("run", "do the thing"); + std::string target; + sub->add_option("target", target, "what to run")->required(); + + const char *argv[] = {"prog", "--name", "mcpp", "-v", + "--nums", "1", "2", "3", "run", "index"}; + try { + app.parse(static_cast(sizeof(argv) / sizeof(argv[0])), argv); + } catch (const CLI::ParseError &) { + return false; + } + + return name == "mcpp" && count == 1 && verbose + && nums == std::vector{1, 2, 3} + && app.got_subcommand(sub) && target == "index"; +} + +// An option nobody declared must be rejected, not ignored. +bool rejects_an_unknown_option() { + CLI::App app; + int count = 0; + app.add_option("-c,--count", count); + + const char *argv[] = {"prog", "--nope"}; + try { + app.parse(2, argv); + } catch (const CLI::ParseError &) { + return true; + } + return false; +} + +// Validators live in their own header; a value outside the range must throw. +bool enforces_a_validator() { + CLI::App app; + int count = 0; + app.add_option("-c,--count", count)->check(CLI::Range(1, 10)); + + const char *argv[] = {"prog", "-c", "99"}; + try { + app.parse(3, argv); + } catch (const CLI::ValidationError &) { + return true; + } catch (const CLI::ParseError &) { + return false; + } + return false; +} + +} // namespace + +int main() { + const bool ok = parses_a_full_command_line() + && rejects_an_unknown_option() + && enforces_a_validator() + && std::string(CLI11_VERSION) == "2.7.2" + && mcpp_compat_cli11_headers_anchor() == 0; + return ok ? 0 : 1; +}