fix: give the mingw target a sysroot that holds mingw-w64 alone #44
Merged
jorijnvdgraaf
merged 12 commits from 2026-10-31 22:35:49 +00:00
claude/issue-43 into master 2026-10-31
docs: which mingw prefixes are viewed, and what keys the std module
All checks were successful
Flatpak / flatpak (pull_request) Successful in 7m28s
The README said every prefix reached clang as a view of its x86_64-w64-mingw32/ and lib/gcc/, in ~/.cache. It now says a prefix is the sysroot as it is unless it also holds a Linux system, names lib64/gcc/ as well, gives the view's place inside the Flatpak and the XDG_RUNTIME_DIR fallback, and says messages name headers by their path in the prefix. AGENTS.template.md describes MingwSysroot's two outcomes and MingwViewToPrefix, has the std PCM keyed on its flags in the cache rule, lists StdPcmKey among what VariantId hashes, and gains two gotchas: anything that changes how the std module is compiled goes into StdPcmFlags, and a CMake external whose recorded configure inputs differ is built again from an empty directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: lint fails when a configuration's std module cannot be built
A host configuration pointed at a sysroot without libc++ cannot get its std module built, so its files cannot be parsed. With an AST rule registered, the run has to skip the rule and fail instead of reporting the files clean, as it did before the fix. The cache is redirected around this run only: the std module's directory is made before its build fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(lint): fail the files whose configuration has no compile command
When GetCompileCommand threw or the std module failed to build, the lint driver cleared the configuration's command, and an empty command means "not a translation unit" (project.cpp, a header): every rule that needs an AST skipped the files quietly and the run passed them as clean. MingwSysroot made that reachable for every mingw configuration whose view cannot be made. The reason is now kept per configuration and reported as an AST failure for each of its files, which fails the run and prints it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: a CMake external configured with other inputs is built from nothing
ExternalReconfigure builds a one-file C library from a local repository as a CMake external, then plants a file in its build directory to stand for what an old configure left. Built again with the same inputs, the directory is kept. With its recorded inputs rewritten the way another crafter-build's configure would have left them, the next build has to start from an empty directory and build the library again. Before the fix the planted file survived the reconfigure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(external): start a CMake build over when its configure inputs change
ConfigureCMake records the flags it injects (compiler, libc++, and for the mingw target CMAKE_SYSROOT) and reconfigures when they differ, but it did so in place. CMake then keeps what the old flags compiled: the Makefiles generator writes --sysroot into build.make, which make does not compare, so nothing was recompiled, and the check results the old configure cached stay too. A mingw external built by a release that gave clang the host's /usr as the sysroot kept its objects reading __libc_single_threaded after the fix for #43, and only deleting <cache>/external/<name>-<hash>/build by hand got rid of them. A build directory with a CMakeCache.txt whose recorded inputs differ is now removed before the configure. It keeps its name, since projects point include directories at headers generated in it. The two crafter-builds the Flatpak's branches are, sharing one cache, rebuild such an external from nothing each time they take turns, where they used to reuse each other's objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: a std module rebuilt in place recompiles the units importing it
StdPcmRebuild builds a project whose main.cpp imports std, then dates the source before the object and the object before std.pcm, as a std module rebuilt after the object leaves them, so that nothing else makes the object stale. The next build has to compile main.cpp again, and the idle build before it must compile nothing. Without -fmodule-file-deps the object was only relinked. The test redirects the cache, since redating a std PCM the real cache shares would rebuild its other users. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: recompile the units whose std module was rebuilt in place
A .cpp unit's depfile listed its source and headers, never the BMIs it imports: clang's driver adds -module-file-deps to --precompile only. A std module rebuilt in its directory, which happens whenever its source changes under one compiler and one set of flags (a libc++ update, or mingw-w64's under the same prefix), left every object that imports it as it was, and the next build only relinked them. Implementation units are now compiled with -fmodule-file-deps, so the std PCM and the project's own PCMs are prerequisites of their objects like headers. Depfiles written before this lack the edge, so a std module that moves directories still needs VariantId to move with it (StdPcmKey). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: key the std module's directory on everything it is compiled with
StdPcmDir named the directory by target, march and toolchain, then grew a case per target for the rest: the wasm flags, and with the first fix for #43 the mingw prefix. Every other flag BuildStdPcm passes went unkeyed, so configurations that differed only there took turns rebuilding one std PCM in place, under the PCMs built against it: a project with -mtune=generic against project.cpp's -mtune=native on every run, or two sysroots for one target. And the mingw prefix was keyed only when there was one, so a build without a prefix landed in the directory an older release had filled with a CRAFTER_MINGW_DIR=/usr build, and relinked its objects. StdPcmFlags is now the one source of the flags BuildStdPcm compiles with, and StdPcmKey names the directory by target, march and toolchain followed by a hash of those flags and, for the mingw target, of the prefix behind its --sysroot. VariantId hashes StdPcmKey in place of the mingw prefix: units do not notice a std module that moved directories, so a build has to move with it. Every std PCM directory and build directory moves once, as after a compiler update; the old ones stay until `clean` and the cache is cleared by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
docs: the mingw sysroot view in the README and AGENTS.template.md
All checks were successful
Flatpak / flatpak (pull_request) Successful in 7m2s
The README says which directories of a mingw-w64 prefix clang sees, and why the rest of a prefix like /usr is kept out of a Windows build. AGENTS.template.md names MingwSysroot in the :Toolchain partition, with the reason the prefix itself is never a sysroot, counts the views among what goes in the cache, and adds the mingw prefix to what VariantId hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: a mingw build sees no header from its prefix's include/
MingwSysroot stages a prefix shaped like a Linux system's /usr with a mingw-w64 cross toolchain in it, without needing a real one: glibc's <sys/single_threaded.h> in include/, gcc's per-target directory, and a stand-in mingw-w64 tree whose libstdc++ has an empty std module. A static library for x86_64-w64-mingw32, so that nothing is linked, compiles a C++ and a C source that stop with #error when they see the prefix's include/ or miss mingw-w64's or libstdc++'s headers. Against the library before the fix, the C++ source stopped on <sys/single_threaded.h>. The test also checks that the std module is not cached in the directory older releases built it in against the prefix itself. It needs only clang, so it runs in CI too, where no mingw-w64 is installed. The stand-in std module's source is written through std::format: the build's import scanner reads string literals as code, and the literal "export module std;" made the test itself a unit of a module std. VariantId checks that a prefix gives a mingw configuration a variant of its own, that two prefixes give two, and that other targets ignore it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>