Skip to content

Commit 6f458e8

Browse files
authored
fix(toolchain): clang cfg gains the C-header axis (v0.0.84) (#198)
* fix(toolchain): clang cfg gains the C-header axis (v0.0.84) 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=<empty dir>): 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). * docs: normalize phrasing — 'direct driver invocation' instead of informal wording
1 parent 19e2070 commit 6f458e8

7 files changed

Lines changed: 58 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,26 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6-
## [0.0.83] — 2026-07-07
6+
## [0.0.84] — 2026-07-08
7+
8+
### 修复
9+
10+
- **clang 驱动配置文件(cfg)补全头文件搜索路径**:`fixup_clang_cfg` 再生成的
11+
cfg 此前仅包含链接相关条目(`-B`/`-L`/动态链接器/rpath),缺少 C 标准库头文件
12+
与内核头文件的搜索路径。该 cfg 服务于直接调用打包内 `clang`/`clang++`
13+
(不经由 mcpp)的场景:缺少这两项时,此类调用仅在宿主系统存在
14+
`/usr/include` 时可编译(依赖宿主环境,违背沙箱自包含约束),在无宿主开发头
15+
文件的环境中直接报头文件缺失错误。本次补充
16+
`-isystem <glibc payload>/include``-isystem <linux-headers payload>/include`,
17+
置于 libc++ 头文件条目之后以保持 `#include_next` 搜索链;生成内容与
18+
xim-pkgindex 侧 `llvm.lua` 安装期生成的 cfg 保持一致,消除两个生成端之间的
19+
内容差异。fixup 修订号升级至 `hermetic-3`,既有 payload 在下一次构建时自动
20+
重新收敛,无需重新安装。验证方式:以 `--sysroot=<空目录>` 屏蔽宿主头文件后,
21+
由 cfg 驱动的 `clang`/`clang++` 直接调用编译与运行均通过;移除上述搜索路径的
22+
对照组按预期失败。mcpp 自身构建路径不受影响(构建 flags 由 linkmodel
23+
独立提供,不读取 cfg)。
24+
25+
726

827
### 修复
928

docs/08-toolchain-internals.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ mcpp's sight — trust-but-verify is the only reliable semantic.
159159
mcpp read-only + verification. The pipeline here is the compatibility
160160
layer until then, and the self-healing mechanism for drift either way.
161161

162-
## 5. The clang cfg: for humans only
162+
## 5. The clang cfg: direct-invocation support only
163163

164-
`bin/clang++.cfg` exists so a human running the bundled `clang++` directly
165-
gets a working, hermetic compiler. mcpp's own builds never read it
164+
`bin/clang++.cfg` exists so that direct invocations of the bundled
165+
`clang++` (outside mcpp) get a working, hermetic compiler configuration. mcpp's own builds never read it
166166
(`--no-default-config` always). The fixup pipeline **regenerates** it
167167
deterministically from the link model — same payload ⇒ byte-identical cfg on
168168
every machine and install path — rather than line-patching whatever an

docs/zh/08-toolchain-internals.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ glibc、payload 从别的 home 继承而来)发生在 mcpp 视野之外,trust-bu
135135
新 home 时——mcpp 退为只读 + 校验。本管线在那之前是兼容层,也是双向漂移的
136136
自愈机制。
137137

138-
## 5. clang cfg:只服务人类
138+
## 5. clang cfg:仅服务直接调用场景
139139

140-
`bin/clang++.cfg` 的存在意义是:人类直接敲打包内 `clang++` 时得到一个可用且
141-
hermetic 的编译器。mcpp 自己的构建从不读它(永远 `--no-default-config`)。
140+
`bin/clang++.cfg` 的职责是:直接调用打包内 `clang++`(不经由 mcpp)时,
141+
获得可用且 hermetic 的编译器配置。mcpp 自己的构建从不读它(永远 `--no-default-config`)。
142142
fixup 管线从链接模型**确定性再生**它——同一 payload ⇒ 任何机器、任何安装路径
143143
产出字节一致的 cfg——而不是对装机产物做行级补丁。Linux 上内容为:CRT 发现
144144
(`-B`)、payload loader + rpath、lld/compiler-rt/libunwind、C++ 驱动附加

mcpp.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcpp"
3-
version = "0.0.83"
3+
version = "0.0.84"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

src/toolchain/fingerprint.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import mcpp.toolchain.detect;
1818

1919
export namespace mcpp::toolchain {
2020

21-
inline constexpr std::string_view MCPP_VERSION = "0.0.83";
21+
inline constexpr std::string_view MCPP_VERSION = "0.0.84";
2222

2323
struct FingerprintInputs {
2424
Toolchain toolchain;

src/toolchain/post_install.cppm

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot,
232232
}
233233
}
234234

235-
std::string common, cxxOnly;
235+
std::string common, cxxOnly, cHdr;
236236
auto cxxInclude = payloadRoot / "include" / "c++" / "v1";
237237
if constexpr (mcpp::platform::is_macos) {
238238
// 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,
256256
}
257257
common += "-fuse-ld=lld\n--rtlib=compiler-rt\n--unwindlib=libunwind\n";
258258

259+
// HEADER axis (C and C++ drivers alike): the C library and kernel
260+
// headers come from the same payloads the link axis uses. Without
261+
// these, a direct `clang hello.c` only works when the HOST happens
262+
// to ship /usr/include — silently non-hermetic, broken on
263+
// header-less machines. For C++ they must come AFTER the libc++
264+
// block (its C-header wrappers reach libc via #include_next), so
265+
// they are collected separately and appended in order below —
266+
// byte-consistent with what llvm.lua's install hook generates.
267+
if (!glibcLibDir.empty()) {
268+
auto glibcInclude = glibcLibDir.parent_path() / "include";
269+
if (std::filesystem::exists(glibcInclude / "features.h"))
270+
cHdr += "-isystem " + glibcInclude.string() + "\n";
271+
constexpr std::string_view kLinuxLimits = "include/linux/limits.h";
272+
auto linuxHeaders = mcpp::xlings::paths::find_sibling_package(
273+
payloadRoot / "bin" / "clang++", "linux-headers", kLinuxLimits);
274+
if (!linuxHeaders)
275+
linuxHeaders = mcpp::xlings::paths::find_home_tool(
276+
"linux-headers", kLinuxLimits);
277+
if (linuxHeaders)
278+
cHdr += "-isystem " + (*linuxHeaders / "include").string() + "\n";
279+
}
280+
259281
if (std::filesystem::exists(cxxInclude)) {
260282
cxxOnly += "-nostdinc++\n-stdlib=libc++\n";
261283
cxxOnly += "-isystem " + cxxInclude.string() + "\n";
@@ -264,6 +286,9 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot,
264286
auto tripleInclude = payloadRoot / "include" / triple / "c++" / "v1";
265287
if (std::filesystem::exists(tripleInclude))
266288
cxxOnly += "-isystem " + tripleInclude.string() + "\n";
289+
}
290+
cxxOnly += cHdr;
291+
if (!triple.empty()) {
267292
auto tripleLib = payloadRoot / "lib" / triple;
268293
if (std::filesystem::exists(tripleLib)) {
269294
cxxOnly += "-L" + tripleLib.string() + "\n";
@@ -281,7 +306,7 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot,
281306
if (!name.ends_with(".cfg")) continue;
282307
const bool isCxx = name.find("++") != std::string::npos;
283308
std::ofstream os(it->path());
284-
os << common << (isCxx ? cxxOnly : std::string{});
309+
os << common << (isCxx ? cxxOnly : cHdr);
285310
}
286311
}
287312

@@ -376,7 +401,7 @@ void llvm_post_install_fixup(const mcpp::config::GlobalConfig& cfg,
376401
// runtime libs. Idempotent via a content-fingerprinted marker.
377402
//
378403
// Bump when the fixup logic changes so existing installs re-run it.
379-
constexpr std::string_view kFixupRev = "hermetic-2";
404+
constexpr std::string_view kFixupRev = "hermetic-3";
380405

381406
export void ensure_post_install_fixup(const mcpp::config::GlobalConfig& cfg,
382407
const std::filesystem::path& payloadRoot,

src/toolchain/probe.cppm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ probe_sysroot(const std::filesystem::path& compilerBin,
311311
// artifact that mcpp's own fixup pipeline now REGENERATES without a
312312
// --sysroot line (the C library comes from the payload link model), so
313313
// the mined value existed only on never-fixed-up installs and pointed at
314-
// an environment directory the payload doesn't own. The cfg is for
315-
// humans running clang++ directly; builds derive everything from the
314+
// an environment directory the payload doesn't own. The cfg serves
315+
// direct driver invocations only; builds derive everything from the
316316
// link model. Kept as a diagnostic only.
317317
if (auto cfg = mcpp::fallback::parse_clang_cfg_sysroot(compilerBin)) {
318318
mcpp::log::debug("probe", std::format(

0 commit comments

Comments
 (0)