Skip to content

包的身份是 (名字, 版本),而描述文件能在版本不变时改变它的产物和链接面 —— index update 不重装,报错指向无关目标 #524

Description

@Sunrisepeak

一句话

包的身份是 (名字, 版本),而描述文件可以在版本不变的前提下改变这个包的产物和链接面。 于是 mcpp index update 换掉了描述文件、却不重装已安装的包,两者当场互相矛盾——而矛盾暴露出来时,报错指向的是毫不相干的目标

CI 结构上看不见这一条:CI 每次都是干净环境,(名字, 版本) 在它眼里从来不会「内容变过」。判据只能在装过旧版的机器上取得。

实例

compat.eui-neo@0.5.7 在 mcpp-index#269 里从「不带 glib」变成「install() 暂存 glib + -Lmcpp_generated/glib/lib -lgio-2.0 …」,版本号没动

一台在 #269 之前装过该包的机器上:

症状一:刷新之前 —— 构建全绿,功能静默损坏

拿到旧描述文件,编出来的程序里根本没有 glib,托盘不工作,没有任何报错

$ mcpp build
    Finished dev [unoptimized + debuginfo] in 3.02s
$ readelf -d ./mytray | grep -cE 'lib(gio|gobject|glib)'
0
$ ./mytray
no tray available

用户不会知道自己需要 mcpp index update

症状二:刷新之后 —— 硬失败,且报错指向无关目标

$ mcpp index update && mcpp build
failed: bin/libXau.so
ld: cannot find -lgio-2.0: No such file or directory
ld: cannot find -lgobject-2.0: No such file or directory
ld: cannot find -lglib-2.0: No such file or directory
failed: bin/libXdmcp.so
(同上)

compat.xau / compat.xdmcp 与 eui-neo 毫无关系,它们只是恰好声明了 kind = "shared"

原因是那几个 flag 进了全局 c_ldflags,而 rule c_shared 对项目里每一个共享库展开它:

rule c_shared
  command = $cc -shared @$out.rsp -o $out $c_ldflags $soname_flag $implib_flag $def_flag $unit_ldflags
c_ldflags = … -L/home/…/xpkgs/compat-x-eui-neo/0.5.7/mcpp_generated/glib/lib -lgio-2.0 -lgobject-2.0 -lglib-2.0 …

-L 指向的目录只有 install() 才会创建:

$ ls ~/.mcpp/registry/data/xpkgs/compat-x-eui-neo/0.5.7/
EUI-NEO-0.5.7   mcpp_generated   v0.5.7.tar.gz        # ← 旧布局:上游包装层,无 install() 归一化
$ ls ~/.mcpp/registry/data/xpkgs/compat-x-eui-neo/0.5.7/mcpp_generated/glib/lib
ls: 不存在                                            # ← flag 指向一个从未被创建的目录

复现

需要一台装过旧版的机器。关键是版本号不变

  1. 用某版描述文件装上 <pkg>@<ver>
  2. 索引里改这个包的描述文件,令其新增由 install() 产出的链接输入(ldflags 里的 -L),不动版本号
  3. mcpp index update
  4. rm -rf target mcpp.lock && mcpp build

⚠️ 只删 target / mcpp.lock 不足以复现:那些共享库若在描述文件变更之前就已建好并缓存,就不会重新链接,构建照样是绿的。我自己前几次本地绿就是这么来的——绿是构建顺序的产物。要连已装包一起删才谈得上对照:

rm -rf ~/.mcpp/registry/data/xpkgs/<ns>-x-<name>/<ver>

用户侧规避

删掉已装包让它重装。验证过:libgio/libgobject/libglib 回到 NEEDED,功能正常。

两条独立的问题

写在一起是因为一条暴露了另一条,但它们可以分开修:

A. 已装包的身份里没有描述文件。 描述文件既决定怎么装(install() 的产出)、又决定怎么链(ldflags/include_dirs),却不参与「这个包是否需要重装」的判定。可能的方向:把描述文件内容摘要纳入已装包的指纹;或让 index update 把描述文件变过的包标记为失效。跟 [依赖的 [build] 输入没进指纹] 是同一类。

B. 一个包的链接输入是全局的。 c_ldflagscompat.eui-neo-lgio-2.0 出现在 libXau.so 的链接行上。即使 A 修好,这一条仍然让任何包的 ldflags 有能力打断无关目标,并且错误信息指向错误的地方。这跟 #519 那条 carries_foreign_link_inputs()ldflags 里有 -L 的包不能构建成动态库)是同一个根:ldflags 是不透明字符串,mcpp 只能整体搬运,没法按作用域安放。 一个结构化的「本包随附的链接输入」键(包内相对目录 + 库名)会同时改善这两处。

诊断质量

这次报错本身也值一提:用户看到的是 linking bin/libXau.so: cannot find -lgio-2.0。从这句话到真因(另一个包的描述文件变了而它没重装)之间隔着三层,没有任何一层是错误信息提到的。如果 A 短期内不好修,至少在 -L 指向的目录不存在时说清楚是哪个包贡献了这个 flag、以及它的安装目录看起来是旧版布局。

环境

  • mcpp 2026.8.28.2(发布版)
  • mcpp-index 9927799
  • Linux x86_64,gcc 16.1.0

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