From 1dfe10ec07c39785e897910540ed84f56d6d2419 Mon Sep 17 00:00:00 2001 From: HikariTish Date: Sun, 4 Oct 2026 19:21:39 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Quill=2013.0.0?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-10-04-add-quill-spec.md | 123 ++++++++++++++++++ docs/descriptor-examples.md | 1 + docs/zh/descriptor-examples.md | 1 + mcpp.toml | 1 + pkgs/o/odygrd.quill.lua | 62 +++++++++ tests/examples/quill-module/mcpp.toml | 10 ++ .../examples/quill-module/src/log_worker.cpp | 11 ++ tests/examples/quill-module/tests/quill.cpp | 52 ++++++++ 8 files changed, 261 insertions(+) create mode 100644 .agents/docs/2026-10-04-add-quill-spec.md create mode 100644 pkgs/o/odygrd.quill.lua create mode 100644 tests/examples/quill-module/mcpp.toml create mode 100644 tests/examples/quill-module/src/log_worker.cpp create mode 100644 tests/examples/quill-module/tests/quill.cpp diff --git a/.agents/docs/2026-10-04-add-quill-spec.md b/.agents/docs/2026-10-04-add-quill-spec.md new file mode 100644 index 00000000..45b2148a --- /dev/null +++ b/.agents/docs/2026-10-04-add-quill-spec.md @@ -0,0 +1,123 @@ +# Quill 13.0.0 接入 mcpp 模块生态 + +日期:2026-10-04。状态:已按方案 A 实施,Linux GCC 与 LLVM/libc++ 验收通过;macOS、Windows 的运行结果由 PR CI 验证。 + +## 1. 范围与消费方式 + +包为 `odygrd.quill@13.0.0`,模块入口保持上游的 `import quill;`。复用正式发布的实验性模块,保留异步日志行为、公开 API 和内置 fmt。没有独立 compat 包、fork、自制 wrapper、额外 feature 或引擎改动。 + +```toml +[dependencies.odygrd] +quill = "13.0.0" +``` + +尚未发布到远程索引,本地使用时需要指向本 checkout: + +```toml +[indices] +odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" } +``` + +消费者写 `import std; import quill;`。可通过 `quill::Frontend` 创建 sink/logger,使用无宏 API `quill::info(logger, "answer={}", 42)` 或运行时级别 `quill::log`。使用上游日志宏时额外写: + +```cpp +#define QUILL_USE_MODULE +#include +``` + +随后可调用 `LOG_INFO` 或 `QUILL_LOG_INFO`。13.0.0 消费端显式定义 `QUILL_USE_MODULE`;不依赖包内 defines 自动传播。宏头在此模式下跳过普通类型头,并补充宏所需的 helper,不重复包含完整实现。`QUILL_MODULE`、`FMTQUILL_MODULE` 由上游模块自行定义,消费者无需设置。 + +两种 API 的行为不完全相同:上游 [`LogFunctions.h`](https://github.com/odygrd/quill/blob/v13.0.0/include/quill/LogFunctions.h) 说明无宏 API 的参数始终求值,存在运行时元数据处理,也不能像宏一样按编译期日志级别完全移除。接入不强制迁移原有宏用法,也不作性能等价承诺。 + +模块公开面以 13.0.0 的模块入口及它实际导出的声明为准;不承诺所有可选头都能通过 import 使用。没有新增 Syslog/Systemd/Android sink、Prometheus 示例或自定义 codec/formatter 导出。完整 Quill 文本头与模块混用、跨 DLL 共享后端不在此次验收范围内。 + +## 2. 固定的上游输入 + +- 上游:[odygrd/quill v13.0.0](https://github.com/odygrd/quill/releases/tag/v13.0.0),MIT;保留根 LICENSE 和 bundled fmt 的声明。 +- Tag commit:`eb802a37c7d585840324886a3d8648c9c2159952`。 +- 归档:`https://github.com/odygrd/quill/archive/refs/tags/v13.0.0.tar.gz`。 +- SHA-256:`88b4a1542125577a4d51cf444c51e34d63618c422ba6a4fa9bd23894b49d696b`;spec 调研时两次独立下载一致,实施中的真实下载通过包摘要校验。 +- 解包根为 `quill-13.0.0/`。普通形态为头文件库,模块入口是 [`src/quill.cc`](https://github.com/odygrd/quill/blob/v13.0.0/src/quill.cc),采用 CRLF。 +- [`CMakeLists.txt`](https://github.com/odygrd/quill/blob/v13.0.0/CMakeLists.txt) 的 `QUILL_BUILD_MODULE` 标为 experimental,编译 `src/quill.cc` 并链接 `Threads::Threads`。 +- 内置格式化库位于 `include/quill/bundled/fmt/`,使用 `fmtquill` 命名空间,无需依赖 `fmtlib.fmt` 或 `compat.fmt`。 + +调研时 GitHub Releases API 的 latest 为 13.0.0;实施时该 API 返回 403,改用 `git ls-remote --tags ... 'refs/tags/v13*'` 确认仍只有 `v13.0.0`。在线 latest 文档/master 出现的 13.1.0 内容未混入固定版本实现。 + +## 3. 接入方案与适配边界 + +采用内联 Form B 描述符 [`pkgs/o/odygrd.quill.lua`](../../pkgs/o/odygrd.quill.lua): + +| 字段 | 实现 | +|---|---| +| `namespace` / `name` | `odygrd` / `quill` | +| `language` / `import_std` | `c++23` / `false`;保留上游 global module fragment,消费者仍可导入 std | +| `modules` | `{ "quill" }` | +| `include_dirs` | `{ "*/include" }`,服务模块内部包含及消费端宏头 | +| `sources` | `{ "*/src/quill.cppm" }`,只编译一个入口 | +| `targets` / `deps` | `quill` lib / 空依赖 | +| Linux 链接 | `ldflags = { "-pthread" }` | +| 三平台下载 | 相同版本、归档和摘要,使用纯字符串 GLOBAL URL | + +安装钩子检查源文件可读且恰有一条 `export module quill;`,再按字节复制为同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。逐文件比较确认 507 个上游文件内容均未改变,唯一新增文件是与原入口字节一致的 `.cppm`,保留了 CRLF。 + +扩展名适配参考 [`fmtlib.fmt`](../../pkgs/f/fmtlib.fmt.lua),避免 Clang 将 `.cc` 当普通翻译单元。实际 GCC、LLVM 构建图均只编译 `.cppm`,分别生成 `quill.gcm`、`quill.pcm`;未增加 `scan_overrides` 或完整生成式 wrapper。没有执行 CMake,`QUILL_BUILD_MODULE=ON` 不是本包的构建开关。 + +线程选项仅在 Linux 最终链接时传入,已检查两套构建图的 `ldflags`。不能只给模块或消费者一方增加影响 PCM 配置的 `-pthread` 编译选项:调研中的宿主 Clang 曾复现配置不一致,分开编译与链接后通过。当前 mcpp GCC/LLVM 构建无需额外线程编译选项。 + +上游对 MinGW 有 `ucrtbase` 分支,未将其泛化为所有 Windows 编译器的链接需求;Windows 的实际需求留待其 CI 工具链验证。未建立 CN 镜像,不声明猜测的地址;以后若增加镜像,须上传相同归档字节并核对摘要与可达性。 + +选择依据:复用模块及安装钩子参考 [Taskflow](2026-10-04-add-taskflow-spec.md),保留上游模块名参考 [`khronos.vulkan-hpp`](../../pkgs/k/khronos.vulkan-hpp.lua),内置 fmt 与 build/test 分别验证参考 [spdlog](2026-07-15-add-spdlog-plan.md)。普通头文件 `compat.quill` 无法提供所需 import;独立 Form A 适配仓会增加维护责任,目前均无必要。 + +## 4. 持久文件与测试契约 + +- 描述符:`pkgs/o/odygrd.quill.lua`。 +- 测试成员:[`tests/examples/quill-module/mcpp.toml`](../../tests/examples/quill-module/mcpp.toml),仅一条 odygrd 本地索引重定向;根 workspace 已登记。 +- 测试入口:[`tests/quill.cpp`](../../tests/examples/quill-module/tests/quill.cpp)。 +- 辅助 TU:[`src/log_worker.cpp`](../../tests/examples/quill-module/src/log_worker.cpp)。 +- 中英文目录:`docs/descriptor-examples.md`、`docs/zh/descriptor-examples.md`。README 已链接这两份完整目录,无需改变其结构。 + +一个测试可执行文件、两个消费 TU,均使用 `import std; import quill;`,覆盖: + +1. 主 TU 启动后端、创建 FileSink/logger;辅助 TU 在工作线程查询同名 logger,断言与主 TU 的指针相同。 +2. 宏 `LOG_INFO` 输出整数和字符串;不包含任何 Quill 头的辅助 TU 通过 `quill::info` 输出 `std::vector`,通过 `quill::log` 输出运行时级别记录。 +3. 两个 TU 分别提交 Debug 日志,Info 阈值下断言它们均未输出。 +4. producer 通过 `std::jthread` join,随后 flush/stop,再读回文件,断言三条有效消息的格式化内容和行数;不依赖时间戳、并发顺序或任意 sleep。 +5. 每次创建独享临时目录;断言失败返回非零,异常写 stderr;结束后停止后端并清理目录。测试超时 30 秒。 + +## 5. 实际验证 + +宿主为 Linux x86_64,命令由 Bash 执行。使用临时解包的 **mcpp 2026.10.1.2**,与 `.github/workflows/validate.yml` 一致;PATH 中较新的 mcpp 未作为验收替代。通过进程级 `MCPP_HOME=/home/helan/.mcpp` 复用 GCC 16.1.0 和 LLVM 22.1.8,没有修改全局默认版本。 + +```sh +export MCPP=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/bin/mcpp +export MCPP_HOME=/home/helan/.mcpp +export MCPP_INDEX_MIRROR=GLOBAL +export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/registry/bin/xlings +"$MCPP" xpkg parse --all-os pkgs/o/odygrd.quill.lua +"$MCPP" test -p quill-module --cache off --timeout 30 +"$MCPP" test -p quill-module --toolchain llvm@22.1.8 --cache off --timeout 30 +"$MCPP" test -p quill-module --timeout 30 +``` + +| 检查 | 实际结果 | +|---|---| +| 隔离临时索引安装及 GCC/LLVM 测试 | 各 `1 passed; 0 failed` | +| 正式 workspace Linux GCC 16.1.0 | `1 passed; 0 failed`,19.78 秒,包含 6.9 秒下载 | +| 正式 workspace Linux LLVM 22.1.8 / libc++ | `1 passed; 0 failed`,6.03 秒 | +| 正式 workspace GCC 增量 | `1 passed; 0 failed`,0.15 秒,构建 0.03 秒 | +| 独立普通消费工程 | `/tmp/quill-implementation/consumer` 指向正式 checkout,`mcpp run --cache off` 实际编译、链接、运行上述日志断言,退出 0 | +| 冷安装 | 隔离工程、正式 workspace、普通消费工程分别实际下载和安装;不将 `--cache off` 本身当作重装证据 | +| 安装文件比较 | 507 个上游文件内容不变,仅新增字节一致的 `.cppm` | +| Lua 语法与三平台 xpkg 解析 | 通过,三平台均解析为 1 个 source、1 个 include 根 | +| 镜像 URL、包身份、保留 namespace | 新描述符通过对应 lint | +| 跨包引用、三平台版本一致性、重复版本 | 全仓对应 lint 通过 | +| CI 选择规则 | 描述符全名匹配唯一 `quill-module` 成员;新增 workspace 成员及测试路径也命中现有规则 | +| diff 空白检查 | 通过 | + +临时工具、独立消费工程及日志位于 `/tmp/quill-implementation/`;原归档和宿主探测材料位于 `/tmp/quill-spec-G8O9Nt/`,不是长期源码依赖。 + +## 6. 未验证与发布边界 + +macOS、Windows 尚未实际构建运行,三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。 + +后续三平台发布前应让 macOS/Windows 运行同一成员;若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应先保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。 diff --git a/docs/descriptor-examples.md b/docs/descriptor-examples.md index f3ce8543..c41ff236 100644 --- a/docs/descriptor-examples.md +++ b/docs/descriptor-examples.md @@ -48,3 +48,4 @@ in the [root README](../README.md#reference-examples). | C++23 module wrapper | [`nlohmann.json`](../pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](../pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](../pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](../pkgs/b/boost-ext.ut.lua) (upstream's own `include/boost/ut.cppm` reproduced verbatim but for one `__argc`/`__argv` shim that Clang-on-MSVC needs; namespace `boost-ext` since it is NOT an official Boost library) | | C++23 module, upstream's own unit | [`khronos.vulkan-hpp`](../pkgs/k/khronos.vulkan-hpp.lua) (Vulkan-Hpp 1.4.357.0 — Khronos generates `vulkan.cppm` / `vulkan_video.cppm` into every Vulkan-Headers release, so `sources` names the two units and NOTHING is authored here; the payload is the same tarball, URL and sha256 as `compat.vulkan-headers`, which is what makes the module and the headers it includes impossible to skew. `import_std = true` is forced by the unit's own unconditional `export import std;`. No `include_dirs`: the headers arrive with the `compat.vulkan` dependency, which is also what satisfies the STATIC dispatcher's direct calls at link — depending on headers alone gives a package that compiles and then fails at every consumer's link. Module names stay upstream's `vulkan` / `vulkan_video`, never `khronos.vulkan`. The second unit `import`s the first and mcpp orders the pair from the scan, which `tests/examples/vulkan-hpp-module/tests/video.cpp` is the regression for) | | C++23 module, upstream's CPU partitions | [`taskflow.taskflow`](../pkgs/t/taskflow.taskflow.lua) (Taskflow 4.1.0, `import tf;`; reuses the four upstream CPU module units and omits the competing CUDA entry point. The checked install hook removes two nonexistent exports, moves the umbrella include/version export into core and imports core first for GCC, and supplies `` to utility for libc++. Only three module files are patched; headers and scheduler implementation stay upstream. No fork; consumers use `tf::version()` because macros are not exported) | +| C++23 module, upstream asynchronous logging | [`odygrd.quill`](../pkgs/o/odygrd.quill.lua) (Quill 13.0.0, upstream experimental `quill` module; the install hook copies `src/quill.cc` byte-for-byte to `.cppm`. Consumers use `import std; import quill;` and the macro-free API, or define `QUILL_USE_MODULE` and include `quill/LogMacros.h` for logging macros. Bundled fmt needs no separate dependency; Linux links with `-pthread`. Multi-TU logger identity, worker-thread output, formatting and filtering are tested) | diff --git a/docs/zh/descriptor-examples.md b/docs/zh/descriptor-examples.md index cf3d08c7..8c87e6b7 100644 --- a/docs/zh/descriptor-examples.md +++ b/docs/zh/descriptor-examples.md @@ -46,3 +46,4 @@ | C++23 module wrapper | [`nlohmann.json`](../../pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](../../pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](../../pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](../../pkgs/b/boost-ext.ut.lua)(逐字复用上游自带的 `include/boost/ut.cppm`,仅加一处 Clang-on-MSVC 需要的 `__argc`/`__argv` shim;命名空间取 `boost-ext`,因其并非 boost 官方库) | | C++23 module,上游自带单元 | [`khronos.vulkan-hpp`](../../pkgs/k/khronos.vulkan-hpp.lua)(Vulkan-Hpp 1.4.357.0 —— Khronos 把 `vulkan.cppm` / `vulkan_video.cppm` 生成进每个 Vulkan-Headers release,所以 `sources` 点名这两个单元即可,本仓**不写一行**包装体;载荷与 `compat.vulkan-headers` 是同一份 tarball、同一个 URL 与 sha256,这让模块与它 include 的头不可能错配。`import_std = true` 由单元自身无条件的 `export import std;` 决定。不声明 `include_dirs`:头随 `compat.vulkan` 依赖到达,而该依赖同时满足静态 dispatcher 在链接期的直接调用 —— 只依赖头会得到一个「能编译、每个消费者都链接失败」的包。模块名保持上游的 `vulkan` / `vulkan_video`,绝不写成 `khronos.vulkan`。第二个单元 `import` 第一个,顺序由 mcpp 扫描决定,`tests/examples/vulkan-hpp-module/tests/video.cpp` 就是这条的回归)| | C++23 module,上游 CPU 分区 | [`taskflow.taskflow`](../../pkgs/t/taskflow.taskflow.lua)(Taskflow 4.1.0,`import tf;`;复用上游四个 CPU 模块单元,排除提供同名主模块的 CUDA 入口。安装钩子逐项校验匹配次数:移除两个不存在的导出,将总头文件及版本导出移至 core 并优先导入 core 以兼容 GCC,为 utility 补 `` 以兼容 libc++。仅适配三个模块文件,头文件和调度实现保持上游原样,无独立 fork;宏不随模块导出,版本查询使用 `tf::version()`) | +| C++23 module,上游异步日志 | [`odygrd.quill`](../../pkgs/o/odygrd.quill.lua)(Quill 13.0.0,上游实验性 `quill` 模块;安装钩子将 `src/quill.cc` 按字节复制为 `.cppm`。消费者使用 `import std; import quill;` 和无宏 API,或定义 `QUILL_USE_MODULE` 并包含 `quill/LogMacros.h` 使用日志宏。自带 fmt,无需额外依赖;Linux 链接使用 `-pthread`。测试覆盖多 TU logger 身份、工作线程输出、格式化和过滤) | diff --git a/mcpp.toml b/mcpp.toml index 785f7f0d..94215859 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -146,6 +146,7 @@ members = [ "tests/examples/sqlite3", "tests/examples/sqlitecpp", "tests/examples/taskflow-module", + "tests/examples/quill-module", "tests/examples/tinyhttps", "tests/examples/usockets", "tests/examples/opencl", diff --git a/pkgs/o/odygrd.quill.lua b/pkgs/o/odygrd.quill.lua new file mode 100644 index 00000000..fee8dbaa --- /dev/null +++ b/pkgs/o/odygrd.quill.lua @@ -0,0 +1,62 @@ +package = { + spec = "1", + namespace = "odygrd", + name = "quill", + description = "Asynchronous logging through the upstream quill C++ module", + licenses = { "MIT" }, + repo = "https://github.com/odygrd/quill", + type = "package", + + xpm = { + linux = { + ["13.0.0"] = { + url = "https://github.com/odygrd/quill/archive/refs/tags/v13.0.0.tar.gz", + sha256 = "88b4a1542125577a4d51cf444c51e34d63618c422ba6a4fa9bd23894b49d696b", + }, + }, + macosx = { + ["13.0.0"] = { + url = "https://github.com/odygrd/quill/archive/refs/tags/v13.0.0.tar.gz", + sha256 = "88b4a1542125577a4d51cf444c51e34d63618c422ba6a4fa9bd23894b49d696b", + }, + }, + windows = { + ["13.0.0"] = { + url = "https://github.com/odygrd/quill/archive/refs/tags/v13.0.0.tar.gz", + sha256 = "88b4a1542125577a4d51cf444c51e34d63618c422ba6a4fa9bd23894b49d696b", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + modules = { "quill" }, + include_dirs = { "*/include" }, + sources = { "*/src/quill.cppm" }, + targets = { ["quill"] = { kind = "lib" } }, + deps = {}, + linux = { + -- 模块与消费者的线程编译配置须一致,线程库在最终链接时引入 + ldflags = { "-pthread" }, + }, + }, +} + +import("xim.libxpkg.pkginfo") + +function install() + local wrap = "quill-" .. pkginfo.version() + local source = path.join(wrap, "src/quill.cc") + local content = assert(io.readfile(source), "odygrd.quill: cannot read " .. source) + local _, count = content:gsub("export module quill;", "") + assert(count == 1, "odygrd.quill: expected exactly one module declaration") + -- Clang 通过接口扩展名识别模块,副本保留上游内容和换行 + os.cp(source, path.join(wrap, "src/quill.cppm")) + + local prefix = pkginfo.install_dir() + os.tryrm(prefix) + os.mkdir(prefix) + os.mv(wrap, path.join(prefix, wrap)) + return true +end diff --git a/tests/examples/quill-module/mcpp.toml b/tests/examples/quill-module/mcpp.toml new file mode 100644 index 00000000..706c8e25 --- /dev/null +++ b/tests/examples/quill-module/mcpp.toml @@ -0,0 +1,10 @@ +# 本成员通过本地索引验证 odygrd.quill 的模块消费 +[package] +name = "quill-module-tests" +version = "0.1.0" + +[indices] +odygrd = { path = "../../.." } + +[dependencies.odygrd] +quill = "13.0.0" diff --git a/tests/examples/quill-module/src/log_worker.cpp b/tests/examples/quill-module/src/log_worker.cpp new file mode 100644 index 00000000..f0d9aa86 --- /dev/null +++ b/tests/examples/quill-module/src/log_worker.cpp @@ -0,0 +1,11 @@ +import std; +import quill; + +bool log_from_worker(quill::Logger* expected) { + auto* logger = quill::Frontend::get_logger("quill-module-test"); + if (logger != expected) return false; + quill::info(logger, "worker values={}", std::vector{1, 2, 3}); + quill::log(logger, quill::LogLevel::Warning, "runtime answer={}", 43); + quill::debug(logger, "filtered worker message"); + return true; +} diff --git a/tests/examples/quill-module/tests/quill.cpp b/tests/examples/quill-module/tests/quill.cpp new file mode 100644 index 00000000..439317ea --- /dev/null +++ b/tests/examples/quill-module/tests/quill.cpp @@ -0,0 +1,52 @@ +import std; +import quill; + +#define QUILL_USE_MODULE +#include + +bool log_from_worker(quill::Logger* expected); + +bool check_logging(std::filesystem::path const& filename) { + quill::Backend::start(); + quill::FileSinkConfig config; + config.set_open_mode('w'); + auto sink = quill::Frontend::create_or_get_sink(filename.string(), config); + auto* logger = quill::Frontend::create_or_get_logger( + "quill-module-test", sink, quill::PatternFormatterOptions{"%(message)"}); + logger->set_log_level(quill::LogLevel::Info); + + LOG_INFO(logger, "macro answer={} text={}", 42, std::string{"hello"}); + LOG_DEBUG(logger, "filtered main message"); + bool worker_ok = false; + { + std::jthread worker([&] { worker_ok = log_from_worker(logger); }); + } + logger->flush_log(); + quill::Backend::stop(); + + std::ifstream file(filename); + std::string text((std::istreambuf_iterator(file)), {}); + return worker_ok && file.is_open() + && text.find("macro answer=42 text=hello") != std::string::npos + && text.find("worker values=[1, 2, 3]") != std::string::npos + && text.find("runtime answer=43") != std::string::npos + && text.find("filtered") == std::string::npos + && std::count(text.begin(), text.end(), '\n') == 3; +} + +int main() { + auto directory = std::filesystem::temp_directory_path() + / ("mcpp-quill-" + std::to_string(std::random_device{}())); + if (!std::filesystem::create_directory(directory)) return 1; + bool ok = false; + try { + ok = check_logging(directory / "output.log"); + } catch (std::exception const& error) { + std::cerr << error.what() << '\n'; + } + quill::Backend::stop(); + std::error_code error; + std::filesystem::remove_all(directory, error); + if (!ok) std::cerr << "Quill module logging assertions failed\n"; + return ok && !error ? 0 : 1; +} From f457ec8a80d14f952ee46c37021e0efbc977a586 Mon Sep 17 00:00:00 2001 From: HikariTish Date: Sun, 4 Oct 2026 19:26:02 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E9=80=82=E9=85=8D=20Quill=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=20macOS=20ARM=20=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-10-04-add-quill-spec.md | 14 +++++++++++--- docs/descriptor-examples.md | 2 +- docs/zh/descriptor-examples.md | 2 +- pkgs/o/odygrd.quill.lua | 17 +++++++++++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.agents/docs/2026-10-04-add-quill-spec.md b/.agents/docs/2026-10-04-add-quill-spec.md index 45b2148a..aaab430b 100644 --- a/.agents/docs/2026-10-04-add-quill-spec.md +++ b/.agents/docs/2026-10-04-add-quill-spec.md @@ -58,7 +58,14 @@ odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" } | Linux 链接 | `ldflags = { "-pthread" }` | | 三平台下载 | 相同版本、归档和摘要,使用纯字符串 GLOBAL URL | -安装钩子检查源文件可读且恰有一条 `export module quill;`,再按字节复制为同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。逐文件比较确认 507 个上游文件内容均未改变,唯一新增文件是与原入口字节一致的 `.cppm`,保留了 CRLF。 +安装钩子检查源文件可读且恰有一条 `export module quill;`,以其内容生成同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。507 个上游文件内容保持不变,适配仅作用于新增的 `.cppm`,保留 CRLF。 + +macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两项精确替换,匹配次数不是一次即失败: + +- x86 intrinsic 包含增加 x86 目标架构条件。Clang 在 ARM 上也能找到 `x86gprintrin.h`,仅靠 `__has_include` 会触发无效汇编约束和不存在的 x86 builtin。 +- 在 global module fragment 中为 Apple 预包含 `mach/mach_error.h`、`mach/thread_act.h`、`mach/thread_policy.h`。否则 Mach 类型在全局模块和 Quill 模块中重复归属,编译报错。 + +失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。 扩展名适配参考 [`fmtlib.fmt`](../../pkgs/f/fmtlib.fmt.lua),避免 Clang 将 `.cc` 当普通翻译单元。实际 GCC、LLVM 构建图均只编译 `.cppm`,分别生成 `quill.gcm`、`quill.pcm`;未增加 `scan_overrides` 或完整生成式 wrapper。没有执行 CMake,`QUILL_BUILD_MODULE=ON` 不是本包的构建开关。 @@ -104,10 +111,11 @@ export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86 | 隔离临时索引安装及 GCC/LLVM 测试 | 各 `1 passed; 0 failed` | | 正式 workspace Linux GCC 16.1.0 | `1 passed; 0 failed`,19.78 秒,包含 6.9 秒下载 | | 正式 workspace Linux LLVM 22.1.8 / libc++ | `1 passed; 0 failed`,6.03 秒 | +| 模块入口适配后的隔离冷安装及 GCC/LLVM | 各 `1 passed; 0 failed`,GCC 19.97 秒、LLVM 6.05 秒;507 个原始文件及生成入口的两处替换均已逐字节核对 | | 正式 workspace GCC 增量 | `1 passed; 0 failed`,0.15 秒,构建 0.03 秒 | | 独立普通消费工程 | `/tmp/quill-implementation/consumer` 指向正式 checkout,`mcpp run --cache off` 实际编译、链接、运行上述日志断言,退出 0 | | 冷安装 | 隔离工程、正式 workspace、普通消费工程分别实际下载和安装;不将 `--cache off` 本身当作重装证据 | -| 安装文件比较 | 507 个上游文件内容不变,仅新增字节一致的 `.cppm` | +| 安装文件比较 | 507 个上游文件内容不变,仅新增带两处模块入口适配的 `.cppm` | | Lua 语法与三平台 xpkg 解析 | 通过,三平台均解析为 1 个 source、1 个 include 根 | | 镜像 URL、包身份、保留 namespace | 新描述符通过对应 lint | | 跨包引用、三平台版本一致性、重复版本 | 全仓对应 lint 通过 | @@ -118,6 +126,6 @@ export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86 ## 6. 未验证与发布边界 -macOS、Windows 尚未实际构建运行,三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。 +首次 PR CI 的 Windows 构建运行通过;macOS ARM 的失败由上述两处模块入口适配处理,最终验收以最新提交的 CI 结果为准。三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。 后续三平台发布前应让 macOS/Windows 运行同一成员;若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应先保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。 diff --git a/docs/descriptor-examples.md b/docs/descriptor-examples.md index c41ff236..536d87aa 100644 --- a/docs/descriptor-examples.md +++ b/docs/descriptor-examples.md @@ -48,4 +48,4 @@ in the [root README](../README.md#reference-examples). | C++23 module wrapper | [`nlohmann.json`](../pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](../pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](../pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](../pkgs/b/boost-ext.ut.lua) (upstream's own `include/boost/ut.cppm` reproduced verbatim but for one `__argc`/`__argv` shim that Clang-on-MSVC needs; namespace `boost-ext` since it is NOT an official Boost library) | | C++23 module, upstream's own unit | [`khronos.vulkan-hpp`](../pkgs/k/khronos.vulkan-hpp.lua) (Vulkan-Hpp 1.4.357.0 — Khronos generates `vulkan.cppm` / `vulkan_video.cppm` into every Vulkan-Headers release, so `sources` names the two units and NOTHING is authored here; the payload is the same tarball, URL and sha256 as `compat.vulkan-headers`, which is what makes the module and the headers it includes impossible to skew. `import_std = true` is forced by the unit's own unconditional `export import std;`. No `include_dirs`: the headers arrive with the `compat.vulkan` dependency, which is also what satisfies the STATIC dispatcher's direct calls at link — depending on headers alone gives a package that compiles and then fails at every consumer's link. Module names stay upstream's `vulkan` / `vulkan_video`, never `khronos.vulkan`. The second unit `import`s the first and mcpp orders the pair from the scan, which `tests/examples/vulkan-hpp-module/tests/video.cpp` is the regression for) | | C++23 module, upstream's CPU partitions | [`taskflow.taskflow`](../pkgs/t/taskflow.taskflow.lua) (Taskflow 4.1.0, `import tf;`; reuses the four upstream CPU module units and omits the competing CUDA entry point. The checked install hook removes two nonexistent exports, moves the umbrella include/version export into core and imports core first for GCC, and supplies `` to utility for libc++. Only three module files are patched; headers and scheduler implementation stay upstream. No fork; consumers use `tf::version()` because macros are not exported) | -| C++23 module, upstream asynchronous logging | [`odygrd.quill`](../pkgs/o/odygrd.quill.lua) (Quill 13.0.0, upstream experimental `quill` module; the install hook copies `src/quill.cc` byte-for-byte to `.cppm`. Consumers use `import std; import quill;` and the macro-free API, or define `QUILL_USE_MODULE` and include `quill/LogMacros.h` for logging macros. Bundled fmt needs no separate dependency; Linux links with `-pthread`. Multi-TU logger identity, worker-thread output, formatting and filtering are tested) | +| C++23 module, upstream asynchronous logging | [`odygrd.quill`](../pkgs/o/odygrd.quill.lua) (Quill 13.0.0, upstream experimental `quill` module; the checked install hook generates `.cppm` from `src/quill.cc`, guards x86 intrinsics by target architecture and includes Apple Mach headers in the global module fragment. Consumers use `import std; import quill;` and the macro-free API, or define `QUILL_USE_MODULE` and include `quill/LogMacros.h` for logging macros. Bundled fmt needs no separate dependency; Linux links with `-pthread`. Multi-TU logger identity, worker-thread output, formatting and filtering are tested) | diff --git a/docs/zh/descriptor-examples.md b/docs/zh/descriptor-examples.md index 8c87e6b7..9fdd25ba 100644 --- a/docs/zh/descriptor-examples.md +++ b/docs/zh/descriptor-examples.md @@ -46,4 +46,4 @@ | C++23 module wrapper | [`nlohmann.json`](../../pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](../../pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](../../pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](../../pkgs/b/boost-ext.ut.lua)(逐字复用上游自带的 `include/boost/ut.cppm`,仅加一处 Clang-on-MSVC 需要的 `__argc`/`__argv` shim;命名空间取 `boost-ext`,因其并非 boost 官方库) | | C++23 module,上游自带单元 | [`khronos.vulkan-hpp`](../../pkgs/k/khronos.vulkan-hpp.lua)(Vulkan-Hpp 1.4.357.0 —— Khronos 把 `vulkan.cppm` / `vulkan_video.cppm` 生成进每个 Vulkan-Headers release,所以 `sources` 点名这两个单元即可,本仓**不写一行**包装体;载荷与 `compat.vulkan-headers` 是同一份 tarball、同一个 URL 与 sha256,这让模块与它 include 的头不可能错配。`import_std = true` 由单元自身无条件的 `export import std;` 决定。不声明 `include_dirs`:头随 `compat.vulkan` 依赖到达,而该依赖同时满足静态 dispatcher 在链接期的直接调用 —— 只依赖头会得到一个「能编译、每个消费者都链接失败」的包。模块名保持上游的 `vulkan` / `vulkan_video`,绝不写成 `khronos.vulkan`。第二个单元 `import` 第一个,顺序由 mcpp 扫描决定,`tests/examples/vulkan-hpp-module/tests/video.cpp` 就是这条的回归)| | C++23 module,上游 CPU 分区 | [`taskflow.taskflow`](../../pkgs/t/taskflow.taskflow.lua)(Taskflow 4.1.0,`import tf;`;复用上游四个 CPU 模块单元,排除提供同名主模块的 CUDA 入口。安装钩子逐项校验匹配次数:移除两个不存在的导出,将总头文件及版本导出移至 core 并优先导入 core 以兼容 GCC,为 utility 补 `` 以兼容 libc++。仅适配三个模块文件,头文件和调度实现保持上游原样,无独立 fork;宏不随模块导出,版本查询使用 `tf::version()`) | -| C++23 module,上游异步日志 | [`odygrd.quill`](../../pkgs/o/odygrd.quill.lua)(Quill 13.0.0,上游实验性 `quill` 模块;安装钩子将 `src/quill.cc` 按字节复制为 `.cppm`。消费者使用 `import std; import quill;` 和无宏 API,或定义 `QUILL_USE_MODULE` 并包含 `quill/LogMacros.h` 使用日志宏。自带 fmt,无需额外依赖;Linux 链接使用 `-pthread`。测试覆盖多 TU logger 身份、工作线程输出、格式化和过滤) | +| C++23 module,上游异步日志 | [`odygrd.quill`](../../pkgs/o/odygrd.quill.lua)(Quill 13.0.0,上游实验性 `quill` 模块;安装钩子以 `src/quill.cc` 生成 `.cppm`,精确适配 x86 intrinsic 的架构条件与 Apple Mach 头的全局模块归属。消费者使用 `import std; import quill;` 和无宏 API,或定义 `QUILL_USE_MODULE` 并包含 `quill/LogMacros.h` 使用日志宏。自带 fmt,无需额外依赖;Linux 链接使用 `-pthread`。测试覆盖多 TU logger 身份、工作线程输出、格式化和过滤) | diff --git a/pkgs/o/odygrd.quill.lua b/pkgs/o/odygrd.quill.lua index fee8dbaa..d17e3a7b 100644 --- a/pkgs/o/odygrd.quill.lua +++ b/pkgs/o/odygrd.quill.lua @@ -51,8 +51,21 @@ function install() local content = assert(io.readfile(source), "odygrd.quill: cannot read " .. source) local _, count = content:gsub("export module quill;", "") assert(count == 1, "odygrd.quill: expected exactly one module declaration") - -- Clang 通过接口扩展名识别模块,副本保留上游内容和换行 - os.cp(source, path.join(wrap, "src/quill.cppm")) + local function patch(before, after) + local pattern = before:gsub("(%W)", "%%%1") + local patched, matches = content:gsub(pattern, function() return after end) + assert(matches == 1, "odygrd.quill: expected exactly one patch match") + content = patched + end + + -- Clang 的资源目录在 ARM 上也含 x86 头,存在性检查不能代替目标架构判断 + patch("#if !defined(__INTEL_COMPILER)\r\n", + "#if !defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))\r\n") + -- Mach 声明属于系统全局模块,须在 Quill 的模块声明前完成包含 + patch("export module quill;\r\n", + "#if defined(__APPLE__)\r\n#include \r\n#include \r\n#include \r\n#endif\r\n\r\nexport module quill;\r\n") + -- Clang 通过接口扩展名识别模块,原始入口保留在归档树中 + io.writefile(path.join(wrap, "src/quill.cppm"), content) local prefix = pkginfo.install_dir() os.tryrm(prefix) From 2b17c5587d1b0410272621b9f3940f1bfd97841e Mon Sep 17 00:00:00 2001 From: HikariTish Date: Sun, 4 Oct 2026 19:30:50 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=A8=20Quill=20?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=A4=B4=E5=B9=B6=E5=85=BC=E5=AE=B9=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E6=8D=A2=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-10-04-add-quill-spec.md | 6 +++--- pkgs/o/odygrd.quill.lua | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.agents/docs/2026-10-04-add-quill-spec.md b/.agents/docs/2026-10-04-add-quill-spec.md index aaab430b..c1de353b 100644 --- a/.agents/docs/2026-10-04-add-quill-spec.md +++ b/.agents/docs/2026-10-04-add-quill-spec.md @@ -58,14 +58,14 @@ odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" } | Linux 链接 | `ldflags = { "-pthread" }` | | 三平台下载 | 相同版本、归档和摘要,使用纯字符串 GLOBAL URL | -安装钩子检查源文件可读且恰有一条 `export module quill;`,以其内容生成同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。507 个上游文件内容保持不变,适配仅作用于新增的 `.cppm`,保留 CRLF。 +安装钩子检查源文件可读且恰有一条 `export module quill;`,以其内容生成同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。507 个上游文件内容保持不变,适配仅作用于新增的 `.cppm`;读取后统一按 LF 匹配,兼容安装环境对 CRLF 的文本转换。 macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两项精确替换,匹配次数不是一次即失败: - x86 intrinsic 包含增加 x86 目标架构条件。Clang 在 ARM 上也能找到 `x86gprintrin.h`,仅靠 `__has_include` 会触发无效汇编约束和不存在的 x86 builtin。 -- 在 global module fragment 中为 Apple 预包含 `mach/mach_error.h`、`mach/thread_act.h`、`mach/thread_policy.h`。否则 Mach 类型在全局模块和 Quill 模块中重复归属,编译报错。 +- 在 global module fragment 中为 Apple 预包含 Mach 头,以及后端使用的 `unistd.h`、`fcntl.h`、`sys/file.h`、`sys/mman.h`、`sched.h`、`time.h` 和遗漏的 ``。否则 Mach 类型、`timeval`、`timespec` 等在全局模块和 Quill 模块中重复归属,编译报错。 -失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。 +失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。后续 CI 的 [macOS 系统头错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892513) 和 [Windows 换行匹配错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892875) 分别对应系统头补全和换行规范化;本地钩子检查已验证 CRLF/LF 生成相同结果,预期替换缺失时安装失败。 扩展名适配参考 [`fmtlib.fmt`](../../pkgs/f/fmtlib.fmt.lua),避免 Clang 将 `.cc` 当普通翻译单元。实际 GCC、LLVM 构建图均只编译 `.cppm`,分别生成 `quill.gcm`、`quill.pcm`;未增加 `scan_overrides` 或完整生成式 wrapper。没有执行 CMake,`QUILL_BUILD_MODULE=ON` 不是本包的构建开关。 diff --git a/pkgs/o/odygrd.quill.lua b/pkgs/o/odygrd.quill.lua index d17e3a7b..520cd35e 100644 --- a/pkgs/o/odygrd.quill.lua +++ b/pkgs/o/odygrd.quill.lua @@ -49,6 +49,8 @@ function install() local wrap = "quill-" .. pkginfo.version() local source = path.join(wrap, "src/quill.cc") local content = assert(io.readfile(source), "odygrd.quill: cannot read " .. source) + -- 安装环境的文本读取可能转换 CRLF,补丁统一按 LF 匹配 + content = content:gsub("\r\n", "\n") local _, count = content:gsub("export module quill;", "") assert(count == 1, "odygrd.quill: expected exactly one module declaration") local function patch(before, after) @@ -59,11 +61,11 @@ function install() end -- Clang 的资源目录在 ARM 上也含 x86 头,存在性检查不能代替目标架构判断 - patch("#if !defined(__INTEL_COMPILER)\r\n", - "#if !defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))\r\n") - -- Mach 声明属于系统全局模块,须在 Quill 的模块声明前完成包含 - patch("export module quill;\r\n", - "#if defined(__APPLE__)\r\n#include \r\n#include \r\n#include \r\n#endif\r\n\r\nexport module quill;\r\n") + patch("#if !defined(__INTEL_COMPILER)\n", + "#if !defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))\n") + -- Apple 系统头与遗漏的标准头须在 Quill 的模块声明前完成包含 + patch("export module quill;\n", + "#if defined(__APPLE__)\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#endif\n\nexport module quill;\n") -- Clang 通过接口扩展名识别模块,原始入口保留在归档树中 io.writefile(path.join(wrap, "src/quill.cppm"), content) From 6aecf6e6640f6049f662e0eea2dc0b3f550f60e9 Mon Sep 17 00:00:00 2001 From: HikariTish Date: Sun, 4 Oct 2026 19:44:10 +0800 Subject: [PATCH 4/4] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=20Quill=20?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=9B=9E=E5=BD=92=E4=B8=8E=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-10-04-add-quill-spec.md | 66 +++++++++++------------ .github/workflows/validate.yml | 2 + tests/check_quill_install.lua | 59 ++++++++++++++++++++ 3 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 tests/check_quill_install.lua diff --git a/.agents/docs/2026-10-04-add-quill-spec.md b/.agents/docs/2026-10-04-add-quill-spec.md index c1de353b..d9b0008d 100644 --- a/.agents/docs/2026-10-04-add-quill-spec.md +++ b/.agents/docs/2026-10-04-add-quill-spec.md @@ -1,6 +1,6 @@ # Quill 13.0.0 接入 mcpp 模块生态 -日期:2026-10-04。状态:已按方案 A 实施,Linux GCC 与 LLVM/libc++ 验收通过;macOS、Windows 的运行结果由 PR CI 验证。 +日期:2026-10-04。状态:已实施,Linux GCC/LLVM、macOS ARM、Windows 构建运行均通过。验收依据见第 5 节。 ## 1. 范围与消费方式 @@ -11,11 +11,11 @@ quill = "13.0.0" ``` -尚未发布到远程索引,本地使用时需要指向本 checkout: +PR 合并发布前,本地使用需要指向索引 checkout;将示例路径替换为自己的仓库路径: ```toml [indices] -odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" } +odygrd = { path = "/path/to/mcpp-index" } ``` 消费者写 `import std; import quill;`。可通过 `quill::Frontend` 创建 sink/logger,使用无宏 API `quill::info(logger, "answer={}", 42)` 或运行时级别 `quill::log`。使用上游日志宏时额外写: @@ -65,13 +65,13 @@ macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两 - x86 intrinsic 包含增加 x86 目标架构条件。Clang 在 ARM 上也能找到 `x86gprintrin.h`,仅靠 `__has_include` 会触发无效汇编约束和不存在的 x86 builtin。 - 在 global module fragment 中为 Apple 预包含 Mach 头,以及后端使用的 `unistd.h`、`fcntl.h`、`sys/file.h`、`sys/mman.h`、`sched.h`、`time.h` 和遗漏的 ``。否则 Mach 类型、`timeval`、`timespec` 等在全局模块和 Quill 模块中重复归属,编译报错。 -失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。后续 CI 的 [macOS 系统头错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892513) 和 [Windows 换行匹配错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892875) 分别对应系统头补全和换行规范化;本地钩子检查已验证 CRLF/LF 生成相同结果,预期替换缺失时安装失败。 +失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。后续 CI 的 [macOS 系统头错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892513) 和 [Windows 换行匹配错误](https://github.com/mcpplibs/mcpp-index/actions/runs/37198733789/job/111425892875) 分别对应系统头补全和换行规范化;仓库中的 [安装钩子检查](../../tests/check_quill_install.lua) 验证 CRLF/LF 生成相同结果,以及输入不可读、模块声明或替换位置缺失/重复时,在任何写入或安装目录操作前失败;该检查已接入 CI lint。 扩展名适配参考 [`fmtlib.fmt`](../../pkgs/f/fmtlib.fmt.lua),避免 Clang 将 `.cc` 当普通翻译单元。实际 GCC、LLVM 构建图均只编译 `.cppm`,分别生成 `quill.gcm`、`quill.pcm`;未增加 `scan_overrides` 或完整生成式 wrapper。没有执行 CMake,`QUILL_BUILD_MODULE=ON` 不是本包的构建开关。 线程选项仅在 Linux 最终链接时传入,已检查两套构建图的 `ldflags`。不能只给模块或消费者一方增加影响 PCM 配置的 `-pthread` 编译选项:调研中的宿主 Clang 曾复现配置不一致,分开编译与链接后通过。当前 mcpp GCC/LLVM 构建无需额外线程编译选项。 -上游对 MinGW 有 `ucrtbase` 分支,未将其泛化为所有 Windows 编译器的链接需求;Windows 的实际需求留待其 CI 工具链验证。未建立 CN 镜像,不声明猜测的地址;以后若增加镜像,须上传相同归档字节并核对摘要与可达性。 +上游对 MinGW 有 `ucrtbase` 分支,未将其泛化为所有 Windows 编译器的链接需求;本次 Windows CI 使用 MSVC ABI 工具链并已通过,未验证 MinGW。未建立 CN 镜像,不声明猜测的地址;以后若增加镜像,须上传相同归档字节并核对摘要与可达性。 选择依据:复用模块及安装钩子参考 [Taskflow](2026-10-04-add-taskflow-spec.md),保留上游模块名参考 [`khronos.vulkan-hpp`](../../pkgs/k/khronos.vulkan-hpp.lua),内置 fmt 与 build/test 分别验证参考 [spdlog](2026-07-15-add-spdlog-plan.md)。普通头文件 `compat.quill` 无法提供所需 import;独立 Form A 适配仓会增加维护责任,目前均无必要。 @@ -91,41 +91,41 @@ macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两 4. producer 通过 `std::jthread` join,随后 flush/stop,再读回文件,断言三条有效消息的格式化内容和行数;不依赖时间戳、并发顺序或任意 sleep。 5. 每次创建独享临时目录;断言失败返回非零,异常写 stderr;结束后停止后端并清理目录。测试超时 30 秒。 -## 5. 实际验证 +## 5. 实际验证与复现 -宿主为 Linux x86_64,命令由 Bash 执行。使用临时解包的 **mcpp 2026.10.1.2**,与 `.github/workflows/validate.yml` 一致;PATH 中较新的 mcpp 未作为验收替代。通过进程级 `MCPP_HOME=/home/helan/.mcpp` 复用 GCC 16.1.0 和 LLVM 22.1.8,没有修改全局默认版本。 +模块实现提交 `2b17c5587d1b0410272621b9f3940f1bfd97841e` 的 [PR CI 验收](https://github.com/mcpplibs/mcpp-index/actions/runs/37199001009) 已通过,包含 Linux GCC/LLVM、macOS ARM、Windows 的真实构建运行,以及 lint、镜像 URL 和图形安装副作用检查。跨平台结果不是仅由描述符解析推断。 + +| 检查 | 结果 | +|---|---| +| Linux GCC 16.1.0、LLVM 22.1.8/libc++ | 开发构建、最终安装补丁后的隔离测试均为 `1 passed; 0 failed` | +| macOS ARM、Windows MSVC ABI | PR CI 各 `1 passed; 0 failed` | +| Linux Release(`-O2`) | GCC、LLVM 各 `1 passed; 0 failed` | +| GCC 增量与独立消费工程 | 增量测试通过;独立 `mcpp run --cache off` 编译、链接和运行断言通过 | +| 冷安装 | 独立测试目录实际下载、解包和运行安装钩子;不将 `--cache off` 本身作为重装证据 | +| 安装文件比较 | 507 个上游文件内容不变,仅新增带模块入口适配的 `.cppm` | +| 安装钩子边界 | LF/CRLF 输出一致;不可读输入、缺失/重复模块声明、缺失/重复替换位置五个负向用例均通过 | +| Lua、schema 和索引 lint | Lua 语法、三平台描述符解析、镜像 URL、包身份、保留 namespace、全仓跨包引用/版本一致性/重复版本检查通过 | + +在仓库根目录使用 Bash 复现。先将 `MCPP_ROOT` 设置为已解包的 mcpp 2026.10.1.2 发布目录;`MCPP_HOME` 可指定已有 GCC/LLVM 工具链目录,默认使用用户目录下的 `.mcpp`。这些设置仅作用于当前 Shell,不切换全局默认版本。安装钩子检查仅需要 Lua 5.4,不需要上游归档、网络或临时调查文件。 ```sh -export MCPP=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/bin/mcpp -export MCPP_HOME=/home/helan/.mcpp +: "${MCPP_ROOT:?请设置 mcpp 2026.10.1.2 发布目录}" +export MCPP="$MCPP_ROOT/bin/mcpp" +export MCPP_HOME="${MCPP_HOME:-$HOME/.mcpp}" export MCPP_INDEX_MIRROR=GLOBAL -export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/registry/bin/xlings +export MCPP_VENDORED_XLINGS="$MCPP_ROOT/registry/bin/xlings" +test "$("$MCPP" --version)" = "mcpp 2026.10.1.2" || exit 1 +lua5.4 tests/check_quill_install.lua "$MCPP" xpkg parse --all-os pkgs/o/odygrd.quill.lua "$MCPP" test -p quill-module --cache off --timeout 30 "$MCPP" test -p quill-module --toolchain llvm@22.1.8 --cache off --timeout 30 "$MCPP" test -p quill-module --timeout 30 +"$MCPP" test -p quill-module --profile release --cache off --timeout 30 +"$MCPP" test -p quill-module --toolchain llvm@22.1.8 --profile release --cache off --timeout 30 ``` -| 检查 | 实际结果 | -|---|---| -| 隔离临时索引安装及 GCC/LLVM 测试 | 各 `1 passed; 0 failed` | -| 正式 workspace Linux GCC 16.1.0 | `1 passed; 0 failed`,19.78 秒,包含 6.9 秒下载 | -| 正式 workspace Linux LLVM 22.1.8 / libc++ | `1 passed; 0 failed`,6.03 秒 | -| 模块入口适配后的隔离冷安装及 GCC/LLVM | 各 `1 passed; 0 failed`,GCC 19.97 秒、LLVM 6.05 秒;507 个原始文件及生成入口的两处替换均已逐字节核对 | -| 正式 workspace GCC 增量 | `1 passed; 0 failed`,0.15 秒,构建 0.03 秒 | -| 独立普通消费工程 | `/tmp/quill-implementation/consumer` 指向正式 checkout,`mcpp run --cache off` 实际编译、链接、运行上述日志断言,退出 0 | -| 冷安装 | 隔离工程、正式 workspace、普通消费工程分别实际下载和安装;不将 `--cache off` 本身当作重装证据 | -| 安装文件比较 | 507 个上游文件内容不变,仅新增带两处模块入口适配的 `.cppm` | -| Lua 语法与三平台 xpkg 解析 | 通过,三平台均解析为 1 个 source、1 个 include 根 | -| 镜像 URL、包身份、保留 namespace | 新描述符通过对应 lint | -| 跨包引用、三平台版本一致性、重复版本 | 全仓对应 lint 通过 | -| CI 选择规则 | 描述符全名匹配唯一 `quill-module` 成员;新增 workspace 成员及测试路径也命中现有规则 | -| diff 空白检查 | 通过 | - -临时工具、独立消费工程及日志位于 `/tmp/quill-implementation/`;原归档和宿主探测材料位于 `/tmp/quill-spec-G8O9Nt/`,不是长期源码依赖。 - -## 6. 未验证与发布边界 - -首次 PR CI 的 Windows 构建运行通过;macOS ARM 的失败由上述两处模块入口适配处理,最终验收以最新提交的 CI 结果为准。三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。 - -后续三平台发布前应让 macOS/Windows 运行同一成员;若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应先保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。 +## 6. 验证边界 + +上游仍将模块标为实验性。当前覆盖 Linux x86_64、macOS ARM 和 Windows MSVC ABI 工具链;未验证 MinGW、跨 DLL、完整 Quill 文本头与模块混用,以及全部可选 sink/codec/metrics,不承诺性能指标。未上传 CN 镜像。 + +升级上游或工具链时应重新执行同一套跨平台测试。若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a58db7d3..be035a20 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -531,6 +531,8 @@ jobs: # release that republished an old tag would leave no other trace. - name: Lint duplicate versions run: lua5.4 tests/check_duplicate_versions.lua pkgs/*/*.lua + - name: Check Quill install hook boundaries + run: lua5.4 tests/check_quill_install.lua # The compatibility measurement's classifier decides a PUBLISHED figure # -- which members "run", which "build", and which were correctly # "refused" -- and the only thing that exercised it was the four-hour diff --git a/tests/check_quill_install.lua b/tests/check_quill_install.lua new file mode 100644 index 00000000..9beaef96 --- /dev/null +++ b/tests/check_quill_install.lua @@ -0,0 +1,59 @@ +-- 本检查以最小模块片段验证真实安装钩子的匹配边界,不访问网络或改动安装目录 +local descriptor = arg[1] or "pkgs/o/odygrd.quill.lua" +local guard = "#if !defined(__INTEL_COMPILER)\n" +local declaration = "export module quill;\n" +local fixture = "module;\n" .. guard .. "#endif\n" .. declaration + +local function run(content) + local output, output_path + local mutations = 0 + local function mutate() mutations = mutations + 1 end + local env = setmetatable({ + import = function(name) assert(name == "xim.libxpkg.pkginfo") end, + pkginfo = { + version = function() return "13.0.0" end, + install_dir = function() return "test-prefix" end, + }, + path = { join = function(...) return table.concat({...}, "/") end }, + io = { + readfile = function(path) + assert(path == "quill-13.0.0/src/quill.cc") + return content + end, + writefile = function(path, value) + output_path, output = path, value + mutate() + end, + }, + os = { tryrm = mutate, mkdir = mutate, mv = mutate }, + }, { __index = _G }) + assert(loadfile(descriptor, "t", env))() + local ok, result = pcall(env.install) + return ok, result, output, output_path, mutations +end + +local ok, result, lf, path = run(fixture) +assert(ok and result == true, "LF input must install") +assert(path == "quill-13.0.0/src/quill.cppm", "module interface output path") +local crlf_ok, crlf_result, crlf = run((fixture:gsub("\n", "\r\n"))) +assert(crlf_ok and crlf_result == true and crlf == lf, "CRLF and LF must generate identical output") +assert(not lf:find("\r", 1, true), "generated content must use normalized newlines") +assert(lf:find("defined(__i386__) || defined(__x86_64__)", 1, true), "x86 headers need an architecture guard") +local module_position = assert(lf:find(declaration, 1, true)) +for _, header in ipairs({ "", "", "", "" }) do + local position = assert(lf:find("#include " .. header, 1, true), "missing global header " .. header) + assert(position < module_position, "system headers must precede the module declaration") +end + +local function rejects(content, expected) + local accepted, message, _, _, mutations = run(content) + assert(not accepted and tostring(message):find(expected, 1, true), expected) + assert(mutations == 0, "invalid input must fail before writing or replacing installed files") +end + +rejects(nil, "cannot read") +rejects("module;\n" .. guard .. "#endif\n", "expected exactly one module declaration") +rejects(fixture .. declaration, "expected exactly one module declaration") +rejects("module;\n" .. declaration, "expected exactly one patch match") +rejects(fixture .. guard .. "#endif\n", "expected exactly one patch match") +print("Quill install hook: LF/CRLF and five invalid-input cases passed")