Skip to content

Commit 6aecf6e

Browse files
committed
test: 补充 Quill 安装回归与验收说明
1 parent 2b17c55 commit 6aecf6e

3 files changed

Lines changed: 94 additions & 33 deletions

File tree

‎.agents/docs/2026-10-04-add-quill-spec.md‎

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Quill 13.0.0 接入 mcpp 模块生态
22

3-
日期:2026-10-04。状态:已按方案 A 实施,Linux GCC 与 LLVM/libc++ 验收通过;macOS、Windows 的运行结果由 PR CI 验证。
3+
日期:2026-10-04。状态:已实施,Linux GCC/LLVM、macOS ARM、Windows 构建运行均通过。验收依据见第 5 节。
44

55
## 1. 范围与消费方式
66

@@ -11,11 +11,11 @@
1111
quill = "13.0.0"
1212
```
1313

14-
尚未发布到远程索引,本地使用时需要指向本 checkout:
14+
PR 合并发布前,本地使用需要指向索引 checkout;将示例路径替换为自己的仓库路径:
1515

1616
```toml
1717
[indices]
18-
odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" }
18+
odygrd = { path = "/path/to/mcpp-index" }
1919
```
2020

2121
消费者写 `import std; import quill;`。可通过 `quill::Frontend` 创建 sink/logger,使用无宏 API `quill::info(logger, "answer={}", 42)` 或运行时级别 `quill::log`。使用上游日志宏时额外写:
@@ -65,13 +65,13 @@ macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两
6565
- x86 intrinsic 包含增加 x86 目标架构条件。Clang 在 ARM 上也能找到 `x86gprintrin.h`,仅靠 `__has_include` 会触发无效汇编约束和不存在的 x86 builtin。
6666
- 在 global module fragment 中为 Apple 预包含 Mach 头,以及后端使用的 `unistd.h`、`fcntl.h`、`sys/file.h`、`sys/mman.h`、`sched.h`、`time.h` 和遗漏的 `<charconv>`。否则 Mach 类型、`timeval`、`timespec` 等在全局模块和 Quill 模块中重复归属,编译报错。
6767

68-
失败证据见 [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 生成相同结果,预期替换缺失时安装失败。
68+
失败证据见 [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。
6969

7070
扩展名适配参考 [`fmtlib.fmt`](../../pkgs/f/fmtlib.fmt.lua),避免 Clang 将 `.cc` 当普通翻译单元。实际 GCC、LLVM 构建图均只编译 `.cppm`,分别生成 `quill.gcm`、`quill.pcm`;未增加 `scan_overrides` 或完整生成式 wrapper。没有执行 CMake,`QUILL_BUILD_MODULE=ON` 不是本包的构建开关。
7171

7272
线程选项仅在 Linux 最终链接时传入,已检查两套构建图的 `ldflags`。不能只给模块或消费者一方增加影响 PCM 配置的 `-pthread` 编译选项:调研中的宿主 Clang 曾复现配置不一致,分开编译与链接后通过。当前 mcpp GCC/LLVM 构建无需额外线程编译选项。
7373

74-
上游对 MinGW 有 `ucrtbase` 分支,未将其泛化为所有 Windows 编译器的链接需求;Windows 的实际需求留待其 CI 工具链验证。未建立 CN 镜像,不声明猜测的地址;以后若增加镜像,须上传相同归档字节并核对摘要与可达性。
74+
上游对 MinGW 有 `ucrtbase` 分支,未将其泛化为所有 Windows 编译器的链接需求;本次 Windows CI 使用 MSVC ABI 工具链并已通过,未验证 MinGW。未建立 CN 镜像,不声明猜测的地址;以后若增加镜像,须上传相同归档字节并核对摘要与可达性。
7575

7676
选择依据:复用模块及安装钩子参考 [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 适配仓会增加维护责任,目前均无必要。
7777

@@ -91,41 +91,41 @@ macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两
9191
4. producer 通过 `std::jthread` join,随后 flush/stop,再读回文件,断言三条有效消息的格式化内容和行数;不依赖时间戳、并发顺序或任意 sleep。
9292
5. 每次创建独享临时目录;断言失败返回非零,异常写 stderr;结束后停止后端并清理目录。测试超时 30 秒。
9393

94-
## 5. 实际验证
94+
## 5. 实际验证与复现
9595

96-
宿主为 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,没有修改全局默认版本。
96+
模块实现提交 `2b17c5587d1b0410272621b9f3940f1bfd97841e` 的 [PR CI 验收](https://github.com/mcpplibs/mcpp-index/actions/runs/37199001009) 已通过,包含 Linux GCC/LLVM、macOS ARM、Windows 的真实构建运行,以及 lint、镜像 URL 和图形安装副作用检查。跨平台结果不是仅由描述符解析推断。
97+
98+
| 检查 | 结果 |
99+
|---|---|
100+
| Linux GCC 16.1.0、LLVM 22.1.8/libc++ | 开发构建、最终安装补丁后的隔离测试均为 `1 passed; 0 failed` |
101+
| macOS ARM、Windows MSVC ABI | PR CI 各 `1 passed; 0 failed` |
102+
| Linux Release(`-O2`) | GCC、LLVM 各 `1 passed; 0 failed` |
103+
| GCC 增量与独立消费工程 | 增量测试通过;独立 `mcpp run --cache off` 编译、链接和运行断言通过 |
104+
| 冷安装 | 独立测试目录实际下载、解包和运行安装钩子;不将 `--cache off` 本身作为重装证据 |
105+
| 安装文件比较 | 507 个上游文件内容不变,仅新增带模块入口适配的 `.cppm` |
106+
| 安装钩子边界 | LF/CRLF 输出一致;不可读输入、缺失/重复模块声明、缺失/重复替换位置五个负向用例均通过 |
107+
| Lua、schema 和索引 lint | Lua 语法、三平台描述符解析、镜像 URL、包身份、保留 namespace、全仓跨包引用/版本一致性/重复版本检查通过 |
108+
109+
在仓库根目录使用 Bash 复现。先将 `MCPP_ROOT` 设置为已解包的 mcpp 2026.10.1.2 发布目录;`MCPP_HOME` 可指定已有 GCC/LLVM 工具链目录,默认使用用户目录下的 `.mcpp`。这些设置仅作用于当前 Shell,不切换全局默认版本。安装钩子检查仅需要 Lua 5.4,不需要上游归档、网络或临时调查文件。
97110

98111
```sh
99-
export MCPP=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/bin/mcpp
100-
export MCPP_HOME=/home/helan/.mcpp
112+
: "${MCPP_ROOT:?请设置 mcpp 2026.10.1.2 发布目录}"
113+
export MCPP="$MCPP_ROOT/bin/mcpp"
114+
export MCPP_HOME="${MCPP_HOME:-$HOME/.mcpp}"
101115
export MCPP_INDEX_MIRROR=GLOBAL
102-
export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86_64/registry/bin/xlings
116+
export MCPP_VENDORED_XLINGS="$MCPP_ROOT/registry/bin/xlings"
117+
test "$("$MCPP" --version)" = "mcpp 2026.10.1.2" || exit 1
118+
lua5.4 tests/check_quill_install.lua
103119
"$MCPP" xpkg parse --all-os pkgs/o/odygrd.quill.lua
104120
"$MCPP" test -p quill-module --cache off --timeout 30
105121
"$MCPP" test -p quill-module --toolchain llvm@22.1.8 --cache off --timeout 30
106122
"$MCPP" test -p quill-module --timeout 30
123+
"$MCPP" test -p quill-module --profile release --cache off --timeout 30
124+
"$MCPP" test -p quill-module --toolchain llvm@22.1.8 --profile release --cache off --timeout 30
107125
```
108126

109-
| 检查 | 实际结果 |
110-
|---|---|
111-
| 隔离临时索引安装及 GCC/LLVM 测试 | 各 `1 passed; 0 failed` |
112-
| 正式 workspace Linux GCC 16.1.0 | `1 passed; 0 failed`,19.78 秒,包含 6.9 秒下载 |
113-
| 正式 workspace Linux LLVM 22.1.8 / libc++ | `1 passed; 0 failed`,6.03 秒 |
114-
| 模块入口适配后的隔离冷安装及 GCC/LLVM | 各 `1 passed; 0 failed`,GCC 19.97 秒、LLVM 6.05 秒;507 个原始文件及生成入口的两处替换均已逐字节核对 |
115-
| 正式 workspace GCC 增量 | `1 passed; 0 failed`,0.15 秒,构建 0.03 秒 |
116-
| 独立普通消费工程 | `/tmp/quill-implementation/consumer` 指向正式 checkout,`mcpp run --cache off` 实际编译、链接、运行上述日志断言,退出 0 |
117-
| 冷安装 | 隔离工程、正式 workspace、普通消费工程分别实际下载和安装;不将 `--cache off` 本身当作重装证据 |
118-
| 安装文件比较 | 507 个上游文件内容不变,仅新增带两处模块入口适配的 `.cppm` |
119-
| Lua 语法与三平台 xpkg 解析 | 通过,三平台均解析为 1 个 source、1 个 include 根 |
120-
| 镜像 URL、包身份、保留 namespace | 新描述符通过对应 lint |
121-
| 跨包引用、三平台版本一致性、重复版本 | 全仓对应 lint 通过 |
122-
| CI 选择规则 | 描述符全名匹配唯一 `quill-module` 成员;新增 workspace 成员及测试路径也命中现有规则 |
123-
| diff 空白检查 | 通过 |
124-
125-
临时工具、独立消费工程及日志位于 `/tmp/quill-implementation/`;原归档和宿主探测材料位于 `/tmp/quill-spec-G8O9Nt/`,不是长期源码依赖。
126-
127-
## 6. 未验证与发布边界
128-
129-
首次 PR CI 的 Windows 构建运行通过;macOS ARM 的失败由上述两处模块入口适配处理,最终验收以最新提交的 CI 结果为准。三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。
130-
131-
后续三平台发布前应让 macOS/Windows 运行同一成员;若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应先保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。
127+
## 6. 验证边界
128+
129+
上游仍将模块标为实验性。当前覆盖 Linux x86_64、macOS ARM 和 Windows MSVC ABI 工具链;未验证 MinGW、跨 DLL、完整 Quill 文本头与模块混用,以及全部可选 sink/codec/metrics,不承诺性能指标。未上传 CN 镜像。
130+
131+
升级上游或工具链时应重新执行同一套跨平台测试。若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。

‎.github/workflows/validate.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ jobs:
531531
# release that republished an old tag would leave no other trace.
532532
- name: Lint duplicate versions
533533
run: lua5.4 tests/check_duplicate_versions.lua pkgs/*/*.lua
534+
- name: Check Quill install hook boundaries
535+
run: lua5.4 tests/check_quill_install.lua
534536
# The compatibility measurement's classifier decides a PUBLISHED figure
535537
# -- which members "run", which "build", and which were correctly
536538
# "refused" -- and the only thing that exercised it was the four-hour

‎tests/check_quill_install.lua‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
-- 本检查以最小模块片段验证真实安装钩子的匹配边界,不访问网络或改动安装目录
2+
local descriptor = arg[1] or "pkgs/o/odygrd.quill.lua"
3+
local guard = "#if !defined(__INTEL_COMPILER)\n"
4+
local declaration = "export module quill;\n"
5+
local fixture = "module;\n" .. guard .. "#endif\n" .. declaration
6+
7+
local function run(content)
8+
local output, output_path
9+
local mutations = 0
10+
local function mutate() mutations = mutations + 1 end
11+
local env = setmetatable({
12+
import = function(name) assert(name == "xim.libxpkg.pkginfo") end,
13+
pkginfo = {
14+
version = function() return "13.0.0" end,
15+
install_dir = function() return "test-prefix" end,
16+
},
17+
path = { join = function(...) return table.concat({...}, "/") end },
18+
io = {
19+
readfile = function(path)
20+
assert(path == "quill-13.0.0/src/quill.cc")
21+
return content
22+
end,
23+
writefile = function(path, value)
24+
output_path, output = path, value
25+
mutate()
26+
end,
27+
},
28+
os = { tryrm = mutate, mkdir = mutate, mv = mutate },
29+
}, { __index = _G })
30+
assert(loadfile(descriptor, "t", env))()
31+
local ok, result = pcall(env.install)
32+
return ok, result, output, output_path, mutations
33+
end
34+
35+
local ok, result, lf, path = run(fixture)
36+
assert(ok and result == true, "LF input must install")
37+
assert(path == "quill-13.0.0/src/quill.cppm", "module interface output path")
38+
local crlf_ok, crlf_result, crlf = run((fixture:gsub("\n", "\r\n")))
39+
assert(crlf_ok and crlf_result == true and crlf == lf, "CRLF and LF must generate identical output")
40+
assert(not lf:find("\r", 1, true), "generated content must use normalized newlines")
41+
assert(lf:find("defined(__i386__) || defined(__x86_64__)", 1, true), "x86 headers need an architecture guard")
42+
local module_position = assert(lf:find(declaration, 1, true))
43+
for _, header in ipairs({ "<mach/thread_act.h>", "<unistd.h>", "<fcntl.h>", "<charconv>" }) do
44+
local position = assert(lf:find("#include " .. header, 1, true), "missing global header " .. header)
45+
assert(position < module_position, "system headers must precede the module declaration")
46+
end
47+
48+
local function rejects(content, expected)
49+
local accepted, message, _, _, mutations = run(content)
50+
assert(not accepted and tostring(message):find(expected, 1, true), expected)
51+
assert(mutations == 0, "invalid input must fail before writing or replacing installed files")
52+
end
53+
54+
rejects(nil, "cannot read")
55+
rejects("module;\n" .. guard .. "#endif\n", "expected exactly one module declaration")
56+
rejects(fixture .. declaration, "expected exactly one module declaration")
57+
rejects("module;\n" .. declaration, "expected exactly one patch match")
58+
rejects(fixture .. guard .. "#endif\n", "expected exactly one patch match")
59+
print("Quill install hook: LF/CRLF and five invalid-input cases passed")

0 commit comments

Comments
 (0)