fix(sysroot): take the std module source from the sysroot's libc++ release

A cross build against a sysroot whose libc++ differs from the
toolchain's failed while precompiling the std module, with a page of
"no member named 'runtime_format' in namespace 'std'". The module source
is a list of using-declarations for what one libc++ release declares, so
it only compiles against that release's headers. BuildStdPcm wants the
sysroot's own <sysroot>/usr/share/libc++/v1/std.cppm, but Alpine
packages the headers without it, and the fallback was the toolchain's
copy whatever its release. That held while the compiler came from the
same distro as the sysroot; with the Flatpak's libc++ 22.1.8 and Alpine
edge on 23.1.2 it no longer does.

The release is now read from _LIBCPP_VERSION in the sysroot's and the
toolchain's <__config>, and SysrootStdModule picks in this order: the
sysroot's own source, the toolchain's when both are the same major
release (a release branch only takes fixes), the copy crafter-build
ships for that release under share/crafter-build/libc++-modules/<major>.
When none fits the build stops with one line naming both releases and
the directory the matching source belongs in, before clang is run. If
either release cannot be read the toolchain's source is tried as
before, since there is nothing to compare.

Matching sources live in crafter-build's own share directory rather
than the LLVM prefix: they are data this tool ships for sysroots, not
part of any one compiler install, and "sources ship" already describes
that directory. `--version` prints the toolchain's libc++ release and
the releases sources are shipped for, the first thing to look at when
such a build fails.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-03 16:37:00 +02:00
commit e15cd3c14d
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
4 changed files with 112 additions and 14 deletions

View file

@ -663,14 +663,13 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
} else if (config.sysroot.empty()) {
stdCppm = GetToolchain().stdCppm.string();
} else {
stdCppm = std::format("{}/usr/share/libc++/v1/std.cppm", config.sysroot);
// not every distro ships the std module sources (Alpine's
// libc++-dev doesn't); the host copy is interchangeable as long
// as the libc++ versions line up, which the compile of the PCM
// against the sysroot's headers verifies anyway
if (!fs::exists(stdCppm) && fs::exists(GetToolchain().stdCppm)) {
stdCppm = GetToolchain().stdCppm.string();
}
// The source has to belong to the libc++ release whose headers
// the sysroot carries. Handing clang the toolchain's own copy
// regardless used to end in a page of "no member named ... in
// namespace 'std'" once the two releases differed.
StdModuleSource source = SysrootStdModule(GetToolchain(), config.sysroot);
if (!source.error.empty()) return source.error;
stdCppm = source.stdCppm.string();
}
std::string sysrootFlag = config.sysroot.empty()
? std::string()