From 3c96d78a7d7a2efc2c72899d9f0115dc90b522d1 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH] fix(sysroot): compile against the sysroot's libc++ headers clang's driver ranks the host toolchain's own libc++ headers (/../include/c++/v1) above --sysroot. BuildStdPcm has forced the sysroot's copy for the std module for that reason, but ordinary compiles did not. A unit that only does `import std;` never noticed. A unit that includes a standard header textually got the host's libc++, configured for the host's libc: cross-building Crafter.Graphics for aarch64 musl, Crafter.Asset's vendored gdeflate died on with "unknown rune table for this platform". Against a glibc sysroot the same unit would compile against the wrong libc++ release and say nothing. Every non-wasm, non-mingw compile with a sysroot now gets `-nostdinc++ -isystem /usr/include/c++/v1`. wasm has its own sysroot handling and mingw is a libstdc++ target. -nostdinc++ leaves -stdlib=libc++ unused on each compile, which clang reports once per unit, so the command also carries -Wno-unused-command-line-argument. The cost is that the driver's unused-argument diagnostics are silent on sysroot builds, including for a user's own misplaced flag (a -l or -Wl, option in compileFlags). Not addressed: external cmake dependencies are still configured without the target or the sysroot, and objects already built in an existing sysroot build directory are not invalidated by the new flags. Co-Authored-By: Claude Fable 5.1 --- implementations/Crafter.Build-Clang.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index 05fa4b8..d10b158 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -296,6 +296,21 @@ CompileCommand Crafter::GetCompileCommand(const Configuration& config) { if (!config.sysroot.empty()) { out.command += std::format(" --sysroot={}", config.sysroot); + // Same reason as in BuildStdPcm: the driver ranks the host + // toolchain's own libc++ headers (/../include/c++/v1) above + // --sysroot. A unit that only does `import std;` never notices, but + // one that includes a standard header textually (vendored C++ such as + // Crafter.Asset's gdeflate) would get host libc++ configured for the + // host libc, which fails on a musl sysroot with "unknown rune table + // for this platform". Force the sysroot's copy. -nostdinc++ leaves + // -stdlib=libc++ unused on every compile ("argument unused during + // compilation"), hence the silencer; the price is that it also hides + // that diagnostic for a user's own misplaced flag (a -l or -Wl, + // option in compileFlags) on sysroot builds. Not for mingw, which + // uses libstdc++ and has no libc++ in its sysroot. + if (!isWasm && config.target != "x86_64-w64-mingw32") { + out.command += std::format(" -nostdinc++ -isystem {}/usr/include/c++/v1 -Wno-unused-command-line-argument", config.sysroot); + } } if (isWasm) { // -mllvm is consumed by codegen but not the link driver, which is the