Skip to content

xpkg store 的已安装查找忽略 namespace:同短名同版本的包会静默顶替,install() 被跳过 #533

Description

@Sunrisepeak

概述

xpkg store 的目录是带命名空间的(<ns>-x-<name>/<version>/),但判断「这个包装过没有」的那层查找不比对 namespace,只看 (name, version)。于是不同命名空间下同短名同版本的两个包会互相顶替 —— 而且失败是静默的,最终报出来的错误和真正的原因毫无关系。

实测环境:mcpp 2026.8.29.1,xlings 2026.8.27.4


复现

compat.libdrm 是一个源码构建包,install() 里建两个 include 根(libdrm 的公开头在根、uapi 头在 libdrm/ 子目录)。把它的版本定为上游版本 2.4.123:

-- pkgs/c/compat.libdrm.lua
["2.4.123"] = {
    url = { GLOBAL = "https://dri.freedesktop.org/libdrm/libdrm-2.4.123.tar.xz" },
    sha256 = "a2b98567a149a74b0f50e91e825f9c0315d86e7be9b74394dae8b298caadb79e",
},

任何装了 Mesa 的机器上构建都会失败,因为 xim:mesa 的依赖里有 xim:libdrm@>=2.4,所以 store 里一定有 xim-x-libdrm/2.4.123

发生了什么

$ mcpp test
   Compiling compat.libdrm v2.4.123          <- 看起来解析成功
error: build failed
failed: bin/libdrm.so
 -shared @bin/libdrm.so.rsp -o bin/libdrm.so ... -Wl,-soname,libdrm.so.2
/bin/sh: 1: -shared: not found

/bin/sh: 1: -shared: not found 与真正的原因隔了三层:

  1. store 认为 libdrm@2.4.123 已安装(它看到的是 xim-x-libdrm/2.4.123),于是跳过 install();
  2. 但版本目录照样被建出来并标记为装好了:
$ ls -a <store>/compat-x-libdrm/2.4.123/
.  ..  .mcpp_ok  .xpkg-install.json  mcpp_generated/

.mcpp_ok.xpkg-install.json 都在,mcpp_generated/ 是 mcpp 自己的 generated_files 写的 —— 只有描述符 install() 该建的那棵树不存在;

  1. 于是 sources 一个都没匹配上,mcpp 生成了一条零输入c_shared 边:
build bin/libdrm.so : c_shared          # 对照正常情况:c_shared obj/a.o obj/b.o ...
  1. 因为没有对象要编,cc 变量根本没被写进 build.ninja(只有 cxx),$cc -shared ... 展开成 -shared ...,shell 去执行 -shared

关键点:与是否声明该依赖无关

这一点值得强调 —— 我最初以为是「本包声明了 xim:libdrm 才会撞」,不是。上面这个 compat.libdrm 完全没有 xim:* 依赖(纯源码构建,deps = {}),撞的是 store 里碰巧存在的任何同名同版本包。

判定方法

把版本号临时改成一个不可能撞的值:

-["2.4.123"] = {
+["2.4.123-probe"] = {

install() 立刻被调用,而且里面的错误会大声报出来:

error: xlings install_packages failed (exit 1) for 'compat.libdrm@2.4.123-probe'
  xlings reported: E_INTERNAL: [libdrm] failed: install hook failed:
    compat.libdrm.lua:219: attempt to call a nil value (field 'tmpdir')

同一份描述符,只差版本号:一个静默跳过、报无关错误,一个正常执行、错误可见。


影响

这条规则实际上是在说:索引里任何 compat 包的 <短名>@<版本> 都不能与生态里某个 xim:* 包相同,而包作者无从知道生态里有什么、将来会加什么。

具体到这次的图形栈,四个包全部被迫改版本号:

想用 实际只能用 撞的是
compat.libdrm 2.4.123 2.4.123.1 xim:libdrm@2.4.123
compat.wayland 1.23.1 1.23.1.1 xim:wayland@1.23.1
freedesktop.wayland(模块层) 1.23.1 1.23.1.2 上面两个
compat.libffi 3.4.4(与生态同版) 3.4.8 xim:libffi@3.4.4

compat.expat 同理只能取 2.7.1 而不是生态的 2.6.2

也就是说:一个包想和生态用同一个上游版本,是表达不出来的 —— 而这恰恰是最常见、最应该被鼓励的情形(消费者和 Mesa 用同一份 libdrm)。


建议

store 的已安装查找按 (namespace, name, version) 匹配,与目录布局 <ns>-x-<name>/<version>/ 一致。

这是 xlings#381(index 侧按 (namespace, name) 建键)在 store 侧的同一个缺口。

在修好之前,至少让它不要静默:如果查找命中了一个 namespace 不同的包,要么报错,要么在跳过 install() 时打印出「命中的是 <ns>-x-<name>/<version>」。现在的表现是把一个包管理器的身份问题伪装成了一条链接器命令行错误。


相关

  • 索引侧的四个包与形态讨论:mcpplibs/mcpp-index(源码构建的图形栈)
  • 顺带一个小的:Form A 的纯 C 库包([targets.x] kind = "shared",没有任何 .cppm)会稳定报
    warning: src/<name>.cppm: lib target without conventional lib root。C 库没有 lib root 可言,这个告警会传染给每个消费者。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions