diff --git a/.agents/docs/fix-xlings-package-home-detection.md b/.agents/docs/fix-xlings-package-home-detection.md new file mode 100644 index 0000000..989c34d --- /dev/null +++ b/.agents/docs/fix-xlings-package-home-detection.md @@ -0,0 +1,24 @@ +# Fix: xlings 包内 mcpp 的 MCPP_HOME 检测 + +## 问题 + +`xlings install mcpp` 安装的 mcpp 二进制位于 `~/.xlings/data/xpkgs/xim-x-mcpp/0.0.20/bin/mcpp`。 +`home_dir()` 检测到 `bin/` 父目录后,将祖父目录作为 MCPP_HOME,导致嵌套沙箱: + +``` +MCPP_HOME = ~/.xlings/data/xpkgs/xim-x-mcpp/0.0.20/ + → registry/ = ~/.xlings/data/xpkgs/xim-x-mcpp/0.0.20/registry/ + → XLINGS_HOME = 同上 + → 工具链安装到 registry/data/xpkgs/xim-x-llvm/20.1.7/ ← 嵌套失败 +``` + +## 修复 + +在 `home_dir()` 的自包含模式检测中,增加 xlings 包路径排除:如果祖先目录中存在 +`data/xpkgs` 模式,说明在 xlings 包内,fallback 到 `~/.mcpp/`。 + +同时提取 `default_mcpp_home()` 统一三平台的 HOME fallback(Windows 用 %USERPROFILE%)。 + +## 改动 + +仅 `src/config.cppm` 的 `home_dir()` 函数,约 20 行。 diff --git a/src/config.cppm b/src/config.cppm index 93b27b2..94be67b 100644 --- a/src/config.cppm +++ b/src/config.cppm @@ -168,31 +168,47 @@ void print_status(std::string_view verb, std::string_view msg) { } } +std::filesystem::path default_mcpp_home() { + // Windows: %USERPROFILE%\.mcpp POSIX: $HOME/.mcpp + if constexpr (mcpp::platform::is_windows) { + if (auto* e = std::getenv("USERPROFILE"); e && *e) + return std::filesystem::path(e) / ".mcpp"; + } + if (auto* e = std::getenv("HOME"); e && *e) + return std::filesystem::path(e) / ".mcpp"; + return std::filesystem::current_path() / ".mcpp"; +} + std::filesystem::path home_dir() { + // 1. Explicit $MCPP_HOME takes priority (CI, advanced users). if (auto* e = std::getenv("MCPP_HOME"); e && *e) return std::filesystem::path(e); - std::error_code ec; auto exe = mcpp::platform::fs::self_exe_path(); if (exe.has_parent_path() && exe.parent_path().filename() == "bin") { - // Dev builds emit binaries at target///bin/, - // matching the bin/ shape. Any ancestor literally named - // "target" disqualifies self-contained mode and falls through - // to $HOME/.mcpp — so first-run on a dev binary doesn't drop - // a half-populated sandbox into target/. - bool isDevPath = false; - for (auto p = exe.parent_path(); - !p.empty() && p != p.parent_path(); + auto candidate = exe.parent_path().parent_path(); + + // Disqualify self-contained mode for two cases: + // a) Dev builds: .../target///bin/ + // b) xlings packages: .../data/xpkgs/xim-x-mcpp//bin/mcpp + // Creating a nested xlings sandbox inside the xpkgs directory + // breaks toolchain installation (nested XLINGS_HOME) and loses + // installed toolchains when the mcpp package version is upgraded. + bool disqualified = false; + for (auto p = candidate; + p.has_parent_path() && p != p.root_path(); p = p.parent_path()) { - if (p.filename() == "target") { isDevPath = true; break; } + if (p.filename() == "target") { disqualified = true; break; } + if (p.filename() == "xpkgs") { + auto parent = p.parent_path().filename().string(); + if (parent == "data") { disqualified = true; break; } + } } - if (!isDevPath) - return exe.parent_path().parent_path(); + if (!disqualified) + return candidate; } - if (auto* e = std::getenv("HOME"); e && *e) - return std::filesystem::path(e) / ".mcpp"; - return std::filesystem::current_path() / ".mcpp"; + return default_mcpp_home(); } std::expected run_capture(const std::string& cmd) {