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>
This commit is contained in:
parent
06b61e84f9
commit
5d25495621
4 changed files with 114 additions and 65 deletions
|
|
@ -247,36 +247,51 @@ void Configuration::GetInterfacesAndImplementations(std::span<fs::path> interfac
|
|||
}
|
||||
}
|
||||
|
||||
std::string Crafter::StdPcmFlags(const Configuration& config) {
|
||||
if (config.target == "x86_64-w64-mingw32") {
|
||||
// -femulated-tls keeps PCM TLS access matching libstdc++'s emutls
|
||||
// definitions; a mismatch surfaces as undefined std::__once_callable.
|
||||
return std::format(" --target={} -march={} -mtune={} -femulated-tls -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier", config.target, config.march, config.mtune);
|
||||
}
|
||||
const bool isWasm = config.target.starts_with("wasm32");
|
||||
const std::string sysrootFlag = config.sysroot.empty() ? std::string() : std::format(" --sysroot={}", config.sysroot);
|
||||
// wasm32 rejects -march, and the std module has to be compiled the way
|
||||
// the units importing it are, a variant pass's flags (e.g.
|
||||
// -mrelaxed-simd) included, or the BMI is not feature-compatible.
|
||||
std::string archFlags = isWasm ? WasmCompileFlags(config) : std::format(" -march={} -mtune={}", config.march, config.mtune);
|
||||
// non-wasm cross builds: the driver ranks the host toolchain's own
|
||||
// libc++ headers (<install>/../include/c++/v1) above --sysroot, so the
|
||||
// PCM would mix host libc++ with the foreign libc. Force the sysroot's
|
||||
// copy.
|
||||
if (!isWasm && !config.sysroot.empty()) {
|
||||
archFlags += std::format(" -nostdinc++ -isystem {}/usr/include/c++/v1", config.sysroot);
|
||||
}
|
||||
return std::format(" --target={} -std=c++26{}{} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier", config.target, sysrootFlag, archFlags);
|
||||
}
|
||||
|
||||
std::string Crafter::StdPcmKey(const Configuration& config, const Toolchain& toolchain) {
|
||||
// A PCM is only valid for the clang that wrote it and the flags it was
|
||||
// written with, so a compiler bump starts a cold directory instead of
|
||||
// handing the new clang a PCM it will crash on, and so does any flag.
|
||||
// With only some of them in the name, configurations that differed in
|
||||
// the rest took turns rebuilding one std PCM in place: -mtune=generic
|
||||
// against project.cpp's -mtune=native on every run, two sysroots, or two
|
||||
// releases sharing a cache, as the Flatpak's branches do, each time under
|
||||
// the PCMs built against the previous one, which clang crashes on rather
|
||||
// than rejecting.
|
||||
std::string compiled = StdPcmFlags(config);
|
||||
// The base command's part: the toolchain's key covers the compiler and
|
||||
// libc++, and for the mingw target what clang finds mingw-w64 through is
|
||||
// the prefix, given whole or through a view (MingwSysroot), named here by
|
||||
// the prefix rather than by the view's own path.
|
||||
if (config.target == "x86_64-w64-mingw32" && !toolchain.mingwPrefix.empty()) {
|
||||
compiled += std::format("|mingw:{}|{}", toolchain.mingwPrefix.string(), toolchain.mingwPrefixIsSystem ? "view" : "whole");
|
||||
}
|
||||
return std::format("{}-{}-{}-{:08x}", config.target, config.march, toolchain.Key(), static_cast<std::uint32_t>(std::hash<std::string>{}(compiled)));
|
||||
}
|
||||
|
||||
fs::path Crafter::StdPcmDir(const Configuration& config) {
|
||||
// The std PCM is cached per target+march+toolchain: a PCM is only valid
|
||||
// for the exact clang that wrote it, so a compiler bump starts a cold
|
||||
// directory instead of handing the new clang a PCM it will crash on. A
|
||||
// wasm variant pass compiles it with extra codegen flags (e.g.
|
||||
// -mrelaxed-simd) — its BMI must not be shared with the baseline's, or
|
||||
// the consuming TUs see a target-feature mismatch. Suffix the cache dir
|
||||
// with the variant flags when present.
|
||||
std::string stdPcmKey = std::format("{}-{}-{}", config.target, config.march, GetToolchain().Key());
|
||||
// The flags every wasm unit gets are compiled into the std PCM as well,
|
||||
// and the importing units have to match them. Two releases that share a
|
||||
// cache and a compiler, as the Flatpak's branches do, would otherwise
|
||||
// take turns rebuilding this one std PCM, each time underneath the PCMs
|
||||
// built against the previous one, and clang crashes on those rather than
|
||||
// rejecting them.
|
||||
if (config.target.starts_with("wasm32")) {
|
||||
stdPcmKey += std::format("-{:08x}", static_cast<std::uint32_t>(std::hash<std::string_view>{}(WasmBaselineFlags)));
|
||||
}
|
||||
// The same goes for the mingw-w64 prefix the mingw std module is
|
||||
// compiled against: releases up to the fix for issue #43 gave clang the
|
||||
// prefix itself as the sysroot and the ones after give it a view of it,
|
||||
// and two prefixes are two sets of headers.
|
||||
if (config.target == "x86_64-w64-mingw32" && !GetToolchain().mingwPrefix.empty()) {
|
||||
stdPcmKey += std::format("-{:08x}", static_cast<std::uint32_t>(std::hash<std::string>{}(GetToolchain().mingwPrefix.string())));
|
||||
}
|
||||
for (const std::string& f : config.wasmVariantFlags) {
|
||||
stdPcmKey += "+";
|
||||
for (char c : f) stdPcmKey += (c == '/' || c == '\\') ? '_' : c;
|
||||
}
|
||||
return GetCacheDir()/stdPcmKey;
|
||||
return GetCacheDir() / StdPcmKey(config, GetToolchain());
|
||||
}
|
||||
|
||||
std::string Crafter::WasmCompileFlags(const Configuration& config) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue