Skip to content

Latest commit

 

History

History
161 lines (119 loc) · 8.33 KB

File metadata and controls

161 lines (119 loc) · 8.33 KB

17 - 项目环境

项目可以声明自己在哪个环境里构建。这一条声明决定项目链接哪个 C 库、以及它的 构建程序找到哪些工具——于是同一份 mcpp.toml 在开发机和 CI 上是同一个构建, 不论这两台机器上还装了别的什么。

[xlings]
subos = "tools"

[xlings.workspace]
"xim:qemu-riscv" = "9.2.4-1"

可运行的工程:examples/07-project-subos/

1. SubOS 是什么

SubOS 是一个目录,里面是一份用户态:它自己的 bin、自己的库视图、自己那套已 装包版本,以及一个自述用的 subos_info 块。mcpp 把它当作「这个项目对着什么 构建」的答案,而且是回答这个问题的唯一机制——不是编译器所在路径,不是 XLINGS_ACTIVE_SUBOS,也不是当前 shell。

存在两种,区别在于目录落在哪里:

声明 目录 与谁共享
未声明 mcpp 初始化的 subos/default 机器上的每个项目
subos = "default" 同一个目录,只是被显式点名 机器上的每个项目
subos = "<name>" <project>/.mcpp/.xlings/subos/<name>/ 不共享

第三行是隔离的那种。它属于该项目,就放在清单旁边,删掉项目它也随之消失。

2. 这条声明决定什么

C 库。 payload-first 的构建链接的是某一个确定的 glibc,而「哪一个」是项目 的性质而非机器的性质。第 8 章讲绑定本身、降级规则,以及一个不自述的 SubOS 会 让这些规则变成什么。

构建程序看见哪些工具(mcpp 2026.8.25.1+)。被声明环境的 bin 放在 build.mcpp 运行时 PATH 的最前面:

PATH=<被声明环境的 bin>:<mcpp 自己启动时的 PATH>

因此构建程序里把 qemu-system-riscv64 写成裸名,拿到的就是那个环境里的副本。 这条通道的契约见第 7 章。

只对声明了的项目生效。 没有 [xlings].subos 的项目拿到的是 mcpp 启动时 的 PATH,逐字节不变。把一个共享目录放到每个项目前面,会让「构建看见什么」 取决于这台机器上还装过什么——同一台机器上的两个项目彼此一致,而同一个项目在 两台机器上不一致。是声明本身把它放到了前面。

前置而非替换。 构建程序理应会调 gitpython3 或 shell,这些都不在 SubOS 里。前置让被声明的环境成为默认答案;其余的仍在它后面可达。

2.1 哪些版本钉生效(2026.9.3+)

指名一个环境,同时改变了工具的版本从哪来。工程自己 [xlings.workspace] 里的条目 总是胜出;不同的是它们叠在什么之上:

工程声明了 它没点名的工具,版本来自
[xlings.workspace],无 subos 机器的环境
[xlings.workspace]subos = "<名>" 那个环境自己的 workspace;机器的不适用

第二行就是隔离的含义。指名的环境有自己的已安装集合,把机器的版本带进去会指向那里 不存在的版本 —— 所以原本依赖「机器上装了就能用」的工程,一旦指名环境,就必须把用到 的都声明出来。

在工程内执行的 xlings use 压过这两者,直到 mcpp 重写环境为止:它是最后合并的那 一层,而人做出的动作应当压过一份文件。

3. 这条声明不决定什么

[xlings.workspace] 声明的是「环境里要有哪些包」,而每个包的载荷目录另有通道交付, 即 MCPP_XPKG_<NAME>_DIR。这与 PATH 是两个问题,答案也保持分开:需要某个包 的数据文件(比如 protoc 自带的 well-known .proto)的构建程序问目录,需要 运行某个程序的构建程序问 PATH

工作区成员的声明不是工作区的声明。 工作区构建中由工作区根持有这个选择;成员的 [xlings] 只在该成员作为独立根被构建时生效。

依赖的声明是另一回事,而且它被采纳([xlings] deps 自 2026.9.5.4,下面那条版本 规则自 2026.9.6.6)。板级支持包知道哪个模拟器够得到它那台机器,规则包知道它驱动哪个 工具包;要消费者把这些再写一遍,正是这类包存在的意义所反对的重复。依赖声明的东西会被 装上,而 MCPP_XPKG_<NAME>_DIR 在那个依赖自己的构建程序里为它作答。

工程与依赖命名同一个包时,只装它的一个版本:身份是 (namespace, name),版本是这个 包上的约束。离产物更近的声明赢,并且覆盖会被报出来;不满足对方所陈述之要求的钉会被拒绝 并点出两侧。见 05 — mcpp.toml 的「一个包一个版本」。

4. 只读取环境,从不创建环境

mcpp 解析被声明的名字,并读取它找到的东西。解析不到的名字是硬失败:

error: selected SubOS 'tools' does not exist at …/.mcpp/.xlings/subos/tools;
create/bootstrap that environment instead of falling back to active/default

回退到 default 或回退到当前活跃的那个,等于用另一个环境顶替清单点名的那个,而 这恰恰会让一份 mcpp.toml 意味着两个不同的构建。创建并填充 SubOS 属于 xlings 这一层——xlings subos new——mcpp 去管理 SubOS 状态则是把分层倒置。

一个存在但不携带 subos_info的环境是降级而非失败:运行时绑定报 inconclusive,没有 payload-first 绑定可用,打印一条提示,构建继续。完整规则见 第 8 章。

5. 什么时候值得用私有环境

  • 产物取决于版本的代码生成器。 protocflatc、着色器编译器:它的输出是 下游一切的输入,所以项目钉住生产者,而不是指望机器上那个恰好兼容。
  • 构建程序要运行的模拟器。 若干裸机包把「在 QEMU 里启动产物」作为验证的一 部分;是哪个 QEMU 属于「验证了什么」的一部分。
  • CI 与开发机不一致的项目,两边都没错,而构建不该察觉到差异。
  • 同一台机器上两个项目需要同一工具的不同版本。 共享目录意味着必有一方落 败;私有环境让这个问题不成立。

代价一侧:隔离环境是一个必须被创建并填充的目录,而这笔账由首次构建来付。 2026.8.29 起 mcpp 会做这件事 —— 声明在 [xlings.workspace] 里的包在首次使用时被供给, 一个尚不存在的具名 [xlings] subos 会被创建而不是被拒绝 —— 但代价是实打实的: 干净机器上的第一次构建会先下载安装,然后才编译。工具很普通、版本也无所谓的项目, 不声明、直接继承机器的那份更划算。

--offline / MCPP_OFFLINEMCPP_NO_AUTO_INSTALL 下,mcpp 转为拒绝而不是 安装,并列出包名以便手动供给 —— 与 [toolchain] 遵守的是同样两个开关,理由也相同: 一次没被要求的下载,不该由构建替工程决定。

这份声明在每台构建本工程的宿主上都会供给,宿主装不了的包是错误,不是被跳过的条目。 只存在于某一个宿主平台的工具因此按平台声明(2026.9.2.1): deps = [{ linux = "qemu-user-aarch64" }] 在 Linux 上声明这个模拟器,在别处什么都不声明。 键与解析规则见第 5 章 §2.13。

哪些命令会安装它。 一条条目可以带档位 —— { version = "0.24.0", when = "run" } —— [feature-xlings.<feature>] 则把工具挂在某个 feature 上。用不到的工具因此不会被下载: 见第 5 章 §2.13。不写档位就是从前的行为。

runner。 [xlings.workspace] 下的程序也是 [target.<triple>].runner 查找其第一个元素 的首选位置,在 PATH 之前(第 5 章 §2.7.3)。两个键合起来,在 CI 宿主上供给用户态模拟器, 并通过它执行交叉构建的产物,而清单不必写出载荷的路径。

6. 什么该写在别处

需求 写在哪里
程序链接的库 [dependencies]
编译器 [toolchain],第 3 章
依赖产出的宿主工具 tools = [...],第 7 章
环境里要有的工具 [xlings.workspace]
只有某个命令或某个 feature 需要的工具 when = "run"[feature-xlings.<f>]
用哪个环境 [xlings] subos

7. 相关章节