概述
mcppls(mcpp-language-server)在后台调用 mcpp emit build-database --format json(#636,2026.9.15.1 起提供)读取工程的构建描述。2026-09-16 在 openxlings/xlings 上:
- 一次调用在规划阶段同步执行
xlings update,这条网络连接挂了约 11.5 分钟,mcpp 一直阻塞。
- 调用方在 5 分钟时结束了 mcpp,但 mcpp 启动的
xlings update 仍持有调用方的标准输出管道,调用方又多等了 6.5 分钟才读到管道结束。
- 同一工程离线运行只要 2.6 s,得到的构建描述与联网成功时逐字节相同。
mcppls 这边会改为默认离线调用,并自己兜住挂起(见文末)。下面六项需要 mcpp 适配:前三项是这次挂起的原因,同样影响在终端里运行 mcpp build 的用户;后三项让调用方能正确判断和提示。
现场(xlings,mcpp 2026.9.15.1,Linux)
- VS Code 经
bin/code 启动(VSCODE_CLI=1,不解析登录 shell 的环境),扩展宿主里没有用户终端中的代理变量与 PATH。mcppls 在此环境中运行 mcpp emit build-database --format json。
- mcpp 规划时执行:
sh -c "cd ~/.mcpp/registry && env -u XLINGS_PROJECT_DIR -u XLINGS_ACTIVE_SUBOS PATH=… XLINGS_HOME=~/.mcpp/registry xlings update 2>&1 </dev/null"
并阻塞在读取上。
xlings update 连 GitHub(IPv6,443 端口),发送队列停在 85 字节,一直没有确认,也没有超时。
- 5 分钟时调用方结束 mcpp(只结束直接子进程)。
sh 与 xlings update 被 init 收养,它们的 fd 3 与调用方读取 mcpp 标准输出的描述符是同一个管道(/proc/<pid>/fd 核对)。调用方直到 xlings update 退出才读到管道结束。
- 对照:
MCPP_OFFLINE=1 mcpp emit build-database --format json 在用户终端环境与上述编辑器环境中都是 2.6 s、退出 0,输出逐字节相同(2,608,930 字节)。
ensure_official_package_index_fresh 的注释已经提到 “xlings update … stalls for minutes on slow/blocked networks (the Termux first-run / build hang)”。按需触发的刷新减少了次数,但一旦触发,仍然没有期限。
需要的适配
A1 离线缺依赖:专门的错误码
- 现状: 离线时缺包、缺工具链或索引未初始化,信封里只有
MCPP_BUILD_DATABASE_PLAN_FAILED,缺的是什么只写在消息文本里。
- 为什么需要: IDE 默认离线调用。“需要下载”与“构建描述写错了”要给用户不同的提示和动作:前者提示在终端运行一次
mcpp build,后者提示查看诊断。调用方不应依赖消息文本。
- 建议:
- 离线时因缺少可下载内容而失败,用专门的错误码,例如
MCPP_OFFLINE_DOWNLOAD_REQUIRED。
- 每个缺少的包、工具链或索引各给一条诊断,消息中含名称、版本、所在索引,以及用户应运行的命令。
- 其他规划失败仍用原错误码。
- 验收: e2e 用例:删除一个依赖包的本地安装,离线运行
emit build-database,信封中出现该错误码,诊断中含包名与版本,退出码 1。
A2 规划期间不把调用方的描述符交给子进程
- 现状:
StdoutToStderr 用 dup(1) 保存的调用方管道没有 close-on-exec,经 popen/std::system 启动的 xlings 与它的后代都继承了这个管道。mcpp 自己结束后,调用方仍读不到管道结束。
- 为什么需要: 任何以管道读取 mcpp 输出的调用方(IDE、CI 脚本、
$(mcpp …))都可能被 mcpp 的后代无限期挂住。这次多挂了 6.5 分钟。
- 建议:
- POSIX:
fcntl(1, F_DUPFD_CLOEXEC, 3) 保存。
- Windows:保存为不可继承的句柄。
- 启动外部程序时,只让子进程继承 0–2(
close_range/posix_spawn_file_actions_addclosefrom_np,或逐个设 FD_CLOEXEC)。
- 验收: e2e 用例:以管道读取
emit build-database 的输出,并用一个会挂起的 xlings 替身([xlings] binary 指向它)。结束 mcpp 后,调用方 1 s 内读到管道结束;替身进程的 /proc/<pid>/fd 中没有调用方的管道。
A3 xlings 调用有期限,并与 mcpp 一起结束
- 现状: 索引刷新与安装调用 xlings 时没有任何期限。mcpp 被结束时,xlings 进程继续运行。
- 为什么需要: 网络不通、代理未生效、IPv6 黑洞时,
emit build-database 与 mcpp build 会一直挂住,只能手动结束。
prepare.cppm 已规定“刷新失败只警告,之后仍用本地数据解析”(L4465-4472),但挂起的刷新永远不会“失败”。
- 建议:
- 索引刷新设整体期限(例如 60 s,可在
config.toml 中配置)。超时视为刷新失败,按既有语义继续用本地数据;本地数据足够时,命令照常成功。
- 下载与安装的期限可以更长,但不应无限。
- xlings 在独立进程组中运行;mcpp 收到 SIGTERM、SIGINT,或自身期限到时,结束该进程组。
- 验收:
- e2e 用例:xlings 替身挂起时,若本地数据足以解析,
emit build-database 在期限后返回并成功。
- 结束 mcpp 后,没有残留的替身进程。
A4 信封的 effects 报告实际发生的联网
- 现状:
--protocol-version 的静态表中,emit build-database 含 network;每次运行的信封从不含 network。
- 为什么需要: 调用方据此判断这次结果是否依赖网络,并在日志与诊断报告中说明“这次读取构建描述时访问了网络”。
- 建议: 本次运行实际执行了索引刷新、下载或 git 访问时,把
network 加入信封的 effects。
- 验收: 刷新后运行的信封含
network;--offline 运行的信封不含。
A5 auto_refresh = false 约束所有隐式刷新
- 现状: 注释写着设为 false 就“require an explicit
mcpp index update”,但工具链自动安装前的刷新与自定义索引首次同步仍会联网。
- 为什么需要: 用户与 IDE 需要一个仅靠配置就可靠的开关。
- 建议: 二选一:
- 让这两处也受
auto_refresh 约束;
- 在
config.toml 注释与文档中写明只有 --offline 能完全禁止联网。
- 验收:
auto_refresh = false 且缺少工具链时,不执行 xlings update,诊断提示运行 mcpp index update。
A6 mcpp-index 的镜像地址
- 现状: mcpp-index 的仓库与制品地址只有 GitHub 一套。
mirror = CN 选择的是包描述中的下载地址;这次现场 mcpp 私有仓库的 .xlings.json 为 "mirror": "CN",xlings update 连接的仍是 GitHub。
- 为什么需要: 在访问 GitHub 不稳定的网络中,镜像只覆盖了下载,刷新仍可能挂起(这次正是 GitHub 连接)。
- 建议: 部署
xlings-res/mcpp-index 的 gitcode 镜像后,为默认索引加上 {"GLOBAL": …, "CN": …} 形态,刷新时按 mirror 选择。
- 验收:
mirror = CN 时,mcpp index update 不访问 github.com。
mcppls 这边的做法(不依赖上述适配也会做)
相关
概述
mcppls(mcpp-language-server)在后台调用
mcpp emit build-database --format json(#636,2026.9.15.1 起提供)读取工程的构建描述。2026-09-16 在 openxlings/xlings 上:xlings update,这条网络连接挂了约 11.5 分钟,mcpp 一直阻塞。xlings update仍持有调用方的标准输出管道,调用方又多等了 6.5 分钟才读到管道结束。mcppls 这边会改为默认离线调用,并自己兜住挂起(见文末)。下面六项需要 mcpp 适配:前三项是这次挂起的原因,同样影响在终端里运行
mcpp build的用户;后三项让调用方能正确判断和提示。现场(xlings,mcpp 2026.9.15.1,Linux)
bin/code启动(VSCODE_CLI=1,不解析登录 shell 的环境),扩展宿主里没有用户终端中的代理变量与 PATH。mcppls 在此环境中运行mcpp emit build-database --format json。xlings update连 GitHub(IPv6,443 端口),发送队列停在 85 字节,一直没有确认,也没有超时。sh与xlings update被 init 收养,它们的 fd 3 与调用方读取 mcpp 标准输出的描述符是同一个管道(/proc/<pid>/fd核对)。调用方直到xlings update退出才读到管道结束。MCPP_OFFLINE=1 mcpp emit build-database --format json在用户终端环境与上述编辑器环境中都是 2.6 s、退出 0,输出逐字节相同(2,608,930 字节)。原因(代码 @ 2fc7b5b)
emit build-database与mcpp build走同一个prepare_build,依赖与工具链解析真实执行,可能刷新索引StdoutToStderr用dup(1)保存原标准输出,没有 close-on-exec,最低空闲描述符正是 3;规划中经popen启动的程序都继承调用方的管道update_index→run_streaming:popen加阻塞fgets加pclose,没有期限;只在非零退出时重试 3 次,挂起时无效MCPP_BUILD_DATABASE_PLAN_FAILED,调用方只能匹配消息文本effects只有read-project、write-global-cache(和exec-build-script),这次刷新了索引也不含networkauto_refresh只约束decide_for_dependency与decide_for_miss;工具链自动安装前的ensure_official_package_index_fresh、工程自定义索引的首次同步不受它约束ensure_official_package_index_fresh的注释已经提到 “xlings update… stalls for minutes on slow/blocked networks (the Termux first-run / build hang)”。按需触发的刷新减少了次数,但一旦触发,仍然没有期限。需要的适配
A1 离线缺依赖:专门的错误码
MCPP_BUILD_DATABASE_PLAN_FAILED,缺的是什么只写在消息文本里。mcpp build,后者提示查看诊断。调用方不应依赖消息文本。MCPP_OFFLINE_DOWNLOAD_REQUIRED。emit build-database,信封中出现该错误码,诊断中含包名与版本,退出码 1。A2 规划期间不把调用方的描述符交给子进程
StdoutToStderr用dup(1)保存的调用方管道没有 close-on-exec,经popen/std::system启动的 xlings 与它的后代都继承了这个管道。mcpp 自己结束后,调用方仍读不到管道结束。$(mcpp …))都可能被 mcpp 的后代无限期挂住。这次多挂了 6.5 分钟。fcntl(1, F_DUPFD_CLOEXEC, 3)保存。close_range/posix_spawn_file_actions_addclosefrom_np,或逐个设FD_CLOEXEC)。emit build-database的输出,并用一个会挂起的 xlings 替身([xlings] binary指向它)。结束 mcpp 后,调用方 1 s 内读到管道结束;替身进程的/proc/<pid>/fd中没有调用方的管道。A3 xlings 调用有期限,并与 mcpp 一起结束
emit build-database与mcpp build会一直挂住,只能手动结束。prepare.cppm已规定“刷新失败只警告,之后仍用本地数据解析”(L4465-4472),但挂起的刷新永远不会“失败”。config.toml中配置)。超时视为刷新失败,按既有语义继续用本地数据;本地数据足够时,命令照常成功。emit build-database在期限后返回并成功。A4 信封的
effects报告实际发生的联网--protocol-version的静态表中,emit build-database含network;每次运行的信封从不含network。network加入信封的effects。network;--offline运行的信封不含。A5
auto_refresh = false约束所有隐式刷新mcpp index update”,但工具链自动安装前的刷新与自定义索引首次同步仍会联网。auto_refresh约束;config.toml注释与文档中写明只有--offline能完全禁止联网。auto_refresh = false且缺少工具链时,不执行xlings update,诊断提示运行mcpp index update。A6 mcpp-index 的镜像地址
mirror = CN选择的是包描述中的下载地址;这次现场 mcpp 私有仓库的.xlings.json为"mirror": "CN",xlings update连接的仍是 GitHub。xlings-res/mcpp-index的 gitcode 镜像后,为默认索引加上{"GLOBAL": …, "CN": …}形态,刷新时按mirror选择。mirror = CN时,mcpp index update不访问 github.com。mcppls 这边的做法(不依赖上述适配也会做)
MCPP_OFFLINE=1调用emit build-database。离线缺依赖时,状态栏提示用户在自己的终端运行mcpp build:代理与凭据只有用户的终端才有。A1 之后改用错误码识别。相关
emit build-database需求)、mcpp emit build-database, program probes as argument vectors, and xlings 2026.9.14.1 (2026.9.15.1, #636) #639(实现)xlings update的连接没有超时,IPv6 不通时也不回退 IPv4,另行报告