Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c927106
design: [xlings] converges on one table, and deps is retired
speak-agent Sep 2, 2026
3ac07da
design: settle what is written, and state the section it is being cha…
speak-agent Sep 2, 2026
158bfc3
design: what the four fields do, read in the xlings source
speak-agent Sep 3, 2026
d4d58fa
design: state the inheritance rule in full, not only the case that su…
speak-agent Sep 3, 2026
87c67eb
design: the documentation the rule needs, and a self-review that chan…
speak-agent Sep 3, 2026
acd9cba
design: four questions answered from the source, and 15.1 withdrawn
speak-agent Sep 3, 2026
161aec2
design: the key space measured, the namespace put where the file puts…
speak-agent Sep 3, 2026
f458495
design: the key form checked against the data, and ns:name rejected w…
speak-agent Sep 3, 2026
b9cb76e
design: the authored project file is the reference, not a SubOS state…
speak-agent Sep 3, 2026
d088490
design: both namespace positions accepted, and mcpp normalises to the…
speak-agent Sep 3, 2026
9ba86b4
design: D8 worked through — activation must be obtained, not requested
speak-agent Sep 3, 2026
c0cc681
design: D8 restated — the flag was the wrong question, the scope is t…
speak-agent Sep 3, 2026
92d219a
design: D8 withdrawn — the merge already does it, and the defect is t…
speak-agent Sep 3, 2026
bd1e0a7
design: rewritten as one document, and the scope fix's precondition c…
speak-agent Sep 3, 2026
dcd9753
design: the migration corrected against the consumer, and an implemen…
speak-agent Sep 3, 2026
1dd2faf
xlings: one table, deps superseded, envs removed, and the install-tim…
speak-agent Sep 3, 2026
d096068
manifest: one address parser, and the conflict compared on the whole pin
speak-agent Sep 3, 2026
a669f37
design: state where the advisory does not reach
speak-agent Sep 3, 2026
e790ea4
design: the ecosystem migration waits, and the reason is a silent reg…
speak-agent Sep 3, 2026
2816089
docs(13): the board-support example uses the one table
speak-agent Sep 3, 2026
6020027
docs+publisher: the subos example was nested under the wrong table, a…
speak-agent Sep 3, 2026
7845da1
tests: the fixtures that used the removed key, and the one assertion …
speak-agent Sep 3, 2026
7088e11
examples: the project-subos example writes the one table
speak-agent Sep 3, 2026
84423bd
design: what implementation found that the design did not
speak-agent Sep 3, 2026
d92a1b9
platform: one macos/macosx spelling rule, and the manifest asks it
speak-agent Sep 3, 2026
bc4c2c9
design: the macos alias is one rule, and mcpp already had it
speak-agent Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
614 changes: 614 additions & 0 deletions .agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,54 @@
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。

## [2026.9.3.1] — 2026-09-03

`[xlings]` 收敛成一张表。`[xlings.workspace]` 说出工程用哪个包、用哪个版本,mcpp
既供给它也把它物化成解析用的钉;`deps` 被取代;`envs` 移除;发布出去的描述符第一次
带上安装期的边。

设计与全部实测见
[`.agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md`](.agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md)。

> **一般形态比它自己的简写弱。** `docs/05` §2.13 写着 `[toolchain]` 是编译器那一项
> 的便捷写法、`[xlings.workspace]` 是一般形态;而 `[toolchain]` 会装
> (`resolve_xpkg_path(…, autoInstall=…)`),一般形态什么都不装。两个键说的是同一件
> 事,差别只在 mcpp 拿这句话去做什么 —— 而它们对应的那套机制,书写面只有一个。

### 变更

- **`[xlings.workspace]` 成为唯一的表。** 一条条目产出两个投影:一个安装地址
(`[<ns>:]<target>[@<version>]`,供给用)与一份解析钉(`[<ns>:]<version>`,写进
`.xlings.json` 的 `workspace` 对象)。命名空间写在键上或版本上都接受——
`"xim:picolibc-riscv" = "1.8.12"` 与 `picolibc-riscv = "xim:1.8.12"` 是同一条,
写在键上必须带引号(TOML 裸键不能含冒号);两半都写且不一致是错误。`""` 表示
「存在即可,版本不限」,这是手写项目文件里已经在用的拼法。平台键取 xlings 自己的
`linux` / `macosx` / `windows` / `default`,`macos` 作为别名保留。

- **`deps` 被取代,但仍然生效并被报告。** 报告里给出该写的那一行。**不拒绝**——拒绝
会落到依赖的 manifest 上,而钉了那个包精确版本的工程改不了它。同一个包在两张表里
给出两个版本则是硬错误:两者按顺序供给而后者赢得钉,接受它等于装一个、解析另一个。

- **`[xlings.envs]` 移除。** 它曾被物化进 `.xlings.json` 而没有任何东西读它:
xlings 里两处 `envs` 分别属于某个程序的 shim 记录和某个 SubOS 的 provider 段,
都不是这个形状;mcpp 自己给程序的运行环境来自 runtime binding。现在这个键是错误
——**一个什么都不做的键,在有东西声称它有效果时更坏**。

- **发布的描述符带上 `xpm.<platform>.deps`。** 此前 `mcpp emit xpkg` 一个字都不写,
安装期的边只能手写进 `xpkg.lua` —— `riscv-virt-rt` 0.3.0 因此发出去时没有它自己
目标行指名的 C 库。声明按**未解析**的形态保留在 `workspaceByPlatform` 里,因为
描述符每个平台一块,而按本机解析已经丢掉了另外两个。

- **`[xlings]` 的三档继承写进文档。** 不写 `subos` 就是机器的环境加上工程自己的条目;
写了 `subos` 就是隔离,机器那层不参与;工程内执行的 `xlings use` 压过两者。这条
行为一直如此,而 mcpp 此前一字未提。

### 行为变化

- `[xlings.envs]` 从被忽略变成硬错误。索引里没有任何包用过它。
- `[xlings] deps` 仍然生效,但会打印一条指出替代写法的警告;`--strict` 下它是错误。
- 同一个包在 `deps` 与 `[xlings.workspace]` 里给出不同版本,现在被拒绝。

## [2026.9.2.1] — 2026-09-02

`[target.<triple>].runner` 对每一个目标生效,启动失败不再无声,`mcpp test` 把跑不起来
Expand Down
120 changes: 71 additions & 49 deletions docs/05-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -1866,63 +1866,85 @@ built" means is the same question `--target` answers (docs/08 §7.4).
Both are warnings, never errors: coverage is release discipline, and the person
who can judge it is looking at the release, not at this build.

### 2.13 `[xlings]` — Build Environment
### 2.13 `[xlings]` — the project's environment

```toml
[xlings.workspace] # what this project's environment contains
cmake = "3.28"
picolibc-riscv = "xim:1.8.12" # namespace on the version
"xim:qemu-user-aarch64" = "7.2.0" # or on the key - quotes required
code = "" # present; version unconstrained
llvm = { macosx = "20", default = "22" }
```

```toml
[xlings]
deps = ["make@4.4", "cmake@3.28", "python@3.13"] # host build-tools to provision
subos = "dev" # a named per-project sandbox
subos = "dev" # a named, isolated environment
```

[xlings.workspace] # pin tool versions (general form of [toolchain])
clang = "20.1.7"
`[xlings]` is mcpp's surface for **xlings' local project mechanism**: the
project `.xlings.json` that gives a directory its own environment. The
subsection names and their meanings are that file's, and mcpp materializes them
into `<project>/.mcpp/.xlings.json` with no translation layer.

[xlings.envs] # env vars applied to the tool environment
OPENBLAS_NUM_THREADS = "1"
```
**`[xlings.workspace]` is the one table.** An entry names a package and the
version this project uses it at. mcpp provisions it — installing it when the
machine does not have it, mapping it when it does — and materializes it as a
resolution pin, so the version the project named is the one its tools resolve
to.

#### Writing an entry

| Form | Means |
|---|---|
| `cmake = "3.28"` | that version |
| `llvm = "22"` | the highest installed `22.*`; a version prefix resolves |
| `code = ""` | present, version unconstrained |
| `picolibc-riscv = "xim:1.8.12"` | from the `xim` index |
| `"xim:picolibc-riscv" = "1.8.12"` | the same entry, namespace written on the key |
| `llvm = { macosx = "20", default = "22" }` | per host platform |

The namespace may be written on either half. Writing it on the key requires
**quotes**, because a TOML bare key cannot contain a colon. Writing it on both
halves with different values is an error, and so is naming one package twice
under two spellings.

Platform keys are xlings' own — `linux`, `macosx`, `windows` — plus `default`.
`macos` and `macosx` are the same platform written in two vocabularies (mcpp's
triples say one, descriptors and xlings' project file say the other) and both
are accepted wherever a platform is named. A table with no key for this host
and no `default` declares nothing here.

#### Which version a tool the project did not name resolves to

| The project declares | The version comes from |
|---|---|
| `[xlings.workspace]`, no `subos` | the machine's environment, with the project's own entries laid over it |
| `[xlings.workspace]` and `subos = "<name>"` | that environment's own workspace; the machine's does not apply |
| neither | the machine's environment |

Declares the project's **build environment**, provisioned through xlings (which mcpp
is built on). The subsection names mirror xlings' own `.xlings.json` schema **1:1**, so
mcpp materializes them verbatim into `<project>/.mcpp/.xlings.json` (no translation
layer): `deps` (host build-tools), `[xlings.workspace]` (tool→version pins),
`subos` (a named sandbox), `[xlings.envs]` (env vars). Use it to declare host tools a
build needs (`make`/`cmake`/`protoc`/…), pin tool versions per project, or set
build-time env vars — without hand-editing `.xlings.json`. `[toolchain]` (§2.7) remains
the ergonomic shorthand for the compiler; `[xlings.workspace]` is the general form.
The middle row is not an omission. A named environment has its own installed
set, and carrying the machine's versions into it would name versions that are
not there. Naming one is how a project asks for isolation; leaving it out is
how it asks for the machine's environment with its own entries on top.

**Values per host platform (2026.9.2.1+).** A `deps` entry and a
`[xlings.workspace]` value may be a table keyed by platform, the form xlings'
own `.xlings.json` accepts for `workspace`:
An `xlings use` performed inside the project outranks this table until mcpp
rewrites the environment, because it is the layer merged last.

```toml
[xlings]
deps = ["xim:ninja", { linux = "qemu-user-aarch64" }, { windows = "nasm", default = "yasm" }]

[xlings.workspace]
gcc = { linux = "15.1.0" }
llvm = { macos = "20", default = "22" }
```

The keys are `linux`, `macos`, `windows` and `default`; `macosx` is accepted as
xlings' spelling of `macos`. mcpp resolves the table against the host it runs
on when the manifest is loaded: the host's key wins, `default` is the fallback,
and a table with neither declares nothing on that host — the entry is absent,
not empty. An unknown key is an error rather than a dropped entry. The axis is
the host operating system only; a package that exists for the OS but not for
the architecture is still a provisioning error on that host.

`subos` selects the root project's **local build/run OS environment**. If the
key is absent, mcpp uses its initialized, release-verified `McppDefault` SubOS;
`subos = "default"` is an explicit `NamedSubos("default")` selection. There is
no CLI/environment override and no implicit following of xlings active/current.

On Linux the selected environment also fixes the loader/libc contract, so
`subos = "el8"` and `subos = "trixie"` can coexist and produce separately
fingerprinted objects. A workspace root overrides member declarations during a
workspace build. Dependency/member SubOS declarations are non-transitive: a
library's declaration applies when it is an independent root, not when its
sources are consumed by another root. A missing named SubOS or missing/
incompatible runtime contract is an error, never a fallback. See
docs/08-toolchain-internals.md §2.1.
#### `deps`, superseded

`deps = ["xim:qemu-riscv@9.2.4-1"]` is the pre-2026.9.3 spelling of the same
statement. It is still honoured and is reported once, with the
`[xlings.workspace]` line to write instead. It is not refused, because a
refusal would reach a *dependency's* manifest, which a project that pinned an
exact version of that package cannot edit.

#### `envs`, removed

`[xlings.envs]` was materialized into `.xlings.json` and read by nothing: a
program's environment is declared by its own package, and an environment's by
that environment. The key is now an error naming both. Nothing in the index
used it.

### 2.14 Host tools from a dependency (mcpp 2026.8.5.1+)

Expand Down
13 changes: 7 additions & 6 deletions docs/13-baremetal.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,12 @@ for hosted targets, the `--no-runner` escape and the not-run reporting of
## Writing a board-support package

A board-support package is an ordinary mcpp package. It declares the emulator
it needs under `[xlings] deps`, exports one C++ module for consumers, and emits
its board facts from `build.mcpp`.
it needs under `[xlings.workspace]`, exports one C++ module for consumers, and
emits its board facts from `build.mcpp`.

**A declaration under `[xlings] deps` provisions the package on the first
build** (since 2026.8.29). It is also what lets `mcpp::xpkg_dir` answer *"where
**A declaration there provisions the package on the first build** (since
2026.8.29; the table is `[xlings.workspace]` since 2026.9.3.1, and the older
`[xlings] deps` still works and says so). It is also what lets `mcpp::xpkg_dir` answer *"where
did that package land"*. Both halves matter: the same declaration installs the
emulator and tells the build program where it went.

Expand Down Expand Up @@ -669,8 +670,8 @@ int main() {
The package's manifest declares the emulator and nothing else:

```toml
[xlings]
deps = ["xim:qemu-riscv@9.2.4-1"]
[xlings.workspace]
qemu-riscv = "xim:9.2.4-1"
```

Linking `clang_rt.builtins` is not optional on this board. picolibc formats
Expand Down
20 changes: 20 additions & 0 deletions docs/17-the-project-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ Declaring it is what puts it there.
the declared environment the default answer; everything else stays reachable
behind it.

### 2.1 Which version pins apply (2026.9.3+)

Naming an environment also changes where a tool's version comes from. A
project's own `[xlings.workspace]` entries always win; what differs is what
they are laid over:

| The project declares | The version of a tool it did not name comes from |
|---|---|
| `[xlings.workspace]`, no `subos` | the machine's environment |
| `[xlings.workspace]` and `subos = "<name>"` | that environment's own workspace; the machine's does not apply |

The second row is what isolation means. A named environment has its own
installed set, and carrying the machine's versions into it would name versions
that are not there — so a project that relied on the machine's tools has to
declare them once it names an environment.

An `xlings use` performed inside the project outranks both, until mcpp rewrites
the environment: it is the layer merged last, and an action a person took
should beat a file.

## 3. What the declaration does not decide

`[xlings] deps` names packages to be present in the environment, and each one's
Expand Down
92 changes: 56 additions & 36 deletions docs/zh/05-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -1591,54 +1591,74 @@ platforms = ["linux", "macos", "windows"]
两者都只是 warning,绝不报错:覆盖度属于发布纪律,而能作判断的人看的是发布,
不是这一次构建。

### 2.13 `[xlings]` — 构建环境
### 2.13 `[xlings]` — 工程的环境

```toml
[xlings.workspace] # 这个工程的环境里有什么
cmake = "3.28"
picolibc-riscv = "xim:1.8.12" # 命名空间写在版本上
"xim:qemu-user-aarch64" = "7.2.0" # 或写在键上 —— 必须带引号
code = "" # 存在即可,版本不限
llvm = { macosx = "20", default = "22" }
```

```toml
[xlings]
deps = ["make@4.4", "cmake@3.28", "python@3.13"] # 要供给的 host 构建工具
subos = "dev" # 命名的项目级沙箱
subos = "dev" # 指名的隔离环境
```

[xlings.workspace] # 固定工具版本([toolchain] 的通用形式)
clang = "20.1.7"
`[xlings]` 是 mcpp 对 **xlings local project 机制**的书写面:让一个目录拥有自己
环境的那份项目 `.xlings.json`。子段名与含义都是那份文件的,mcpp 原样物化进
`<project>/.mcpp/.xlings.json`,没有翻译层。

[xlings.envs] # 应用到工具环境的环境变量
OPENBLAS_NUM_THREADS = "1"
```
**`[xlings.workspace]` 是唯一的表。** 一条条目写出工程用哪个包、用哪个版本。
mcpp 既供给它——机器上没有就装,有就映射——也把它物化成解析用的钉,于是工程写下
的版本就是它的工具解析到的版本。

声明项目的**构建环境**,经 xlings(mcpp 的底座)供给。子段名与 xlings 自身的
`.xlings.json` schema **1:1** 对齐,因此 mcpp 把它们**原样**物化进
`<项目>/.mcpp/.xlings.json`(无翻译层):`deps`(host 构建工具)、`[xlings.workspace]`
(工具→版本固定)、`subos`(命名沙箱)、`[xlings.envs]`(环境变量)。用它声明构建所需的
host 工具(`make`/`cmake`/`protoc`…)、按项目固定工具版本、或设构建期环境变量——无需手改
`.xlings.json`。`[toolchain]`(§2.7)仍是编译器的便捷简写;`[xlings.workspace]` 是其通用形式。
#### 条目的形式

**按宿主平台取值(2026.9.2.1+)。** `deps` 的一个条目与 `[xlings.workspace]` 的一个值可以是
按平台为键的表,即 xlings 自身 `.xlings.json` 对 `workspace` 接受的形式:
| 形式 | 含义 |
|---|---|
| `cmake = "3.28"` | 该版本 |
| `llvm = "22"` | 已装的最高 `22.*`;版本前缀会被解析 |
| `code = ""` | 存在即可,版本不限 |
| `picolibc-riscv = "xim:1.8.12"` | 来自 `xim` 索引 |
| `"xim:picolibc-riscv" = "1.8.12"` | 同一条,命名空间写在键上 |
| `llvm = { macosx = "20", default = "22" }` | 按宿主平台 |

```toml
[xlings]
deps = ["xim:ninja", { linux = "qemu-user-aarch64" }, { windows = "nasm", default = "yasm" }]
命名空间写在哪一半都可以。写在键上**必须带引号**,因为 TOML 的裸键不能含冒号。
两半都写且不一致是错误;同一个包用两种拼法出现两次也是错误。

[xlings.workspace]
gcc = { linux = "15.1.0" }
llvm = { macos = "20", default = "22" }
```
平台键是 xlings 自己的 —— `linux`、`macosx`、`windows`,外加 `default`。`macos`
与 `macosx` 是同一个平台的两套词汇(mcpp 的三元组说前者,描述符与 xlings 的项目
文件说后者),**凡是点名平台的地方两者都接受**。表里既没有本机这一项也没有
`default`,就表示在这里什么都不声明。

#### 工程没点名的工具,其版本的来源

| 工程声明了 | 版本来自 |
|---|---|
| `[xlings.workspace]`,无 `subos` | 机器的环境,工程自己的条目叠在上面 |
| `[xlings.workspace]` 与 `subos = "<名>"` | 那个环境自己的 workspace;机器的不适用 |
| 两者都没有 | 机器的环境 |

中间那行不是遗漏。指名的环境有自己的已安装集合,把机器的版本带进去会指向那里不
存在的版本。**写 subos 就是要隔离,不写就是要机器的环境加上自己的条目。**

在工程内执行的 `xlings use` 压过这张表,直到 mcpp 重写环境为止——它是最后合并的
那一层。

#### `deps`,已被取代

键为 `linux`、`macos`、`windows` 与 `default`;`macosx` 作为 xlings 对 `macos` 的拼写也被
接受。mcpp 在加载清单时按运行它的宿主解析这张表:宿主对应的键优先,`default` 兜底,两者
都没有时该条目在本宿主上不作声明 —— 是缺席,不是空值。未知的键是错误,不是被丢弃的条目。
这条轴只到宿主操作系统:一个包存在于该 OS 但不存在于该架构时,在那台宿主上仍是
provisioning 错误。
`deps = ["xim:qemu-riscv@9.2.4-1"]` 是同一句话在 2026.9.3 之前的拼法。它仍然生效,
并且会被报告一次,同时给出该写的 `[xlings.workspace]` 那一行。**不拒绝**——拒绝会
落到**依赖**的 manifest 上,而钉了那个包精确版本的工程改不了它。

`subos` 选择根项目用于 build/run 的**本地开发 OS 环境**。未声明该键时固定使用 mcpp 已初始化、
经 release 验证的 `McppDefault`;`subos = "default"` 则仍是显式的
`NamedSubos("default")`。没有 CLI/环境变量 override,也不会隐式跟随 xlings active/current。
#### `envs`,已移除

在 Linux 上,所选环境同时固定 loader/libc contract,所以 `el8`、`trixie` 可在同一机器共存,
并进入不同构建指纹。workspace 整体构建时由 workspace root 覆盖 member 声明;member/依赖中的
SubOS 不传递——库只有作为独立 root 开发时才使用自己的声明,作为别人的源码依赖时使用消费者
root 的环境。指定的命名 SubOS 不存在、缺少或使用不兼容 runtime contract 都会直接报错,不会
回退 default/active/编译器烙入状态。参见 docs/08-toolchain-internals.md §2.1。
`[xlings.envs]` 曾被物化进 `.xlings.json`,而没有任何东西读它:程序的环境由它自己
的包声明,环境的环境由那个环境声明。现在这个键是错误,并同时点名这两者。索引里没有
任何包用过它。

### 2.14 依赖产出的 host 工具(mcpp 2026.8.5.1+)

Expand Down
Loading
Loading