@@ -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.
594624inline 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 ();
0 commit comments