From 52c9da5677c934731a2814f62db2cb18b1f31177 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Wed, 8 Jul 2026 02:30:22 +0800 Subject: [PATCH 1/2] fix(toolchain): clang cfg gains the C-header axis (v0.0.84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixup_clang_cfg's regenerated cfg covered the link axis (-B/-L/loader/ rpath) but omitted the C library and kernel headers — a direct `clang hello.c` only worked when the HOST happened to ship /usr/include (silently non-hermetic; hard failure on header-less machines). The cfg now carries -isystem for the glibc payload headers and the linux-headers payload, ordered AFTER the libc++ block (its C-header wrappers reach libc via #include_next) — byte-consistent with what xim-pkgindex's llvm.lua install hook generates, so the two cfg writers can no longer diverge. The C driver cfg (clang.cfg) gets the headers directly; the C++ cfg gets them after the libc++ includes. Fixup rev bumped to hermetic-3 so existing payloads re-converge on their next build. Verified with host headers masked (--sysroot=): cfg-driven bare clang and clang++ both compile and run; negative control without the payload headers fails as expected. mcpp's own builds are unaffected (the link model has always supplied its own header flags). --- CHANGELOG.md | 17 ++++++++++++++++- mcpp.toml | 2 +- src/toolchain/fingerprint.cppm | 2 +- src/toolchain/post_install.cppm | 31 ++++++++++++++++++++++++++++--- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 853439d..15698c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,22 @@ > 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。 > 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。 -## [0.0.83] — 2026-07-07 +## [0.0.84] — 2026-07-08 + +### 修复 / 完善 + +- **clang cfg 头文件轴补齐(供人类直接使用的完备性)**:`fixup_clang_cfg` 再生的 + cfg 此前只覆盖链接轴(`-B`/`-L`/loader/rpath),缺 C 库与内核头——裸 + `clang hello.c` 只有在宿主恰好装有 `/usr/include` 时才能编译(静默不 hermetic, + 无宿主头的机器直接失败)。现补上 `-isystem /include` 与 + `-isystem /include`,置于 libc++ 头块**之后** + (保持 `#include_next` 链),与 xim-pkgindex 侧 `llvm.lua` 装机生成的 cfg + 内容一致——两个写手不再漂移。fixup rev 升至 `hermetic-3`,存量 payload 在 + 下次构建时自动再收敛。已实测:宿主头被屏蔽(`--sysroot=<空目录>`)下, + cfg 驱动的裸 `clang`/`clang++` 编译运行均成功。mcpp 自身构建不受影响 + (linkmodel 一直自带头轴)。 + + ### 修复 diff --git a/mcpp.toml b/mcpp.toml index 9d74167..a21282b 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,6 +1,6 @@ [package] name = "mcpp" -version = "0.0.83" +version = "0.0.84" description = "Modern C++ build & package management tool" license = "Apache-2.0" authors = ["mcpp-community"] diff --git a/src/toolchain/fingerprint.cppm b/src/toolchain/fingerprint.cppm index c864490..78bcadd 100644 --- a/src/toolchain/fingerprint.cppm +++ b/src/toolchain/fingerprint.cppm @@ -18,7 +18,7 @@ import mcpp.toolchain.detect; export namespace mcpp::toolchain { -inline constexpr std::string_view MCPP_VERSION = "0.0.83"; +inline constexpr std::string_view MCPP_VERSION = "0.0.84"; struct FingerprintInputs { Toolchain toolchain; diff --git a/src/toolchain/post_install.cppm b/src/toolchain/post_install.cppm index 22986a0..a987cc8 100644 --- a/src/toolchain/post_install.cppm +++ b/src/toolchain/post_install.cppm @@ -232,7 +232,7 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot, } } - std::string common, cxxOnly; + std::string common, cxxOnly, cHdr; auto cxxInclude = payloadRoot / "include" / "c++" / "v1"; if constexpr (mcpp::platform::is_macos) { // macOS keeps its historical cfg semantics: the C library and the @@ -256,6 +256,28 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot, } common += "-fuse-ld=lld\n--rtlib=compiler-rt\n--unwindlib=libunwind\n"; + // HEADER axis (C and C++ drivers alike): the C library and kernel + // headers come from the same payloads the link axis uses. Without + // these, a direct `clang hello.c` only works when the HOST happens + // to ship /usr/include — silently non-hermetic, broken on + // header-less machines. For C++ they must come AFTER the libc++ + // block (its C-header wrappers reach libc via #include_next), so + // they are collected separately and appended in order below — + // byte-consistent with what llvm.lua's install hook generates. + if (!glibcLibDir.empty()) { + auto glibcInclude = glibcLibDir.parent_path() / "include"; + if (std::filesystem::exists(glibcInclude / "features.h")) + cHdr += "-isystem " + glibcInclude.string() + "\n"; + constexpr std::string_view kLinuxLimits = "include/linux/limits.h"; + auto linuxHeaders = mcpp::xlings::paths::find_sibling_package( + payloadRoot / "bin" / "clang++", "linux-headers", kLinuxLimits); + if (!linuxHeaders) + linuxHeaders = mcpp::xlings::paths::find_home_tool( + "linux-headers", kLinuxLimits); + if (linuxHeaders) + cHdr += "-isystem " + (*linuxHeaders / "include").string() + "\n"; + } + if (std::filesystem::exists(cxxInclude)) { cxxOnly += "-nostdinc++\n-stdlib=libc++\n"; cxxOnly += "-isystem " + cxxInclude.string() + "\n"; @@ -264,6 +286,9 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot, auto tripleInclude = payloadRoot / "include" / triple / "c++" / "v1"; if (std::filesystem::exists(tripleInclude)) cxxOnly += "-isystem " + tripleInclude.string() + "\n"; + } + cxxOnly += cHdr; + if (!triple.empty()) { auto tripleLib = payloadRoot / "lib" / triple; if (std::filesystem::exists(tripleLib)) { cxxOnly += "-L" + tripleLib.string() + "\n"; @@ -281,7 +306,7 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot, if (!name.ends_with(".cfg")) continue; const bool isCxx = name.find("++") != std::string::npos; std::ofstream os(it->path()); - os << common << (isCxx ? cxxOnly : std::string{}); + os << common << (isCxx ? cxxOnly : cHdr); } } @@ -376,7 +401,7 @@ void llvm_post_install_fixup(const mcpp::config::GlobalConfig& cfg, // runtime libs. Idempotent via a content-fingerprinted marker. // // Bump when the fixup logic changes so existing installs re-run it. -constexpr std::string_view kFixupRev = "hermetic-2"; +constexpr std::string_view kFixupRev = "hermetic-3"; export void ensure_post_install_fixup(const mcpp::config::GlobalConfig& cfg, const std::filesystem::path& payloadRoot, From 619b835bdec66daf005eae4ac797730547138cb6 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Wed, 8 Jul 2026 02:39:06 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20normalize=20phrasing=20=E2=80=94=20?= =?UTF-8?q?'direct=20driver=20invocation'=20instead=20of=20informal=20word?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 28 ++++++++++++++++------------ docs/08-toolchain-internals.md | 6 +++--- docs/zh/08-toolchain-internals.md | 6 +++--- src/toolchain/probe.cppm | 4 ++-- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15698c7..ba1e55b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,22 @@ ## [0.0.84] — 2026-07-08 -### 修复 / 完善 - -- **clang cfg 头文件轴补齐(供人类直接使用的完备性)**:`fixup_clang_cfg` 再生的 - cfg 此前只覆盖链接轴(`-B`/`-L`/loader/rpath),缺 C 库与内核头——裸 - `clang hello.c` 只有在宿主恰好装有 `/usr/include` 时才能编译(静默不 hermetic, - 无宿主头的机器直接失败)。现补上 `-isystem /include` 与 - `-isystem /include`,置于 libc++ 头块**之后** - (保持 `#include_next` 链),与 xim-pkgindex 侧 `llvm.lua` 装机生成的 cfg - 内容一致——两个写手不再漂移。fixup rev 升至 `hermetic-3`,存量 payload 在 - 下次构建时自动再收敛。已实测:宿主头被屏蔽(`--sysroot=<空目录>`)下, - cfg 驱动的裸 `clang`/`clang++` 编译运行均成功。mcpp 自身构建不受影响 - (linkmodel 一直自带头轴)。 +### 修复 + +- **clang 驱动配置文件(cfg)补全头文件搜索路径**:`fixup_clang_cfg` 再生成的 + cfg 此前仅包含链接相关条目(`-B`/`-L`/动态链接器/rpath),缺少 C 标准库头文件 + 与内核头文件的搜索路径。该 cfg 服务于直接调用打包内 `clang`/`clang++` + (不经由 mcpp)的场景:缺少这两项时,此类调用仅在宿主系统存在 + `/usr/include` 时可编译(依赖宿主环境,违背沙箱自包含约束),在无宿主开发头 + 文件的环境中直接报头文件缺失错误。本次补充 + `-isystem /include` 与 `-isystem /include`, + 置于 libc++ 头文件条目之后以保持 `#include_next` 搜索链;生成内容与 + xim-pkgindex 侧 `llvm.lua` 安装期生成的 cfg 保持一致,消除两个生成端之间的 + 内容差异。fixup 修订号升级至 `hermetic-3`,既有 payload 在下一次构建时自动 + 重新收敛,无需重新安装。验证方式:以 `--sysroot=<空目录>` 屏蔽宿主头文件后, + 由 cfg 驱动的 `clang`/`clang++` 直接调用编译与运行均通过;移除上述搜索路径的 + 对照组按预期失败。mcpp 自身构建路径不受影响(构建 flags 由 linkmodel + 独立提供,不读取 cfg)。 diff --git a/docs/08-toolchain-internals.md b/docs/08-toolchain-internals.md index c3ba30c..1047039 100644 --- a/docs/08-toolchain-internals.md +++ b/docs/08-toolchain-internals.md @@ -159,10 +159,10 @@ mcpp's sight — trust-but-verify is the only reliable semantic. mcpp read-only + verification. The pipeline here is the compatibility layer until then, and the self-healing mechanism for drift either way. -## 5. The clang cfg: for humans only +## 5. The clang cfg: direct-invocation support only -`bin/clang++.cfg` exists so a human running the bundled `clang++` directly -gets a working, hermetic compiler. mcpp's own builds never read it +`bin/clang++.cfg` exists so that direct invocations of the bundled +`clang++` (outside mcpp) get a working, hermetic compiler configuration. mcpp's own builds never read it (`--no-default-config` always). The fixup pipeline **regenerates** it deterministically from the link model — same payload ⇒ byte-identical cfg on every machine and install path — rather than line-patching whatever an diff --git a/docs/zh/08-toolchain-internals.md b/docs/zh/08-toolchain-internals.md index f24664d..ae88beb 100644 --- a/docs/zh/08-toolchain-internals.md +++ b/docs/zh/08-toolchain-internals.md @@ -135,10 +135,10 @@ glibc、payload 从别的 home 继承而来)发生在 mcpp 视野之外,trust-bu 新 home 时——mcpp 退为只读 + 校验。本管线在那之前是兼容层,也是双向漂移的 自愈机制。 -## 5. clang cfg:只服务人类 +## 5. clang cfg:仅服务直接调用场景 -`bin/clang++.cfg` 的存在意义是:人类直接敲打包内 `clang++` 时得到一个可用且 -hermetic 的编译器。mcpp 自己的构建从不读它(永远 `--no-default-config`)。 +`bin/clang++.cfg` 的职责是:直接调用打包内 `clang++`(不经由 mcpp)时, +获得可用且 hermetic 的编译器配置。mcpp 自己的构建从不读它(永远 `--no-default-config`)。 fixup 管线从链接模型**确定性再生**它——同一 payload ⇒ 任何机器、任何安装路径 产出字节一致的 cfg——而不是对装机产物做行级补丁。Linux 上内容为:CRT 发现 (`-B`)、payload loader + rpath、lld/compiler-rt/libunwind、C++ 驱动附加 diff --git a/src/toolchain/probe.cppm b/src/toolchain/probe.cppm index 4de603b..5331c12 100644 --- a/src/toolchain/probe.cppm +++ b/src/toolchain/probe.cppm @@ -311,8 +311,8 @@ probe_sysroot(const std::filesystem::path& compilerBin, // artifact that mcpp's own fixup pipeline now REGENERATES without a // --sysroot line (the C library comes from the payload link model), so // the mined value existed only on never-fixed-up installs and pointed at - // an environment directory the payload doesn't own. The cfg is for - // humans running clang++ directly; builds derive everything from the + // an environment directory the payload doesn't own. The cfg serves + // direct driver invocations only; builds derive everything from the // link model. Kept as a diagnostic only. if (auto cfg = mcpp::fallback::parse_clang_cfg_sysroot(compilerBin)) { mcpp::log::debug("probe", std::format(