fix(mingw): view only a prefix that holds a Linux system

The first fix showed clang every mingw prefix through a view of its
mingw-w64 parts. That broke a prefix that holds mingw-w64 alone, where
Windows libraries installed with --prefix land in include/ and lib/:
with CRAFTER_MINGW_DIR=/opt/mingw, <SDL2/SDL.h> and -lSDL2 were no
longer found. Such a prefix is clang's sysroot whole again. Only a
prefix that also holds a Linux system gets the view
(Toolchain::mingwPrefixIsSystem): the host's /usr inside the Flatpak,
whatever its include/ holds, and a prefix whose include/ has a C
library's features.h, which every glibc and musl header includes and
mingw-w64 has no use for, as CRAFTER_MINGW_DIR=/usr has.

MakeMingwSysroot makes the view in the cache, or in
$XDG_RUNTIME_DIR/crafter.build when the cache's filesystem takes no
symbolic links (vfat, exFAT, CIFS without unix extensions): the XDG
spec promises them there, and nothing else in the cache needs them.
Its path is absolute, because CMake runs the compiler from directories
of its own and a relative cache did not resolve from there. The prefix
is resolved once, absolute and lexically normal, so /opt/mingw/ and
/opt/mingw are one prefix and not two builds.

clang names a header by the path it found it at, so every diagnostic
from a viewed mingw-w64 header named a hashed cache directory.
RunBuildCommand now writes paths into the view as paths into the
prefix, as BuildStdPcm did for its manifest messages already.

Also from the review of PR #44: SysrootCompileFlags builds a mingw
command, which can make a view, so it redirects the cache like the
other tests; the manifest filter's comment names what it still keeps
out; the C path asks MingwSysroot() once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 22:58:30 +01:00
commit 06b61e84f9
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
8 changed files with 262 additions and 78 deletions

View file

@ -39,14 +39,20 @@ export namespace Crafter {
fs::path wasiSysroot;
// The mingw-w64 install prefix, the directory that holds
// x86_64-w64-mingw32/ and lib/gcc/x86_64-w64-mingw32/ (/usr for a
// distro package), as an absolute path. Empty means clang's own
// discovery, which follows the mingw gcc on PATH. Set by
// distro package), absolute and lexically normal. Empty means clang's
// own discovery, which follows the mingw gcc on PATH. Set by
// CRAFTER_MINGW_DIR, else the host's /usr when running sandboxed and
// it has mingw-w64 (the sandbox's own /usr never does). clang never
// gets the prefix itself: the mingw target's --sysroot is
// MingwSysroot(), a view of it. Keyed into the mingw target's
// VariantId and std PCM directory.
// it has mingw-w64 (the sandbox's own /usr never does). What the
// mingw target passes as --sysroot is MingwSysroot(), not this:
// see mingwPrefixIsSystem.
fs::path mingwPrefix;
// The prefix holds a Linux system as well as mingw-w64: the host's
// /usr in the sandbox, or a prefix whose include/ has a C library's
// features.h, as /usr has. clang searches a mingw sysroot's include/
// and lib/, so such a prefix is shown to it only through a view of
// its mingw-w64 parts; any other prefix is the sysroot itself, and
// Windows libraries installed into its include/ and lib/ are found.
bool mingwPrefixIsSystem = false;
// `clang++ -print-target-triple` and the first line of
// `clang++ --version`; both empty when clang++ could not be run.
std::string hostTriple;
@ -89,18 +95,32 @@ export namespace Crafter {
const Toolchain& GetToolchain();
// GetToolchain(), or a std::runtime_error carrying its error.
const Toolchain& RequireToolchain();
// Makes, in the first of `parents` that can hold one, a directory that
// shows clang only the mingw-w64 parts of `prefix`: links named
// x86_64-w64-mingw32, lib/gcc and lib64/gcc to the same paths under
// `prefix`, at <parent>/mingw-sysroot/<hash of prefix>, and returns it.
// A view already there is reused; one another process makes at the same
// moment is as good as one made here. Throws when no parent can hold
// one, with the reason for each.
fs::path MakeMingwSysroot(const fs::path& prefix, std::span<const fs::path> parents);
// The --sysroot of every compile, link and CMake configure for the mingw
// target: a directory in crafter-build's cache holding only links to the
// x86_64-w64-mingw32/, lib/gcc/ and lib64/gcc/ of GetToolchain()'s
// mingwPrefix, made on the first call of a process. Empty without a
// prefix. The prefix cannot be the sysroot: clang takes a mingw sysroot
// for a tree that holds mingw-w64 alone and searches its include/ and
// lib/ as well, which for /usr (the host's, in the Flatpak) are the
// Linux system's own. A Windows build then saw glibc's headers, and
// target, settled on the first call of a process: empty without a
// mingwPrefix, the prefix itself for one that holds mingw-w64 alone, and
// for a system prefix the view MakeMingwSysroot makes in crafter-build's
// cache, or in $XDG_RUNTIME_DIR/crafter.build when the cache's
// filesystem takes no symbolic links. A system prefix cannot be the
// sysroot: clang takes a mingw sysroot for a tree that holds mingw-w64
// alone and searches its include/ and lib/ as well, which for /usr are
// the Linux system's own. A Windows build then saw glibc's headers, and
// libstdc++, finding <sys/single_threaded.h>, had shared_ptr read
// __libc_single_threaded, which nothing on Windows defines (issue #43).
// Throws when the view cannot be made.
const fs::path& MingwSysroot();
// `text` with every path into the view MingwSysroot() made written as
// the same path into the prefix it shows, so that compiler messages name
// a header where the user installed it rather than in a hashed cache
// directory; `text` unchanged when no view was made.
std::string MingwViewToPrefix(std::string text);
// _LIBCPP_VERSION as the <__config> in `includeDir` (an include/c++/v1)
// defines it, XXYYZZ; 0 when the file is missing or has no such line.
std::int32_t LibcxxVersion(const fs::path& includeDir);