Skip to content

Commit eac311e

Browse files
committed
docs: the device layer and the compatibility commitment (2026.9.4.2)
docs/18 specifies the four device actions, why termination is a property of the slot rather than of the argv, and why the emulator/hardware choice is a feature of one board package rather than two packages. docs/19 states what a project's own review process asks and cannot currently cite: which releases are supported, which surfaces are stable, and which — build fingerprints, cache layout, target/ — are explicitly not interfaces. Both mirrored in Chinese, per this repository's convention. The plan document records the second round: two findings the plan did not predict (a second propagation path for dependency-supplied RunGlobal entries, and two fast paths that would have made the new slots and --locked silently vacuous), and the six-axis reading that follows.
1 parent 93cf907 commit eac311e

10 files changed

Lines changed: 571 additions & 3 deletions

File tree

.agents/docs/2026-09-04-commercial-grade-baremetal-embedded-plan.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 商业级可用:mcpp × xlings 的裸机与嵌入式总体方案
22

3-
2026-09-04 · 多仓库总体方案 · **v4:P0 引擎切片已实施**(PR #550);其余批次待实施
3+
2026-09-04 · 多仓库总体方案 · **v5:P0 引擎切片 + B/C/D/E 四轴已实施**(PR #550#551)
44

55
前置讨论:
66
[`2026-08-21-baremetal-ecosystem-assessment.md`](2026-08-21-baremetal-ecosystem-assessment.md)(七角度评估) ·
@@ -471,3 +471,42 @@ clang 对一次 float 乘法仍发出 `vmul.f32` —— 在没有 FPU 的 Cortex
471471
⚠️ **软浮点行在没有 builtins 时链接不了浮点代码**(实测:`undefined symbol:
472472
__aeabi_fmul`),这正是 §3.1.1 把 `compiler-rt-builtins` 与 C 库并列为 P0 的理由。
473473
整数程序不受影响 —— e2e 332 的四行启动用例即为整数程序。
474+
475+
---
476+
477+
## 11. 第二轮实施(2026-09-04,PR #551)
478+
479+
### 11.1 六轴读数的变化
480+
481+
|| v4 | 现在 |
482+
|---|---|---|
483+
| **A 覆盖** | 🟡 引擎能编七行 | 🟡 不变(C 库源码包与板级包仍未做) |
484+
| **B 可信** | ❌ 零真机 | 🟢 **模拟器与真机成为同一个包的两个 feature**;真机路径可声明、可解析、判据齐备,尚无实机运行记录 |
485+
| **C 闭环** | ❌ 三个槽都没有 |`flash`/`monitor`/`debug` + `runner-exclusive`,四槽一读点 |
486+
| **D 可复现** |`--locked` 不存在 |`--locked`/`--frozen` 断言并点名漂移;关掉快路径以免空转 |
487+
| **E 可交付** | ❌ 全空白 | 🟢 `mcpp sbom`(CycloneDX 1.5)+ `docs/19` 支持窗口;许可闭包门与离线快照仍未做 |
488+
| **F 可扩展** || ✅ 未受损:新板 = 新包,引擎 diff 为零 |
489+
490+
### 11.2 ⭐⭐ 方案 §2.2 的判断被实施证实,§2.3 的被加强
491+
492+
* **两值语义是对的。** `debug` 起服务端、客户端归 IDE 这条边界成立,`debug`
493+
`monitor` 在实现里逐字段同形,没有出现方案担心的「会话协议」。
494+
* **`runner-exclusive` 比方案写的更必要。** 方案说它是「第一块真板挖出的一列」;
495+
实施时发现它还必须**只紧不松** —— 图里任何一个包知道设备是互斥的,它就是互斥
496+
的,后来的包保持沉默不得放松它。
497+
498+
### 11.3 ⚠️ 实施挖出的、方案没有的两条
499+
500+
1. **一条规则的第二份拷贝。** 依赖提供的 RunGlobal 条目抵达根工程走的是与
501+
`apply()` **不同**的路径(`prepare.cppm` 的 BFS 之后)。只接了前者时,
502+
`mcpp flash` 报「没有配置」而 `mcpp run` 找得到同一个构建程序发出的 runner。
503+
两处现在都遍历槽表。
504+
2. **快路径会让新槽与 `--locked` 双双空转。** `try_fast_run` 直接 exec 缓存产物,
505+
于是 `mcpp flash` 打印 `Running target/…/bin/p`;`try_fast_build` 跳过解析,
506+
于是被改坏的锁通过了 `--locked`。两处都按**性质**设闸(槽是不是 run、是不是
507+
要求断言),不是按旗标。
508+
509+
### 11.4 仍未做
510+
511+
`mcpplibs/picolibc` + `compiler-rt-builtins` 源码包 · 三个板级包 · `xim:probe-rs` ·
512+
真机 CI · 许可闭包门(`--deny-license`)· 离线整仓快照 · openarch 第四后端(P3)。

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,61 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [2026.9.4.2] — 2026-09-04
7+
8+
`runner` 长出三个兄弟槽,`--locked` 成为断言,新增 `mcpp sbom`
9+
10+
四件事在一个工程被采用之前会被问到,而它们此前都没有答案:产物如何抵达设备、
11+
构建是否可复现、里面装了什么、以及模拟器与真机是一个包还是两个。
12+
13+
### 设备槽
14+
15+
`run` / `flash` / `monitor` / `debug` 是同一种形状 —— **板知道、工具执行**的一段
16+
argv。`runner` 从 2026.8.19 起就承载着这个形状;再写三个特例命令就是把它再承载
17+
三遍。所以**槽成了参数**:指令表各一行、一个读点、一种 CLI 形状。
18+
19+
```toml
20+
[target.thumbv7em-none-eabihf]
21+
flash = ["probe-rs", "download", "--verify", "--chip", "STM32L475VG", "{}"]
22+
```
23+
24+
⚠️ **没有任何 argv 能说出它们哪个会终止。** `run`/`flash` 跑完并交回判决;
25+
`monitor`/`debug` 没有自然终点,于是「进程还活着」对后者是成功、对前者是卡死。
26+
引擎从****读出这件事,因为 `openocd -c "program … exit"``openocd -c "init"`
27+
的拼写直到板所选的那个参数为止都一样。
28+
29+
⚠️ **`runner-exclusive` 是第一件物理板需要而模拟器从不需要的事。** 一块板一个探针
30+
是互斥锁,两个工作者去够它不会失败 —— 它们互相穿插。板自己说一次,工程永远不必
31+
记得 `-j1`
32+
33+
### 模拟器与硬件是一个 feature,不是一次分叉
34+
35+
两者差别只在设备槽的 argv。⭐ 这**不需要任何引擎机制** —— `mcpp::has_feature()`
36+
本来就存在,分层按规定在起作用。
37+
38+
### `--locked`
39+
40+
锁一直是解析之后写、从不读回;它自己的头注释就这么说。现在它是一条**断言**:
41+
发生的解析必须等于记录的解析,不等则点名移动了的包与两个版本。
42+
43+
⚠️⚠️ **而它绝不能遇上快路径。** 实测:在加上那道闸之前,一份被故意改坏的锁通过了
44+
`mcpp build --locked` 并打印 `Finished` —— 旗标被接受、构建正确、断言从未跑到。
45+
**被跳过的判据比不存在的判据更糟,因为那个绿会被当成一次验证。**
46+
47+
### `mcpp sbom`
48+
49+
CycloneDX 1.5,覆盖**已记录**的解析。物料清单需要的一切都已在 `mcpp.lock` 里,
50+
所以这是一种输出格式而不是一套机制。⚠️ 它读锁而不是重新解析 —— 一份描述了与所
51+
构建者不同的图的文档比没有更糟。
52+
53+
### 两个传播点,以及先被漏掉的那一个
54+
55+
依赖提供的 RunGlobal 条目抵达根工程,走的是与包自身指令**不同**的代码路径。只接了
56+
前者时,`mcpp flash` 报「no flash is configured」,而 `mcpp run` 找得到同一个构建
57+
程序三行之外发出的 runner。两处现在都遍历槽表,而不是各自点名 `runner`
58+
59+
指令协议版本 6。新增 `docs/18-devices.md``docs/19-supported-versions.md`(中英双份)。
60+
661
## [2026.9.4.1] — 2026-09-04
762

863
Cortex-M 落地为七个目标行,freestanding 链接开启死代码段消除。

docs/18-devices.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# 18 — Reaching a Device
2+
3+
This document specifies how mcpp executes, writes, observes and debugs an
4+
artifact that runs somewhere other than the machine that built it, and how a
5+
project selects between an emulator and physical hardware.
6+
7+
Related documents: [13 — Bare-Metal and Freestanding Targets](13-baremetal.md)
8+
covers the targets these actions apply to; [07 — build.mcpp](07-build-mcpp.md)
9+
is the reference for the directive protocol a board-support package speaks;
10+
[11 — Machine Output](11-machine-output.md) is the interface a debugger client
11+
or IDE uses.
12+
13+
## Four actions, one shape
14+
15+
An artifact that cannot run on the build machine needs something to stand in
16+
front of it. Four things are asked of such an artifact, and all four are an
17+
argv that a board knows and a tool performs:
18+
19+
| Command | Slot | What it does |
20+
|---|---|---|
21+
| `mcpp run` | `runner` | executes the artifact |
22+
| `mcpp flash` | `flash` | writes it to the device |
23+
| `mcpp monitor` | `monitor` | observes what the device prints |
24+
| `mcpp debug` | `debug` | starts the device's debug server |
25+
26+
Each is declared the same way, by a board-support package:
27+
28+
```cpp
29+
mcpp::flash("probe-rs");
30+
mcpp::flash("download");
31+
mcpp::flash("--verify");
32+
mcpp::flash("--chip");
33+
mcpp::flash("STM32L475VG");
34+
```
35+
36+
or by a project, overriding what a dependency supplied:
37+
38+
```toml
39+
[target.thumbv7em-none-eabihf]
40+
flash = ["probe-rs", "download", "--verify", "--chip", "STM32L475VG", "{}"]
41+
```
42+
43+
The artifact path is appended, or substituted for `{}` when the template
44+
contains it. One token per call: argv is ordered, and a single string cannot
45+
say where its boundaries are.
46+
47+
The program is located by mcpp rather than by the system: a declared payload's
48+
`bin/` first, then `PATH`. A tool that is nowhere is an error decided before
49+
any process starts, rather than a fallback to bare execution.
50+
51+
## Termination is a property of the slot
52+
53+
The four actions differ in one way the engine must act on, and no argv can
54+
express it.
55+
56+
| Semantics | Slots | Meaning |
57+
|---|---|---|
58+
| `OneShot` | `run`, `flash` | runs to completion; the exit code is the verdict |
59+
| `LongLived` | `monitor`, `debug` | has no natural end; the operator ends it |
60+
61+
`openocd -c "program image.elf verify reset exit"` terminates and `openocd -c
62+
"init"` does not, and the two are spelled alike up to the argument the board
63+
chose. The engine therefore reads termination from the slot, and a board cannot
64+
get it wrong by writing its argv differently.
65+
66+
`mcpp debug` starts a **server** and stops there. The client that attaches is
67+
the user's debugger or their IDE, which learns what it needs through the
68+
machine-output protocol. mcpp does not drive the client.
69+
70+
## Absence is reported, never substituted
71+
72+
`mcpp run` on a hosted target with no runner executes the artifact directly,
73+
because the host can run it. There is no corresponding reading of "no flasher":
74+
nothing else writes an image to a device. An undeclared `flash`, `monitor` or
75+
`debug` is therefore an error on every target, naming the slot and printing the
76+
key to paste.
77+
78+
Succeeding at `mcpp flash` by running the program on the build host would be
79+
the exact failure the slot exists to prevent.
80+
81+
## An exclusive device
82+
83+
A physical board is a mutex; an emulator is not. `mcpp test` runs test binaries
84+
on a worker pool, and two processes reaching for one probe do not fail cleanly
85+
— they interleave, and the verdict describes neither test.
86+
87+
The board states this about itself:
88+
89+
```cpp
90+
mcpp::runner_exclusive();
91+
```
92+
93+
`mcpp test` then runs one test at a time on that target, and reports that it is
94+
doing so. A project never has to remember `-j1`.
95+
96+
## Emulator and hardware are one package
97+
98+
A board reached through an emulator and the same board reached through a debug
99+
probe differ in the argv of their device slots and in nothing else. The linker
100+
script, the startup code, the memory map and the exported module are the same
101+
board. Publishing two packages to vary four strings duplicates all of it and
102+
lets the copies drift.
103+
104+
The choice is therefore a feature of one package:
105+
106+
```toml
107+
[features]
108+
default = ["emulator"]
109+
emulator = []
110+
hardware = []
111+
```
112+
113+
```cpp
114+
int main() {
115+
if (mcpp::has_feature("hardware")) {
116+
for (auto a : {"probe-rs", "run", "--chip", "STM32L475VG"})
117+
mcpp::runner(a);
118+
for (auto a : {"probe-rs", "download", "--verify", "--chip", "STM32L475VG"})
119+
mcpp::flash(a);
120+
mcpp::runner_exclusive();
121+
} else {
122+
mcpp::runner(qemu_path());
123+
for (auto a : {"-machine", "mps2-an385", "-nographic", "-semihosting",
124+
"-no-reboot", "-kernel"})
125+
mcpp::runner(a);
126+
}
127+
return 0;
128+
}
129+
```
130+
131+
The consumer selects an environment where it selects everything else:
132+
133+
```toml
134+
[dependencies]
135+
demo-board-rt = { version = "0.1.0", features = ["hardware"] }
136+
```
137+
138+
A slot the chosen environment does not supply stays absent. An emulator has no
139+
debug probe, so under the emulator feature `mcpp debug` reports that none is
140+
configured rather than inventing one.
141+
142+
This required no engine mechanism. The engine reads slots and knows nothing
143+
about emulators or probes; `mcpp::has_feature` already existed. That the
144+
question is answerable without adding anything is the layering working as
145+
specified.
146+
147+
## Precedence and reporting
148+
149+
Two producers exist for every slot, with ordinary precedence: what the author
150+
of the project wrote beats what a dependency supplied. The override is reported
151+
rather than applied in silence.
152+
153+
```
154+
note [target.thumbv7em-none-eabihf].flash overrides the flash a dependency supplied
155+
```
156+
157+
Exactly one dependency may supply a given slot. Link flags from two
158+
dependencies concatenate and that is correct; two flashers cannot, and
159+
appending produces an argv that is neither one's. A second provider is an error
160+
naming both packages.

docs/19-supported-versions.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# 19 — Supported Versions and Compatibility
2+
3+
This document states which releases are supported, for how long, and what may
4+
change between them. It exists because a project adopting mcpp is asked these
5+
questions by its own review process, and an answer that lives only in
6+
maintainers' heads cannot be cited.
7+
8+
## Versioning
9+
10+
Releases are named `YYYY.M.D.N` — the date of the release and the ordinal of
11+
that day's release. The scheme carries no compatibility promise in its digits:
12+
`2026.9.4.1` is not "a minor release" of `2026.9.3.2`. What may and may not
13+
change is stated below rather than encoded in the number.
14+
15+
## What is supported
16+
17+
| | |
18+
|---|---|
19+
| **Supported** | the most recent release |
20+
| **Security-fixed** | the most recent release, and the last release of the preceding calendar month |
21+
| **Unsupported** | everything older |
22+
23+
A release is superseded rather than withdrawn. Published assets and index
24+
entries for older versions remain in place, because a project may have pinned
25+
one and removing it would break a build that was working.
26+
27+
## What may change between releases
28+
29+
The engine's own interfaces are not all equally stable, and the difference is
30+
worth stating precisely.
31+
32+
| Surface | Stability |
33+
|---|---|
34+
| `mcpp.toml` keys | Additive. An existing key keeps its meaning; an unrecognised key is reported, never silently ignored |
35+
| CLI commands and flags | Additive. A removed spelling is kept as an alias |
36+
| Machine output (`--message-format json`) | Versioned by `schemaVersion`; see [11](11-machine-output.md) |
37+
| `build.mcpp` directive protocol | Versioned; see `kProtocolVersion`. An engine refuses a program declaring a **higher** version rather than guessing |
38+
| `mcpp.lock` format | Versioned by `schemaVersion`; older files are migrated on read |
39+
| Target table rows | Additive. A row's tier may rise; a row is not removed while a published package targets it |
40+
| Build fingerprints, cache layout, `target/` contents | **Not an interface.** These change without notice, and nothing should parse them |
41+
42+
⚠️ A `build.mcpp` calling a function its engine's bundled `mcpp` module does not
43+
have fails at the **compile** of the build program, not through a protocol
44+
error. The protocol number governs directives on the wire; the typed API is
45+
governed by which engine is installed. Both are stated here because the failure
46+
a package author sees depends on which one they crossed.
47+
48+
## Reproducing a build
49+
50+
`mcpp.lock` records what a build resolved. `--locked` asserts that a resolution
51+
matches it and fails naming the package that moved:
52+
53+
```
54+
error: --locked was given and this resolution differs from mcpp.lock:
55+
mcpplibs.cmdline 0.0.1 -> 0.0.2
56+
```
57+
58+
A release build, an audit or a CI job should pass `--locked`. It disables the
59+
build fast path, so the assertion always runs.
60+
61+
⚠️ The lock does not yet constrain resolution — it records and verifies it.
62+
Pinning a resolution to the lock as an input is a separate change to the
63+
resolver.
64+
65+
## Bill of materials
66+
67+
`mcpp sbom` writes a CycloneDX 1.5 document describing the **recorded**
68+
resolution:
69+
70+
```bash
71+
mcpp sbom -o sbom.json
72+
```
73+
74+
It reads `mcpp.lock` rather than resolving again, because a document describing
75+
a different graph from the one that was built is worse than no document. A
76+
component whose licence mcpp does not know is emitted as `NOASSERTION` rather
77+
than omitted: an absent field reads as "not examined".
78+
79+
## Offline and air-gapped use
80+
81+
`--offline` (or `MCPP_OFFLINE=1`) prevents every network access: index refresh,
82+
package download and toolchain installation. A build that would need one of
83+
those fails naming what it needed, rather than reaching out.
84+
85+
## Reporting a problem
86+
87+
Defects and security reports go to the issue tracker of the repository that
88+
owns the component — the engine, the package index, or the package itself. A
89+
report that names the version, the host, the target and the command is
90+
actionable; one that does not usually results in a request for those four.

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
- [15 - Cross-Compilation Over openkal](15-openkal-cross.md)
2121
- [16 - The Target Triple](16-the-target-triple.md)
2222
- [17 - The Project Environment](17-the-project-environment.md)
23+
- [18 - Reaching a Device](18-devices.md)
24+
- [19 - Supported Versions and Compatibility](19-supported-versions.md)
2325

2426
## Specifications
2527

0 commit comments

Comments
 (0)