From a7a48c4b8a448210893f103055f49d9b4537e479 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 27 Sep 2026 12:40:24 +0800 Subject: [PATCH 1/3] 0.16.0: rules-qt relies on mcpp 2026.9.27.1 -- the host row carries the Linux C++ runtime, and a package with no Qt source gets no synthesised program - rules-qt's engine floor is 2026.9.27.1. The Qt fixtures state `cxx_runtime = "toolchain-coupled"` in their Linux host rows, `[target.x86_64-linux-gnu]` and `[target.aarch64-linux-gnu]`, instead of project-wide in `[build]`: a plain `mcpp build` now applies the host's own row (mcpp#704). macOS and the MSVC ABI keep mcpp's default, as before. - compile() no longer carries the 0.15.2 branch that silenced the missing-SDK report in a synthesised program with no Qt source: the engine synthesises no program for such a package (mcpp#715). The qt-import-only criterion asserts exactly that -- no build program runs and nothing reports a missing SDK. - docs/rules-qt.md and the README state the floor and both behaviours. Co-Authored-By: Claude Opus 5.5 --- .github/scripts/check-deps-and-qt.sh | 7 ++++--- README.md | 4 ++-- docs/deps.md | 2 +- docs/rules-qt.md | 8 ++++---- mcpp.toml | 2 +- rules/qt.cppm | 14 ++++---------- src/plugins.cppm | 2 +- tests/qt-consumer/mcpp.toml | 19 +++++++++++-------- tests/qt-import-only/mcpp.toml | 5 ++--- tests/qt-sdk-consumer/mcpp.toml | 13 +++++++++++-- tests/qt-widgets-consumer/mcpp.toml | 19 +++++++++++-------- 11 files changed, 52 insertions(+), 43 deletions(-) diff --git a/.github/scripts/check-deps-and-qt.sh b/.github/scripts/check-deps-and-qt.sh index 728b998..8320faf 100644 --- a/.github/scripts/check-deps-and-qt.sh +++ b/.github/scripts/check-deps-and-qt.sh @@ -333,10 +333,11 @@ qt_import_only() { rm -rf target mkdir -p target/ci "$MCPP" build 2>&1 | tee target/ci/build.log - grep -q 'build.mcpp running' target/ci/build.log || fail "mcpp synthesised no build program for the rule" + ! grep -q 'build.mcpp running' target/ci/build.log || + fail "mcpp synthesised a build program for a package with no Qt source (mcpp#715)" ! grep -q 'no Qt SDK' target/ci/build.log || - fail "a synthesised program with no SDK and no Qt source reported a missing SDK" - echo "ok: a package that enables rules-qt only for its module builds without a report" + fail "a package with no Qt source reported a missing SDK" + echo "ok: a package that enables rules-qt only for its module runs no build program and reports nothing" } case "${1:-}" in diff --git a/README.md b/README.md index 8caef87..624a8aa 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ module name the member declares, and configures it there. ```toml [build-dependencies.mcpp] -plugins = { version = "0.15.2", features = ["rules-spirv"], host-module = true } +plugins = { version = "0.16.0", features = ["rules-spirv"], host-module = true } ``` ```cpp @@ -52,7 +52,7 @@ families, and of how the engine routes a file to a rule, is in | `rules-cuda` | `mcpp.rules.cuda` | 2026.9.6.6 | Compiles CUDA (`*.cu`) through clang with an LLVM toolchain or nvcc with a GCC one. | [rules](docs/rules.md#rules-cuda) | | `rules-hip` | `mcpp.rules.hip` | 2026.9.6.6 | Compiles HIP (`*.hip`) with the project's clang on the NVIDIA platform. | [rules](docs/rules.md#rules-hip) | | `rules-metal` | `mcpp.rules.metal` | 2026.9.8.1 | Compiles `.metal` shaders into Metal libraries with the host's Xcode and deploys them beside the program. | [rules](docs/rules.md#rules-metal) | -| `rules-qt` | `mcpp.rules.qt` | 2026.9.26.2 | Runs `moc`, `uic`, `rcc` and Qt's Linguist tools as actions, links the Qt modules and places their runtime. | [rules-qt](docs/rules-qt.md) | +| `rules-qt` | `mcpp.rules.qt` | 2026.9.27.1 | Runs `moc`, `uic`, `rcc` and Qt's Linguist tools as actions, links the Qt modules and places their runtime. | [rules-qt](docs/rules-qt.md) | | `rules-slang` | `mcpp.rules.slang` | 2026.9.7.1 | Compiles Slang (`*.slang`) and embeds or places the result. | [rules](docs/rules.md#rules-slang) | | `rules-spirv` | `mcpp.rules.spirv` | 2026.9.6.6 | Compiles GLSL and HLSL shader stages to SPIR-V and embeds or places the result. | [rules](docs/rules.md#rules-spirv) | | `rules-swift` | `mcpp.rules.swift` | 2026.9.8.1 | Compiles a package's `.swift` sources into one module the C and C++ sources call. | [rules](docs/rules.md#rules-swift) | diff --git a/docs/deps.md b/docs/deps.md index a27827b..fb2a2d3 100644 --- a/docs/deps.md +++ b/docs/deps.md @@ -24,7 +24,7 @@ Module `mcpp.deps.archive`; engine floor: 2026.9.26.2. ```toml [build-dependencies.mcpp] -plugins = { version = "0.15.2", features = ["deps-vcpkg"], host-module = true } +plugins = { version = "0.16.0", features = ["deps-vcpkg"], host-module = true } ``` ```cpp diff --git a/docs/rules-qt.md b/docs/rules-qt.md index 0857025..03bf139 100644 --- a/docs/rules-qt.md +++ b/docs/rules-qt.md @@ -1,12 +1,12 @@ # `rules-qt` -`mcpp.rules.qt` runs Qt's code generators (`moc`, `uic`, `rcc`) and Linguist tools (`lupdate`, `lrelease`, `lconvert`) as build actions, links the Qt modules and places their runtime beside the program. The rule declares no SDK and pins no version: the project names the Qt it builds with, in `build.mcpp` or in its own `[xlings]` table. Module `mcpp.rules.qt`; engine floor 2026.9.26.2 (mcpp#702); from 0.13.0. +`mcpp.rules.qt` runs Qt's code generators (`moc`, `uic`, `rcc`) and Linguist tools (`lupdate`, `lrelease`, `lconvert`) as build actions, links the Qt modules and places their runtime beside the program. The rule declares no SDK and pins no version: the project names the Qt it builds with, in `build.mcpp` or in its own `[xlings]` table. Module `mcpp.rules.qt`; engine floor 2026.9.27.1 (mcpp#704, mcpp#715) from 0.16.0, 2026.9.26.2 (mcpp#702) before; from 0.13.0. ## Use ```toml [build-dependencies.mcpp] -plugins = { version = "0.15.2", features = ["rules-qt"], host-module = true } +plugins = { version = "0.16.0", features = ["rules-qt"], host-module = true } # The SDK and its version are the project's declaration. [target.'cfg(any(windows, linux, macos))'.xlings.workspace] @@ -59,7 +59,7 @@ mcpp::rules::qt::options o; o.root = "D:/Qt/6.11.1/msvc2022_64"; // or leave empty and set QT_ROOT_DIR ``` -A package that enables `rules-qt` only to import `mcpp.rules.qt`, and writes no `build.mcpp`, runs the program mcpp synthesises. When it has no SDK and no `.ui`, `.qrc` or `.ts` of its own, that program reports nothing (0.15.2; mcpp#715). +A package that enables `rules-qt` only to import `mcpp.rules.qt`, and has no `build.mcpp` and no `.ui`, `.qrc` or `.ts` of its own, gets no synthesised build program, so nothing reports a missing SDK for it (mcpp 2026.9.27.1, mcpp#715; 0.15.2 silenced the report in the plugin, 0.16.0 leaves it to the engine). ## Options @@ -95,4 +95,4 @@ A program packed by `mcpp pack` starts on a machine that has only its operating The rule names none of these libraries (0.15.0; 0.13.0 and 0.14.0 declared glib, zstd and zlib on Linux and served QtCore only). A Qt from elsewhere carries what its installer arranged. On Linux, QtNetwork additionally loads `libgssapi_krb5` and `libbrotlidec`, which the ecosystem does not publish yet. -Qt's official Linux build uses the shared libstdc++, so a Linux program states `[build] cxx_runtime = "toolchain-coupled"` (mcpp's docs/20) and builds with a gcc toolchain; under a libc++ toolchain the rule warns. The statement is project-wide because mcpp reads a `[target.]` table only when a target is named (mcpp#704). +Qt's official Linux build uses the shared libstdc++, so a Linux program states `cxx_runtime = "toolchain-coupled"` (mcpp's docs/20) in its Linux host rows, `[target.x86_64-linux-gnu]` and `[target.aarch64-linux-gnu]`, and builds with a gcc toolchain; under a libc++ toolchain the rule warns. A plain `mcpp build` applies the host's row since mcpp 2026.9.27.1 (mcpp#704); with an older engine the statement goes in `[build]`, which applies it on every platform. diff --git a/mcpp.toml b/mcpp.toml index f99e027..e0e30bf 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] name = "plugins" namespace = "mcpp" -version = "0.15.2" +version = "0.16.0" description = "Official mcpp build plugins: rule packages under mcpp.rules.*, build-time utilities under mcpp.tools.*, each member selected by a feature" license = "Apache-2.0" authors = ["mcpp-community"] diff --git a/rules/qt.cppm b/rules/qt.cppm index 67f4534..7d32896 100644 --- a/rules/qt.cppm +++ b/rules/qt.cppm @@ -362,16 +362,10 @@ inline bool compile(options opt = {}) { const sdk_source source = locate(opt); const auto& sdks = source.roots; if (sdks.empty()) { - // A package that enables `rules-qt` only to import this module, such as - // a library of build logic, and writes no `build.mcpp` gets the program - // mcpp synthesises, which calls compile() with the defaults. Without an - // SDK and without a `.ui`, `.qrc` or `.ts` of its own, that program - // asked for nothing, so it says nothing (0.15.2; mcpp#715). - std::error_code missing_ec; - if (!fs::exists(fs::path(mcpp::manifest_dir()) / "build.mcpp", missing_ec) - && detail::device(".ui").empty() && detail::device(".qrc").empty() - && detail::device(".ts").empty()) - return true; + // Reached by a build program that asked for Qt: since mcpp 2026.9.27.1 + // (mcpp#715) a package that enables `rules-qt` only to import this + // module, and has no `.ui`, `.qrc` or `.ts` of its own, gets no + // synthesised program at all. detail::warn(std::format( "{}: no Qt SDK{}. Nothing Qt-specific is planned. Name one with options::root or " "QT_ROOT_DIR, or declare a payload, which `mcpp build` provisions:\n" diff --git a/src/plugins.cppm b/src/plugins.cppm index 5e4c2ff..91014bc 100644 --- a/src/plugins.cppm +++ b/src/plugins.cppm @@ -49,7 +49,7 @@ export namespace mcpp::plugins { // // One package, one version: the number lives in mcpp.toml, and the CI step // `the collection states its own version` compares the two. -inline constexpr std::string_view version = "0.15.2"; +inline constexpr std::string_view version = "0.16.0"; } // namespace mcpp::plugins diff --git a/tests/qt-consumer/mcpp.toml b/tests/qt-consumer/mcpp.toml index 00821bc..55a805a 100644 --- a/tests/qt-consumer/mcpp.toml +++ b/tests/qt-consumer/mcpp.toml @@ -40,16 +40,19 @@ plugins = { path = "../..", features = ["rules-qt"], host-module = true } "xim:qt-base" = "6.11.1" [build] -# Qt's Linux libraries load the shared libstdc++ (`libstdc++.so.6`), so the -# program uses the toolchain's shared C++ runtime rather than embedding a second -# one (mcpp's docs/20). The statement is project-wide because mcpp reads a -# `[target.]` table only when a target is named (mcpp#704). Elsewhere -# mcpp reports what it delivers instead: on macOS the default, and under clang -# on the MSVC ABI the static CRT; a PE image resolves its imports per DLL, so -# Qt's DLLs keep their own CRT. -cxx_runtime = "toolchain-coupled" sources = ["src/*.cpp", "res/*.qrc", "*.ts"] [targets.qt-consumer] kind = "bin" main = "src/main.cpp" + +# Qt's Linux libraries load the shared libstdc++ (`libstdc++.so.6`), so on +# Linux the program uses the toolchain's shared C++ runtime rather than +# embedding a second one (mcpp's docs/20). The row is the host's own, which a +# plain `mcpp build` applies since mcpp 2026.9.27.1 (mcpp#704); macOS and +# the MSVC ABI keep mcpp's default. +[target.x86_64-linux-gnu] +cxx_runtime = "toolchain-coupled" + +[target.aarch64-linux-gnu] +cxx_runtime = "toolchain-coupled" diff --git a/tests/qt-import-only/mcpp.toml b/tests/qt-import-only/mcpp.toml index 2ce4777..4c741a2 100644 --- a/tests/qt-import-only/mcpp.toml +++ b/tests/qt-import-only/mcpp.toml @@ -1,8 +1,8 @@ # Fixture: a package that enables `rules-qt` only to import `mcpp.rules.qt`, # the shape of a library of build logic that other packages' build programs # import (GalTranslPP's `gpp.build`). It writes no `build.mcpp`, has no `.ui`, -# `.qrc` or `.ts`, and declares no Qt, so the program mcpp synthesises for it -# asks for nothing and reports nothing (0.15.2; mcpp#715). +# `.qrc` or `.ts`, and declares no Qt, so mcpp synthesises no build program +# for it and nothing reports a missing SDK (mcpp 2026.9.27.1; mcpp#715). [package] name = "qt-import-only" version = "0.1.0" @@ -19,7 +19,6 @@ import_std = false plugins = { path = "../..", features = ["rules-qt"], host-module = true } [build] -cxx_runtime = "toolchain-coupled" sources = ["src/*.cpp"] [targets.qt-import-only] diff --git a/tests/qt-sdk-consumer/mcpp.toml b/tests/qt-sdk-consumer/mcpp.toml index 165f4ce..2a91af9 100644 --- a/tests/qt-sdk-consumer/mcpp.toml +++ b/tests/qt-sdk-consumer/mcpp.toml @@ -24,10 +24,19 @@ plugins = { path = "../..", features = ["rules-qt"], host-module = true } "xim:qt-base" = "6.11.1" [build] -# Qt's Linux libraries load the shared libstdc++; see tests/qt-consumer. -cxx_runtime = "toolchain-coupled" sources = ["src/*.cpp"] [targets.qt-sdk-consumer] kind = "bin" main = "src/main.cpp" + +# Qt's Linux libraries load the shared libstdc++ (`libstdc++.so.6`), so on +# Linux the program uses the toolchain's shared C++ runtime rather than +# embedding a second one (mcpp's docs/20). The row is the host's own, which a +# plain `mcpp build` applies since mcpp 2026.9.27.1 (mcpp#704); macOS and +# the MSVC ABI keep mcpp's default. +[target.x86_64-linux-gnu] +cxx_runtime = "toolchain-coupled" + +[target.aarch64-linux-gnu] +cxx_runtime = "toolchain-coupled" diff --git a/tests/qt-widgets-consumer/mcpp.toml b/tests/qt-widgets-consumer/mcpp.toml index fcc68eb..ff968b6 100644 --- a/tests/qt-widgets-consumer/mcpp.toml +++ b/tests/qt-widgets-consumer/mcpp.toml @@ -31,16 +31,19 @@ plugins = { path = "../..", features = ["rules-qt"], host-module = true } # QtGui's runtime closure (libdbus, fontconfig, xcb, ...); on Windows, the VC++ # runtime its DLLs import. [build] -# Qt's Linux libraries load the shared libstdc++ (`libstdc++.so.6`), so the -# program uses the toolchain's shared C++ runtime rather than embedding a second -# one (mcpp's docs/20). The statement is project-wide because mcpp reads a -# `[target.]` table only when a target is named (mcpp#704). Elsewhere -# mcpp reports what it delivers instead: on macOS the default, and under clang -# on the MSVC ABI the static CRT; a PE image resolves its imports per DLL, so -# Qt's DLLs keep their own CRT. -cxx_runtime = "toolchain-coupled" sources = ["src/*.cpp", "ui/*.ui"] [targets.qt-widgets-consumer] kind = "bin" main = "src/main.cpp" + +# Qt's Linux libraries load the shared libstdc++ (`libstdc++.so.6`), so on +# Linux the program uses the toolchain's shared C++ runtime rather than +# embedding a second one (mcpp's docs/20). The row is the host's own, which a +# plain `mcpp build` applies since mcpp 2026.9.27.1 (mcpp#704); macOS and +# the MSVC ABI keep mcpp's default. +[target.x86_64-linux-gnu] +cxx_runtime = "toolchain-coupled" + +[target.aarch64-linux-gnu] +cxx_runtime = "toolchain-coupled" From 265375dea323328fbd546e76d99837eef29afdeb Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 27 Sep 2026 17:19:17 +0800 Subject: [PATCH 2/3] ci: consumers build with mcpp 2026.9.27.1 The release 0.16.0's rules-qt is written against: the host row carries the Qt fixtures' Linux C++ runtime (mcpp#704), and qt-import-only asserts that no build program is synthesised for a package with no Qt source (mcpp#715). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0917a1..e4fa593 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,12 @@ env: # directory, the `prepare` role with `output_dir` for an installation, a # stamp moved past the inputs that changed, and on Windows the imported DLLs # placed beside the program after the link. - MCPP_VERSION: 2026.9.26.2 + # + # 2026.9.27.1 IS WHAT 0.16.0's `rules-qt` NEEDS: a plain `mcpp build` + # applies the host's own `[target.]` row (mcpp#704), where the Qt + # fixtures state their Linux C++ runtime, and a package with no Qt source + # gets no synthesised build program (mcpp#715), which qt-import-only asserts. + MCPP_VERSION: 2026.9.27.1 # AN ENGINE BUILT FROM SOURCE, WHEN A DISPATCH NAMES ONE. # # Empty on every push and pull request, so the steps run the release above. From 72eecc96b66150d18c79f63e4799f237982d0bbf Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 27 Sep 2026 17:40:35 +0800 Subject: [PATCH 3/3] docs(agents): the GalTranslPP outlook and the mcpp 2026.9.27.1 ecosystem plan with its execution record Co-Authored-By: Claude Opus 5.5 --- ...-27-galtranslpp-upstream-vs-pr2-outlook.md | 123 ++++++ ...6-09-27-mcpp-2026.9.27.1-ecosystem-plan.md | 405 ++++++++++++++++++ 2 files changed, 528 insertions(+) create mode 100644 .agents/docs/2026-09-27-galtranslpp-upstream-vs-pr2-outlook.md create mode 100644 .agents/docs/2026-09-27-mcpp-2026.9.27.1-ecosystem-plan.md diff --git a/.agents/docs/2026-09-27-galtranslpp-upstream-vs-pr2-outlook.md b/.agents/docs/2026-09-27-galtranslpp-upstream-vs-pr2-outlook.md new file mode 100644 index 0000000..2c7c830 --- /dev/null +++ b/.agents/docs/2026-09-27-galtranslpp-upstream-vs-pr2-outlook.md @@ -0,0 +1,123 @@ +# GalTranslPP:PR2 与上游最新的差异,及后续构建优化的上限 + +日期:2026-09-27。 + +| 对象 | 提交 | 说明 | +|---|---|---| +| 共同基点 | `8a2c76a` | PR2 分叉处 | +| 上游最新 | julixian/GalTranslPP `e64f93e`(2026-09-27 03:45,"update build system") | 基点之后 2 个提交,48 个文件,+1319/−730 | +| PR2 | Sunrisepeak/GalTranslPP `43ee88f`(`ci/mcpp-plugins-deps-qt`) | 基点之后 15 个提交,21 个文件,+771/−567;plugins 0.15.2,CI run 36272623612 全绿 | + +## 1. 结论 + +1. **构建架构已趋同,依赖获取策略相反。** + - 上游采纳了 PR2 的构建结构:共享构建逻辑作为模块 `gpp.build` 导入,rules-qt 的翻译流程,deps-vcpkg 与 deps-cmake 的机制,以 `${mcpp.self} stage` 动作写出 Release 布局,runtime-stage。两边都删除了旧的 5 个头文件与 `qt-root.txt`。 + - 分歧在取得方式:上游要求开发者预装 vcpkg、CMake、Qt,并在源码中填写 Qt 路径;Python 解压、`Release.py`、`windeployqt` 保留为构建后的手动步骤。PR2 由 xlings 取得全部工具,构建本身完成部署。 +2. **差异规模大,PR2 不能直接变基。** 两边改动重叠 17 个文件;试合并产生 10 处冲突:4 个成员的 `mcpp.toml` 与 `build.mcpp`、`how-to-build.md`、`Release.py`。下一版应基于 `e64f93e` 重做,而不是合并 PR2。 +3. **上游有三处优于 PR2,应当吸收:** + - 插件经 `reexport = true` 只在 `gpp.build` 声明一次; + - Windows 专属的 defines、系统库、链接选项放进 `[target.windows.*]` 或按驱动生成; + - 源码中的 `#pragma comment(lib, …)` 已删除,系统库由清单声明。 +4. **上游复制了插件的约 508 行代码(`deps/{tools,vcpkg,cmake}.ixx`),暴露了插件的缺口。** 起因是 `deps-vcpkg`、`deps-cmake` 这两个 feature 无条件声明 `xim:vcpkg`、`xim:cmake`,只要启用就会下载。上游为了用本机工具,只能改用内部 feature `deps`(清单注释写明"消费方不应直接使用"),再在本地复制并修改这两个模块。复制的代码依赖插件内部接口(`absolute_from_root`、`deploy_after`、`link_libraries`),插件升级时可能失效。 +5. **本次新验证的事实:GalTranslPP 今天就能把所有声明集中到一处。** 见 §3。 + +## 2. 差异表 + +| 维度 | 上游 `e64f93e` | PR2 `43ee88f` | 更优 | +|---|---|---|---| +| 插件版本 | 0.15.1;只在 `gpp.build` 声明,`reexport = true` 转交各成员(1 处) | 0.15.2;根 `[workspace.dependencies]`、`gpp.build` 各写一次(2 处);GPPCLI、GPPGUI、Updater 与 `gpp.build` 各列 features | 上游 | +| 插件 features | `deps`(内部 feature)、`rules-qt` | `deps-vcpkg`、`deps-cmake`、`deps-archive`、`rules-qt` | PR2(公开接口) | +| vcpkg | 本机安装;`gpp-build.ixx` 中的 `vcpkg_root` 或 PATH;使用复制并修改的模块 | `xim:vcpkg`,可用 `options.root` 改用本机 | 各有取向 | +| CMake | 本机安装;`cmake_executable` 或 PATH | `xim:cmake` | 各有取向 | +| Qt 来源 | 必填:`qt_root = R"(D:\Qt\6.11.1\msvc2022_64)"`,为空或无效即报错;不读 `QT_ROOT_DIR` | `gpp::qt_root` → `QT_ROOT_DIR` → 成员声明的 `xim:qt-base` 6.11.1 | PR2 | +| Qt 运行时部署 | `deploy_plugins = {}`、`qt_languages = {}`;手动执行 `windeployqt` ×2 | 由构建部署 platforms/styles/imageformats 与 `qt_zh_CN.qm` | PR2(需维护者确认取向) | +| 嵌入式 Python | 手动解压 zip | `deps-archive` 在构建时解压 | PR2 | +| BaseConfig、SampleProject | `Release.py` 复制 | 构建部署,并写入 Release 布局 | PR2 | +| 7z.dll | 仓库内二进制 `3rdParty/7z.dll`(1.9 MB),`Release.py` 与 runtime-stage 复制 | `xim:7zip`,已从仓库删除 | PR2 | +| OpenCC 数据 | 构建复制到 `Release/<成员>/BaseConfig/opencc` | 构建部署到程序旁,并写入 Release 布局 | 相同(PR2 另支持 `mcpp run`) | +| Updater 发布 | Updater 自身的构建程序写入 GPPGUI(`Updater.exe`)与 GUICORE(`Updater_new.exe`) | GPPGUI 取工具边产物 `dep_bin("gpp.updater")` 再复制 | 上游(不依赖宿主工具产物,避开 mcpp#711) | +| Windows 专属设置 | 各成员 `[target.windows.build]`;`/DEBUG`、`/OPT` 按驱动在代码中生成;runtime-stage 在 `[target.windows.build-dependencies]` | defines 放在 `[workspace.build]`,所有目标通用;`-Wl,/DEBUG` 写在 profile;runtime-stage 无条件声明 | 上游 | +| 源码 | 删除 `#pragma comment`;`Tool.cpp` 的 `#ifdef` 改为包住整个函数;Updater 改用 `QCoreApplication` 与 `import boost`;GUI 的 GIL 释放移出 try;ElaWidgetTools 子模块更新 | 未改源码 | 上游 | +| vcpkg 目录 | `vcpkg-scripts/{ports,triplets}`,`install_root` 显式 | 原 `ports/`,默认 `install_root` | 上游(目录更清晰) | +| `mcpp run` / `mcpp pack` | 程序旁缺少 BaseConfig 与 Qt 插件,不能直接运行或打包 | 可用 | PR2 | +| 文档 | 手动安装 8 项工具,填写路径,构建后 3 步 | 手动安装 git、xlings、VS Build Tools;`> 注:` 说明可配置项 | PR2 | +| CI | 无 | 验证工作流(三道检查) | PR2 | +| 仓库内构建代码 | 1139 行(其中复制的插件代码 508 行,`Release.py` 43 行) | 741 行 | PR2 | +| 首次构建所需手动操作 | 约 13 步 | 4 步 | PR2 | + +## 3. 本次验证:声明可集中到 `gpp.build` + +**探针**:一个工作区,成员 `app` 以 `host-module` 依赖一个非成员的路径包 `shared`(对应 `gpp.build`)。mcpp 2026.9.26.2,plugins 0.15.2。工程位于 `scratchpad/reexport-probe`。 + +| `shared` 中的声明 | 成员构建程序观察到的结果 | +|---|---| +| `mcpp.plugins = { …, features = ["deps-archive", "rules-qt"], host-module = true, reexport = true }` | 成员可以 `import mcpp.rules.qt` 与 `import mcpp.deps.archive` | +| `"probe.stage" = { path = …, tools = ["stage"], reexport = true }` | `dep_bin("probe.stage", "stage")` 返回宿主工具路径 | +| `[xlings.workspace] "xim:qt-base" = "6.11.1"` | `xpkg_dir("xim", "qt-base")` 与 `rules::qt::root()` 均为 `…/xim-x-qt-base/6.11.1` | +| 删除上一行(对照组) | 两者均为空 | + +**推论**:插件版本、features、runtime-stage、`xim:qt-base` 与 `xim:7zip` 都可以只在 `gpp.build` 声明一次。 + +- PR2 为 mcpp#713、#714 所做的逐成员声明因此不再需要。这两个 issue 对根清单仍然成立,但不再阻碍本项目。 +- 附带发现:`gpp.build` 声明了 Qt 之后,插件合成的规则程序会走"有 SDK"的分支。在 Linux 上它会输出 libc++ 提示;在 Windows 上预计不输出。0.15.2 的静默条件只覆盖"无 SDK",应推广为"无 build.mcpp 且无设备源时,不论有无 SDK 都直接返回"(见 §4 的 P2)。 + +## 4. 后续优化能做到什么地步 + +### 4.1 分层与前提 + +| 层 | 内容 | 前提 | 由谁完成 | +|---|---|---|---| +| S0 | 上游 `e64f93e` 现状 | — | — | +| S1 | PR2(参照) | mcpp 2026.9.26.2、plugins 0.15.2 | 已完成 | +| S2 | 基于 `e64f93e` 重做,保留其结构与本机工具模式;吸收 PR2 的自动部署(Python、BaseConfig、SampleProject、Qt 插件与翻译、7z);所有声明集中到 `gpp.build`;Qt 按 `qt_root` → `QT_ROOT_DIR` → `xim:qt-base` 的顺序选取 | 现有发布版即可 | 我方(GalTranslPP 新 PR) | +| S3 | S2 + plugins 0.16:P1、P2 | 插件发布 | 我方(mcpp-plugins) | +| S4 | S3 + 下一版 mcpp:#705、#707、#710、#711、#716、#712 | mcpp 发布;mcpp main 在 2026.9.26.2 之后只有一个文档提交,尚无相关修复 | mcpp 上游 | +| S5 | 长期:不再需要 VS Build Tools(让 vcpkg 通过 chainload 使用 mcpp 的 clang);action 可设环境与工作目录(#708);feature 可声明自身所需的工具(#709) | vcpkg triplet 工作、mcpp 能力 | 生态 | + +插件侧的两项: + +- **P1**:`deps-vcpkg`、`deps-cmake`、`deps-archive` 不再无条件声明工具载荷。工具按 `options` → 消费方声明的 `xim:*` → PATH 的顺序选取,与 rules-qt 选取 Qt 的方式一致。于是"注释掉一行"就能在自动模式与本机模式之间切换,上游复制的 508 行可以删除。 +- **P2**:合成的规则程序在没有 build.mcpp 且没有设备源时,一律直接返回。 + +### 4.2 各层对比 + +测量来源: +- "实测"均为 GitHub Windows runner、vcpkg 二进制缓存命中、fast-release,来自 run 36272623612; +- "预计"未经测量; +- S0 没有测量:CI 上需要 Qt 在线安装器,无法运行。 + +| 指标 | S0 上游 | S1 PR2 | S2 | S3 | S4 | S5 | +|---|---|---|---|---|---|---| +| 手动安装 | git、xlings、mcpp、VS BT、CMake、vcpkg(克隆+bootstrap)、Qt(在线安装器,需账户)、Python | git、xlings、mcpp、VS BT | 同 S0(本机模式);Qt 可改由 xim 取得 | 自动模式:同 S1;本机模式:同 S0 | 同 S3 | git、xlings、mcpp | +| 必须配置的路径 | 1(`qt_root`) | 0 | 0 | 0 | 0 | 0 | +| 构建后手动步骤 | 3(解压 Python、`Release.py`、`windeployqt` ×2) | 0 | 0 | 0 | 0 | 0 | +| 声明位置:插件版本 / Qt / 7zip / runtime-stage | 1 / 源码路径 / 仓库二进制 / 3 | 2 / 4 / 4 / 3 | 1 / 1 / 1 / 1 | 同 S2 | 同 S2 | 同 S2 | +| 仓库内构建代码 | 1139 行 | 741 行 | 约 1200 行(预计) | 约 700 行(预计) | 同 S3 | 同 S3 | +| 自动模式 ⇄ 本机模式 | 仅本机 | 仅自动(vcpkg/Qt 可指定路径,但仍会下载) | Qt 可切换;vcpkg/CMake 仅本机 | 全部可切换,注释掉一行即可 | 同 S3 | 同 S3 | +| `mcpp run` / `mcpp pack` | 否 | 是 | 是 | 是 | 是 | 是 | +| 首次 emit(包缓存为空) | 未测 | 258 s(实测) | 约同 S1 | 约同 S1 | 下降(预计):不再安装第二套 LLVM(#710),emit 不再完整构建宿主工具(#707);幅度待实测 | 同 S4 | +| GPPCLI / GPPGUI 编译 | 未测 | 6.2 / 18.2 min(实测) | 约同 S1 | 约同 S1 | 约同 S1 | 约同 S1 | +| 无改动的第二次构建 | 未测 | 0.8 min,无安装(实测) | 约同 S1 | 约同 S1 | 约同 S1 | 约同 S1 | +| 构建后再次 emit | 约同 S1(同样存在 Updater 工具边) | 62 s(实测) | 约同 S1 | 约同 S1 | 秒级(预计,同 profile 时):#705 修复后,Updater 不再被重建 | 同 S4 | +| 删除载荷后的恢复 | — | 需手动删除 provisioning 印记 | 同 S1 | 同 S1 | 自动重新安装(#716) | 同 S4 | +| 交叉构建时的 Updater | 正确(由自身构建发布) | 取宿主产物(#711) | 正确(沿用上游做法) | 正确 | 正确 | 正确 | + +### 4.3 上限的解读 + +- **S2 是现有发布版下的上限。** 首次构建只需 4 步(安装 xlings、安装 mcpp、克隆、`mcpp build`),所有版本只写在一处,同时保留上游偏好的本机工具模式。它唯一的缺口是 vcpkg 与 CMake 无法按需改由 xim 取得。 +- **S3 使两种模式对称。** 同一份仓库既能零配置构建,也能使用本机工具,切换方式是注释掉 `gpp.build` 中的一行;上游复制的插件代码可以全部删除。 +- **S4 只改变耗时与健壮性,不改变操作步骤。** 收益集中在 emit:构建后的 emit 从 62 s 降到秒级,首次 emit 省去第二套 LLVM。编译时间由源码规模决定,各层不变;要继续缩短,只能靠二进制缓存(vcpkg 已缓存)或减少 LTO/opt,与构建系统无关。 +- **S5 才能去掉 VS Build Tools。** 这取决于 vcpkg 的 triplet 能否通过 chainload 使用 mcpp 的 clang,以及 ElaWidgetTools 的 CMake 构建能否同样改用 clang,不在短期范围内。 + +## 5. 建议的下一步 + +1. **GalTranslPP 新 PR(S2)**: + - 从 `e64f93e` 开分支,保留上游结构、源码修正与 `reexport`; + - 把插件、runtime-stage、`xim:qt-base`、`xim:7zip` 集中到 `gpp.build`; + - Qt 选取顺序:`qt_root` 为空时交给 rules-qt 的默认顺序; + - 迁入 PR2 的自动部署,删除 `Release.py`; + - `how-to-build.md` 以 `> 注:` 并列两种模式; + - 在 Windows CI 上实测。 + - 需要与维护者确认:Qt 插件与 `qt_zh_CN` 是否由构建部署(上游当前明确置空),以及默认采用哪种模式。 +2. **plugins 0.16(S3 前提)**:实现 P1 与 P2,并新增 fixture:只使用本机 vcpkg/CMake 时不下载 xim 工具;无设备源的包在有 SDK 时保持静默。 +3. **mcpp**:S4 所需的修复均已有 issue(#705、#707、#710、#711、#712、#716),无需新提。可以补一条观察:一个宿主模块包含多个模块单元时,需要手动把无内部依赖的模块指定为 `lib.path`(见上游 `gpp-build/mcpp.toml` 的注释)。它是否提成 issue,待 S2 实测后决定。 diff --git a/.agents/docs/2026-09-27-mcpp-2026.9.27.1-ecosystem-plan.md b/.agents/docs/2026-09-27-mcpp-2026.9.27.1-ecosystem-plan.md new file mode 100644 index 0000000..04d341a --- /dev/null +++ b/.agents/docs/2026-09-27-mcpp-2026.9.27.1-ecosystem-plan.md @@ -0,0 +1,405 @@ +# mcpp 2026.9.27.1 生态闭环方案:云端交接的承接与 prepare.cppm 的架构拆分 + +日期:2026-09-27。 + +依据: +- 云端会话 session_01MajuX4J8ewFeWvZJjzRJt5 的任务报告 `HANDOVER-2026-09-27.md` 及其事件日志; +- 协助会话 session_014ZdaYzeVm2jHDPvpDgpGrw 的事件日志; +- 本地对四个 PR 的构建、测试与只读审查; +- 本地对 prepare.cppm 拆分的编译实验。 + +## 0. 目标与范围 + +目标沿用云端会话的 /goal: +- **修复范围:** mcpp-community/mcpp #704 至 #716,openxlings/xlings #620、#621。 +- **PR 与 CI:** 每个仓库一个 PR(除非必要不拆分,标题带版本号),CI 全部通过。 +- **评审:** 自我评审,包括生态级评审。 +- **发布:** 包括 GitCode 资源(本地 gtc),并登记到 mcpp-index 与 xim-pkgindex。 +- **真实验证:** 在 `xlings subos` 沙箱中进行,mcpp 使用 CN 镜像;验证通过后评论并关闭 issue。 +- **文档:** 规范与文档采用学术、简洁、陈述句风格,文档与代码注释不使用表情符号。 + +本方案新增一项:把 `src/build/prepare.cppm` 按架构拆分,每个文件不超过 2,000 至 3,000 行(§4)。 + +## 1. 已核实的现状 + +### 1.1 四个 PR + +| 仓库 | PR(fork 内) | 提交 | 本地核实结果 | +|---|---|---|---| +| libxpkg | speak-agent/libxpkg#1 | 6a14dd4,基于上游 main 385a1e9 | revision 字段对各种资源形态统一读取,默认 0,双向兼容。缺口:`resolve_resource()` 返回的 `ResolvedResource` 不含 revision(`src/xpkg-compat.cppm:24-30`),经 `ref` 别名的版本无法通过公开接口得到正确修订号 | +| xim-pkgindex | speak-agent/xim-pkgindex#1 | d43caff,基于上游 main 4c1dc850 | `check-revision.lua` 本地运行 PASS(比较 4 个已发布条目,校验 5 个,1 个配方变更)。fork CI 的 4 项失败均因 `glibc-2.44.3-r1` 资源 404,其余检查全部通过 | +| xlings | speak-agent/xlings#1 | 9b4ea10,基于 b4324f7;上游 main 为 41d5c19 | 变基无冲突(上游多出的提交只改两个文档)。`mcpp.lock` 的 `mcpplibs.xpkg` 条目沿用了 0.0.57 的 hash,必须在 libxpkg 0.0.58 发布后重新生成。另有两个健壮性缺口,见 §2 D4 | +| mcpp | speak-agent/mcpp#1 | bba733a(4 个提交),基于上游 main 52549fb | 见 §1.2 | + +### 1.2 mcpp bba733a 的本地验证 + +bba733a 在云端从未构建。本地以 mcpp 2026.9.26.2、GCC 16.1 构建。 + +| 项 | 结果 | +|---|---| +| `mcpp build` | 通过,99 s | +| `mcpp test`(根) | 129 通过,0 失败。云端唯一的失败项 `ScaffoldTransaction.ReadWriteAndCopyFailuresRollbackCompletely` 由 root 运行导致,非 root 下通过 | +| `mcpp test -p ` | versioning、platform、manifest、toolchain-model、buildmcpp 通过;libs、log、source-kind、dyndep 没有测试 | +| e2e 788、798、799、800、801、803 | 通过 | +| e2e 802(#704) | 在默认工具链为 llvm@22.1.8 的机器上失败:测试断言 `NEEDED libstdc++`,而清单未固定工具链。在测试清单中写入 `[toolchain] default = "gcc@16.1.0"` 后三项断言全部通过,说明 #704 的修复正确,缺陷在测试本身 | + +逐 issue 审查的结论:12 个 issue 的修复均对应 issue 的原始描述,未发现错误(#706 是已合入上游的文档提交,不在本 PR 中)。需要处理的是以下三点: +- **#714 是行为变更:** 未解析的 `workspace = true` 由静默变为空版本,改为加载时报错。在 mcpp-plugins、mcpp-index、GalTranslPP、mcpp-language-server 中均未发现依赖旧行为的清单。 +- **#710 覆盖不足:** 非成员宿主工具包的工具链路径只有单元测试,没有 e2e。 +- **note 代码改名:** `MCPP_BUILD_DATABASE_HOST_TOOL_UNBUILT` 改为 `..._DEFERRED`,没有兼容别名;mcpp-language-server 不读取这两个代码。 + +与已发布的 xlings 2026.9.26.x 搭配时,所有新路径都能正确回退: +- 没有 `install_targets` 事件时,走版本文法选择(`xlings.cppm:976-977`); +- 没有 `.xpkg-install.json` 时,按 revision 0 处理(`xlings.cppm:1063-1070`)。 + +因此 mcpp 在技术上可以先于 xlings 发布。但 mcpp 固定的 xlings 版本(`kXlingsVersion`)需要指向已发布的 2026.9.27.1,所以发布顺序仍以 §3 为准。 + +打包时导出 `LOCPATH` 与 `GCONV_PATH` 的实现在 PR 头 `src/pack/pack.cppm:1056-1058`。审查中"未实现"的结论查的是本地 main,已更正。 + +### 1.3 环境 + +- **磁盘:** 根分区原有 3.6 GB 可用。清理本会话临时目录后为 12 GB,仍不足以宽裕地构建 glibc(新建 subos,外加源码与构建树)。可清理的候选目录见 §6 M1,由用户决定。 +- **云端会话:** `RemoteTrigger get_run_log` 可读取云端会话的事件日志(只读,工具输入输出被截断)。无法向云端会话发消息,也无法取回其沙箱中的文件。 +- **临时分支:** speak-agent/mcpp-index 上的 `handover/2026-09-27` 已删除。 + +## 2. 决策(待 review) + +| # | 决策 | 理由 | +|---|---|---| +| D1 | mcpp 只开一个 PR,版本为 2026.9.27.1,同时包含缺陷修复、特性与 prepare.cppm 的架构拆分(§4)。逐字节对照的基线取拆分前的 PR 头构建 | 用户决定(2026-09-27 review):每个仓库一个 PR | +| D2 | glibc 2.44.3-r1 资源在本地重新构建,不下载云端产物 | 用户决定。构建不可复现:约 280 个目标文件的调试信息含构建路径。重建后 sha256 必然变化,只需修改 `pkgs/g/glibc.lua:261`,测试以正则校验 sha256,无需改动 | +| D3 | libxpkg 在同一 PR 中为 `ResolvedResource` 增加 revision,并补测试 | revision 应能通过库的公开解析接口取得,而不是要求每个客户端自行遍历 `ref` | +| D4 | xlings 在同一 PR 中补齐重装的中断恢复:(1) 把旧载荷移入 `/stale/` 之前,先写入记录原路径的标记;(2) 每次安装开始时处理 `stale/` 中所属进程已不存在的条目:原路径缺失则移回,否则删除。它同时覆盖进程被强制终止的情况,以及"移出旧目录"与"重建空目录"之间被中断的窗口。Windows 上占用中的载荷无法移动时,报告失败并点名占用的路径 | 当前实现只在同一进程内以析构函数回滚,进程被强制终止时旧载荷永久留在 `stale/`,且没有清理路径。这是本 PR 新引入的代码,缺口应在同一 PR 中闭合 | +| D5 | #714 的报错保留,在 CHANGELOG 中标为行为变更 | 旧行为会静默产生空版本依赖,是缺陷;没有已知消费方依赖它 | +| D6 | mcpp 同一 PR 内补充三项:e2e 802 固定 gcc 工具链;新增非成员宿主工具包工具链的 e2e(#710);CHANGELOG 注明 note 代码改名 | §1.2 的验证与审查结果 | +| D7 | mcpp-plugins 下一版为 0.16.0 | 引擎最低版本升到 2026.9.27.1,属于兼容性变更,按次版本号发布 | +| D8 | 合并与发布由本会话完成:libxpkg、xlings、xim-pkgindex、mcpp-index、mcpp-plugins。mcpp 在 CI 全绿后合并前再向用户确认一次 | 用户决定(2026-09-27 review):"一起处理" | + +## 3. 工作分解与依赖 + +### 3.1 依赖图 + +``` +A1 libxpkg 修正 ─► B1 libxpkg 上游 PR ─► 合并 ─► tag 0.0.58 ─► GitCode ─► mcpp-index 登记 + │ +A2 xlings 变基与修正 ──────────────────────────────────────────────────────┴─► B2 重新生成 mcpp.lock ─► xlings 上游 PR ─► 发布 2026.9.27.1 +A3 glibc r1 本地构建 ─► 上传 GitHub 与 GitCode ─► B3 xim-pkgindex 上游 PR ─► 合并 │ +A4 mcpp 修正 ─► B4a mcpp 分支推到上游并开 PR(CI 以 xlings 2026.9.26.x 运行) │ + └─► C0 plugins 以该分支做预验证 ▼ + B4b 版本钉改为 2026.9.27.1 ─► CI ─► 用户合并与发布 + │ +C1 plugins 0.16.0 ◄──────────────────────────────────────────────────────────────────────────────────┘ +D 沙箱真实验证 ─► E 评论并关闭 issue;GCC issue +P prepare.cppm 拆分(并入 mcpp 的同一 PR,在 B4a 之前完成) +``` + +A1 至 A4 可以并行。B3 不依赖 B2:旧版 xlings 忽略 revision 字段,行为不变。 + +### 3.2 阶段 A:修正现有 PR + +| 项 | 内容 | 验收 | +|---|---|---| +| A1 libxpkg | D3 | 本地测试通过 | +| A2 xlings | 变基到 41d5c19;D4 的中断恢复与 e2e(模拟"进程终止后留下的条目"和"原路径缺失"两种情况);CHANGELOG 注明 `install_summary.success` 只计本次实际安装的数量 | 本地单元测试与 `install_revision_test.sh` 通过 | +| A3 glibc | 建立 `gfxbuild` subos(`xlings subos new gfxbuild`,安装 gcc make ninja python cmake zlib expat);运行 `bash .agents/tools/graphics/build-glibc.sh 2.44 2.44.3 1`;更新 `glibc.lua:261` 的 sha256;用 `publish.sh` 上传到 xlings-res/glibc,GitHub 用 Sunrisepeak 身份,GitCode 用 gtc;下载两端并用 `cmp` 核对 | 两端逐字节一致;fork CI 的 4 项转绿 | +| A4 mcpp | D6 三项 | 本地 `mcpp test` 与相关 e2e 通过 | + +### 3.3 阶段 B:上游 PR 与发布 + +**上游 PR 的开法:** 分支推到上游仓库后开 PR,以便上游 CI 与发布所需的密钥生效。上游 PR 开出后,关闭 fork 内对应的 PR。 + +**推送身份:** +- mcpp、mcpp-plugins 用 speak-agent; +- libxpkg、xim-pkgindex、mcpp-index 与 xlings-res(GitHub)用 Sunrisepeak; +- xlings 两个身份均可。 + +**B1 libxpkg:** +1. 推送到上游分支,开 PR; +2. CI 通过后合并; +3. 打 tag `0.0.58`(不带 `v`,沿用 0.0.45 之后的惯例); +4. GitHub tag 归档用 gtc 镜像为 GitCode `mcpp-res/xpkg` 的 `xpkg-0.0.58.tar.gz`,两端 sha256 一致; +5. 在 mcpp-index 的 `pkgs/x/xpkg.lua` 三个平台表中各加入 0.0.58; +6. 开 PR 并合并,等待 Publish Index Artifact 完成。 + +**B2 xlings:** +1. 以 mcpp 重新解析,生成 `mcpp.lock`; +2. 上游 PR,CI 通过后合并; +3. 手动触发 `release.yml`:自动完成构建、GitHub Release、索引发布和小资源镜像; +4. 大资源在本地用 `tools/mirror-latest.sh xlings` 上传 GitCode; +5. 合并机器人开出的 xim-pkgindex 版本 PR; +6. 按 2026.9.26.3 的发布后验证文档做抽查。 + +**B3 xim-pkgindex:** A3 完成后开上游 PR,CI 通过后合并。 + +**B4 mcpp:** +- **(a)** 修正后的分支推到 mcpp-community/mcpp 并开 PR。三平台 CI 在 xlings 2026.9.26.x 下运行,验证回退路径。 +- **(b)** xlings 2026.9.27.1 发布后,修改以下版本钉,`check_version_pins.sh` 负责检查: + - `src/xlings/xlings.cppm:114` 的 `kXlingsVersion`; + - `.github/workflows` 中的 release.yml 7 处、cross-build-test.yml 2 处、bootstrap-macos.yml、ci-linux-e2e.yml、ci-fresh-install.yml 3 处; + - `.github/actions` 中的 bootstrap-mcpp 与 setup-macos-llvm; + - 若 xlings 的一致性向量有变,刷新 `semver-vectors.tsv`。 +- **(c)** CI 全绿后交用户合并。推送 tag `v2026.9.27.1` 触发 `release.yml`,自动构建三平台、生成 release manifest、镜像到 xlings-res/mcpp(GitHub 与 GitCode),并开出 xim-pkgindex 版本 PR。最后合并该 PR。 + +### 3.4 阶段 C:mcpp-plugins 0.16.0 + +- **C0 预验证:** `ci.yml` 的 `workflow_dispatch` 接受 mcpp 的分支名。在 B4a 之后以该分支运行 plugins 全部 CI,mcpp 发布前就能发现生态回归。 +- **C1 内容:** + - 引擎最低版本改为 2026.9.27.1:`ci.yml:54`、README 功能表、`docs/deps.md` 与 `docs/rules-qt.md` 的版本说明。 + - 删除 `rules/qt.cppm:365-374` 的静默分支:#715 修复后,引擎不再为这种包合成程序。fixture `qt-import-only` 改为断言"没有构建程序运行",作为引擎行为的回归测试。 + - 四个 Qt fixture 的 `cxx_runtime = "toolchain-coupled"` 从 `[build]` 移到 `[target.x86_64-linux-gnu]` 与 `[target.aarch64-linux-gnu]`,同时成为 #704 的端到端验证。 + - `check-deps-and-qt.sh` 中七处 `--profile dev` 是 #705 的规避手段。逐处去掉,并在三平台上确认第二次构建不重新安装。 + - `docs/rules-qt.md:62、98` 中关于 #715、#704 的规避说明改写为现行行为。 +- **发布:** 按 mcpp-plugins 发布流程:tag、GitCode 镜像、mcpp-index 登记。 + +### 3.5 阶段 D:沙箱真实验证 + +**环境:** +1. `xlings subos new eco-0927`; +2. 命令一律经 `xlings subos use eco-0927 --sandbox --cmd ""` 执行; +3. 在沙箱内执行 `mcpp self config --mirror CN`,并确认 `MCPP_HOME` 位于沙箱内,不改动用户的 `~/.mcpp`。 + +| issue | 场景 | 通过标准 | +|---|---|---| +| #704 | e2e 802 的工程 | 宿主构建读取宿主三元组的配置行 | +| #705 | 路径包宿主工具,消费方嵌套在其目录中 | 第二次构建不重建工具 | +| #707 | 含未构建宿主工具的 `emit build-database` | 输出 note DEFERRED,不构建工具 | +| #708 | 带 env 与 cwd 的 action | 命令在指定环境和目录下运行 | +| #709 | 特性声明 tools | `dep_bin` 可见 | +| #710 | 工作区固定 llvm,宿主工具子构建 | 不安装第二套 LLVM | +| #711 | artifacts 边 | 发布物是按目标平台构建的产物 | +| #712 | `"xim:libglvnd" = "1.7"` | `xpkg_dir` 非空 | +| #713 | 根 `[xlings.workspace]` | 成员构建程序可见载荷 | +| #714 | 非成员 `workspace = true`;`sources = []` | 按名报错;不推断库目标 | +| #715 | 只为导入而启用 rules-qt 的包 | 不合成程序 | +| #716 | 删除载荷后构建 | 联网时重新安装,离线时按名报错 | +| xlings #620 | 先用 xlings 2026.9.26.3 安装 glibc(revision 0),再升级到 2026.9.27.1 并执行 `xlings install glibc` | 输出重装提示,载荷为 revision 1 | +| xlings #621 | 执行 `setlocale(LC_ALL, "C.UTF-8")` 与 `iconv -f UTF-8 -t GBK`;qt-demo 执行 `mcpp run` | 两项调用成功;Qt 不再输出 UTF-8 locale 提示 | + +**消费方验证:** GalTranslPP fork 以 mcpp 2026.9.27.1 与 plugins 0.16.0 运行 Windows CI,测量首次 emit、构建后再次 emit,并确认未安装 llvm@20.1.7。预期结果见 `2026-09-27-galtranslpp-upstream-vs-pr2-outlook.md` §4 的 S4 一栏。该验证只更新 fork 分支,不向上游提 PR;是否基于上游最新重做,另行决定。 + +### 3.6 阶段 E:收尾 + +1. 在各 issue 下评论验证方式与结果,然后关闭。 +2. 在 mcpp 仓库提交 GCC issue(先创建 `upstream` 标签),内容为 §4.3 的实验结论与最小触发条件,并附存档分支 `wip/prepare-split`。在 mcpp 之外缩小出独立复现后,报告 GCC bugzilla。 +3. 更新交付记录与记忆。mcpp 发布后不提交记录 PR(用户决定,2026-09-27):发布链与验证读数只写入本文件的执行记录与各 issue 的评论。 + +## 4. prepare.cppm 的架构拆分 + +### 4.1 现状 + +`src/build/prepare.cppm` 共 16,105 行: + +- **第 1 至 2,360 行:** 辅助函数与 23 个导出声明(`BuildContext`、`BuildOverrides`、`PlanNote`、`merge_conditional_config` 等)。 +- **第 2,362 至 16,102 行:** 一个函数 `prepare_build`,约 13,740 行,占全文件 85%。 + +`prepare_build` 内部约有 180 个顶层局部变量和数十个按引用捕获的 lambda,共用同一个栈帧。它的实际阶段如下: + +| 阶段 | 行 | 内容 | +|---|---|---| +| P0 | 2362-2857 | 清单与工作区解析 | +| P1 | 2857-3843 | 工具链规格、目标轴、`--target` 校验、L1 条件合并 | +| P2 | 3843-4970 | `resolve_target_toolchain` 闭包的定义;真正调用在第 9247 行 | +| P3 | 4970-5404 | 图构建前的 xlings 载荷 | +| P4 | 5404-8862 | 依赖图解析,约 3,460 行,含约 20 个相互递归的闭包与环检测 | +| P5 | 8866-9250 | 图确定后的工具链决议 | +| P6 | 9251-10067 | 特性激活与能力累积 | +| P7 | 10067-10866 | 宿主工具供给;第 10774 行递归调用 `prepare_build` | +| P8 | 10866-11186 | 依赖的 build.mcpp、能力绑定、版本下限 | +| P9-P10 | 11188-12517 | 目标侧解析、依赖链接形式 | +| P11-P12 | 12519-13801 | 扫描与校验、标准模块、指纹 | +| P13 | 13811-16102 | `BuildContext` 的填充:计划、汇编、Windows 资源、全局缓存、`mcpp.lock`、`resolution.json` | + +对外接口保持稳定:`prepare_build` 被 35 个文件使用,`BuildOverrides` 被 9 个文件使用。 + +### 4.2 存档方案 wip/prepare-split 的评估 + +存档方案从文件前部移出约 2,250 行,放进 5 个独立命名模块: +- context、manifest_merge:以 `export import` 转出; +- manifest_steps、target_env、provision:仅供内部使用。 + +`prepare.cppm` 仍有 13,870 行,`prepare_build` 原样未动。结论是否决,理由如下: + +1. **没有触及结构问题:** 规模问题在 `prepare_build` 这个函数本身,存档方案只移动了它之前的辅助代码。 +2. **扩大了公开接口:** 增加了 5 个公开模块名,单元测试已经直接导入其中之一(`mcpp.build.prepare.target_env`)。 +3. **在 GCC 16.1 下无法构建:** 见 §4.3。 + +### 4.3 GCC 16.1 的约束(本地实验) + +每项实验都是一次完整构建。 + +| # | 组织方式 | 编译器 | 结果 | +|---|---|---|---| +| E1 | 存档方案:5 个独立命名模块 | GCC 16.1 | 在 `src/main.cpp:5`(`import mcpp.cli;`)内部编译器错误 | +| E2 | 同 E1 | LLVM 22.1.8 | 构建成功,59 s | +| E3 | E1 的 5 个模块改为 `mcpp.build.prepare` 的接口分区,全部 `export import` | GCC 16.1 | 同一位置内部编译器错误 | +| E4 | bba733a 加一个几乎为空的接口模块:导入列表与 context 相同(13 个),由 prepare 导入 | GCC 16.1 | 同一位置内部编译器错误 | +| E5 | bba733a 把两个真实函数的定义移入实现单元 `module mcpp.build.prepare;` | GCC 16.1 | 构建成功,冷构建 158 s | +| E6 | E5 再加实现分区 `module mcpp.build.prepare:state;`:导入列表同 E4,只被实现单元导入 | GCC 16.1 | 构建成功;增量构建 2.8 s,导入方未重新编译 | + +结论: +- **触发条件是结构性的:** 在 `mcpp.build.prepare` 与其导入之间插入一层新的接口单元(独立命名模块或接口分区),GCC 16.1 就会在读取命名空间时崩溃,与接口单元的内容无关(E4)。 +- **可行的形式:** 实现单元,以及只被实现单元导入的实现分区,不进入导入方的接口依赖链,可以构建(E5、E6)。 +- **仅限 GCC:** 同类问题只出现在 GCC,LLVM 正常(E2)。 + +`src/project.cppm` 中为避开导入 `mcpp.xlings.address_set` 而复制 `package_key` 的做法,属于同一约束。 + +### 4.4 目标架构 + +**原则:** +1. **公开接口不变:** `mcpp.build.prepare` 仍是唯一的公开模块名,导出集合不变。 +2. **主接口只放声明:** 主接口单元只含导出类型、导出函数的声明和少量内联函数;不导入任何分区;不定义 `prepare_build`。 +3. **阶段是普通函数:** 每个阶段写成 `std::expected phase(PrepareState&)`,状态只以引用传递,不按值复制。 +4. **状态私有:** `PrepareState` 与阶段函数的声明放在实现分区 `:state` 中,只被实现单元导入,不进入主接口的编译产物。 +5. **定义在实现单元:** 各阶段的定义放在实现单元中。阶段之间只通过 `PrepareState` 通信,彼此不导入。 + +**文件划分**(行数为估计,均不超过 2,000 行): + +| 文件 | 单元 | 内容 | 约行数 | +|---|---|---|---| +| `src/build/prepare.cppm` | 主接口 | 导出类型、导出函数的声明(含默认参数) | 1,100 | +| `src/build/prepare/state.cppm` | 实现分区 `:state` | `PrepareState`、阶段函数声明、`GlobalConfigCache`、`FixupReporter` | 500 | +| `src/build/prepare/exports.cpp` | 实现单元 | 原前部导出函数的定义(条件合并、profile 解析等) | 1,200 | +| `src/build/prepare/driver.cpp` | 实现单元 | `prepare_build`:依次调用各阶段 | 200 | +| `src/build/prepare/manifest.cpp` | 实现单元 | P0、P1 | 1,500 | +| `src/build/prepare/toolchain.cpp` | 实现单元 | P2、P5(原为同一闭包,合为一个单元) | 1,600 | +| `src/build/prepare/xlings.cpp` | 实现单元 | P3 | 450 | +| `src/build/prepare/graph_load.cpp` | 实现单元 | `loadVersionDep`,git、路径与版本依赖的解析 | 1,700 | +| `src/build/prepare/graph.cpp` | 实现单元 | P4 的工作表引擎与环检测 | 1,700 | +| `src/build/prepare/features.cpp` | 实现单元 | P6 至 P8 | 1,950 | +| `src/build/prepare/target_side.cpp` | 实现单元 | P9、P10 | 1,330 | +| `src/build/prepare/scan.cpp` | 实现单元 | P11、P12 | 1,280 | +| `src/build/prepare/plan.cpp` | 实现单元 | P13:计划、汇编、Windows 资源 | 1,470 | +| `src/build/prepare/finish.cpp` | 实现单元 | 全局缓存、`mcpp.lock`、`resolution.json`、返回 | 830 | + +**导入关系:** +- 导入方只依赖主接口,与今天相同; +- 实现单元隐式导入主接口,并显式导入 `:state`; +- 实现单元彼此不导入,依赖深度为 2。 + +**附带收益(实测):** +- **今天:** 修改 `prepare_build` 中的一个字符串,prepare 的接口随之变化,会重新编译 prepare、12 个下游接口单元与 `main.cpp`,增量构建 71 s。 +- **拆分后:** 同样的修改只重新编译所在的实现单元并重新链接。E6 中的小单元为 2.8 s;按阶段文件的规模估计为 10 至 30 s。 + +mcpp 按接口内容决定下游是否重新编译:接口内容不变时,导入方不会重新编译。 + +### 4.5 实施步骤 + +每一步都能构建且保持行为不变: + +1. **基线:** 用拆分前的 PR 头(bba733a 加 A4 修正)构建的 mcpp,对一组固定工程生成 `resolution.json` 与 `emit build-database` 的输出,作为逐字节对照的金标准。工程覆盖:单包、工作区、交叉目标、宿主工具供给、git 依赖、预编译依赖。 +2. **引入 `PrepareState`:** 仍在同一文件中,把跨阶段的局部变量改为 `state.x`,闭包改为捕获 `state`。这一步风险最高,单独成为一个提交。 +3. **逐阶段提取为函数:** 仍在同一文件中,每个阶段一个提交,调用形式为 `if (auto r = phase(state); !r) return std::unexpected(r.error());`。 +4. **跨平台门槛:** 先把 `prepare_build` 移入 `driver.cpp`,并建立 `:state` 分区,此时尚未搬移任何阶段。以此推送一次,由三平台 CI 确认模块组织可行,再继续搬移。 +5. **逐个移动到实现单元:** 每次移动一到两个阶段,纯粹搬移代码。最后移出前部导出函数的定义。 +6. **收尾:** + - 若 `project.cppm` 的复制已无必要,恢复导入 `mcpp.xlings.address_set`; + - 加入行数检查:`src/build/prepare/` 下每个文件不超过 2,500 行,由 CI 执行。 + +**每一步的验证:** +- 以 GCC 16.1 自举构建; +- `mcpp test` 与按成员测试; +- 完整 e2e 套件 `tests/e2e/run_all.sh`; +- 与金标准逐字节对照。 + +第 2、4、5 步完成后,额外在三平台 CI 上构建。 + +**规模:** 预计约 16 个提交,并入 mcpp 2026.9.27.1 的同一 PR(D1)。 + +### 4.6 风险 + +| 风险 | 缓解 | +|---|---| +| 副作用顺序改变:安装、下载、输出交织在各阶段中,e2e 114 断言了扫描标记的输出顺序 | 只搬移代码、不重排语句;以完整 e2e 与金标准对照验证 | +| 第 10774 行的递归调用与全局缓冲区 `pending_flag_words_notes()`(第 2371 行清空) | `PrepareState` 按调用创建;全局缓冲区的问题只记录、不在本次修改,避免扩大范围 | +| 意外按值复制大型状态 | 阶段签名一律使用 `PrepareState&`,并删除 `PrepareState` 的拷贝构造 | +| 后续有人新增接口分区或独立模块,再次触发 GCC 缺陷 | 在 `prepare.cppm` 开头以注释写明 §4.3 的约束和 GCC issue 编号;CI 以 GCC 16.1 构建会立即暴露问题 | +| 另外两个平台的编译器对实现分区的支持:mcpp 在 Windows 上用 llvm@20.1.7(MSVC ABI)构建,在 macOS 上用 llvm@22.1.8 | 第 2 步后先加一个最小的 `:state` 分区,由三平台 CI 验证,再进行后续搬移;若某平台不支持,`:state` 退回为主接口中的非导出声明(E5 已验证这种形式) | + +### 4.7 其他超过 3,000 行的文件 + +以下文件留作后续,采用同样的方法: +- `modules/manifest/src/toml.cppm`(4,695 行); +- `src/build/ninja_backend.cppm`(4,059 行); +- `src/build/execute.cppm`(3,263 行)。 + +## 5. 风险 + +| 风险 | 缓解 | +|---|---| +| 磁盘不足,导致 glibc 构建或并行构建失败 | 开始 A3 前至少腾出 20 GB(§6 M1) | +| xlings 的 GitCode 大资源需要手动镜像,遗漏会使 CN 用户安装失败 | B2 的验收包括下载 GitCode 资源并核对 sha256 | +| mcpp 固定的 xlings 版本早于它发布到 GitHub 与 GitCode | B4b 在 B2 完成且 GitCode 核对通过之后才开始 | +| #714 的报错影响未知的第三方清单 | CHANGELOG 标为行为变更;报错信息点名具体条目与修正方法 | +| plugins 删除规避代码后,旧版 mcpp 的用户受影响 | 0.16.0 在 README 与 CI 中声明引擎最低版本;0.15.x 保持可用 | +| glibc revision 1 以 `/` 填充路径:setuid 程序中,可信目录与 `TZDIR` 的前缀比较不再匹配(行为是拒绝而非放行);ld.so 的诊断输出中会出现连续的 `/` | 写入 xim-pkgindex PR 的说明与 xpackage 规范的 glibc 条目;沙箱验证覆盖普通程序的 locale 与 gconv | +| 云端提交的代码只在本地构建过一次 | 所有修正提交后,以上游三平台 CI 与完整 e2e 为准,不以本地结果代替 | + +## 6. 用户决定(2026-09-27 review) + +| # | 事项 | 决定 | +|---|---|---| +| M1 | 磁盘 | 删除本地 mcpp 与 xlings 项目不再使用的构建产物;已删除 xlings、libxpkg 与 7 个旧 mcpp 工作树的 `target/`,可用空间 27 GB | +| M2 | prepare.cppm 拆分的 PR 归属 | 并入 mcpp 的同一 PR(D1) | +| M3 | xlings 的合并与发布 | 由本会话完成(D8) | +| M4 | plugins 版本 | 0.16.0(D7) | + +## 7. 自我评审记录 + +对初稿逐项复核后做了以下修正: + +| # | 发现 | 处理 | +|---|---|---| +| R1 | 初稿写"13 项修复",实际为 12 个 issue(#706 是已合入的文档提交) | 已更正 §1.2 | +| R2 | 初稿的 D4 提出以一次 rename 交换目录。POSIX 没有可移植的目录原子交换,Windows 也没有 | 改为"写入标记,下次安装时恢复或清理",同时覆盖进程被强制终止的情况 | +| R3 | "修改 prepare.cppm 会重编所有导入方"原为推断 | 已实测(71 s、12 个下游接口单元与 `main.cpp`),并补充了 mcpp 按接口内容决定是否重编的观察 | +| R4 | 初稿写"MSVC 与 Apple clang"。实际 mcpp 在 Windows 上用 llvm@20.1.7,在 macOS 上用 llvm@22.1.8 构建 | 已更正 §4.6,并在 §4.5 增加三平台门槛步骤:先验证模块组织,再搬移代码 | +| R5 | 拆分设计分析建议主接口导入各阶段分区。按 E3、E4,接口依赖链中新增的接口单元会触发 GCC 缺陷;主接口导入实现分区尚未验证 | §4.4 改为主接口不导入任何分区,`prepare_build` 移入实现单元;`:state` 只被实现单元导入(E6 已验证) | +| R6 | 审查报告称 mcpp 未实现 LOCPATH 与 GCONV_PATH,它查的是本地 main 而非 PR 头 | 已以 PR 头核实并更正 §1.2 | +| R7 | 初稿没有写上游 PR 的开法与推送身份 | 已补入 §3.3 | +| R8 | 初稿遗漏 glibc revision 1 的兼容性说明,以及 xlings `install_summary.success` 语义变化的 CHANGELOG | 已补入 §5 与 A2 | + +仍未验证、需在执行中确认的事项: +- glibc 在本机的构建时间与磁盘占用; +- xlings 的 GitCode 大资源镜像能否从本机网络完成; +- 实现分区在 Windows 与 macOS 上的构建(§4.5 第 4 步); +- #710 非成员宿主工具包的 e2e(D6 新增)。 + +## 8. 执行记录 + +### 8.1 已完成 + +| 项 | 结果 | +|---|---| +| libxpkg 0.0.58 | 同一提交内补入 `ResolvedResource::revision` 与测试(D3),并把 CI 用的 mcpp 由 2026.8.4.1 升到 2026.9.26.2:索引要求 mcpp 不低于 2026.9.18.3,旧版本钉已使 main 的 CI 失效。openxlings/libxpkg#42 合并为 bb777e9,tag `0.0.58`;GitCode `mcpp-res/xpkg` 0.0.58 与 GitHub tag 归档逐字节一致(sha256 `61621a21…894c`);mcpplibs/mcpp-index#479 合并,索引已发布 | +| 一次错误操作 | 第一次合并因未加 `--admin` 而未完成,随后的命令仍在旧的 main(385a1e9,0.0.57 的内容)上建了 tag `0.0.58`。该 tag 在数秒内删除,期间无人下载其归档;之后在核实合并提交与版本号后重新打 tag。此后的发布步骤先核实合并状态,再打 tag | +| glibc 2.44.3-r1 | 在本机 `gfxbuild` subos 中重建,构建脚本自检全部通过(C.utf8 由载荷的 localedef 编译并由其 libc 加载;UTF-8 到 GBK 经载荷的 gconv 转换)。sha256 `5a02e37f…3623`,与云端产物不同(调试信息含构建路径)。发布到 xlings-res/glibc 的 GitHub 与 GitCode,两端独立下载核对一致 | +| xim-pkgindex | 配方 sha256 改为本地产物;openxlings/xim-pkgindex#892 CI 全部通过(含用已发布 xlings 安装新资源的 linux-install-test),合并为 5358ce31。以已发布的 xlings 2026.9.26.3 在隔离的 XLINGS_HOME 中全新安装 glibc 2.44.3:`locale -a` 列出 C.utf8,UTF-8 的"中"转为 GBK `d6 d0` | +| mcpp | 发布分支 `release/2026.9.27.1`,上游草稿 PR mcpp-community/mcpp#719。新增提交:e2e 804(#710 的路径宿主工具包,2026.9.26.2 失败、本分支通过)、e2e 802 固定 gcc、CHANGELOG 兼容性说明;Windows 上 action 的 `cwd` 改用普通绝对路径(e2e 799 在 Windows 上发现命令运行在 C:\Windows) | +| mcpp CI 中已知的红灯 | `macOS ARM64 — xlings LLVM end-to-end (xcode-27)` 与 `e2e suite (macOS ARM64, self-host, xcode-27)`:ld64.lld 无法读取 Xcode 27 SDK 的 TAPI 文件(arm64e.x1),main 自 2026-09-23 起同样失败,已由 mcpp#669 跟踪,属上游 LLVM 问题,与本 PR 无关 | +| GCC 缺陷 | mcpp-community/mcpp#721(标签 `upstream-bug`),附 §4.3 的实验 | +| mcpp-plugins 0.16.0 | 分支 `release/0.16.0`:rules-qt 引擎下限 2026.9.27.1;Qt fixture 的 `cxx_runtime` 移到 Linux 宿主行;删除 0.15.2 的静默分支,qt-import-only 断言"不运行构建程序且不报告缺少 SDK"。以 PR 头构建的 mcpp 在本地运行四项 Qt 检查全部通过;以 mcpp 分支手动触发的三平台预验证进行中 | +| 修正 §3.4 | `check-deps-and-qt.sh` 中的 `--profile dev` 不是 #705 的规避:它让第二次构建在每个平台上都经过规划,从而加强"不重新安装"的断言,保留不改 | +| 沙箱验证脚本 | `~/.xlings/subos/eco-0927/work/verify/`:s705、s712、s713、s714 在 2026.9.26.2 上失败、在 PR 头上通过;x620_621 与 s621qt 待发布后运行。沙箱内 `/tmp` 私有、HOME 与宿主共享,因此 `MCPP_HOME` 与 `XLINGS_HOME` 一律指向 subos 目录,不改动用户的 `~/.mcpp` | + +| xlings | 中断恢复(D4):移出旧载荷前写入标记,每条 `install` 命令开始时恢复或删除进程已不存在的遗留条目;8 个单元测试与 e2e R9。`mcpp.lock` 以 mcpp 2026.9.26.2 对已发布的 libxpkg 0.0.58 重新生成。上游 CI 在 Windows 上发现 `InstallStateRevision.TheStampRecordsTheRevisionItWasGiven` 在删除临时目录时文件仍被打开,修正后 9 项全部通过;openxlings/xlings#622 合并为 44c1c54 | +| mcpp Windows | 单元测试 `test_workspace_inheritance` 在 clang 20.1.7 与 MSVC STL 下无法以字面量构造 `std::optional`(该单元同时包含 gtest 头文件并导入 std),改为先断言 `has_value()` 再比较字符串 | + +### 8.2 prepare.cppm 的拆分与架构审查 + +agent 完成第一层拆分(17 个提交):`PrepareState` 取代约 180 个局部变量,`prepare_build` 拆为 11 个阶段函数,分布在实现单元与实现分区 `:state` 中。主会话随后做架构审查,在同一分支上补了 6 个提交: + +| 审查项 | 发现 | 处理 | +|---|---|---| +| 接口单元 | 仍含前部约 1,700 行定义(2,414 行),修改辅助函数仍使全部导入方重编 | 定义按用途移入 `config.cpp`、`options.cpp`、`toolchain_env.cpp`、`fetch.cpp`;接口降到 777 行,只含导出类型、导出内联函数与声明 | +| 依赖 | 每个实现单元复制全部 87 条导入 | 按命名空间使用收窄,删除 835 条;导出到 `mcpp::build` 的模块与纯再导出模块保留 | +| 跨阶段闭包 | 约 40 个存入 `PrepareState` 的 `[&]` 闭包在后续阶段调用,可能引用已返回阶段的局部变量 | 静态检查(3 处可疑,逐一确认为误报);AddressSanitizer 加 `detect_stack_use_after_return=1` 运行对照场景与 12 个 e2e:无报告 | +| 一致性 | 阶段文件缺头注释;`:state` 中阶段声明乱序;接口布局注释与实际不符;长度检查脚本引用了本仓库之外的文档 | 全部修正,布局注释引用 #721 | +| 可测性 | 各阶段共用的纯函数只能经完整构建间接测试 | 新增 `test_prepare_helpers`(5 个测试),纯函数按项目惯例"为单元测试导出" | +| 性能 | — | 修改 P13 的增量构建 12 s(拆分前 71 s,只重编 `plan.o`);`emit build-database` 单次 0.47 s,前后一致 | +| 粒度 | 4 个阶段函数仍有 1,900–2,300 行 | 不在本 PR 内继续拆,登记为 mcpp#722,附拆分方案与验收标准 | + +验证:GCC 16.1 与 LLVM 22.1.8 构建;130 个单元测试;7 个对照场景的规划输出与拆分前逐字节一致;e2e 799、802–804。整合后的分支 f582a6c1 已推送到 #719,三平台 CI 进行中。 + +### 8.3 进行中 + +- xlings 2026.9.27.1 的发布(release.yml run 36299039862),之后在本机补传 GitCode 大资源并合并 xim-pkgindex 的版本 PR。