From b8df13eb6f65a63a648e17b7474d5eb4bb1fba7e Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 00:25:52 +0800 Subject: [PATCH 01/12] fix: resolve LLVM toolchain install failure on Linux Three fixes for `mcpp toolchain install llvm` failing with "xpkg payload missing": 1. install_with_progress(): use direct `xlings install -y` command on ALL platforms (not just Windows). The direct command avoids stdin closure (/dev/null` 吞掉所有错误信息 + +`xlings.cppm:432-434` 构建的命令: + +```bash +cd ~/.mcpp && ... xlings interface install_packages --args '...' 2>/dev/null std::expected { -#if defined(_WIN32) - // Workaround: xlings on Windows may extract large packages (e.g. LLVM) - // into its global data dir instead of the mcpp sandbox, because the - // extraction subprocess doesn't inherit XLINGS_HOME. Detect this and - // copy the payload into the sandbox so mcpp remains self-contained. + // Workaround: xlings may extract large packages (e.g. LLVM) into its + // global data dir instead of the mcpp sandbox, because the extraction + // subprocess doesn't always inherit XLINGS_HOME. Detect this and copy + // the payload into the sandbox so mcpp remains self-contained. + // Originally Windows-only; extended to all platforms for the same + // reason (xlings subprocess XLINGS_HOME propagation is unreliable). if (!std::filesystem::exists(verdir)) { - // Try xlings' own data dir (where `xlings self install` placed it) - auto xhome = std::getenv("USERPROFILE"); + const char* xhome = nullptr; + if constexpr (mcpp::platform::is_windows) { + xhome = std::getenv("USERPROFILE"); + } if (!xhome) xhome = std::getenv("HOME"); if (xhome) { // xlings stores xpkgs at /.xlings/data/xpkgs/ or @@ -635,7 +638,6 @@ Fetcher::resolve_xpkg_path(std::string_view target, } } } -#endif if (!std::filesystem::exists(verdir)) { return std::unexpected(CallError{ std::format("xpkg payload missing: {}", verdir.string())}); diff --git a/src/xlings.cppm b/src/xlings.cppm index b7faf06..d31cb80 100644 --- a/src/xlings.cppm +++ b/src/xlings.cppm @@ -609,24 +609,31 @@ int install_with_progress(const Env& env, std::string_view target, auto argsJson = std::format( R"({{"targets":["{}"],"yes":true}})", target); - if constexpr (mcpp::platform::is_windows) { - mcpp::platform::env::set("XLINGS_HOME", env.home.string()); - mcpp::platform::env::set("XLINGS_PROJECT_DIR", ""); - std::error_code ec_mkdir; - std::filesystem::create_directories(env.home, ec_mkdir); - // Use direct `install` command instead of `interface install_packages` - // on Windows. The NDJSON interface may have issues with large packages - // where the extraction subprocess doesn't respect XLINGS_HOME. - auto directCmd = std::format("{} install {} -y", - env.binary.string(), target); - int directRc = mcpp::platform::process::run_silent(directCmd); + // All platforms: try direct `xlings install ... -y` first. + // The direct command is more reliable for large packages (e.g. LLVM + // ~800MB) because: + // - it doesn't pipe through NDJSON interface (simpler subprocess chain) + // - xlings manages its own stdin/stdout/stderr + // - extraction subprocess coordination works normally + // The NDJSON interface path is kept as a fallback for progress reporting. + { + auto directCmd = build_command_prefix(env) + + std::format(" install {} -y {}", target, mcpp::platform::shell::silent_redirect); + // Use std::system() directly — do NOT redirect stdin via std::string { if constexpr (mcpp::platform::is_windows) { - // Fallback to interface path if direct install fails return std::format("{} interface install_packages --args {} {}", - env.binary.string(), + build_command_prefix(env), shq(argsJson), mcpp::platform::null_redirect); } else { From 052eec249b5f3e0a27ba35e0c10d47396fd89f24 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 00:33:15 +0800 Subject: [PATCH 02/12] fix: add missing import mcpp.platform in package_fetcher.cppm Also replace raw WIFEXITED/WEXITSTATUS with platform::process::extract_exit_code() in xlings.cppm. --- src/pm/package_fetcher.cppm | 1 + src/xlings.cppm | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pm/package_fetcher.cppm b/src/pm/package_fetcher.cppm index 12be496..167a9f1 100644 --- a/src/pm/package_fetcher.cppm +++ b/src/pm/package_fetcher.cppm @@ -14,6 +14,7 @@ export module mcpp.pm.package_fetcher; import std; import mcpp.config; +import mcpp.platform; import mcpp.pm.compat; import mcpp.pm.index_spec; import mcpp.xlings; diff --git a/src/xlings.cppm b/src/xlings.cppm index d31cb80..572848e 100644 --- a/src/xlings.cppm +++ b/src/xlings.cppm @@ -622,10 +622,8 @@ int install_with_progress(const Env& env, std::string_view target, // Use std::system() directly — do NOT redirect stdin via Date: Thu, 21 May 2026 00:50:12 +0800 Subject: [PATCH 03/12] fix(ci): use 'toolchain default' instead of non-existent --toolchain flag --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1c417e..a40a881 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,14 +141,14 @@ jobs: MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)") # Install LLVM toolchain into mcpp's sandbox "$MCPP" toolchain install llvm 20.1.7 - # Verify install succeeded - "$MCPP" toolchain list | grep -q llvm + # Set as default so the build picks it up + "$MCPP" toolchain default llvm@20.1.7 # Build a hello-world project with the installed toolchain TMP=$(mktemp -d) cd "$TMP" "$MCPP" new hello cd hello - "$MCPP" build --toolchain llvm@20.1.7 + "$MCPP" build "$MCPP" run - name: Fresh user experience (xlings install mcpp → new → run) From 7e9a020c239af155d5ab7218d4053bc651b02765 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 01:13:11 +0800 Subject: [PATCH 04/12] fix: remove import mcpp.platform from package_fetcher.cppm The added import changed the module dependency graph fingerprint, invalidating BMI cache on macOS CI and exposing a pre-existing Xcode 16.4 SDK incompatibility during std module precompilation. Use #if defined(_WIN32) instead of if constexpr for the USERPROFILE check to avoid changing the module import set. --- src/pm/package_fetcher.cppm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pm/package_fetcher.cppm b/src/pm/package_fetcher.cppm index 167a9f1..988092e 100644 --- a/src/pm/package_fetcher.cppm +++ b/src/pm/package_fetcher.cppm @@ -14,7 +14,6 @@ export module mcpp.pm.package_fetcher; import std; import mcpp.config; -import mcpp.platform; import mcpp.pm.compat; import mcpp.pm.index_spec; import mcpp.xlings; @@ -614,9 +613,9 @@ Fetcher::resolve_xpkg_path(std::string_view target, // reason (xlings subprocess XLINGS_HOME propagation is unreliable). if (!std::filesystem::exists(verdir)) { const char* xhome = nullptr; - if constexpr (mcpp::platform::is_windows) { - xhome = std::getenv("USERPROFILE"); - } +#if defined(_WIN32) + xhome = std::getenv("USERPROFILE"); +#endif if (!xhome) xhome = std::getenv("HOME"); if (xhome) { // xlings stores xpkgs at /.xlings/data/xpkgs/ or From b1b872dc0f9ad4d1230976926fab424bc8064f50 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 01:26:44 +0800 Subject: [PATCH 05/12] fix(ci-macos): add MCPP_HOME env and MCPP_VENDORED_XLINGS for test step macOS CI was missing the MCPP_HOME job-level env var that Linux CI has. The freshly-built mcpp resolved to a fresh ~/.mcpp/ sandbox, triggering a clean std module precompile that exposed a pre-existing Xcode 16.4 + LLVM 20.1.7 sysroot incompatibility. Fix: set MCPP_HOME=/Users/runner/.mcpp (consistent with Linux CI) and export MCPP_VENDORED_XLINGS in the test step so the sandbox reuses the pre-installed xlings binary. --- .github/workflows/ci-macos.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 5f3e522..de12f0b 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -19,6 +19,8 @@ jobs: name: macOS ARM64 — xlings LLVM end-to-end runs-on: macos-15 timeout-minutes: 30 + env: + MCPP_HOME: /Users/runner/.mcpp steps: - uses: actions/checkout@v4 @@ -285,9 +287,12 @@ jobs: - name: Unit + integration tests via `mcpp test` run: | - # Use freshly-built mcpp (has --mirror support) + # Use freshly-built mcpp with the shared sandbox (MCPP_HOME set + # via job-level env). MCPP_VENDORED_XLINGS ensures the sandbox + # uses the pre-installed xlings binary. MCPP=$(find target -path "*/bin/mcpp" | head -1) MCPP=$(cd "$(dirname "$MCPP")" && pwd)/$(basename "$MCPP") + export MCPP_VENDORED_XLINGS="$XLINGS_BIN" "$MCPP" self config --mirror GLOBAL "$MCPP" test From 722cb0e144ffcacae63fb478d93cf91ca2aaf377 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 01:52:05 +0800 Subject: [PATCH 06/12] fix: don't use xcrun SDK as sysroot for xlings LLVM on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: probe_sysroot() falls back to xcrun --show-sdk-path on macOS when the compiler doesn't report a sysroot via -print-sysroot. For xlings-installed LLVM 20.1.7, this sets --sysroot to the Xcode SDK path. Combined with -nostdinc++ from clang++.cfg, this breaks C runtime header resolution — macOS SDK headers reference internal macros (_CTYPE_A, etc.) that are only defined via default include paths which --sysroot overrides. Fix: remove the xcrun SDK fallback in probe_sysroot(). If the compiler itself doesn't report a sysroot, none should be used. The xcrun fallback was designed for Apple's system clang, not for standalone xlings LLVM which provides its own libc++ headers. Also revert the ci-macos.yml MCPP_HOME workaround — the real bug is fixed, no workaround needed. --- .github/workflows/ci-macos.yml | 7 +------ src/toolchain/probe.cppm | 14 +++++++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index de12f0b..5f3e522 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -19,8 +19,6 @@ jobs: name: macOS ARM64 — xlings LLVM end-to-end runs-on: macos-15 timeout-minutes: 30 - env: - MCPP_HOME: /Users/runner/.mcpp steps: - uses: actions/checkout@v4 @@ -287,12 +285,9 @@ jobs: - name: Unit + integration tests via `mcpp test` run: | - # Use freshly-built mcpp with the shared sandbox (MCPP_HOME set - # via job-level env). MCPP_VENDORED_XLINGS ensures the sandbox - # uses the pre-installed xlings binary. + # Use freshly-built mcpp (has --mirror support) MCPP=$(find target -path "*/bin/mcpp" | head -1) MCPP=$(cd "$(dirname "$MCPP")" && pwd)/$(basename "$MCPP") - export MCPP_VENDORED_XLINGS="$XLINGS_BIN" "$MCPP" self config --mirror GLOBAL "$MCPP" test diff --git a/src/toolchain/probe.cppm b/src/toolchain/probe.cppm index b356775..b87ecf1 100644 --- a/src/toolchain/probe.cppm +++ b/src/toolchain/probe.cppm @@ -262,9 +262,17 @@ probe_sysroot(const std::filesystem::path& compilerBin, auto s = trim_line(*r); if (!s.empty() && std::filesystem::exists(s)) return s; } - // macOS fallback: use xcrun to discover the SDK path - if (auto sdk = mcpp::platform::macos::sdk_path()) - return *sdk; + // macOS: do NOT fall back to xcrun SDK path for xlings-installed LLVM. + // xlings LLVM uses -nostdinc++ (via clang++.cfg) to provide its own + // libc++ headers. Combining --sysroot= with -nostdinc++ + // breaks C runtime header resolution (_CTYPE_A, memcpy, errno etc. + // become undeclared) because the SDK headers assume the C runtime + // macros are available via default include paths, which --sysroot + // overrides. + // + // If the compiler itself reports a sysroot (-print-sysroot), use it — + // that's an explicit choice by the toolchain. But the xcrun SDK is + // meant for Apple's system clang, not for standalone xlings LLVM. return {}; } From e345067f21d5bb8a16090e9787b4543414bb7b12 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 02:05:55 +0800 Subject: [PATCH 07/12] fix: prefer xcrun SDK over cfg-baked sysroot on macOS probe_sysroot() called -print-sysroot first, which on macOS with xlings LLVM just echoed back the --sysroot from clang++.cfg. That cfg-baked path points to CommandLineTools SDK, which may differ from the active Xcode SDK. When the two SDKs have different header versions, std module precompilation fails with undeclared _CTYPE_A and related C runtime macro errors. Fix: on macOS, probe xcrun --show-sdk-path FIRST (always returns the active SDK), then fall back to -print-sysroot for non-macOS. The xcrun sysroot passed on the command line overrides the cfg-baked one. --- src/toolchain/probe.cppm | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/toolchain/probe.cppm b/src/toolchain/probe.cppm index b87ecf1..e0001f6 100644 --- a/src/toolchain/probe.cppm +++ b/src/toolchain/probe.cppm @@ -254,6 +254,15 @@ probe_target_triple(const std::filesystem::path& compilerBin, std::filesystem::path probe_sysroot(const std::filesystem::path& compilerBin, const std::string& envPrefix) { + // macOS: prefer xcrun's SDK path over -print-sysroot. + // xlings LLVM bakes --sysroot into clang++.cfg at install time, and + // -print-sysroot echoes that back. If the CLT/Xcode SDK was updated + // after the LLVM package was cached, the cfg-baked path may be stale + // or point to an incompatible SDK. xcrun always returns the currently + // active SDK, so mcpp passes it on the command line to override the cfg. + if (auto sdk = mcpp::platform::macos::sdk_path()) + return *sdk; + auto r = run_capture(std::format("{}{} -print-sysroot {}", envPrefix, mcpp::xlings::shq(compilerBin.string()), @@ -262,17 +271,6 @@ probe_sysroot(const std::filesystem::path& compilerBin, auto s = trim_line(*r); if (!s.empty() && std::filesystem::exists(s)) return s; } - // macOS: do NOT fall back to xcrun SDK path for xlings-installed LLVM. - // xlings LLVM uses -nostdinc++ (via clang++.cfg) to provide its own - // libc++ headers. Combining --sysroot= with -nostdinc++ - // breaks C runtime header resolution (_CTYPE_A, memcpy, errno etc. - // become undeclared) because the SDK headers assume the C runtime - // macros are available via default include paths, which --sysroot - // overrides. - // - // If the compiler itself reports a sysroot (-print-sysroot), use it — - // that's an explicit choice by the toolchain. But the xcrun SDK is - // meant for Apple's system clang, not for standalone xlings LLVM. return {}; } From 1838fb238e1ce45ea39a578c861edc7ffde47070 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 02:17:00 +0800 Subject: [PATCH 08/12] fix: skip --sysroot for std module precompile on macOS Root cause: on macOS, xlings LLVM's clang++.cfg already contains a --sysroot for the macOS SDK. When mcpp additionally passes --sysroot on the command line during std module precompilation, it changes the C header include ordering. The macOS SDK's ___wctype.h references _CTYPE_A (defined in _ctype.h), but the duplicate --sysroot flag prevents _ctype.h from being included transitively during module purview compilation, causing "undeclared identifier '_CTYPE_A'" errors. Fix: detect apple/darwin target triple in stdmod.cppm and skip the --sysroot flag for std module precompile. The cfg's own --sysroot handles macOS SDK discovery correctly for this compilation mode. Regular compilation (flags.cppm) and linking still use the probed sysroot as before. Also restore probe_sysroot() to its original logic (xcrun fallback) since the issue was in stdmod.cppm passing sysroot, not in probing it. --- src/toolchain/probe.cppm | 15 ++++++--------- src/toolchain/stdmod.cppm | 13 ++++++++++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/toolchain/probe.cppm b/src/toolchain/probe.cppm index e0001f6..ef25032 100644 --- a/src/toolchain/probe.cppm +++ b/src/toolchain/probe.cppm @@ -254,15 +254,6 @@ probe_target_triple(const std::filesystem::path& compilerBin, std::filesystem::path probe_sysroot(const std::filesystem::path& compilerBin, const std::string& envPrefix) { - // macOS: prefer xcrun's SDK path over -print-sysroot. - // xlings LLVM bakes --sysroot into clang++.cfg at install time, and - // -print-sysroot echoes that back. If the CLT/Xcode SDK was updated - // after the LLVM package was cached, the cfg-baked path may be stale - // or point to an incompatible SDK. xcrun always returns the currently - // active SDK, so mcpp passes it on the command line to override the cfg. - if (auto sdk = mcpp::platform::macos::sdk_path()) - return *sdk; - auto r = run_capture(std::format("{}{} -print-sysroot {}", envPrefix, mcpp::xlings::shq(compilerBin.string()), @@ -271,6 +262,12 @@ probe_sysroot(const std::filesystem::path& compilerBin, auto s = trim_line(*r); if (!s.empty() && std::filesystem::exists(s)) return s; } + // macOS fallback: use xcrun to discover the SDK path. + // The sysroot is used for regular compilation flags (flags.cppm) but + // skipped for std module precompilation on macOS (stdmod.cppm) to + // avoid breaking SDK internal header dependencies. + if (auto sdk = mcpp::platform::macos::sdk_path()) + return *sdk; return {}; } diff --git a/src/toolchain/stdmod.cppm b/src/toolchain/stdmod.cppm index 34a1d06..7c457f8 100644 --- a/src/toolchain/stdmod.cppm +++ b/src/toolchain/stdmod.cppm @@ -94,7 +94,18 @@ std::expected ensure_built( std::string sysroot_flag; if (!tc.sysroot.empty()) { - sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); + // On macOS, skip --sysroot for std module precompilation. + // xlings LLVM ships a clang++.cfg with a --sysroot that is tuned to + // work for normal compilation. Adding a second --sysroot (even the + // identical value) changes header include ordering and breaks macOS + // SDK internal dependencies — ___wctype.h references _CTYPE_A which + // is defined in _ctype.h, but the duplicate --sysroot flag causes + // the C runtime header to not be included transitively during module + // purview compilation. Let the cfg's own --sysroot handle it. + bool skip_sysroot = tc.targetTriple.find("apple") != std::string::npos + || tc.targetTriple.find("darwin") != std::string::npos; + if (!skip_sysroot) + sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); } bool std_cached = std::filesystem::exists(sm.bmiPath) && std::filesystem::exists(sm.objectPath); From 078873de416480c4927b0ed2c3863ed8151ec1f7 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 02:27:55 +0800 Subject: [PATCH 09/12] fix: override stale cfg sysroot with xcrun SDK for macOS std precompile The previous fix skipped --sysroot entirely on macOS, but xlings LLVM's clang++.cfg still applies its own --sysroot pointing to CommandLineTools SDK. When the active SDK is Xcode (different path), the cfg's stale sysroot causes _CTYPE_A undeclared errors. Fix: on macOS, always probe xcrun and pass the active SDK as --sysroot to OVERRIDE the cfg-baked stale path. This ensures the std module precompile uses the correct SDK regardless of what was baked into clang++.cfg at LLVM install time. --- src/toolchain/stdmod.cppm | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/toolchain/stdmod.cppm b/src/toolchain/stdmod.cppm index 7c457f8..e881dee 100644 --- a/src/toolchain/stdmod.cppm +++ b/src/toolchain/stdmod.cppm @@ -93,19 +93,21 @@ std::expected ensure_built( sm.objectPath = sm.cacheDir / "std.o"; std::string sysroot_flag; - if (!tc.sysroot.empty()) { - // On macOS, skip --sysroot for std module precompilation. - // xlings LLVM ships a clang++.cfg with a --sysroot that is tuned to - // work for normal compilation. Adding a second --sysroot (even the - // identical value) changes header include ordering and breaks macOS - // SDK internal dependencies — ___wctype.h references _CTYPE_A which - // is defined in _ctype.h, but the duplicate --sysroot flag causes - // the C runtime header to not be included transitively during module - // purview compilation. Let the cfg's own --sysroot handle it. - bool skip_sysroot = tc.targetTriple.find("apple") != std::string::npos - || tc.targetTriple.find("darwin") != std::string::npos; - if (!skip_sysroot) - sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); + bool is_macos_target = tc.targetTriple.find("apple") != std::string::npos + || tc.targetTriple.find("darwin") != std::string::npos; + if (is_macos_target) { + // macOS: always pass the *active* SDK path (from xcrun) to override + // any stale --sysroot baked into clang++.cfg by xlings at install + // time. The cfg path may point to CommandLineTools SDK while the + // runner has Xcode active, or vice versa. A mismatched SDK causes + // ___wctype.h → _CTYPE_A undeclared errors during std module + // precompilation because the SDK's internal C headers reference + // macros defined in a sibling header that the wrong SDK doesn't + // include transitively. + if (auto sdk = mcpp::platform::macos::sdk_path()) + sysroot_flag = std::format(" --sysroot='{}'", sdk->string()); + } else if (!tc.sysroot.empty()) { + sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); } bool std_cached = std::filesystem::exists(sm.bmiPath) && std::filesystem::exists(sm.objectPath); From ae7114e001747c0c41aeea8d25afc14aeebb16d3 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 02:39:19 +0800 Subject: [PATCH 10/12] fix: skip --sysroot entirely for macOS std module precompile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI step 9 proves that clang++ WITHOUT explicit --sysroot precompiles std.cppm correctly on macOS — the clang++.cfg's built-in --sysroot and -isystem flags handle SDK header resolution properly. When mcpp passes an explicit --sysroot (even the identical value), it changes Clang's internal header search order, breaking the transitive inclusion of _ctype.h before ___wctype.h. The macOS SDK's ___wctype.h references _CTYPE_A which is only defined via the default cfg-driven include chain. Fix: detect apple/darwin target and skip --sysroot for std module precompile, letting clang++.cfg handle it. Non-macOS platforms (Linux, Windows) continue to use the probed sysroot as before. --- src/toolchain/stdmod.cppm | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/toolchain/stdmod.cppm b/src/toolchain/stdmod.cppm index e881dee..801c22e 100644 --- a/src/toolchain/stdmod.cppm +++ b/src/toolchain/stdmod.cppm @@ -93,21 +93,20 @@ std::expected ensure_built( sm.objectPath = sm.cacheDir / "std.o"; std::string sysroot_flag; - bool is_macos_target = tc.targetTriple.find("apple") != std::string::npos - || tc.targetTriple.find("darwin") != std::string::npos; - if (is_macos_target) { - // macOS: always pass the *active* SDK path (from xcrun) to override - // any stale --sysroot baked into clang++.cfg by xlings at install - // time. The cfg path may point to CommandLineTools SDK while the - // runner has Xcode active, or vice versa. A mismatched SDK causes - // ___wctype.h → _CTYPE_A undeclared errors during std module - // precompilation because the SDK's internal C headers reference - // macros defined in a sibling header that the wrong SDK doesn't - // include transitively. - if (auto sdk = mcpp::platform::macos::sdk_path()) - sysroot_flag = std::format(" --sysroot='{}'", sdk->string()); - } else if (!tc.sysroot.empty()) { - sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); + if (!tc.sysroot.empty()) { + // macOS (apple/darwin target): do NOT pass --sysroot for std module + // precompilation. xlings LLVM's clang++.cfg already contains a + // --sysroot for the macOS SDK plus -isystem for libc++ headers. + // Passing an explicit --sysroot on the command line (even the same + // value) changes Clang's internal header search order, causing the + // macOS SDK's ___wctype.h to not find _CTYPE_A (defined in a + // sibling C runtime header that's only included transitively via + // the cfg's default search path). The CI "import std" test proves + // that running clang++ WITHOUT an explicit --sysroot works correctly. + bool is_macos = tc.targetTriple.find("apple") != std::string::npos + || tc.targetTriple.find("darwin") != std::string::npos; + if (!is_macos) + sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); } bool std_cached = std::filesystem::exists(sm.bmiPath) && std::filesystem::exists(sm.objectPath); From 45dec7f2d01b54ddab7b0c99c00ef9d6381523d9 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 02:52:16 +0800 Subject: [PATCH 11/12] fix: bypass stale clang++.cfg for macOS std module precompile When xlings copies LLVM to mcpp's sandbox, clang++.cfg retains hardcoded absolute paths from the original install (--sysroot and -isystem pointing to ~/.xlings/... instead of ~/.mcpp/...). These stale paths cause _CTYPE_A undeclared errors during std module precompilation. Fix: on macOS Clang, pass --no-default-config to ignore the stale cfg, then explicitly provide the correct -isystem (libc++ headers from the sandbox LLVM root) and --sysroot (active SDK from xcrun). This produces the same header search behavior as a fresh clang++ invocation with the correct paths. --- src/toolchain/stdmod.cppm | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/toolchain/stdmod.cppm b/src/toolchain/stdmod.cppm index 801c22e..0220d75 100644 --- a/src/toolchain/stdmod.cppm +++ b/src/toolchain/stdmod.cppm @@ -92,21 +92,27 @@ std::expected ensure_built( : mcpp::toolchain::gcc::std_bmi_path(sm.cacheDir); sm.objectPath = sm.cacheDir / "std.o"; + // Build sysroot + include flags for std module precompilation. + // On macOS, xlings LLVM's clang++.cfg contains hardcoded --sysroot and + // -isystem paths from the original install location. When the LLVM package + // is copied to mcpp's sandbox, these cfg paths become stale (still point + // to the original xlings directory). We override both: + // --sysroot → current active SDK (from xcrun) + // --no-default-config → ignore stale cfg entirely + // -isystem → correct libc++ headers in the sandbox copy std::string sysroot_flag; - if (!tc.sysroot.empty()) { - // macOS (apple/darwin target): do NOT pass --sysroot for std module - // precompilation. xlings LLVM's clang++.cfg already contains a - // --sysroot for the macOS SDK plus -isystem for libc++ headers. - // Passing an explicit --sysroot on the command line (even the same - // value) changes Clang's internal header search order, causing the - // macOS SDK's ___wctype.h to not find _CTYPE_A (defined in a - // sibling C runtime header that's only included transitively via - // the cfg's default search path). The CI "import std" test proves - // that running clang++ WITHOUT an explicit --sysroot works correctly. - bool is_macos = tc.targetTriple.find("apple") != std::string::npos - || tc.targetTriple.find("darwin") != std::string::npos; - if (!is_macos) - sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); + bool is_macos = tc.targetTriple.find("apple") != std::string::npos + || tc.targetTriple.find("darwin") != std::string::npos; + if (is_macos && is_clang(tc)) { + // Ignore the stale clang++.cfg and provide correct flags directly. + auto llvmRoot = tc.binaryPath.parent_path().parent_path(); + auto libcxxInclude = llvmRoot / "include" / "c++" / "v1"; + sysroot_flag = " --no-default-config"; + sysroot_flag += std::format(" -isystem'{}'", libcxxInclude.string()); + if (auto sdk = mcpp::platform::macos::sdk_path()) + sysroot_flag += std::format(" --sysroot='{}'", sdk->string()); + } else if (!tc.sysroot.empty()) { + sysroot_flag = std::format(" --sysroot='{}'", tc.sysroot.string()); } bool std_cached = std::filesystem::exists(sm.bmiPath) && std::filesystem::exists(sm.objectPath); From efdf1ddb97efbc76eee19b9a5474fa09b1cfc3fb Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 21 May 2026 03:03:11 +0800 Subject: [PATCH 12/12] fix: apply --no-default-config for all macOS Clang compilation The stale clang++.cfg issue affects not just std module precompile but also regular compilation via ninja. flags.cppm now applies the same --no-default-config + correct -isystem + xcrun --sysroot fix for all macOS Clang compilation, bypassing the cfg's stale paths. --- src/build/flags.cppm | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/build/flags.cppm b/src/build/flags.cppm index 6f0a445..e7278c7 100644 --- a/src/build/flags.cppm +++ b/src/build/flags.cppm @@ -88,9 +88,24 @@ CompileFlags compute_flags(const BuildPlan& plan) { include_flags += " -I" + escape_path(abs); } - // Sysroot + // Sysroot + config override for macOS. + // On macOS, xlings LLVM's clang++.cfg contains hardcoded --sysroot and + // -isystem paths from the original install location. When the package is + // copied to mcpp's sandbox, these paths become stale. We pass + // --no-default-config to ignore the cfg and provide correct paths. std::string sysroot_flag; - if (!plan.toolchain.sysroot.empty()) { + bool is_macos_clang = mcpp::toolchain::is_clang(plan.toolchain) + && (plan.toolchain.targetTriple.find("apple") != std::string::npos + || plan.toolchain.targetTriple.find("darwin") != std::string::npos); + if (is_macos_clang) { + auto llvmRoot = plan.toolchain.binaryPath.parent_path().parent_path(); + auto libcxxInclude = llvmRoot / "include" / "c++" / "v1"; + sysroot_flag = " --no-default-config"; + sysroot_flag += " -isystem" + escape_path(libcxxInclude); + if (auto sdk = mcpp::platform::macos::sdk_path()) + sysroot_flag += " --sysroot=" + escape_path(*sdk); + f.sysroot = sysroot_flag; + } else if (!plan.toolchain.sysroot.empty()) { sysroot_flag = " --sysroot=" + escape_path(plan.toolchain.sysroot); f.sysroot = sysroot_flag; }