feat(toolchain): resolve the compiler in one place
Every clang++, clang and llvm-ar the tool spawned was a bare name on PATH, the std module source was /usr/share/libc++/v1/std.cppm, the WASI sysroot /usr/share/wasi-sysroot and the mingw headers /usr/x86_64-w64-mingw32, spelled out at eighteen call sites. A toolchain installed anywhere else, which is what the Flatpak ships under /app, had no way in. Crafter.Build:Toolchain resolves all of it once per process: CRAFTER_BUILD_LLVM_DIR, else a clang++ installed beside crafter-build (<prefix>/bin, found through the same walk GetCrafterBuildHome does), else PATH. What sits beside the compiler is used when present: its libclang.so, its clang-c headers, its std module source, its WASI sysroot. An explicit prefix without a clang++ is an error rather than a fallback, for the same reason CRAFTER_BUILD_LIBCLANG was already exclusive. CRAFTER_BUILD_LIBCXX_DIR points at a libc++ install other than the compiler's own, and CRAFTER_MINGW_DIR now does what its help line always claimed: it is passed as --sysroot for the mingw target. Binaries linked by a toolchain outside /usr get an rpath to its lib dir, since its libc++.so.1 is not on the loader's default path. HostTarget() is the resolved clang's -print-target-triple. A clang built without a vendor reports x86_64-unknown-linux-gnu where Arch's reports x86_64-pc-linux-gnu, so target equality that decides whether a test runs locally and which targets a bare `test` sweeps goes through SameTarget, which ignores the vendor field. The strings themselves are never rewritten: clang rejects a PCM whose triple differs from the importing unit's. The examples and README stop hardcoding the host triple; the default is already the host. `crafter-build --version` prints the version baked in at build time (CRAFTER_BUILD_VERSION, else `git describe`, else 0.0.0-dev), the compiler and the host triple, and is answered before any argument is forwarded to project.cpp. The version is a define on the library, so each version is its own library variant and relinks the executable and tests once per commit. build.sh gains a shebang and `set -eu`, honours CRAFTER_BUILD_LLVM_DIR and CRAFTER_BUILD_VERSION, skips the glslang clone when it is already there, and writes a fresh archive instead of appending to one that may hold members from an older layout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
68e9b6bfa5
commit
046609ff6f
18 changed files with 767 additions and 190 deletions
|
|
@ -14,6 +14,7 @@ module;
|
|||
module Crafter.Build:Platform_impl;
|
||||
import std;
|
||||
import :Platform;
|
||||
import :Toolchain;
|
||||
import :Clang;
|
||||
import :Interface;
|
||||
import :External;
|
||||
|
|
@ -595,7 +596,7 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
std::vector<std::string> folders;
|
||||
|
||||
// Iterate through the directory and collect all subdirectories
|
||||
for (const auto& entry : fs::directory_iterator("/usr/x86_64-w64-mingw32/include/c++")) {
|
||||
for (const auto& entry : fs::directory_iterator(GetToolchain().mingwSysroot / "include" / "c++")) {
|
||||
if (entry.is_directory()) {
|
||||
folders.push_back(entry.path().filename().string());
|
||||
}
|
||||
|
|
@ -608,7 +609,7 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
|
||||
std::string mingWversion = folders.front();
|
||||
|
||||
fs::path stdCc = fs::path(std::format("/usr/x86_64-w64-mingw32/include/c++/{}/bits/std.cc", mingWversion));
|
||||
fs::path stdCc = GetToolchain().mingwSysroot / "include" / "c++" / mingWversion / "bits" / "std.cc";
|
||||
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCc)) {
|
||||
// -femulated-tls keeps PCM TLS access matching libstdc++'s emutls
|
||||
|
|
@ -621,7 +622,7 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
if (copyEc) {
|
||||
return std::format("copy {} -> {}: {}", stdCc.string(), stdCppm.string(), copyEc.message());
|
||||
}
|
||||
return RunBuildCommand(std::format("clang++ --target={} -march={} -mtune={} -femulated-tls -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", config.target, config.march, config.mtune, stdCppm.string(), stdPcm.string()));
|
||||
return RunBuildCommand(std::format("{} --target={} -march={} -mtune={} -femulated-tls -O3 -std=c++26 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, config.march, config.mtune, stdCppm.string(), stdPcm.string()));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -634,15 +635,15 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
if (isWasm) {
|
||||
stdCppm = std::format("{}/share/libc++/v1/std.cppm", config.sysroot);
|
||||
} else if (config.sysroot.empty()) {
|
||||
stdCppm = "/usr/share/libc++/v1/std.cppm";
|
||||
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("/usr/share/libc++/v1/std.cppm")) {
|
||||
stdCppm = "/usr/share/libc++/v1/std.cppm";
|
||||
if (!fs::exists(stdCppm) && fs::exists(GetToolchain().stdCppm)) {
|
||||
stdCppm = GetToolchain().stdCppm.string();
|
||||
}
|
||||
}
|
||||
std::string sysrootFlag = config.sysroot.empty()
|
||||
|
|
@ -669,7 +670,7 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
|
|||
archFlags += std::format(" -nostdinc++ -isystem {}/usr/include/c++/v1", config.sysroot);
|
||||
}
|
||||
if(!fs::exists(stdPcm) || fs::last_write_time(stdPcm) < fs::last_write_time(stdCppm)) {
|
||||
return RunBuildCommand(std::format("clang++ --target={} -std=c++26 -stdlib=libc++{}{} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", config.target, sysrootFlag, archFlags, stdCppm, stdPcm.string()));
|
||||
return RunBuildCommand(std::format("{} --target={} -std=c++26{}{} -O3 -Wno-reserved-identifier -Wno-reserved-module-identifier --precompile {} -o {}", GetBaseCommand(config), config.target, sysrootFlag, archFlags, stdCppm, stdPcm.string()));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -689,17 +690,24 @@ fs::path Crafter::GetCacheDir() {
|
|||
}
|
||||
|
||||
std::string Crafter::GetBaseCommand(const Configuration& config) {
|
||||
std::string stdlib;
|
||||
if(config.target == "x86_64-w64-mingw32") {
|
||||
stdlib = "";
|
||||
} else {
|
||||
stdlib = "-stdlib=libc++";
|
||||
const Toolchain& tc = GetToolchain();
|
||||
std::string command = tc.clangxx.string();
|
||||
if (config.target == "x86_64-w64-mingw32") {
|
||||
// libstdc++ from the mingw toolchain, found by clang's own mingw
|
||||
// discovery unless the sysroot was given explicitly.
|
||||
if (tc.mingwSysrootExplicit) command += std::format(" --sysroot={}", tc.mingwSysroot.string());
|
||||
return command;
|
||||
}
|
||||
return std::format("clang++ {}", stdlib);
|
||||
command += " -stdlib=libc++";
|
||||
// A sysroot build (wasm, a cross rootfs) takes libc++ from the sysroot;
|
||||
// only host builds can be pointed at an explicit libc++ install.
|
||||
if (config.sysroot.empty()) command += tc.LibcxxCompileFlags();
|
||||
return command;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr std::array<std::string_view, 11> CrafterBuildModules = {
|
||||
constexpr std::array<std::string_view, 12> CrafterBuildModules = {
|
||||
"Crafter.Build-Toolchain",
|
||||
"Crafter.Build-Shader",
|
||||
"Crafter.Build-Platform",
|
||||
"Crafter.Build-Interface",
|
||||
|
|
@ -713,7 +721,7 @@ namespace {
|
|||
"Crafter.Build",
|
||||
};
|
||||
|
||||
void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir) {
|
||||
void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir, const Configuration& host) {
|
||||
CacheLock lock(cacheDir);
|
||||
// Re-derived under the lock: another builder may have refreshed the
|
||||
// cache from its own sources while we waited.
|
||||
|
|
@ -729,12 +737,12 @@ namespace {
|
|||
continue;
|
||||
}
|
||||
std::string cmd = std::format(
|
||||
"clang++ --target={} -march=native -mtune=native "
|
||||
"-std=c++26 -stdlib=libc++ -O3 "
|
||||
"{} --target={} -march=native -mtune=native "
|
||||
"-std=c++26 -O3 "
|
||||
"-Wno-reserved-identifier -Wno-reserved-module-identifier "
|
||||
"-fprebuilt-module-path={} "
|
||||
"--precompile {} -o {}",
|
||||
HostTarget(), cacheDir.string(), cppmPath.string(), pcmPath.string());
|
||||
GetBaseCommand(host), host.target, cacheDir.string(), cppmPath.string(), pcmPath.string());
|
||||
CommandResult r = Crafter::RunCommandChecked(cmd);
|
||||
if (r.exitCode != 0) {
|
||||
throw std::runtime_error(std::format("Failed to precompile {} (exit {}): {}", name, r.exitCode, r.output));
|
||||
|
|
@ -748,6 +756,7 @@ namespace {
|
|||
}
|
||||
|
||||
Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const std::string_view> args) {
|
||||
RequireToolchain();
|
||||
fs::path absProject = fs::canonical(projectFile);
|
||||
ProjectFileGuard guard(absProject);
|
||||
fs::path buildDir = absProject.parent_path() / "build";
|
||||
|
|
@ -775,7 +784,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
|
|||
throw std::runtime_error(std::format("Failed to build std.pcm: {}", stdResult));
|
||||
}
|
||||
|
||||
EnsureCrafterBuildPcms(sourceDir, cacheDir);
|
||||
EnsureCrafterBuildPcms(sourceDir, cacheDir, hostConfig);
|
||||
|
||||
// After the host PCMs exist (a dependency's build imports Crafter.Build
|
||||
// from that cache) and before the staleness check (its artifacts are
|
||||
|
|
@ -792,13 +801,13 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
|
|||
// and a dependency archive built in release holds ThinLTO bitcode,
|
||||
// which GNU ld can't read.
|
||||
std::string compileCmd = std::format(
|
||||
"clang++ --target={} -march=native -mtune=native "
|
||||
"-std=c++26 -stdlib=libc++ -shared -fPIC -O3 -fuse-ld=lld "
|
||||
"{} --target={} -march=native -mtune=native "
|
||||
"-std=c++26 -shared -fPIC -O3 -fuse-ld=lld "
|
||||
"-Wno-return-type-c-linkage "
|
||||
"-fprebuilt-module-path={}{} "
|
||||
"{} -o {}{}",
|
||||
hostConfig.target, cacheDir.string(), preDeps.compileFlags,
|
||||
absProject.string(), soPath.string(), preDeps.linkFlags);
|
||||
"{} -o {}{}{}",
|
||||
GetBaseCommand(hostConfig), hostConfig.target, cacheDir.string(), preDeps.compileFlags,
|
||||
absProject.string(), soPath.string(), preDeps.linkFlags, GetToolchain().LibcxxLinkFlags());
|
||||
|
||||
std::string result = RunBuildCommand(compileCmd);
|
||||
if (!result.empty()) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue