Skip to content

Commit efba585

Browse files
committed
feat(target): Android, iOS and wasm are rows, and the object format is an axis
Section 3 of the design record draws the boundary exactly: a package can add a language, a tool, an action, a payload and a generated module, and IT CANNOT ADD A TRIPLE. Identity is three strings and `kKnownTargets` is compiled into the binary, so every layer below the first -- the `.apk` step, the `.app` step, the `.html`+`.wasm` step, the runner, the signing, the non-C++ glue -- waits on a row here and on nothing else in the engine. Registering the rows is what turns each of those from a plugin with nowhere to attach into a plugin that can be written. aarch64-linux-android x86_64-linux-android aarch64-ios wasm32-emscripten ALL FOUR ARE `planned`, WHICH IS A REFUSAL AND NOT A GAP. The tier gate answers `tier-planned` naming the row: error: target 'aarch64-linux-android' is registered but not yet supported (planned) -- no toolchain is published for it yet error: target 'aarch64-linux-androideabi' (which resolves to 'aarch64-linux-android') is registered but not yet supported rather than `unknown target`, which was false, or a build that resolves and produces nothing, which section 3.1 argues would be worse than the row's absence. What each row still needs is a PAYLOAD in every case and never engine work: `xim:android-ndk`, `xim:emsdk`, and for iOS a licence reading before a packaging decision. THE OBJECT FORMAT IS NOW ONE ANSWER, AND THAT IS WHAT #597 ACTUALLY NEEDED. The binary format was never a field. 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 -- which is affordable only 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. A site that is missed does not fail: it silently answers ELF, because ELF is what every `else` branch in the tree assumes. `ObjectFormat` is that addition made once, with `is_pe()` / `is_mach_o()` / `is_wasm()` reading it. It is deliberately NOT the same question as `is_freestanding()`. 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. ANDROID'S PLACEMENT IS THE MODELLING DECISION: `env = "android"` on a `linux` OS, not `os = "android"`. The kernel IS Linux, so ELF, the `unix` family and `nasm -f elf64` are already right; an OS value would have made every one of them wrong by default and required a new answer at each site. What differs from `gnu` is bionic, the loader path and the SDK -- which is what an `env` value is for. `androideabi` resolves to the same env: the EABI half is the ARM calling convention, which the arch segment already carries. `is_apple()` exists because a site that means "Apple" and asks "macOS" gets iOS wrong in the direction that still links. iOS shares the object format, the linker, the `arm64` spelling and `codesign` with macOS, and differs in the SDK and the deployment-target flag. No deployment target is baked into `llvm_triple()` for it, unlike the macOS branch: `-miphoneos-version-min` belongs to the layer that owns the SDK and the bundle, and a default here would be a second place that answers it. A DISPLAY DEFECT THE ROWS EXPOSED. `x86_64-linux-android` showed no `cross` tag, because that test compared arch and OS only -- and this target agrees with an x86_64 Linux host on both. An Android artifact needs bionic's loader at `/system/bin/linker64`, which no ordinary Linux host has, so it cannot run there. Spelled as a property rather than by adding `env != env`, which would have taken `x86_64-linux-musl` with it -- that one is static and does run here. 48 new cells in tests/matrix/expected.tsv, one per (mode, host, compiler) the table declares, all `unsupported / tier-planned`; the declared per-host counts move with them, because compare.sh checks the total before it checks a cell. The day a row is wired, its cells go red and say so. Verified locally: 50/50 payload and 24/24 graph on linux-x86_64. Docs: `docs/21`'s segment tables gain the new values, a section states the object-format axis and why it is not the freestanding question, and the host/target matrix gains four rows. Both READMEs' platform tables record what each row waits on. Both languages.
1 parent 26ec4c9 commit efba585

9 files changed

Lines changed: 564 additions & 39 deletions

File tree

.agents/docs/2026-09-11-distribution-plugins-and-platform-decomposition.md

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -710,24 +710,59 @@ requests **no** format and asserts the set is still non-empty.
710710
| A dist member's tool is absent | The tool is a host lookup, and an empty path becomes an argv token | Refuse in the build program, naming the tool and where it was looked for |
711711
| The produced distributable is valid and empty | Section 2's measured 52 KB installer | Each member asserts a floor on its own output, on the **success** path, through `mcpp::warning` |
712712

713-
### 9.7 What sections 4 to 7 promise that this pass does not deliver
714-
715-
Stated here rather than discovered later. Each is blocked on something no
716-
amount of engine work supplies:
717-
718-
- **`xim:android-ndk`, `xim:emsdk`** (§7 step 5) and the **Android row**
719-
(step 6). Both recipes are measured working in a scratch directory (§3.1);
720-
turning either into a payload means fetching and republishing a
721-
multi-gigabyte vendor toolchain with a derived 133-file module surface. That
722-
is its own release, not a side effect of this one.
723-
- **The iOS row** (step 7) and `dist-apple`'s iOS half. Not measurable on a
724-
Linux host, and §3.1 states the two possibilities rather than choosing.
725-
- **Web** (step 8). [#597](https://github.com/mcpp-community/mcpp/issues/597)
726-
is a target-model change.
727-
- **`dist-android`, `dist-web`**. Each waits on its row.
728-
729-
The engine additions in §2.2 are what make each of those a single verifiable
730-
change when it comes. None of them is a prerequisite for the others.
713+
### 9.7 The engine's half of section 3, and what is left after it
714+
715+
Section 3's boundary is exact: a package can add a language, a tool, an action,
716+
a payload and a generated module, and it **cannot add a triple**. Every layer
717+
below the first — the `.apk` step, the `.app` step, the `.html`+`.wasm` step,
718+
the runner, the signing, the non-C++ glue — therefore waits on a row in
719+
`kKnownTargets` and on nothing else in the engine. So the rows are engine work
720+
and belong in the same release as §2.2:
721+
722+
| Row | Tier | What it still needs |
723+
|---|---|---|
724+
| `aarch64-linux-android`, `x86_64-linux-android` | `planned` | `xim:android-ndk` |
725+
| `aarch64-ios` | `planned` | the iPhoneOS SDK, and a licence reading first (§9.8) |
726+
| `wasm32-emscripten` | `planned` | `xim:emsdk` |
727+
728+
**`planned` is a refusal, not a gap.** The tier gate answers `tier-planned`
729+
naming the row, so `mcpp build --target aarch64-linux-android` says the
730+
vocabulary has this target and nothing is wired yet — rather than `unknown
731+
target`, which was false, or a build that resolves and produces nothing, which
732+
§3.1 argues would be worse than the row's absence. Each cell is declared in
733+
`tests/matrix/expected.tsv`, so the day a row is wired the matrix goes red and
734+
says so.
735+
736+
**Web needed one thing the other two did not, and it was not a table row.** The
737+
binary format was never a field: it was re-derived from `os` at each site that
738+
needed it, which is affordable while the answer has two values. `wasm32` is the
739+
first target whose format is neither, and a third value turns those derivations
740+
into an addition at every such site — where a missed site does not fail, it
741+
silently answers ELF. `ObjectFormat` is that addition made once. This is the
742+
substance of [#597](https://github.com/mcpp-community/mcpp/issues/597)'s
743+
"changes the target model rather than extending a table", and with §3.1 having
744+
answered the standard-library half, #597 is now one problem rather than two.
745+
746+
**Android's placement is the modelling decision.** `env = "android"` on a
747+
`linux` OS, not `os = "android"`: the kernel is Linux, so ELF, the `unix`
748+
family and `nasm -f elf64` are already right, and an OS value would have made
749+
every one of them wrong by default and required a new answer at each site. What
750+
differs from `gnu` is bionic, the loader path and the SDK, which is what an
751+
`env` value is for.
752+
753+
What remains outside this pass, each blocked on something no engine work
754+
supplies:
755+
756+
- **`xim:android-ndk`, `xim:emsdk`** (§7 step 5). Both recipes are measured
757+
working in a scratch directory (§3.1); turning either into a payload means
758+
fetching and republishing a multi-gigabyte vendor toolchain with a derived
759+
133-file module surface. That is its own release, not a side effect of this
760+
one — and the rows landing first is exactly §7's ordering argument, which
761+
said doing the payloads first would make each row small. The rows turned out
762+
to be the cheap half either way.
763+
- **The iPhoneOS SDK.** Not measurable on a Linux host, and §9.8 gives the
764+
decision procedure rather than the measurement.
765+
- **`dist-android`, `dist-web`**. Each waits on its payload, not on its row.
731766

732767
### 9.8 The iOS SDK: a three-tier policy rather than an open question
733768

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,9 @@ list` reports for this machine):
419419
| `aarch64-none-elf` · `x86_64-none-elf` | llvm 22 — bare metal, no C library by default ² | preview |
420420
| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22 — Cortex-M4/M7 soft float, M23, M33F/M55F ² | preview |
421421
| `riscv64-linux-musl` · `aarch64-linux-gnu` · `x86_64-macos` || planned |
422+
| `aarch64-linux-android` · `x86_64-linux-android` | needs `xim:android-ndk`; `import std` measured working on the NDK's clang | planned |
423+
| `aarch64-ios` | needs the iPhoneOS SDK, which is a licence question before it is a packaging one | planned |
424+
| `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 |
422425

423426
`verified` an image has been built **and run** for the row, qemu and wine
424427
included · `preview` it builds and links, and no emulator run has been recorded

README.zh-CN.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,9 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
406406
| `aarch64-none-elf` · `x86_64-none-elf` | llvm 22——裸机,默认不带 C 库 ² | preview |
407407
| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22——Cortex-M4/M7 软浮点、M23、M33F/M55F ² | preview |
408408
| `riscv64-linux-musl` · `aarch64-linux-gnu` · `x86_64-macos` || planned |
409+
| `aarch64-linux-android` · `x86_64-linux-android` |`xim:android-ndk`;`import std` 在 NDK 自带的 clang 上已实测可用 | planned |
410+
| `aarch64-ios` | 待 iPhoneOS SDK,而它先是一个许可问题再是一个打包问题 | planned |
411+
| `wasm32-emscripten` |`xim:emsdk`;`import std``em++` 上已实测可用,目标模型见 [#597](https://github.com/mcpp-community/mcpp/issues/597) | planned |
409412

410413
`verified` 该行的镜像已被构建**并运行**过,qemu 与 wine 都算 · `preview` 可构建
411414
可链接,未记录过模拟器运行 · `planned` 已登记在词表中,尚未接线 —— 面向这类目标

docs/21-the-target-triple.md

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,26 @@ and the build reports what it resolved.
3838

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

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

4848
| Platform | `env` names | Values |
4949
|---|---|---|
50-
| `linux` | the **C library** | `gnu` (glibc), `musl` |
50+
| `linux` | the **C library** | `gnu` (glibc), `musl`, `android` (bionic) |
5151
| `windows` | the **object ABI** | `gnu` (Itanium C++ ABI), `msvc` (Microsoft's) |
5252
| `none` | the **object format** | `elf` |
53-
| `macos` | nothing; the platform carries no segment ||
53+
| `macos`, `ios`, `emscripten` | nothing; the platform carries no segment ||
54+
55+
`android` is a **C library** and therefore sits where `musl` sits, on a `linux`
56+
OS. That placement is the whole of the modelling decision: the kernel *is*
57+
Linux, so ELF, the `unix` family and `nasm -f elf64` are already right, and an
58+
`os = "android"` would have made every one of them wrong by default and needed
59+
a new answer at each site. What differs from `gnu` is bionic, the loader path
60+
and the SDK — which is exactly what an `env` value is for.
5461

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

78+
### The object format is an axis, not a derivation
79+
80+
A triple's binary format used to be nothing at all: it was re-derived from `os`
81+
wherever it was needed. `is_pe()` asked `os == "windows"`, artifact naming asked
82+
again, the packer asked a third time. That is affordable while the answer has
83+
two values.
84+
85+
`wasm32` is the first target in mcpp's vocabulary whose format is neither, and a
86+
third value turns those derivations into an addition **at every such site** — and
87+
a site that is missed does not fail. It silently answers ELF, because ELF is
88+
what every `else` branch in the tree assumes. So the format is now one answer:
89+
90+
| target | format |
91+
|---|---|
92+
| `x86_64-linux-gnu`, `aarch64-linux-android`, `riscv64-none-elf` | ELF |
93+
| `aarch64-macos`, `aarch64-ios` | Mach-O |
94+
| `x86_64-windows-gnu`, `x86_64-windows-msvc` | PE |
95+
| `wasm32-emscripten` | wasm |
96+
97+
It is **not** the same question as "is there an operating system to link
98+
against". A bare-metal RISC-V image is ELF with no OS; a wasm module has an
99+
OS-like layer (Emscripten's POSIX emulation) and is not ELF. Merging the two
100+
axes is the mistake this replaces.
101+
71102
## Declining The Third Segment
72103

73104
`<arch>-<os>` is a complete target on every platform:
@@ -444,6 +475,10 @@ other's rows.
444475
| `thumbv8m.base-none-eabi` | preview | `llvm@22.1.8` | payload | payload | payload | payload |
445476
| `thumbv8m.main-none-eabi` | verified | `llvm@22.1.8` | payload | payload | payload | payload |
446477
| `thumbv8m.main-none-eabihf` | preview | `llvm@22.1.8` | payload | payload | payload | payload |
478+
| `aarch64-linux-android` | planned || planned | planned | planned | planned |
479+
| `x86_64-linux-android` | planned || planned | planned | planned | planned |
480+
| `aarch64-ios` | planned || planned | planned | planned | planned |
481+
| `wasm32-emscripten` | planned || planned | planned | planned | planned |
447482

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

docs/zh/21-the-target-triple.md

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,24 @@ C 库。选中 `x86_64-linux-musl` 就是选中 musl-gcc 载荷,选中
3232

3333
|| 内容 ||
3434
|---|---|---|
35-
| `arch` | 指令集 | `x86_64``aarch64``riscv64` |
36-
| `os` | 操作系统,或 `none` | `linux``windows``macos``none` |
37-
| `env` | 见下 —— 它在每个平台上是不同的轴 | `gnu``musl``msvc``elf` |
35+
| `arch` | 指令集 | `x86_64``aarch64``riscv64``wasm32` |
36+
| `os` | 操作系统,或 `none` | `linux``windows``macos``ios``emscripten``none` |
37+
| `env` | 见下 —— 它在每个平台上是不同的轴 | `gnu``musl``msvc``android``elf` |
3838

3939
第三段值得留意,因为它在各处命名的并不是同一类东西:
4040

4141
| 平台 | `env` 命名 | 取值 |
4242
|---|---|---|
43-
| `linux` | **C 库** | `gnu`(glibc)、`musl` |
43+
| `linux` | **C 库** | `gnu`(glibc)、`musl``android`(bionic) |
4444
| `windows` | **对象 ABI** | `gnu`(Itanium C++ ABI)、`msvc`(微软的) |
4545
| `none` | **对象格式** | `elf` |
46-
| `macos` | 无;该平台不带这一段 ||
46+
| `macos``ios``emscripten` | 无;该平台不带这一段 ||
47+
48+
`android` 是一个 **C 库**,所以它落在 `musl` 落的那个位置上,OS 段仍是 `linux`
49+
这个位置就是这处建模决定的全部:内核**就是** Linux,所以 ELF、`unix` family、
50+
`nasm -f elf64` 全都已经是对的;而一个 `os = "android"` 会让这三样默认全错,并
51+
且要求在每一处站点给出一个新答案。它与 `gnu` 的差别是 bionic、加载器路径和 SDK
52+
—— 而这恰好就是 `env` 这一段存在的意义。
4753

4854
在 Windows 上这一段经常被读错,因为 `gnu` 这个词暗示了一个并不在场的 C 库。
4955
对一份按构建期体系为 `x86_64-windows-gnu` 构建的产物实测:
@@ -59,6 +65,27 @@ compiler-rt,C 库是 musl,C++ 运行时是 libc++,平台是 openkal。`gnu` 是
5965
LLVM 词表里「非 MSVC 的那套 ABI」的标签,继承自 MinGW,而 clang 需要这个
6066
拼写来选中正确的内部工具链。mcpp 改不了它。
6167

68+
### 对象格式是一个轴,不是一处推导
69+
70+
一个三元组的二进制格式过去根本不是任何东西:它在每一处需要它的地方从 `os` 重新
71+
推导一遍。`is_pe()``os == "windows"`,产物命名再问一遍,打包器问第三遍。答案
72+
只有两个取值时,这是负担得起的。
73+
74+
`wasm32` 是 mcpp 词表里第一个格式不属于那两个的目标,而第三个取值会把那些推导变成
75+
**在每一处这样的站点上的一次添加** —— 而漏掉的那一处不会报错。它会静默地答 ELF,
76+
因为 ELF 正是这棵树里每一个 `else` 分支所假设的东西。于是格式现在是一个答案:
77+
78+
| 目标 | 格式 |
79+
|---|---|
80+
| `x86_64-linux-gnu``aarch64-linux-android``riscv64-none-elf` | ELF |
81+
| `aarch64-macos``aarch64-ios` | Mach-O |
82+
| `x86_64-windows-gnu``x86_64-windows-msvc` | PE |
83+
| `wasm32-emscripten` | wasm |
84+
85+
它与「有没有一个操作系统可供链接」**不是**同一个问题。一个裸机 RISC-V 映像是 ELF
86+
且没有 OS;一个 wasm 模块有一层类 OS 的东西(Emscripten 的 POSIX 模拟)而不是
87+
ELF。把这两个轴并成一个,正是这处改动要消除的那个错误。
88+
6289
## 省略第三段
6390

6491
`<arch>-<os>` 在每个平台上都是一个完整的目标:
@@ -398,6 +425,10 @@ CRT;图供给时是 `musl`。一个目标字符串,两个不同的 C 库 ——
398425
| `thumbv8m.base-none-eabi` | preview | `llvm@22.1.8` | 载荷 | 载荷 | 载荷 | 载荷 |
399426
| `thumbv8m.main-none-eabi` | verified | `llvm@22.1.8` | 载荷 | 载荷 | 载荷 | 载荷 |
400427
| `thumbv8m.main-none-eabihf` | preview | `llvm@22.1.8` | 载荷 | 载荷 | 载荷 | 载荷 |
428+
| `aarch64-linux-android` | planned || planned | planned | planned | planned |
429+
| `x86_64-linux-android` | planned || planned | planned | planned | planned |
430+
| `aarch64-ios` | planned || planned | planned | planned | planned |
431+
| `wasm32-emscripten` | planned || planned | planned | planned | planned |
401432

402433
`载荷` 这里有工具链载荷产出它 · `` 没有载荷,但依赖可以供给系统 ·
403434
`系统` 在机器上被找到,不是 mcpp 装的 · `SDK` 平台自己的 ·

0 commit comments

Comments
 (0)