读者: 要把一个工具或一份预编译库打包出来,好让 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
endinstall() 把解开的目录树放到 mcpp 会去找的位置;config() 登记这个
载荷提供什么。本章其余内容,都是这两个函数在多做一些事。
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 会随包一起移除它们,而一份复制会
比它的主人活得更久。
未合入分支上的一份配方,在发布之前可由消费者通过 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 无法核验消费者能否解析到它:那正是「对着已发布描述符 做沙箱核验」的用途,也是唯一核验已发布字节而不是工作树的东西。