Skip to content

Commit 3afbc4f

Browse files
committed
2026.8.24.5 — say what the env segment names, where it does not name a C library
`x86_64-windows-gnu` 之上一行写着 `c-abi musl`,读者两次在同一处停下来问 「这个 gnu / musl 的矛盾还在吗」。它不是矛盾:Windows 上 `gnu` 命名的是 **对象 ABI**,它对应的行是 `c++-abi`,不是 `c-abi`。 ⭐ 沉默作为**诊断**是对的,作为**报告**是不够的。报告里没有一行叫 `gnu`, 于是读者把它映到最像 C 库名字的那一行。加警告则是错的 —— 它会在每一次 合法的 MinGW 构建上出现,而且说的内容本身是假的。 Target x86_64-windows-gnu → x86_64-w64-windows-gnu (gnu names the object ABI, not a C library) ── 布尔是对事实的有损编码 ────────────────────────────── `envNamesCAbi` 只回答「该段是不是 C 库」,足以压住错误的警告, 却给不出正确的名字。换成 `EnvAxis { Unknown, CLibrary, ObjectAbi, ObjectFormat }`: linux gnu / musl C 库 ← 可以与图矛盾,报警告 windows gnu / msvc 对象 ABI ← 不矛盾,报告加注 none elf 对象格式 ← 同上 一个枚举,三个平台,零 `#if`。 ⚠️ **该提示不出现在 C 库来自载荷时。** 那种情况下 C 库正是三元组选中的 —— 三元组就是选中它的方式 —— `gnu → ucrt` 是可见的因果,加注就成了每次 普通 Windows 构建上的噪声。作用域与既有那条警告的作用域一致 (`if (!ts.cAbi.fromGraph()) return std::nullopt;`),这不是巧合。 实测三种情形互不干扰: --target x86_64-windows-gnu 加注,无警告 --target x86_64-linux 什么都不加(没作请求) --target x86_64-linux-gnu 警告 + 可粘贴的改法 单元测试 targetside 41 → 45,含一条断言「载荷来源时不加注」的反例; 93 通过 / 0 失败。docs/15 中英双份同步。
1 parent 6ad9a57 commit 3afbc4f

8 files changed

Lines changed: 229 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,35 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [2026.8.24.5] — 2026-08-25
7+
8+
### 改进
9+
10+
- **三元组的 env 段在每个平台上命名不同的轴,而报告此前只是对此保持沉默。**
11+
12+
```
13+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu
14+
c-abi musl (openkal-musl@0.3.3, graph)
15+
```
16+
17+
⭐ 沉默作为**诊断**是对的 —— 在 Windows 上报「名字请求了 `gnu` C ABI」
18+
会在每一次合法的 MinGW 构建上出现,而且说的是错的。作为**报告**则不够:
19+
读者在其中找不到一行叫 `gnu`,于是把它映到最像 C 库名字的那一行。
20+
它真正对应的是 `c++-abi`
21+
22+
于是在该段不命名 C 库的平台上,报告直接说出它命名的是什么:
23+
24+
```
25+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu (gnu names the object ABI, not a C library)
26+
```
27+
28+
内部把 `envNamesCAbi` 这个布尔换成 `EnvAxis`,因为布尔是对事实的有损编码:
29+
该段在 Linux 上是 C 库、Windows 上是对象 ABI、无操作系统时是对象格式。
30+
一个只回答「是否为第一种」的布尔,能压住错误的警告,却给不出正确的名字。
31+
32+
⚠️ 该提示**不出现**在 C 库来自载荷时:那种情况下 C 库正是三元组选中的,
33+
`gnu → ucrt` 是可见的因果,加注就成了每次普通 Windows 构建上的噪声。
34+
635
## [2026.8.24.4] — 2026-08-24
736

837
### 修复

docs/15-openkal-cross.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ GNU ABI, `msvc` for PE with Microsoft's — and both are compatible with more th
129129
one C library. The mismatch report is therefore scoped to platforms where the
130130
segment names a C library.
131131

132+
Silence is right as a diagnostic and insufficient as a report. A reader sees
133+
134+
```
135+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu
136+
c-abi musl (openkal-musl@0.3.3, graph)
137+
```
138+
139+
finds no row called `gnu`, and maps it to the nearest thing that resembles a C
140+
library name. The row it belongs to is `c++-abi`. Where the segment does not
141+
name a C library, the report therefore names what it does:
142+
143+
```
144+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu (gnu names the object ABI, not a C library)
145+
```
146+
147+
The segment carries a different axis on each platform — the C library on Linux,
148+
the object ABI on Windows, the object format where there is no operating system
149+
— and one value records which, rather than a boolean recording only whether the
150+
first case holds.
151+
132152
## Bare Metal
133153

134154
A target with no operating system is the same model with the platform layer

docs/zh/15-openkal-cross.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,25 @@ mcpp build --target x86_64-linux
110110
`msvc` 是 PE 加微软的 —— 而两者都与不止一种 C 库相容。
111111
因此该项报出被限定在该段命名 C 库的那些平台上。
112112

113+
⭐ 沉默作为**诊断**是对的,作为**报告**是不够的。读者看到
114+
115+
```
116+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu
117+
c-abi musl (openkal-musl@0.3.3, graph)
118+
```
119+
120+
在其中找不到一行叫 `gnu`,于是把它映到最像 C 库名字的那一行。
121+
它真正对应的是 `c++-abi`。因此在该段不命名 C 库的平台上,
122+
报告直接说出它命名的是什么:
123+
124+
```
125+
Target x86_64-windows-gnu → x86_64-w64-windows-gnu (gnu names the object ABI, not a C library)
126+
```
127+
128+
该段在每个平台上承载不同的轴 —— Linux 上是 C 库,Windows 上是对象 ABI,
129+
没有操作系统时是对象格式 —— 因此记录的是**它是哪一个**,
130+
而不是一个只记录「是否为第一种」的布尔。
131+
113132
## 裸机
114133

115134
一个没有操作系统的目标,是同一个模型,只是平台层由固件而非内核供给。

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 = "2026.8.24.4"
3+
version = "2026.8.24.5"
44
description = "Modern C++ build & package management tool"
55
license = "Apache-2.0"
66
authors = ["mcpp-community"]

src/build/prepare.cppm

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5818,10 +5818,16 @@ prepare_build(bool print_fingerprint,
58185818
auto bare = *tt; bare.env.clear();
58195819
in.requestFreeTarget = bare.str();
58205820
}
5821-
// Only on Linux does the segment name a C library. On Windows
5822-
// it names the object ABI and on bare metal the object format,
5823-
// and neither is the axis the graph's C library sits on.
5824-
in.envNamesCAbi = tt->os == "linux";
5821+
// The segment names a different axis on each platform, and
5822+
// saying WHICH lets the report gloss it instead of merely
5823+
// withholding a warning. Only the C-library case can contradict
5824+
// what the graph resolved; the other two are simply a different
5825+
// question, and the report says so.
5826+
in.envAxis =
5827+
tt->os == "linux" ? tsd::EnvAxis::CLibrary
5828+
: tt->os == "windows" ? tsd::EnvAxis::ObjectAbi
5829+
: tt->is_freestanding() ? tsd::EnvAxis::ObjectFormat
5830+
: tsd::EnvAxis::Unknown;
58255831

58265832
// `sysroot = ""` and "no sysroot key" are different answers and
58275833
// must not be collapsed: the first says this project wants no

src/targetside/model.cppm

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,32 @@ constexpr std::string_view origin_name(Origin o) {
6464
}
6565
return "none";
6666
}
67+
// What the env segment of a target triple names — an axis that changes with the
68+
// OS, which is why the triple alone cannot answer "is `gnu` a C library".
69+
//
70+
// linux `gnu` / `musl` the C LIBRARY
71+
// windows `gnu` / `msvc` the OBJECT ABI (both allow several C libraries)
72+
// none `elf` the OBJECT FORMAT
73+
//
74+
// The distinction is load-bearing twice over. It decides whether a mismatch
75+
// between the segment and the resolved C library is a contradiction worth
76+
// reporting, and — when it is NOT — it lets the report say what the segment
77+
// does mean, so that a reader looking at `x86_64-windows-gnu` above a line
78+
// reading `c-abi musl` is not left to work out which row `gnu` belongs to.
79+
enum class EnvAxis { Unknown, CLibrary, ObjectAbi, ObjectFormat };
80+
81+
// The noun for an axis, as it appears in the report. Empty for `Unknown`,
82+
// because a report that cannot name the axis says nothing rather than guessing.
83+
inline std::string_view env_axis_noun(EnvAxis a) {
84+
switch (a) {
85+
case EnvAxis::CLibrary: return "a C library";
86+
case EnvAxis::ObjectAbi: return "the object ABI";
87+
case EnvAxis::ObjectFormat: return "the object format";
88+
case EnvAxis::Unknown: break;
89+
}
90+
return {};
91+
}
92+
6793

6894
// ── One layer of the target side ─────────────────────────────────────────────
6995
//
@@ -132,6 +158,7 @@ struct TargetSide {
132158
// bearing and belongs in the report.
133159
std::string llvmTriple;
134160

161+
135162
Layer compiler;
136163
Layer compilerRuntime;
137164
Layer kernelAbi;
@@ -146,9 +173,9 @@ struct TargetSide {
146173
// the request turns out to describe nothing. Built by the caller, which is
147174
// the only place that still holds mcpp's own triple.
148175
std::string requestFreeTarget;
149-
// Whether the env segment names a C library on this platform. See the
150-
// member of the same name on `Inputs`.
151-
bool envNamesCAbi = false;
176+
// What the env segment names on this platform. See the member of the same
177+
// name on `Inputs`.
178+
EnvAxis envAxis = EnvAxis::Unknown;
152179

153180
// The single question the five former derivation sites actually asked.
154181
//
@@ -326,17 +353,20 @@ struct Inputs {
326353
std::string requestedCAbi;
327354
// The same target spelled without that segment, for the suggestion.
328355
std::string requestFreeTarget;
329-
// ⚠️ WHETHER THE ENV SEGMENT NAMES A C LIBRARY ON THIS PLATFORM, WHICH IS
330-
// NOT TRUE EVERYWHERE AND WAS ASSUMED TO BE.
356+
// ⚠️ WHAT THE ENV SEGMENT NAMES ON THIS PLATFORM. It is a different axis
357+
// per OS, and a boolean here was a lossy encoding of that.
358+
//
359+
// On Linux the segment names the C library — `gnu` is glibc, `musl` is musl
360+
// — which is the case the request check was written for. On Windows it
361+
// names the OBJECT ABI: `gnu` is PE with the GNU ABI and `msvc` is PE with
362+
// Microsoft's, and both are compatible with more than one C library. On a
363+
// target with no operating system it names the object FORMAT.
331364
//
332-
// The segment carries a different axis depending on the OS. On Linux it
333-
// names the C library — `gnu` is glibc, `musl` is musl — which is the case
334-
// the request check was written for. On Windows it names the OBJECT ABI:
335-
// `gnu` is PE with the GNU ABI and `msvc` is PE with Microsoft's, and both
336-
// are compatible with more than one C library. Reporting a Windows build as
337-
// "asking for the `gnu` C ABI" describes an axis the name never addressed,
338-
// and the correction it suggested named a target that does not exist.
339-
bool envNamesCAbi = false;
365+
// Reporting a Windows build as "asking for the `gnu` C ABI" describes an
366+
// axis the name never addressed, and the correction it suggested named a
367+
// target that does not exist. Knowing which axis it IS lets the report say
368+
// so instead of merely staying silent.
369+
EnvAxis envAxis = EnvAxis::Unknown;
340370

341371
std::optional<Provider> compilerRuntime;
342372
std::optional<Provider> kernelAbi;
@@ -393,7 +423,7 @@ inline TargetSide resolve(const Inputs& in) {
393423
ts.llvmTriple = in.llvmTriple;
394424
ts.requestedCAbi = in.requestedCAbi;
395425
ts.requestFreeTarget = in.requestFreeTarget;
396-
ts.envNamesCAbi = in.envNamesCAbi;
426+
ts.envAxis = in.envAxis;
397427

398428
// compiler — always a payload, never a package.
399429
if (!in.compilerFamily.empty())
@@ -592,7 +622,7 @@ check_requirements(const TargetSide& ts, std::span<const Requirement> reqs) {
592622
// first. Telling someone their target name is wrong is only useful once there
593623
// is a right one to give them.
594624
inline std::optional<std::string> check_request(const TargetSide& ts) {
595-
if (!ts.envNamesCAbi) return std::nullopt;
625+
if (ts.envAxis != EnvAxis::CLibrary) return std::nullopt;
596626
if (ts.requestedCAbi.empty()) return std::nullopt;
597627
if (ts.cAbi.absent()) return std::nullopt;
598628
if (ts.cAbi.interfaceName == ts.requestedCAbi) return std::nullopt;
@@ -714,8 +744,36 @@ inline std::string format_report(const TargetSide& ts, std::string_view targetNa
714744
// status line's own padding, and the layer lines below are indented to sit
715745
// under it.
716746
std::string head = (ts.llvmTriple.empty() || ts.llvmTriple == targetName)
717-
? std::format("{}\n", targetName)
718-
: std::format("{} → {}\n", targetName, ts.llvmTriple);
747+
? std::format("{}", targetName)
748+
: std::format("{} → {}", targetName, ts.llvmTriple);
749+
750+
// ⚠️ WHEN THE SEGMENT IS NOT A C LIBRARY, SAY WHAT IT IS — HERE, WHERE THE
751+
// READER IS LOOKING AT IT.
752+
//
753+
// `x86_64-windows-gnu` above a line reading `c-abi musl` is not a
754+
// contradiction: on Windows `gnu` names the object ABI, and the row it
755+
// actually corresponds to is `c++-abi`. But the report contains no row
756+
// called `gnu`, so a reader maps it to the nearest thing that looks like a
757+
// C library name and concludes the build disagrees with itself. Measured
758+
// twice, by the same reader, on two different days.
759+
//
760+
// A warning would be wrong — it would fire on every legitimate MinGW build
761+
// and would say something false. A noun on the head line is not a
762+
// diagnostic; it is the missing half of a name the report was already
763+
// showing.
764+
//
765+
// Scoped to the case that actually reads as a contradiction: the segment is
766+
// present, it does not name a C library here, and the C library came from
767+
// somewhere the segment did not choose. A payload C library IS selected by
768+
// the triple, so `gnu → ucrt` follows visibly and needs no gloss.
769+
if (ts.envAxis != EnvAxis::Unknown && ts.envAxis != EnvAxis::CLibrary
770+
&& !ts.requestedCAbi.empty()
771+
&& !ts.cAbi.absent() && ts.cAbi.fromGraph()
772+
&& ts.cAbi.interfaceName != ts.requestedCAbi) {
773+
head += std::format(" ({} names {}, not a C library)",
774+
ts.requestedCAbi, env_axis_noun(ts.envAxis));
775+
}
776+
head += '\n';
719777

720778
auto body = format_layers(ts, verbose);
721779
if (!body.empty() && body.back() == '\n') body.pop_back();

src/version.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@ import std;
3131

3232
export namespace mcpp {
3333

34-
inline constexpr std::string_view MCPP_VERSION = "2026.8.24.4";
34+
inline constexpr std::string_view MCPP_VERSION = "2026.8.24.5";
3535

3636
} // namespace mcpp

tests/unit/test_targetside.cpp

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ TEST(TargetSideRequest, AFilledEnvSegmentStatesNothing) {
407407
auto in = payload_linux();
408408
in.compilerFamily = "llvm";
409409
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
410-
in.envNamesCAbi = true;
410+
in.envAxis = ts::EnvAxis::CLibrary;
411411
// `--target x86_64-linux` — the project declined to name a C library, so
412412
// the parser's fill must not become a claim it can be held to.
413413
in.requestedCAbi.clear();
@@ -424,7 +424,7 @@ TEST(TargetSideRequest, AWrittenEnvSegmentThatDisagreesIsReported) {
424424
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
425425
in.requestedCAbi = "gnu";
426426
in.requestFreeTarget = "x86_64-linux";
427-
in.envNamesCAbi = true;
427+
in.envAxis = ts::EnvAxis::CLibrary;
428428
auto why = ts::check_request(ts::resolve(in));
429429
ASSERT_TRUE(why.has_value());
430430
EXPECT_NE(why->find("`gnu`"), std::string::npos);
@@ -438,7 +438,7 @@ TEST(TargetSideRequest, APrebuiltCLibraryIsWhatTheRequestSelected) {
438438
auto in = payload_linux();
439439
in.compilerFamily = "llvm";
440440
in.requestedCAbi = "musl";
441-
in.envNamesCAbi = true;
441+
in.envAxis = ts::EnvAxis::CLibrary;
442442
// No graph supplier: the payload's C library IS the request's answer, so
443443
// there is nothing to contradict even when the names differ.
444444
EXPECT_EQ(ts::check_request(ts::resolve(in)), std::nullopt);
@@ -456,10 +456,80 @@ TEST(TargetSideRequest, TheSegmentIsOnlyACLibraryWhereItNamesOne) {
456456
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
457457
in.requestedCAbi = "gnu";
458458
in.requestFreeTarget = "x86_64-windows";
459-
in.envNamesCAbi = false;
459+
in.envAxis = ts::EnvAxis::ObjectAbi;
460460
EXPECT_EQ(ts::check_request(ts::resolve(in)), std::nullopt);
461461
}
462462

463+
// ⭐ AND WHEN IT DOES NOT NAME ONE, THE REPORT SAYS WHAT IT DOES NAME.
464+
//
465+
// Staying silent is correct as a DIAGNOSTIC and insufficient as a REPORT. The
466+
// reader sees `x86_64-windows-gnu` above a line reading `c-abi musl`, finds no
467+
// row called `gnu`, and maps it to the nearest thing that looks like a C
468+
// library name. Measured twice, by the same reader, on two different days.
469+
TEST(TargetSideReport, AnEnvSegmentThatIsNotACLibraryIsGlossed) {
470+
auto in = payload_linux();
471+
in.targetOs = "windows";
472+
in.compilerFamily = "llvm";
473+
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
474+
in.requestedCAbi = "gnu";
475+
in.requestFreeTarget = "x86_64-windows";
476+
in.envAxis = ts::EnvAxis::ObjectAbi;
477+
478+
auto r = ts::format_report(ts::resolve(in), "x86_64-windows-gnu");
479+
EXPECT_NE(r.find("gnu names the object ABI, not a C library"),
480+
std::string::npos) << r;
481+
}
482+
483+
// A bare-metal target's segment names the object FORMAT, and the same gloss
484+
// applies with a different noun. One enum, three platforms, no `#if`.
485+
TEST(TargetSideReport, OnBareMetalTheSegmentNamesTheObjectFormat) {
486+
auto in = payload_linux();
487+
in.targetOs = "none";
488+
in.compilerFamily = "llvm";
489+
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
490+
in.requestedCAbi = "elf";
491+
in.requestFreeTarget = "riscv64-none";
492+
in.envAxis = ts::EnvAxis::ObjectFormat;
493+
494+
auto r = ts::format_report(ts::resolve(in), "riscv64-none-elf");
495+
EXPECT_NE(r.find("elf names the object format, not a C library"),
496+
std::string::npos) << r;
497+
}
498+
499+
// ⚠️ AND IT DOES NOT FIRE WHEN THE C LIBRARY CAME FROM A PAYLOAD.
500+
//
501+
// A payload C library IS what the triple selected — the triple is how it was
502+
// selected — so `gnu → ucrt` follows visibly and a gloss would be noise on
503+
// every ordinary Windows build.
504+
TEST(TargetSideReport, NoGlossWhenTheTripleItselfChoseTheCLibrary) {
505+
auto in = payload_linux();
506+
in.targetOs = "windows";
507+
in.compilerFamily = "llvm";
508+
in.requestedCAbi = "gnu";
509+
in.requestFreeTarget = "x86_64-windows";
510+
in.envAxis = ts::EnvAxis::ObjectAbi;
511+
// cAbi left as the payload's — not from the graph.
512+
513+
auto r = ts::format_report(ts::resolve(in), "x86_64-windows-gnu");
514+
EXPECT_EQ(r.find("not a C library"), std::string::npos) << r;
515+
}
516+
517+
// On Linux the segment DOES name a C library, so there is nothing to gloss —
518+
// the disagreement is reported as a warning instead, which is a different
519+
// mechanism and must not double up.
520+
TEST(TargetSideReport, NoGlossWhereTheSegmentNamesACLibrary) {
521+
auto in = payload_linux();
522+
in.compilerFamily = "llvm";
523+
in.cAbi = provider("openkal-musl", "0.3.3", "musl");
524+
in.requestedCAbi = "gnu";
525+
in.requestFreeTarget = "x86_64-linux";
526+
in.envAxis = ts::EnvAxis::CLibrary;
527+
528+
auto r = ts::format_report(ts::resolve(in), "x86_64-linux-gnu");
529+
EXPECT_EQ(r.find("not a C library"), std::string::npos) << r;
530+
EXPECT_NE(ts::check_request(ts::resolve(in)), std::nullopt);
531+
}
532+
463533
// ── Rule two: declared requirements ──────────────────────────────────────────
464534

465535
TEST(TargetSideRequirements, ARequirementIsCheckedAgainstWhatResolved) {

0 commit comments

Comments
 (0)