Skip to content

Commit f457ec8

Browse files
committed
fix: 适配 Quill 模块的 macOS ARM 构建
1 parent 1dfe10e commit f457ec8

4 files changed

Lines changed: 28 additions & 7 deletions

File tree

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ odygrd = { path = "/home/helan/community/mcpp-community/mcpp-index" }
5858
| Linux 链接 | `ldflags = { "-pthread" }` |
5959
| 三平台下载 | 相同版本、归档和摘要,使用纯字符串 GLOBAL URL |
6060

61-
安装钩子检查源文件可读且恰有一条 `export module quill;`,再按字节复制为同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。逐文件比较确认 507 个上游文件内容均未改变,唯一新增文件是与原入口字节一致的 `.cppm`,保留了 CRLF。
61+
安装钩子检查源文件可读且恰有一条 `export module quill;`,以其内容生成同目录 `src/quill.cppm`。原 `.cc` 保留但不加入编译源集。507 个上游文件内容保持不变,适配仅作用于新增的 `.cppm`,保留 CRLF。
62+
63+
macOS ARM 的 PR CI 暴露两处上游模块入口问题,安装钩子执行两项精确替换,匹配次数不是一次即失败:
64+
65+
- x86 intrinsic 包含增加 x86 目标架构条件。Clang 在 ARM 上也能找到 `x86gprintrin.h`,仅靠 `__has_include` 会触发无效汇编约束和不存在的 x86 builtin。
66+
- 在 global module fragment 中为 Apple 预包含 `mach/mach_error.h`、`mach/thread_act.h`、`mach/thread_policy.h`。否则 Mach 类型在全局模块和 Quill 模块中重复归属,编译报错。
67+
68+
失败证据见 [PR CI 的 macOS job](https://github.com/mcpplibs/mcpp-index/actions/runs/37198494639/job/111425198293)。适配不修改 Quill 头文件、导出列表或日志实现。
6269

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

@@ -104,10 +111,11 @@ export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86
104111
| 隔离临时索引安装及 GCC/LLVM 测试 | 各 `1 passed; 0 failed` |
105112
| 正式 workspace Linux GCC 16.1.0 | `1 passed; 0 failed`,19.78 秒,包含 6.9 秒下载 |
106113
| 正式 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 个原始文件及生成入口的两处替换均已逐字节核对 |
107115
| 正式 workspace GCC 增量 | `1 passed; 0 failed`,0.15 秒,构建 0.03 秒 |
108116
| 独立普通消费工程 | `/tmp/quill-implementation/consumer` 指向正式 checkout,`mcpp run --cache off` 实际编译、链接、运行上述日志断言,退出 0 |
109117
| 冷安装 | 隔离工程、正式 workspace、普通消费工程分别实际下载和安装;不将 `--cache off` 本身当作重装证据 |
110-
| 安装文件比较 | 507 个上游文件内容不变,仅新增字节一致的 `.cppm` |
118+
| 安装文件比较 | 507 个上游文件内容不变,仅新增带两处模块入口适配的 `.cppm` |
111119
| Lua 语法与三平台 xpkg 解析 | 通过,三平台均解析为 1 个 source、1 个 include 根 |
112120
| 镜像 URL、包身份、保留 namespace | 新描述符通过对应 lint |
113121
| 跨包引用、三平台版本一致性、重复版本 | 全仓对应 lint 通过 |
@@ -118,6 +126,6 @@ export MCPP_VENDORED_XLINGS=/tmp/quill-implementation/mcpp-2026.10.1.2-linux-x86
118126

119127
## 6. 未验证与发布边界
120128

121-
macOS、Windows 尚未实际构建运行,三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。
129+
首次 PR CI 的 Windows 构建运行通过;macOS ARM 的失败由上述两处模块入口适配处理,最终验收以最新提交的 CI 结果为准。三平台描述符解析不等于运行验收。本节记录本地验证边界,跨平台结果以 PR CI 为准;未上传 CN 镜像。上游仍将模块标为实验性;本次不承诺所有 sink/codec/metrics、跨 DLL、完整文本头混用或性能指标。
122130

123131
后续三平台发布前应让 macOS/Windows 运行同一成员;若需要超出模块入口的小范围适配、改动日志实现或 mcpp 引擎,应先保留失败复现并重新审查范围,不以跳过平台或静默改成头文件包代替验收。

‎docs/descriptor-examples.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ in the [root README](../README.md#reference-examples).
4848
| 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) |
4949
| 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) |
5050
| 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 `<algorithm>` 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) |
51-
| 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) |
51+
| 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) |

‎docs/zh/descriptor-examples.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@
4646
| 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 官方库) |
4747
| 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` 就是这条的回归)|
4848
| C++23 module,上游 CPU 分区 | [`taskflow.taskflow`](../../pkgs/t/taskflow.taskflow.lua)(Taskflow 4.1.0,`import tf;`;复用上游四个 CPU 模块单元,排除提供同名主模块的 CUDA 入口。安装钩子逐项校验匹配次数:移除两个不存在的导出,将总头文件及版本导出移至 core 并优先导入 core 以兼容 GCC,为 utility 补 `<algorithm>` 以兼容 libc++。仅适配三个模块文件,头文件和调度实现保持上游原样,无独立 fork;宏不随模块导出,版本查询使用 `tf::version()`) |
49-
| 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 身份、工作线程输出、格式化和过滤) |
49+
| 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 身份、工作线程输出、格式化和过滤) |

‎pkgs/o/odygrd.quill.lua‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,21 @@ function install()
5151
local content = assert(io.readfile(source), "odygrd.quill: cannot read " .. source)
5252
local _, count = content:gsub("export module quill;", "")
5353
assert(count == 1, "odygrd.quill: expected exactly one module declaration")
54-
-- Clang 通过接口扩展名识别模块,副本保留上游内容和换行
55-
os.cp(source, path.join(wrap, "src/quill.cppm"))
54+
local function patch(before, after)
55+
local pattern = before:gsub("(%W)", "%%%1")
56+
local patched, matches = content:gsub(pattern, function() return after end)
57+
assert(matches == 1, "odygrd.quill: expected exactly one patch match")
58+
content = patched
59+
end
60+
61+
-- Clang 的资源目录在 ARM 上也含 x86 头,存在性检查不能代替目标架构判断
62+
patch("#if !defined(__INTEL_COMPILER)\r\n",
63+
"#if !defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))\r\n")
64+
-- Mach 声明属于系统全局模块,须在 Quill 的模块声明前完成包含
65+
patch("export module quill;\r\n",
66+
"#if defined(__APPLE__)\r\n#include <mach/mach_error.h>\r\n#include <mach/thread_act.h>\r\n#include <mach/thread_policy.h>\r\n#endif\r\n\r\nexport module quill;\r\n")
67+
-- Clang 通过接口扩展名识别模块,原始入口保留在归档树中
68+
io.writefile(path.join(wrap, "src/quill.cppm"), content)
5669

5770
local prefix = pkginfo.install_dir()
5871
os.tryrm(prefix)

0 commit comments

Comments
 (0)