Skip to content

xpkg Lua 描述符的 mcpp 构建规则无法按版本区分 #290

Description

@wellwei

问题

当前 xpkg .lua 描述符中,mcpp = { ... } 构建规则块对所有 xpm 版本一视同仁。一个包的 sourcesdepscflags 等字段无法表达"某个源文件仅存在于特定版本"或"某个依赖仅特定版本需要"。

具体案例 —— ggml-org.llamacppb10069b10107 两个版本):

mcpp = {
    sources = {
        "ggml/src/ggml.c",
        "src/llama.cpp",
        -- 下面两个文件仅在 b10107 中存在,b10069 的上游源码树里没有
        "src/models/laguna.cpp",
        "src/models/hunyuan-dense.cpp",
    },
}

xpm = {
    linux   = { ["b10069"] = {...}, ["b10107"] = {...} },
    macosx  = { ["b10069"] = {...}, ["b10107"] = {...} },
    windows = { ["b10069"] = {...}, ["b10107"] = {...} },
}

选择 b10069 编译时,laguna.cpp 在上游源码树中不存在,构建失败。目前的应对方式是依赖外部验证脚本(check_llamacpp_snapshot.py)做事后审计,而非在描述符层面精确表达。

这带来的连锁问题:

  1. 描述符无法准确表达构建意图。 写描述符时维护者不知道哪些 sources 属于哪些版本;删除旧版本时不知道哪些 sources 可以安全移除。

  2. 依赖外部验证而非语法约束。 大多数 mcpp-index 的包没有 llama.cpp 那样的快照测试。版本间的源文件漂移是静默的,直到有人选了某个版本编译失败。

  3. 限制了索引的版本覆盖范围。 维护多版本的成本随版本间差异线性增长。tinyhttps 有 9 个版本,因为它没有 mcpp 块(纯 xpm 表面)。而真正需要多版本支持的复杂库(openssl、curl 等)反而因为 mcpp 块无法适配版本差异,只能保留极少的版本。

不是 Feature 系统的问题

Feature gating 的条件维度是"消费者想要什么功能"(--features a,b,c)。版本是"上游发布了什么内容"(xpm 表中的版本键)。两者正交。用 feature 模拟版本选通会破坏 feature 自身的语义。

不是 Contract Admission 的问题

min_mcpp 的语义是准入检查——"mcpp 版本不够就拒收这个描述符",不是"选不同版本的构建规则"。它解决的是索引向前兼容性,不是包内跨版本差异。

不是拆成多个独立包能优雅解决的问题

如果把 llama.cpp 的 b10069 和 b10107 拆成两个包,就失去了"这是同一个库的不同版本"这一语义,也无法共享它们之间 95% 相同的构建规则。

在现有设计体系中的位置

mcpp 已有两个条件化构建配置的机制,它们证明了架构可以容纳条件化的构建输入:

  • 平台条件块macosx = { ... })——在解析时按平台键合并入基础 mcpp 块。平台信息在解析时已知。
  • ConditionalConfig[target.'cfg(os=linux)'.build])——在 prepare_build() 中 target triple 确定后评估,合并匹配的构建输入。当前谓词域只覆盖 os/arch/toolchain。

版本条件块与上述两者的关系:

条件化构建配置
    │
    ├── 平台条件块      ← 已在 Lua 描述符中存在
    │   维度:os,时机:解析时(已知)
    │
    ├── ConditionalConfig  ← 已在 mcpp.toml 中存在
    │   维度:target triple,时机:prepare_build()
    │   谓词:os/arch/toolchain(无 version)
    │
    └── 版本条件块      ← 缺失
        维度:xpm 选中的版本,时机:resolve_semver() 之后
        谓词:version_req(可复用已有的 version_req::matches())

平台条件块证明了同一个 mcpp 块内按维度拆分子表的语法模式是可行的。版本条件块在语法层面的差异仅在于:版本维度在解析时未知,需要把子表暂存到 Manifest,延迟到版本确定后再合并——合并逻辑本身(append(BuildInputs&, const BuildInputs&))不需要变。

相关背景

  • 上游 mcpplibs/mcpp-index 已有 60+ 个包,其中多版本包(如 ggml-org.llamacpp、mcpplibs.tinyhttps、mcpplibs.xpkg、mcpplibs.llmapi)的维护摩擦直接源于此限制
  • mcpp 的设计原则之一是"结构化意图保持到单一汇聚点";当前缺失版本选通迫使维护者在描述符之外散落验证脚本,恰好违背了这条纪律
  • Feature System v2 的设计文档中明确拒绝了几件事(自由格式 feature-flags、通用约束 DSL、版本化 capability),但版本条件构建规则不在其中——它从未被讨论过,是一个空白地带,而非被刻意排除的设计选择

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