|
| 1 | +# resolve_xpkg_path() 的 copy 优先级问题分析 |
| 2 | + |
| 3 | +**Date**: 2026-05-22 |
| 4 | + |
| 5 | +## 一、当前流程 |
| 6 | + |
| 7 | +`resolve_xpkg_path()` (`src/pm/package_fetcher.cppm:580-718`) 的执行顺序: |
| 8 | + |
| 9 | +``` |
| 10 | +resolve_xpkg_path(target, autoInstall) |
| 11 | +│ |
| 12 | +├─ resolve() ← 第一次调用 |
| 13 | +│ ├─ sandbox 里有?→ 直接返回 ✅ |
| 14 | +│ ├─ sandbox 里没有?→ 检查 ~/.xlings/ |
| 15 | +│ │ ├─ ~/.xlings/ 里有?→ copy 到 sandbox → 返回 ✅ |
| 16 | +│ │ └─ ~/.xlings/ 里没有?→ 返回 error |
| 17 | +│ └─ 返回 error |
| 18 | +│ |
| 19 | +├─ resolve() 成功?→ return(不会触发 install) |
| 20 | +│ |
| 21 | +├─ autoInstall=false?→ return error |
| 22 | +│ |
| 23 | +├─ install() ← 只有 resolve() 失败且 autoInstall=true 才走到这里 |
| 24 | +│ └─ xlings interface install_packages |
| 25 | +│ |
| 26 | +└─ resolve() ← 第二次调用(install 后再 resolve) |
| 27 | + └─ 同上逻辑(sandbox → copy → error) |
| 28 | +``` |
| 29 | + |
| 30 | +## 二、问题:copy 短路了 install |
| 31 | + |
| 32 | +**核心问题**:只要 `~/.xlings/` 里有这个包,`resolve()` 就会直接 copy 并返回成功, |
| 33 | +**永远不会走到 `install()` 路径**。 |
| 34 | + |
| 35 | +### 场景 1:用户之前用系统 xlings 装过 LLVM |
| 36 | + |
| 37 | +``` |
| 38 | +~/.xlings/data/xpkgs/xim-x-llvm/20.1.7/ ← 存在(旧版本) |
| 39 | +~/.mcpp/registry/data/xpkgs/xim-x-llvm/20.1.7/ ← 不存在 |
| 40 | +
|
| 41 | +resolve(): |
| 42 | + sandbox 没有 → 检查 ~/.xlings/ → 有 → copy → 返回成功 |
| 43 | + ↑ 完全跳过 install,即使 ~/.xlings/ 里的版本可能有问题 |
| 44 | +``` |
| 45 | + |
| 46 | +**后果**: |
| 47 | +- mcpp 拿到的是 xlings 全局环境的旧包,可能跟 mcpp sandbox 不兼容 |
| 48 | +- ELF RUNPATH 指向 `~/.xlings/...`(这就是 libatomic bug 的根源) |
| 49 | +- mcpp 无法确保拿到的包是用 `XLINGS_HOME=~/.mcpp/registry` 安装的 |
| 50 | + |
| 51 | +### 场景 2:全局也没有,需要全新安装 |
| 52 | + |
| 53 | +``` |
| 54 | +~/.xlings/data/xpkgs/xim-x-llvm/20.1.7/ ← 不存在 |
| 55 | +~/.mcpp/registry/data/xpkgs/xim-x-llvm/20.1.7/ ← 不存在 |
| 56 | +
|
| 57 | +resolve(): |
| 58 | + sandbox 没有 → 检查 ~/.xlings/ → 也没有 → 返回 error |
| 59 | +
|
| 60 | +install(): |
| 61 | + xlings interface install_packages → exitCode=0 |
| 62 | + 但 LLVM 实际没装到 sandbox(xlings bug) |
| 63 | + 也没装到 ~/.xlings/(安装可能不完整) |
| 64 | +
|
| 65 | +resolve()(第二次): |
| 66 | + sandbox 没有 → ~/.xlings/ 也没有 → 返回 "xpkg payload missing" |
| 67 | +``` |
| 68 | + |
| 69 | +**后果**:全新安装完全失败(就是你遇到的情况) |
| 70 | + |
| 71 | +### 场景 3:sandbox 里已有 |
| 72 | + |
| 73 | +``` |
| 74 | +~/.mcpp/registry/data/xpkgs/xim-x-llvm/20.1.7/ ← 存在 |
| 75 | +
|
| 76 | +resolve(): |
| 77 | + sandbox 有 → 直接返回成功 |
| 78 | + ↑ 不检查版本、完整性、RUNPATH 正确性 |
| 79 | +``` |
| 80 | + |
| 81 | +**后果**:如果之前拷贝的包有问题(比如 RUNPATH 错误),不会自动修复 |
| 82 | + |
| 83 | +## 三、问题分层 |
| 84 | + |
| 85 | +| 层 | 问题 | 严重度 | |
| 86 | +|----|------|--------| |
| 87 | +| **优先级反转** | copy 优先于 install,导致 install 路径几乎不被执行 | 高 | |
| 88 | +| **来源不可信** | 从 `~/.xlings/` 拷贝的包不是为 mcpp sandbox 构建的 | 高 | |
| 89 | +| **无完整性检查** | copy 后不验证包是否完整、路径是否正确 | 中 | |
| 90 | +| **install 路径不可靠** | xlings NDJSON interface 安装大包时返回成功但未实际安装 | 高 | |
| 91 | +| **无版本/时间戳校验** | 不检查 `~/.xlings/` 的包是否比 sandbox 的更新 | 低 | |
| 92 | + |
| 93 | +## 四、理想的执行流程 |
| 94 | + |
| 95 | +``` |
| 96 | +resolve_xpkg_path(target, autoInstall) |
| 97 | +│ |
| 98 | +├─ 1. sandbox 里有且完整?→ 直接返回 ✅ |
| 99 | +│ |
| 100 | +├─ 2. autoInstall? |
| 101 | +│ ├─ 是 → install()(用 XLINGS_HOME=sandbox 安装到 sandbox) |
| 102 | +│ │ ├─ 成功且 sandbox 里有?→ 返回 ✅ |
| 103 | +│ │ └─ 失败 → 走 fallback |
| 104 | +│ └─ 否 → 走 fallback |
| 105 | +│ |
| 106 | +├─ 3. fallback: ~/.xlings/ 里有? |
| 107 | +│ ├─ 是 → copy + post-copy fixup → 返回 ✅ |
| 108 | +│ └─ 否 → 返回 error |
| 109 | +│ |
| 110 | +└─ 4. 返回结果 |
| 111 | +``` |
| 112 | + |
| 113 | +关键变化:**install 优先于 copy**。copy 只是 fallback,不是首选路径。 |
| 114 | + |
| 115 | +## 五、修复方案 |
| 116 | + |
| 117 | +### 方案 A:调换 install 和 copy 的优先级 |
| 118 | + |
| 119 | +将 `resolve()` 中的 copy workaround 移到 `install()` 之后: |
| 120 | + |
| 121 | +``` |
| 122 | +resolve_xpkg_path(target, autoInstall): |
| 123 | + 1. check sandbox → return if exists |
| 124 | + 2. if autoInstall → install via xlings |
| 125 | + 3. check sandbox again → return if exists |
| 126 | + 4. FALLBACK: copy from ~/.xlings/ (workaround) |
| 127 | + 5. check sandbox again → return if exists |
| 128 | + 6. error: payload missing |
| 129 | +``` |
| 130 | + |
| 131 | +**优点**:install 路径得到优先执行,copy 只是最后兜底 |
| 132 | +**缺点**:如果 install 慢或失败,用户体验变差(之前可以秒拷贝) |
| 133 | + |
| 134 | +### 方案 B:install 优先 + copy fallback + 超时 |
| 135 | + |
| 136 | +``` |
| 137 | +resolve_xpkg_path(target, autoInstall): |
| 138 | + 1. check sandbox → return if exists |
| 139 | + 2. if autoInstall → try install (with timeout) |
| 140 | + 3. check sandbox → return if exists |
| 141 | + 4. copy from ~/.xlings/ if available |
| 142 | + 5. post-copy fixup (patchelf RUNPATH) |
| 143 | + 6. return or error |
| 144 | +``` |
| 145 | + |
| 146 | +**优点**:兼顾速度(install 失败时快速 fallback)和正确性 |
| 147 | +**缺点**:增加超时逻辑的复杂度 |
| 148 | + |
| 149 | +### 方案 C:install 优先 + install 直接调用(非 NDJSON) |
| 150 | + |
| 151 | +之前排查发现 NDJSON interface 路径安装大包不可靠。`install_with_progress()` |
| 152 | +已有"直接调用" fallback(`std::system("xlings install ... -y")`)。 |
| 153 | + |
| 154 | +将工具链安装改为使用 `install_with_progress()`(直接调用模式)而非 |
| 155 | +`install()`(NDJSON interface 模式): |
| 156 | + |
| 157 | +``` |
| 158 | +resolve_xpkg_path(target, autoInstall): |
| 159 | + 1. check sandbox → return if exists |
| 160 | + 2. if autoInstall → install_with_progress (direct mode) |
| 161 | + 3. check sandbox → return if exists |
| 162 | + 4. copy from ~/.xlings/ as fallback |
| 163 | + 5. return or error |
| 164 | +``` |
| 165 | + |
| 166 | +**优点**: |
| 167 | +- 修复了 NDJSON interface 安装大包不可靠的问题 |
| 168 | +- install 正确执行时,包直接装到 sandbox,无需 copy |
| 169 | +- copy 只在 install 真正失败时兜底 |
| 170 | + |
| 171 | +**缺点**:需要在 package_fetcher 层引入 install_with_progress |
| 172 | + |
| 173 | +### 方案 D:保持 copy 优先但增加 post-copy fixup(当前状态) |
| 174 | + |
| 175 | +当前 PR #67 的做法:保持 copy 优先,但在工具链 post-install 时修正 RUNPATH。 |
| 176 | + |
| 177 | +**优点**:改动最小,已实施 |
| 178 | +**缺点**: |
| 179 | +- copy 仍然优先于 install,install 路径几乎不被测试 |
| 180 | +- 依赖 `~/.xlings/` 有正确的包(全新机器无 `~/.xlings/` 则完全失败) |
| 181 | +- 每个工具链都需要写对应的 fixup |
| 182 | + |
| 183 | +## 六、建议 |
| 184 | + |
| 185 | +**短期(已完成)**:方案 D — post-copy fixup 兜底 |
| 186 | + |
| 187 | +**中期(推荐)**:方案 C — install 优先 + 直接调用模式 |
| 188 | +- 修改 `resolve_xpkg_path()` 的流程顺序 |
| 189 | +- 工具链安装使用 `install_with_progress()`(直接调用) |
| 190 | +- copy 降级为 fallback |
| 191 | +- 这是最务实的方案,解决了优先级反转和 NDJSON 不可靠两个问题 |
| 192 | + |
| 193 | +**长期**:方案 C + 在 copy fallback 后统一做 RUNPATH fixup |
| 194 | +- 将 patchelf fixup 从各工具链的 post-install 提取到 copy 出口统一处理 |
| 195 | +- 未来加新工具链不会再遗漏 |
0 commit comments