Skip to content

Latest commit

 

History

History
182 lines (140 loc) · 7.65 KB

File metadata and controls

182 lines (140 loc) · 7.65 KB

32 —— 编写一个载荷

读者: 要把一个工具或一份预编译库打包出来,好让 mcpp 工程声明它、 由 mcpp 安装它的人。

本章回答的那一个问题: 一个 xim: 载荷由什么构成,以及它的描述符 必须说清什么,消费者才能点名它并得到一个能用的程序。

不在这里: 发布供他人 import 的源码包,那是 11 —— 发布一个库;让宿主的库能被产物 够到,那是 33 —— 编写运行时适配包;以及 消费一个载荷,那是 23 —— 项目环境。

在此之前:31 —— 编写规则包——规则声明 它所驱动的载荷。在此之后: 33 —— 编写运行时适配包。

载荷的定义

一切由 mcpp 安装而不编译的东西:编译器、着色器编译器、设备工具包、 模拟器、探针驱动、预编译的 C 库。工程在 [xlings.workspace] 中点名它, 或者规则包在 [feature-xlings.<f>] 中点名它,mcpp 会在构建运行之前把它 供给到位 —— 或者,写了 provision = "on-request" 时,在构建程序请求它时 供给(docs/23)。工程也可以陈述某个载荷来自本机的别处 ([xlings.overrides]),那时它根本不会被安装;无论哪一种,配方都不受影响。

一个载荷在 xim-pkgindex 中是一个 Lua 文件:一个描述它的 package 表,加两个把它放好并登记的函数。

能用的最短形态:

package = {
    spec = "2",
    name = "glslang",
    description = "Khronos reference GLSL/ESSL front end and validator",
    licenses = {"BSD-3-Clause", "Apache-2.0", "MIT"},
    type = "package",
    archs = {"x86_64"},

    xpm = {
        linux = {
            ["latest"]  = { ref = "15.1.0" },
            ["15.1.0"]  = {
                url = {
                    GLOBAL = "https://github.com/…/glslang-15.1.0-linux-x86_64.tar.gz",
                    CN     = "https://gitcode.com/…/glslang-15.1.0-linux-x86_64.tar.gz",
                },
                sha256 = "87167c9cb32f258addbedb607639b2c1f484c029ba91542a92f19ead21d65d13",
            },
        },
    },
}

function install()
    local dir = pkginfo.install_dir()
    os.tryrm(dir)
    os.mv("glslang-15.1.0", dir)
    return true
end

function config()
    xvm.add(package.name)
    return true
end

install() 把解开的目录树放到 mcpp 会去找的位置;config() 登记这个 载荷提供什么。本章其余内容,都是这两个函数在多做一些事。

安装钩子收到的环境(mcpp 2026.9.12.2+)

mcpp 安装工程所依赖的包时,该包的 install() 会在环境中收到本次构建的 目标,采用的变量名与遵循的规则与构建程序一致 (build.mcpp):每个变量都存在,没有取值时为空, 因此一个钩子绝不会读到从启动 mcpp 的那个进程继承来的值。

变量 依赖安装期间的取值
MCPP_TARGET 本次构建所请求的目标三元组;原生构建时为宿主三元组
MCPP_TARGET_OS、MCPP_TARGET_ARCH、MCPP_TARGET_ENV 该三元组的各段
MCPP_COMPILER、MCPP_CXX_STDLIB 空

工具链相关的取值为空,因为工具链在依赖图之后才解析:图中的某个包可能 供给目标侧的层,所以依赖安装时编译器与标准库都尚未确定,而一个猜测了 取值的钩子可能构建出错误的变体。在 Windows 上,空变量就是不存在的 变量,os.getenv 对它返回 nil。工具链载荷自己的钩子不会收到这些 变量。

钩子可以用目标来拒绝或给出诊断,但不得把某种变体构建进一个名称未体现 该变体的存储目录:存储目录按包名与版本区分,否则第一个消费者就会替 之后所有消费者决定变体。针对某一个 C++ 标准库编译的包应以 requires = ["mcpp:c++-abi=libstdc++"] 陈述这一点,该需求会在工具链 解析之后被检查(22 —— 目标侧)。

描述符必须做对的四件事

一个版本,两个 URL。 每个版本都带 GLOBAL 与 CN 两个 URL,以及 一个 sha256。两个镜像服务的是同一份字节;处在任一镜像之后的消费者都 解析到同一个哈希,而只写一个 URL 的描述符对半个生态是不可用的。

latest 是一个引用,不是一个版本。 ["latest"] = { ref = "15.1.0" }。 它是消费者不点名版本时拿到的东西,移动它是一个刻意的动作——钉在 15.1.0 的消费者不受影响。

archs 与平台表是解析要读的东西。 只为 linux 与 x86_64 发布的 载荷就这样声明,于是别的平台上的消费者会被点名拒绝,而不是拿到一个 跑不起来的东西。

依赖是带下界的 xim: 地址。

deps = { "xim:gcc-runtime@>=15", "xim:glibc@>=2.38" },

让载荷可被够到

只会解包的载荷不可用。三条声明把一个目录变成构建能消费的东西。

路径上的一个程序。 xvm.add(package.name) 登记该载荷的 bin/,于是 mcpp 能按裸名找到这个程序。规则包应当写程序名,不写路径——mcpp 会 搜索图中每一个被声明载荷的 bin/,然后才是 PATH,并且能准确报出它 搜过哪些目录。

消费者要链接或加载的库。

exports = {
    runtime = { libdirs = { "lib" } },
},

elfpatch 从每个依赖里读这一项,写进消费者的 RPATH——这正是一叠载荷 不需要任何人设置 LD_LIBRARY_PATH 就能解析的原因。

头文件,好让这个环境里的编译器能对着它构建。 sysroot.declare_libs(...) 与头文件声明把载荷放进 SubOS 的 sysroot 视图。是声明而不是复制——xlings 会随包一起移除它们,而一份复制会 比它的主人活得更久。

从检出目录解析配方(2026.9.14.2+)

未合入分支上的一份配方,在发布之前可由消费者通过 mcpp home 的 config.toml 中的索引覆盖来解析:

# $MCPP_HOME/config.toml
[index.repos.xim]
url = "/work/xim-pkgindex"      # a checkout of the branch

下一条命令会把该条目写入 registry 的 .xlings.json 并打印一行;此后 索引跟随该检出目录,包括第一次构建之后才提交的内容:

       Index xim -> /work/xim-pkgindex ([index.repos.xim] in config.toml)

从该索引安装时,Provisioning 旁边会重复这一行。删去这张表后,下一条 命令会恢复 registry 原先的条目,除非该条目在 mcpp 写入之后被改动过。 以上对已经运行过的 home 同样成立;2026.9.14.2 之前的 mcpp 只在创建 home 时应用这张表。

档位:需要载荷的命令

"xim:qemu-arm" = { version = "9.2.4-1", when = "run" }

when 是选中该载荷的那个 feature 之外的第二道独立闸门。feature 说的是谁需要这个工具;档位说的是什么时候。模拟器是运行时需要、 编译时不需要的,所以一个只构建固件、从不烧录的 CI 任务一个字节都不 下载。

当前边界

  • 载荷发布到 xim-pkgindex——那是一个独立仓库,有它自己的评审;mcpp 本身没有任何东西负责发布载荷。
  • latest 与「表里版本号最大的那个」是两个不同的问题,想要最新已发布 版本的消费者应当点名 latest。
  • 载荷自己的 CI 无法核验消费者能否解析到它:那正是「对着已发布描述符 做沙箱核验」的用途,也是唯一核验已发布字节而不是工作树的东西。