fix: give the mingw target a sysroot that holds mingw-w64 alone

Inside the Flatpak the mingw-w64 prefix is the host's /usr, and it was
clang's --sysroot as is. clang's mingw driver takes a sysroot for a tree
that holds mingw-w64 alone, and adds its include/ to the include path
and its lib/ to the library path. For /usr those are the Linux system's
own: every header mingw-w64 lacks came from glibc, and libstdc++'s
<ext/atomicity.h>, finding <sys/single_threaded.h>, had shared_ptr read
__libc_single_threaded, which nothing on Windows defines. Every program
that used one failed to link, among them every Crafter.Graphics Win32
executable, through Crafter.Asset's GLB loader. CRAFTER_MINGW_DIR=/usr
did the same outside the Flatpak (#43).

The sysroot is now MingwSysroot(): a directory in the cache, one per
prefix, holding only links to the prefix's x86_64-w64-mingw32/, lib/gcc/
and lib64/gcc/ (where openSUSE keeps gcc). Through it clang finds
mingw-w64, gcc's version and libstdc++ exactly as it does through the
mingw gcc on PATH, and nothing else. It is made by the first mingw
compile, link or CMake configure of a process, so no other command
writes to the cache for it and a failure to make it is that build's
error. The Toolchain field is now mingwPrefix, since clang never gets
the prefix itself, and it is made absolute when the toolchain is
resolved, as it ends up in link targets and directory names. Messages
keep naming paths under the prefix rather than under the view.

The prefix is also part of the mingw target's VariantId and of its std
PCM directory. Units do not track the std PCM they import: when the
fixed release built in a directory a broken build had left, the std
module was rebuilt and the objects compiled against glibc's headers
were only relinked, failing as before. As with a compiler update, a new
directory is what gets them rebuilt. It also keeps two releases that
share a cache, as the Flatpak's branches do, from rebuilding one std PCM
under each other's PCMs, and two prefixes apart. Without a prefix both
stay as they were: clang's own discovery has not changed.

Two shorter ways do not work: --sysroot=<prefix>/x86_64-w64-mingw32
loses the directory gcc's version names, so <string> is not found, and
clang ignores --gcc-toolchain and --gcc-install-dir for this target.

Fixes #43

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

View file

@ -628,6 +628,17 @@ export namespace Crafter {
compileKey += "|B:";
compileKey += WasmBaselineFlags;
}
// The mingw-w64 prefix decides what the mingw target compiles
// against, and until issue #43 clang got it as the sysroot
// itself, which put the host's own headers on the include path.
// Unkeyed, the release that fixed it kept the objects compiled
// against glibc's headers: the build after the update only
// relinked them, and failed to link as before. No prefix keeps
// the id as it was, since clang's own discovery has not changed.
if (target == "x86_64-w64-mingw32" && !toolchain.mingwPrefix.empty()) {
compileKey += "|M:";
compileKey += toolchain.mingwPrefix.string();
}
// Sorted by ApplyStandardArgs, so flag order on the command line
// doesn't split the cache.
for (const std::string& a : projectArgs) {

View file

@ -39,11 +39,14 @@ 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), passed to clang as --sysroot for the mingw target.
// 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).
fs::path mingwSysroot;
// distro package), as an absolute path. 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.
fs::path mingwPrefix;
// `clang++ -print-target-triple` and the first line of
// `clang++ --version`; both empty when clang++ could not be run.
std::string hostTriple;
@ -86,6 +89,18 @@ export namespace Crafter {
const Toolchain& GetToolchain();
// GetToolchain(), or a std::runtime_error carrying its error.
const Toolchain& RequireToolchain();
// 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
// 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();
// _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);