diff --git a/CHANGELOG.md b/CHANGELOG.md index 853439d..ba1e55b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,26 @@ > 本文件追踪 `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`/动态链接器/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/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, 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(