Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion .agents/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
---
```

274 records.
275 records.

## By subject

Expand All @@ -40,10 +40,15 @@ Records that declare one. Everything else is listed by date below.

- [Two answers and two silences: the scanner's second grammar, and the manifest keys nothing reads](2026-09-09-two-answers-and-two-silences.md) — active

### plugins

- [The category the plugin taxonomy does not name, and what a platform actually decomposes into](2026-09-11-distribution-plugins-and-platform-decomposition.md) — active

## By date

### 2026-09

- [The category the plugin taxonomy does not name, and what a platform actually decomposes into](2026-09-11-distribution-plugins-and-platform-decomposition.md) — active
- [Two answers and two silences: the scanner's second grammar, and the manifest keys nothing reads](2026-09-09-two-answers-and-two-silences.md) — active
- [A dlopen surface no closure walks, and a process with two unwinders](2026-09-09-dlopen-surface-and-two-unwinders.md) — landed
- [The documentation as a book: a chapter-by-chapter design](2026-09-08-the-documentation-as-a-book.md) — active
Expand Down
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,71 @@

## [Unreleased]

## [2026.9.11.1] - 2026-09-11

### `mcpp pack --format <name>` 分派到包,而引擎里不再需要住进任何一种分发格式

`tar` 与 `dir` 回答的问题,和 `msi` 与 `appimage` 回答的问题是同一个 —— 输出取什么
形状 —— 所以它们是一个 flag 的取值,而不是第二个 flag 的开端。分界是:`mcpp pack`
拥有机制,以及那一种通用格式(一个解开就能跑的归档,它不需要知道任何别人的发布);
其余每一种格式都住在包里。dpkg 的 control 字段、AppImage 的 runtime、WiX 的 schema、
Apple 的公证,其中任何一个被绑进引擎,都会把一次 mcpp 的发布耦合到一次 mcpp 并不控制
的发布上。这与本项目早已为语言做过的论证是同一个 —— Slang 被支持,而引擎里没有它的
名字。

三样与格式无关的东西被加进引擎,「与格式无关」正是判断某样东西该不该进引擎的判据:

- **一棵 `artifact` action 可以消费的暂存树。** `mcpp pack` 一直在算它 —— 依赖闭包,
过了 strip 策略、调试信息拆分与 `include`/`exclude` —— 然后把它压掉,目录就没了。
`${mcpp.stage_dir}` 把它暴露出来。它是一个 **bundle** 树(`bin/`、`lib/`、可重定位),
这正是 AppImage、`.app`、`.msi` 要的形状;要一棵 FHS 树的格式(`.deb`、`.rpm`)自己
负责重排,因为一个文件该落在哪个目录是那个格式的知识。
- **`[package]` 的其余字段进入构建程序。** `MCPP_PKG_VERSION` / `_DESCRIPTION` /
`_LICENSE` / `_AUTHORS` / `_REPO`,以及对应的 `mcpp::package_*()`。每一种安装包格式
都要写版本号;在这之前,项目只能把版本号在成员自己的 options 里再写一遍,而那份副本
会与 `[package]` 漂移,且没有任何东西能发现。
- **`--format` 经图解析它的取值。** 包用 `mcpp::provides_pack_format("<name>")` 声明,
`--format <name>` 在解析后的依赖里找到提供方并把暂存树交给它。未知取值点名**当下确实
可用**的那些,而不是一份固定清单,并且这次拒绝发生在任何东西被编译之前。

**无条件声明,有条件提交。** 这是整套分派最承重的一条规则,也是最容易被成员作者写错
的一条 —— 因为写错了对作者自己仍然照常工作:他永远传的是自己那个格式。声明必须不加闸,
否则引擎永远回答不出「这张图提供哪些格式」;提交必须加闸,否则普通 `mcpp build` 会多出
一条它不该有的边。对一个谁都没为之提交的格式,mcpp 会拒绝并点名,而不是报告一次「什么
包都没产出」的成功打包。

**`mcpp pack --format <name>` 会 prepare 两次,而两次之间没有任何值被重新推导。** 一条
`artifact` action 是一条 ninja 边,而暂存树是 mcpp 在链接**之后**产出的,所以这棵树不
可能成为构建出它自己那一次 pass 的输入。第一趟收集声明并拒绝未知格式;随后是构建与暂存;
第二趟设上 `pack_format` 与 `pack_stage_dir` 并构建提供方提交的那条边。第二趟用的三元组
与暂存路径,都是第一趟和 `make_plan` 已经回答过的 —— 重新推导一遍会得到那种「在作者所有
机器上都一致、只在他没有的那台上不一致」的缺陷。

**build.ninja 的头行多了第四个字段 `dist=`,而快路径要求它读作 `none`。** 格式故意
**不进指纹**:进了就要为「把一棵已经构建好的树打成包」付一次全量重编。于是两张图落在同
一个目录里,而 `target/<triple>/<fp>/build.ninja` 是被两条快路径回放的共享可变状态 ——
这正是这一行上另外两个字段(`graph=`、`accel=`)已经各自记过一次的那种失败。判据放在
单元测试里而不是端到端:实测(2026-09-11)即使忽略这个字段,pack 之后的普通构建也会因为
更早的一个新鲜度条件而重新生成图,所以端到端断言无论字段是否生效都会通过,连字段被删掉
都照样通过。

**`${mcpp.stage_dir}` 在两种位置上是拒绝而不是空展开:** 本次构建不在打包时,以及
role 不是 `artifact` 时。一个空路径仍然是命令接受的 token,而工具随后读到的是构建目录
根 —— 那个目录存在,所以这个错误会产出一个看起来合理的产物而不是一条诊断。实测过的原型
是那个「有效的、空的、52 KB 的安装包,并且没有任何诊断」。

写了 `${mcpp.stage_dir}` 的 action 会自动获得一条对 `<暂存树>.stage-manifest` 的依赖
(一个兄弟文件,永不是成员,所以它不会跑进任何人的安装包里)。依赖由引擎添加,因为「用
了」本身就意味着「依赖」:没有它,这条边只在链接产物变化时才变脏,而一个闭包多出了某个
依赖的共享库、同时程序自己的字节没变的情况,会把上一次的可分发物原地留下并报告为最新。

构建程序协议升到 v9(`mcpp:pack-format=`)。这条指令带非空 `tag`,因此会随构建程序的
缓存记录一起被回放 —— 读它的那一趟是 `mcpp pack`,而那从来不是一个项目的第一次构建。

文档:`docs/10`(`--format` 那一个轴)、`docs/30`(三类成员的分类表、新占位符与新访问器)、
`docs/31`(分发成员的六条约束),中英双份。


## [2026.9.10.2] - 2026-09-10

### dlopen 面检查:不适用的那一趟也会发布记录,并且不会盖掉已经量出来的答案
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ list` reports for this machine):
| `aarch64-none-elf` · `x86_64-none-elf` | llvm 22 — bare metal, no C library by default ² | preview |
| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22 — Cortex-M4/M7 soft float, M23, M33F/M55F ² | preview |
| `riscv64-linux-musl` · `aarch64-linux-gnu` · `x86_64-macos` | — | planned |
| `aarch64-linux-android` · `x86_64-linux-android` | needs `xim:android-ndk`; `import std` measured working on the NDK's clang | planned |
| `aarch64-ios` | needs the iPhoneOS SDK, which is a licence question before it is a packaging one | planned |
| `wasm32-emscripten` | needs `xim:emsdk`; `import std` measured working on `em++`, and the target model is [#597](https://github.com/mcpp-community/mcpp/issues/597) | planned |

`verified` an image has been built **and run** for the row, qemu and wine
included · `preview` it builds and links, and no emulator run has been recorded
Expand Down
3 changes: 3 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,9 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
| `aarch64-none-elf` · `x86_64-none-elf` | llvm 22——裸机,默认不带 C 库 ² | preview |
| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22——Cortex-M4/M7 软浮点、M23、M33F/M55F ² | preview |
| `riscv64-linux-musl` · `aarch64-linux-gnu` · `x86_64-macos` | — | planned |
| `aarch64-linux-android` · `x86_64-linux-android` | 待 `xim:android-ndk`;`import std` 在 NDK 自带的 clang 上已实测可用 | planned |
| `aarch64-ios` | 待 iPhoneOS SDK,而它先是一个许可问题再是一个打包问题 | planned |
| `wasm32-emscripten` | 待 `xim:emsdk`;`import std` 在 `em++` 上已实测可用,目标模型见 [#597](https://github.com/mcpp-community/mcpp/issues/597) | planned |

`verified` 该行的镜像已被构建**并运行**过,qemu 与 wine 都算 · `preview` 可构建
可链接,未记录过模拟器运行 · `planned` 已登记在词表中,尚未接线 —— 面向这类目标
Expand Down
55 changes: 51 additions & 4 deletions docs/10-pack-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,55 @@ mcpp pack --mode self-contained # alias: --mode bundle-all
mcpp pack --target x86_64-linux-musl # equivalent to --mode static
mcpp pack --target aarch64-linux-musl # ARM64 equivalent
mcpp pack --format dir # output as a directory, no tarball
mcpp pack --format appimage # a format a package in the graph provides
mcpp pack -o myapp.tar.gz # filename only: lands at target/dist/myapp.tar.gz
mcpp pack -o /abs/path/myapp.tar.gz # includes a directory: output to the literal path
mcpp pack --profile dev # build with a different profile (default: release)
mcpp pack --no-strip # ship the artifacts as built
mcpp pack --debug-symbols dbg/ # write the separated *.debug files under dbg/
```

### `--format` owns one axis, and the engine owns two of its values

`tar` and `dir` answer the same question `msi` and `appimage` answer — what
shape does the output take — so they are values of one flag rather than the
beginning of a second one. The split between what the engine holds and what a
package holds is:

> **`mcpp pack` owns the mechanism and the one universal format. Every other
> format lives in a package, and `mcpp pack` dispatches to it.**

The universal format is what it already produces: an archive that extracts and
runs. It is universal in the only sense that matters here — it needs no
knowledge of anyone else's release. Everything past it does. dpkg's control
fields, AppImage's runtime, WiX's schema, Apple's notarisation, Android's
signing scheme: each one bound into the engine would couple an mcpp release to
a release mcpp does not control. The same argument the project already made for
languages, where Slang is supported without being named in the engine.

So the value set is open (mcpp 2026.9.11.1+). `--format <name>` finds the
package in the resolved graph that declares `<name>` and hands it the staged
tree; an unknown value names what *is* available rather than a fixed list:

```
error: unknown --format 'bogus'.
available in this build: tar, dir, appimage
A format past `tar` and `dir` comes from a package in the resolved graph, which declares
it with `mcpp::provides_pack_format("<name>")` in its build program. Add the package
that provides 'bogus' to [build-dependencies] and activate its feature.
```

The refusal arrives before anything is compiled. Writing such a package is
[Producing a distributable](30-build-mcpp.md#producing-a-distributable-pack_format--stage_dir-20269111);
the engine's three additions are a staged tree an `artifact` action can consume,
the rest of `[package]` in the build program, and this dispatch. Each is
format-neutral, which is the test for whether something belongs in the engine
at all.

A dispatched format applies to a **program** target. A library package ships an
interface plus prebuilt binaries per triple and has no single staged tree, so
`mcpp pack <lib> --format <name>` is refused rather than ignored.

When `-o` is given a bare filename, the output is placed under `target/dist/`;
when it includes a directory (relative or absolute), the literal path is used.

Expand Down Expand Up @@ -421,8 +463,13 @@ macOS **program** bundling (the Mach-O dependency closure, via `otool -L` /
`LC_LOAD_DYLIB`, and `install_name_tool` for relocation) is still on the
roadmap; until it lands `mcpp pack <program>` refuses on that format rather than
producing something that only looks like a bundle. Windows DLL bundling beyond
the current `.zip`, and distribution formats such as `.deb` / `.rpm` / AppImage,
are also on the roadmap. This document evolves alongside the
`mcpp pack` implementation; for the latest options, refer to
`mcpp pack --help`.
the current `.zip` is also on the roadmap.

Distribution formats such as `.deb`, `.rpm`, AppImage and `.msi` are **not** on
this list, and that is a decision rather than an omission: they live in
packages and reach the user through `--format <name>`, for the reason the
section above gives. Nothing further needs to join `[pack]`'s built-in modes.

This document evolves alongside the `mcpp pack` implementation; for the latest
options, refer to `mcpp pack --help`.

45 changes: 40 additions & 5 deletions docs/21-the-target-triple.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,26 @@ and the build reports what it resolved.

| Segment | Content | Example |
|---|---|---|
| `arch` | instruction set | `x86_64`, `aarch64`, `riscv64` |
| `os` | operating system, or `none` | `linux`, `windows`, `macos`, `none` |
| `env` | see below — it is a different axis per platform | `gnu`, `musl`, `msvc`, `elf` |
| `arch` | instruction set | `x86_64`, `aarch64`, `riscv64`, `wasm32` |
| `os` | operating system, or `none` | `linux`, `windows`, `macos`, `ios`, `emscripten`, `none` |
| `env` | see below — it is a different axis per platform | `gnu`, `musl`, `msvc`, `android`, `elf` |

The third segment is the one that repays attention, because it does not name
the same kind of thing everywhere:

| Platform | `env` names | Values |
|---|---|---|
| `linux` | the **C library** | `gnu` (glibc), `musl` |
| `linux` | the **C library** | `gnu` (glibc), `musl`, `android` (bionic) |
| `windows` | the **object ABI** | `gnu` (Itanium C++ ABI), `msvc` (Microsoft's) |
| `none` | the **object format** | `elf` |
| `macos` | nothing; the platform carries no segment | — |
| `macos`, `ios`, `emscripten` | nothing; the platform carries no segment | — |

`android` is a **C library** and therefore sits where `musl` sits, on a `linux`
OS. That placement is the whole of the modelling decision: the kernel *is*
Linux, so ELF, the `unix` family and `nasm -f elf64` are already right, and an
`os = "android"` would have made every one of them wrong by default and needed
a new answer at each site. What differs from `gnu` is bionic, the loader path
and the SDK — which is exactly what an `env` value is for.

On Windows the segment is frequently misread, because the word `gnu` suggests a
C library that is not there. Measured on an artefact built for
Expand All @@ -68,6 +75,30 @@ openkal. `gnu` is LLVM's label for the non-MSVC ABI, inherited from MinGW, and
clang requires that spelling to select the right internal toolchain. mcpp
cannot rename it.

### The object format is an axis, not a derivation

A triple's binary format used to be nothing at all: it was re-derived from `os`
wherever it was needed. `is_pe()` asked `os == "windows"`, artifact naming asked
again, the packer asked a third time. That is affordable while the answer has
two values.

`wasm32` is the first target in mcpp's vocabulary whose format is neither, and a
third value turns those derivations into an addition **at every such site** — and
a site that is missed does not fail. It silently answers ELF, because ELF is
what every `else` branch in the tree assumes. So the format is now one answer:

| target | format |
|---|---|
| `x86_64-linux-gnu`, `aarch64-linux-android`, `riscv64-none-elf` | ELF |
| `aarch64-macos`, `aarch64-ios` | Mach-O |
| `x86_64-windows-gnu`, `x86_64-windows-msvc` | PE |
| `wasm32-emscripten` | wasm |

It is **not** the same question as "is there an operating system to link
against". A bare-metal RISC-V image is ELF with no OS; a wasm module has an
OS-like layer (Emscripten's POSIX emulation) and is not ELF. Merging the two
axes is the mistake this replaces.

## Declining The Third Segment

`<arch>-<os>` is a complete target on every platform:
Expand Down Expand Up @@ -444,6 +475,10 @@ other's rows.
| `thumbv8m.base-none-eabi` | preview | `llvm@22.1.8` | payload | payload | payload | payload |
| `thumbv8m.main-none-eabi` | verified | `llvm@22.1.8` | payload | payload | payload | payload |
| `thumbv8m.main-none-eabihf` | preview | `llvm@22.1.8` | payload | payload | payload | payload |
| `aarch64-linux-android` | planned | — | planned | planned | planned | planned |
| `x86_64-linux-android` | planned | — | planned | planned | planned | planned |
| `aarch64-ios` | planned | — | planned | planned | planned | planned |
| `wasm32-emscripten` | planned | — | planned | planned | planned | planned |

`payload` a toolchain payload here produces it · `graph` no payload, but a
dependency can supply the system · `system` located on the machine, not
Expand Down
Loading
Loading