// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Build:Toolchain; import std; namespace fs = std::filesystem; export namespace Crafter { // The compiler crafter-build drives and what sits next to it, resolved // once per process by GetToolchain(). Every clang++, clang and llvm-ar // the tool spawns, the libclang the linter dlopens, the std module // source and the default sysroots come from here, so a toolchain // installed anywhere other than /usr (the Flatpak carries its own under // /app) needs no per-call-site knowledge. struct Toolchain { // LLVM install prefix. Empty when the tools are bare names on PATH. fs::path prefix; fs::path clangxx; fs::path clang; fs::path llvmAr; // Where clang-c/Index.h lives when it is not on the default include // path; empty otherwise. fs::path llvmIncludeDir; // The exact libclang to dlopen; nullopt means search the usual names. std::optional libclang; // A libc++ install other than the compiler's own // (CRAFTER_BUILD_LIBCXX_DIR); empty means clang finds its own. fs::path libcxxDir; // libc++'s std module source for host builds. fs::path stdCppm; // The _LIBCPP_VERSION of the libc++ stdCppm belongs to (220108 for // 22.1.8); 0 when its <__config> could not be read. std::int32_t libcxxVersion = 0; // Std module sources for libc++ releases other than the toolchain's // own, one /std.cppm each, for sysroots that carry libc++ // without them: /share/crafter-build/libc++-modules. // Empty when the crafter prefix is unknown; need not exist. fs::path stdModulesDir; fs::path wasiSysroot; // The mingw-w64 install prefix, the directory that holds // x86_64-w64-mingw32/ and lib/gcc/x86_64-w64-mingw32/ (/usr for a // distro package), passed to clang as --sysroot for the mingw target. // Empty means clang's own discovery, which follows the mingw gcc on // PATH. Set by CRAFTER_MINGW_DIR, else the host's /usr when running // sandboxed and it has mingw-w64 (the sandbox's own /usr never does). fs::path mingwSysroot; // `clang++ -print-target-triple` and the first line of // `clang++ --version`; both empty when clang++ could not be run. std::string hostTriple; std::string clangVersion; // Why clang++ could not be resolved or run; empty when it could. std::string error; // Flags for compiling against libcxxDir; empty without one. std::string LibcxxCompileFlags() const; // Flags for linking against the libc++ the compiler will use: a -L // and an rpath for /lib when the prefix is not the system one // (clang searches only its per-target runtime directory and the // system paths, and a bundled toolchain's libc++.so.1 is on neither // the linker's nor the loader's default path), or for an explicit // libcxxDir. std::string LibcxxLinkFlags() const; // Names this toolchain for cache directory names: the clang version // followed by a hash of what decides which headers and libraries a // PCM is built against. Two toolchains with different keys never // share a PCM. std::string Key() const; }; // What ResolveToolchain reads, so a test can feed it a staged layout. struct ToolchainInputs { // The CRAFTER_* variables, present only when set and non-empty. std::map> env; // The of /share/crafter-build; empty when unknown. fs::path crafterPrefix; // Where the host's root shows inside a sandbox (/run/host in a // Flatpak); empty outside one. fs::path hostRoot; }; // Pure path resolution: an env override wins, then a toolchain installed // beside crafter-build (/bin/clang++), then bare names on // PATH. Never runs a process, so hostTriple and clangVersion stay empty. Toolchain ResolveToolchain(const ToolchainInputs& inputs); // ResolveToolchain over the real environment plus the two clang probes, // computed once and cached. Never throws. const Toolchain& GetToolchain(); // GetToolchain(), or a std::runtime_error carrying its error. const Toolchain& RequireToolchain(); // _LIBCPP_VERSION as the <__config> in `includeDir` (an include/c++/v1) // defines it, XXYYZZ; 0 when the file is missing or has no such line. std::int32_t LibcxxVersion(const fs::path& includeDir); // "23.1.2" for 230102. std::string LibcxxVersionString(std::int32_t version); struct StdModuleSource { fs::path stdCppm; // One line saying why no source fits; empty when stdCppm is set. std::string error; }; // The std module source to precompile for a native (non-wasm) build // against `sysroot`. The module source is a list of using-declarations // for names the headers of the same libc++ release declare, so one from // another release fails on every name that release added or dropped. In // order: the sysroot's own /usr/share/libc++/v1/std.cppm, the // toolchain's when both are the same major release, the copy for the // sysroot's release under toolchain.stdModulesDir. Not every distro // packages the module sources with its libc++ (Alpine does not), which // is why the sysroot's own copy cannot be the only answer. When the // sysroot's release cannot be read, the toolchain's source is returned // and the compile is the check. StdModuleSource SysrootStdModule(const Toolchain& toolchain, const fs::path& sysroot); // The version baked in when crafter-build was built; "0.0.0-dev" for a // build that was given none. std::string_view CrafterBuildVersion(); }