@@ -10852,6 +10852,68 @@ prepare_build(bool print_fingerprint,
1085210852 // paths and library search flags do not, and leaving them out
1085310853 // is what makes this probe cheap AND cacheable across every
1085410854 // package that shares this build's target side.
10855+ //
10856+ // `hostStripMacros` — Windows-host leak through `--target=`
10857+ // substitution. A freestanding cross compile (`--target=
10858+ // riscv64-none-elf`) on a Windows host still sees `_WIN32` (and
10859+ // the `__MINGW*__` family) in the preprocessor output: those
10860+ // are the HOST driver's predefines, and unlike the `__APPLE__` /
10861+ // `__linux__` family on Linux/macOS, the `--target=` substitution
10862+ // does NOT strip them for the freestanding target. Without the
10863+ // strip, the probe's `-dM` dump records `_WIN32` defined
10864+ // regardless of what the realised `[c-abi] presents = "posix"`
10865+ // asked for, and the probe fails with a mismatch that is a
10866+ // property of the host's driver, not of the declaration being
10867+ // checked. The strip is added ONLY when the host is Windows;
10868+ // Linux and macOS drivers' defaults do not contaminate
10869+ // `--target=` substitutions in the same way, and adding `-U`
10870+ // tokens there would have to be defended as harmless rather
10871+ // than measured (the wave's measurement caught exactly four
10872+ // host-side leaks — listed below — and adding to that set is
10873+ // the right way to extend the strip; speculatively unstripping
10874+ // everywhere is not).
10875+ //
10876+ // `hostStripFlags` — host-side wchar leakage on Windows ×
10877+ // freestanding. The probe runs `-E -dM -x c++` with no source
10878+ // unit and no include path; the `__SIZEOF_WCHAR_T__` it reads
10879+ // comes from the toolchain's own defaults, which clang on a
10880+ // Windows host sets from `<winnt.h>` (16 bits) even with
10881+ // `--target=riscv64-none-elf`. The realisation closes this by
10882+ // ALWAYS emitting `-fno-short-wchar` for `decl.wcharBits = 32`
10883+ // (`mcpp.toolchain.cenv`'s wchar branch, just rewritten — the
10884+ // old "freestanding skips the flag" rule was an unverified
10885+ // assumption that the wave's measurement caught as false). So
10886+ // when the host is Windows AND the target is freestanding, the
10887+ // host-strip set above AND `-ffreestanding` are both needed —
10888+ // the former for the preprocessor predefines, the latter so the
10889+ // driver does not pick up the Windows CRT's `<wchar.h>` even
10890+ // when the build's own include path doesn't carry one. Outside
10891+ // that combination the strip is unnecessary: Linux/macOS hosts
10892+ // do not leak `_WIN32` through `--target=`, and the wchar fix
10893+ // lives in the realisation, not the probe.
10894+ std::vector<std::string> hostStripMacros;
10895+ std::vector<std::string> hostStripFlags;
10896+ if (mcpp::platform::is_windows) {
10897+ // The four names measured as leaking through `--target=` on
10898+ // a Windows host (2026-09-18, openkal-llvm-runtime#24,
10899+ // windows-host × riscv64-none-elf). Adding to this set is
10900+ // a measurement-driven change, not a guess; pin new entries
10901+ // here with the failing build that named them.
10902+ hostStripMacros = {
10903+ "-U_WIN32",
10904+ "-U_WIN64",
10905+ "-U__MINGW32__",
10906+ "-U__MINGW64__",
10907+ };
10908+ if (tt && tt->is_freestanding())
10909+ // Windows host's driver would otherwise pull in
10910+ // MinGW's `<wchar.h>` even with no source unit, so the
10911+ // wchar probe sees 2 instead of the declared 32.
10912+ // `-ffreestanding` is what every real compile on a
10913+ // freestanding target already adds (`openkal-musl`'s
10914+ // `cflags`, mcpp's own freestanding handling).
10915+ hostStripFlags.push_back("-ffreestanding");
10916+ }
1085510917 if (tc->cEnvExpectWcharBits != 0 || tc->cEnvExpectLongBytes != 0
1085610918 || !tc->cEnvExpectDefined.empty()
1085710919 || !tc->cEnvExpectUndefined.empty()) {
@@ -10860,10 +10922,12 @@ prepare_build(bool print_fingerprint,
1086010922 probeArgv.push_back(tc->crossTargetFlag);
1086110923 for (auto& t : tc->cEnvTokens) probeArgv.push_back(t);
1086210924 for (auto& t : tc->cEnvBuiltinsTokens) probeArgv.push_back(t);
10925+ for (auto& t : hostStripFlags) probeArgv.push_back(t);
1086310926 auto probe = mcpp::toolchain::cenv_probe::verify(
1086410927 tc->binaryPath, probeArgv,
1086510928 tc->cEnvExpectWcharBits, tc->cEnvExpectLongBytes,
10866- tc->cEnvExpectDefined, tc->cEnvExpectUndefined);
10929+ tc->cEnvExpectDefined, tc->cEnvExpectUndefined,
10930+ hostStripMacros);
1086710931 if (!probe) {
1086810932 refusal::record(refusal::Code::CEnvUnrealisable);
1086910933 return std::unexpected(probe.error());
0 commit comments