diff --git a/.github/tools/check_docs_structure.sh b/.github/tools/check_docs_structure.sh
index 911ba7cf..443c9d52 100755
--- a/.github/tools/check_docs_structure.sh
+++ b/.github/tools/check_docs_structure.sh
@@ -17,6 +17,8 @@
# 11. every chapter states its reader, its question and its exclusions
# 12. a citation naming a section lands in the chapter that contains it
# 13. every table the manifest reference documents is in the lookup index
+# 14. a link labelled with a chapter number points at that chapter, by its title
+# 15. every table row is inside a table
#
# What it deliberately does NOT check: whether a chapter documents what is
# implemented, whether an assertion's strength matches its evidence, or whether
@@ -144,13 +146,17 @@ for f in .agents/docs/[0-9]*.md; do
|| bad "$f: front matter declares no valid \`status\` (active | landed | superseded | abandoned)"
done
-# ── 9. every relative link in docs/ and examples/ resolves, fragment included ─
+# ── 9. every relative link in docs/, examples/ and the READMEs resolves ──────
#
# Rule 3 catches `docs/NN-*.md` named anywhere, including from source comments.
# This is the other half: a Markdown link in a document that points at a file
# which is not there. Both halves are needed -- a chapter moved in this batch
# would satisfy one and break the other.
#
+# THE TWO TOP-LEVEL READMEs ARE IN THE SET. They are the entry point to every
+# tree below them and they carry more relative links than most chapters, and
+# until they were added here nothing checked one of those links at all.
+#
# THE FRAGMENT IS PART OF THE LINK. The first version of this rule discarded
# it (`(?:#[^)]*)?`), so a link to a section that had been renamed resolved to
# the file and was reported correct. Renaming 100 headings for register in one
@@ -195,7 +201,9 @@ def anchors_of(path):
out.add(s if n == 0 else f"{s}-{n}")
return out
-files = list(pathlib.Path("docs").rglob("*.md")) + list(pathlib.Path("examples").rglob("*.md"))
+files = (list(pathlib.Path("docs").rglob("*.md"))
+ + list(pathlib.Path("examples").rglob("*.md"))
+ + [pathlib.Path("README.md"), pathlib.Path("README.zh-CN.md")])
cache = {}
bad = 0
for f in files:
@@ -227,11 +235,19 @@ PYCHECK
# 简体中文 `[features]` section had no body at all, and 简体中文 §2.11 was
# missing the `identity` verdict table. Both predate this check and both are
# invisible to every other one.
+#
+# THE PAIR AT THE ROOT IS CHECKED TOO, AND IT IS WHERE THE COST WAS HIGHEST.
+# `README.zh-CN.md` carried 14 target rows against the English 21: the seven it
+# lacked were every bare-metal row, so a reader of the 简体中文 README saw a
+# tool with no freestanding support at all. Heading count, code-block count and
+# `` count were all equal, which is why every other check was green.
python3 - <<'PYPARITY' || fail=1
import pathlib, sys, re
bad = 0
-for en in sorted(pathlib.Path("docs").glob("*.md")):
- zh = pathlib.Path("docs/zh") / en.name
+pairs = [(en, pathlib.Path("docs/zh") / en.name)
+ for en in sorted(pathlib.Path("docs").glob("*.md"))]
+pairs.append((pathlib.Path("README.md"), pathlib.Path("README.zh-CN.md")))
+for en, zh in pairs:
if not zh.exists():
continue
def count(f):
@@ -330,6 +346,107 @@ for k in missing:
sys.exit(1 if missing else 0)
PYLOOKUP
+# ── 14. a link labelled with a chapter number points at that chapter ─────────
+#
+# Rule 3 checks that a named path exists and rule 9 that a link resolves. Both
+# passed on `[docs/13 -- Bare-Metal and Freestanding Targets](docs/40-baremetal.md)`
+# in README.md: the renumbering rewrote the path and left the label, so the
+# README told its reader to read chapter 13 for eleven of the twenty-one rows
+# in its own target table. A label that names a number is an assertion about
+# where the reader is being sent, and it is checkable against the path.
+python3 - <<'PYLABEL' || fail=1
+import re, pathlib, sys
+LINK = re.compile(r"\[([^\]]+)\]\((?!https?:|mailto:)([^)\s#]+)(?:#[^)\s]+)?\)")
+NUM = re.compile(r"(?:docs/|^|[^0-9a-zA-Z])(\d{2})(?:\s*(?:--|—|-|\s)|$)")
+files = (list(pathlib.Path("docs").rglob("*.md"))
+ + [pathlib.Path("README.md"), pathlib.Path("README.zh-CN.md")])
+bad = 0
+for f in files:
+ for m in LINK.finditer(f.read_text(errors="ignore")):
+ label, path = m.group(1), m.group(2)
+ base = pathlib.Path(path).name
+ target_no = re.match(r"(\d{2})-", base)
+ label_no = NUM.match(label.strip())
+ if not target_no or not label_no:
+ continue
+ if target_no.group(1) != label_no.group(1):
+ print(f"FAIL: {f}: label `{label}` names chapter "
+ f"{label_no.group(1)}, the link goes to {base}")
+ bad += 1
+ continue
+ # The number agrees. In the two READMEs the label is also expected to
+ # carry the chapter's own title, because that is where a renumbering or
+ # a rename rots unseen and five 简体中文 labels were translated from the
+ # English titles rather than taken from the chapters.
+ #
+ # NOT IN docs/. Measured across the tree: sixty-odd links there label a
+ # chapter by its SUBJECT on purpose -- `[30 -- build.mcpp]`,
+ # `[04 -- \u00a72.6.1]`, `[10 -- Packaging & Release]` -- and that is a
+ # convention, not a defect. A check that would require editing all of
+ # them is imposing a new rule rather than enforcing an existing one.
+ if f.name not in ("README.md", "README.zh-CN.md"):
+ continue
+ said = label.strip()[label_no.end(1):].strip(" -\u2014\u2013:\uff1a")
+ if not said:
+ continue
+ target = (f.parent / path) if not path.startswith("docs/") else pathlib.Path(path)
+ if not target.is_file():
+ continue
+ head = target.read_text(errors="ignore").split("\n")[0]
+ title = re.sub(r"^#\s*\d{2}\s*(?:\u2014\u2014|\u2014|--|-)?\s*", "", head).strip()
+ # A PREFIX rather than equality: shortening a title by dropping its tail
+ # is honest, and `[30 -- Build Programs]` for `Build Programs:
+ # \u0060build.mcpp\u0060` is the shape that takes. Words the chapter does not
+ # use are what this rejects -- a label translated from the other
+ # language's title rather than taken from the chapter's own.
+ if title and not title.startswith(said):
+ print(f"FAIL: {f}: label says `{said}`, chapter {base} is titled `{title}`")
+ bad += 1
+sys.exit(1 if bad else 0)
+PYLABEL
+
+# ── 15. every table row is inside a table ───────────────────────────────────
+#
+# Rule 10 counts a translation's table rows, and a COUNT cannot see WHERE a row
+# is. One row of the target table was moved to line 1 of README.zh-CN.md, above
+# the document's own title, and rule 10 stayed green at 61 rows against 61: the
+# row was still in the file. What a reader saw was a stray table row before the
+# heading, and only a reader saw it.
+#
+# The check is positional rather than numeric: a maximal run of lines beginning
+# with `|` is a table only if its second line is a delimiter row. A row that has
+# been moved somewhere else lands in a run of its own and has no delimiter.
+python3 - <<'PYROW' || fail=1
+import re, pathlib, sys
+DELIM = re.compile(r"^\|[\s:|-]+\|?\s*$")
+files = (list(pathlib.Path("docs").rglob("*.md"))
+ + [pathlib.Path("README.md"), pathlib.Path("README.zh-CN.md")])
+bad = 0
+for f in files:
+ lines, infence, run = f.read_text(errors="ignore").split("\n"), False, []
+ def close(run):
+ global bad
+ if not run:
+ return
+ if len(run) < 2 or not DELIM.match(run[1][1]):
+ n, text = run[0]
+ print(f"FAIL: {f}:{n}: a table row outside a table: {text[:60]}")
+ bad += 1
+ for n, line in enumerate(lines, 1):
+ if line.startswith("```"):
+ infence = not infence
+ close(run); run = []
+ continue
+ if infence:
+ continue
+ if line.startswith("|"):
+ run.append((n, line))
+ else:
+ close(run); run = []
+ close(run)
+sys.exit(1 if bad else 0)
+PYROW
+
if [[ "$fail" -eq 0 ]]; then
echo "OK: docs structure checks pass"
fi
diff --git a/README.md b/README.md
index 64de02b1..578ec150 100644
--- a/README.md
+++ b/README.md
@@ -20,20 +20,23 @@
## Highlights
-- **Native C++23 module support** — `import std` handled automatically, file-level incremental builds, automatic module dependency analysis, zero manual configuration
-- **Pure modular self-hosting** — mcpp itself consists of 43+ C++23 modules and builds itself; the module pipeline is battle-tested
-- **Works out of the box** — one-command install, bundled GCC 16 / LLVM 20 toolchains downloaded into an isolated sandbox, never polluting your system
-- **Integrated dependency management** — SemVer constraint resolution, lockfile, cross-project BMI cache, custom package indices
-- **Multi-package workspaces** — unified lockfile and version management for larger projects
+- **Modular build system** — C++ modules first: `import std` handled automatically, file-level incremental builds, automatic dependency analysis, nothing to configure
+- **Build plugins and heterogeneous hardware** — `build.mcpp` and rule packages extend the build; CUDA, HIP, SYCL, Vulkan/SPIR-V and Ascend C are each a rule package
+- **Package management and a module-library ecosystem** — SemVer constraints, lockfile, cross-project BMI cache, custom indices; a library from [mcpplibs](https://github.com/mcpplibs) is two lines away from `import`
+- **Toolchain management and cross-compilation** — `family@version` installed on demand; `--target` moves the same build to Windows, macOS, Cortex-M or RISC-V bare metal, and one source tree reaches several hosted targets over openkal
+- **Environment and runtime** — the user-space environment xlings provides: toolchains and dependencies stay in an isolated sandbox, and a runner puts the artifact on a board or an emulator
+- **Pure modular self-hosting** — mcpp is written entirely in C++23 module interface units and builds itself
## Why mcpp
-mcpp is built specifically for **C++23 module-first development**. If you want to use `import std`, module interface units (`.cppm`), module partitions, and other modern C++ features in your project, mcpp gives you a smooth, friendly experience on Linux, macOS ARM64, and Windows x86_64:
+mcpp is built specifically for **C++23 module-first development**. If you want to use `import std`, module interface units (`.cppm`), module partitions, and other modern C++ features in your project, mcpp gives you a smooth, friendly experience on Linux, macOS ARM64, and Windows x86_64.
-- **Modular by default** — projects created by `mcpp new` use C++23 modules directly; `import std` just works
-- **File-level incremental builds** — three-layer optimization based on P1689 dyndep (front-end dirty check + per-file scanning + BMI restat); only the modules that actually changed get recompiled
-- **Create & build in one go** — `mcpp new hello && cd hello && mcpp build`; toolchains install automatically, no compiler or build-system setup required
-- **A modular ecosystem** — [mcpplibs](https://github.com/mcpplibs) offers a growing set of directly `import`-able C++ module libraries, plus support for custom package indices
+C++ normally spreads these five jobs across five tools, and mcpp is one command
+for all five. The second row names what each column is usually recognised as.
+
+| mcpp | build system | build plugins | package manager | toolchain manager | environment and runtime |
+|---|---|---|---|---|---|
+| **closest to** | CMake + Ninja | CMake modules, xmake rules | vcpkg, Conan | rustup, nvm | conda, Nix |
> [!NOTE]
> **Early-stage project** — mcpp is under active development; interfaces and behavior may change in future releases.
@@ -255,6 +258,29 @@ import mcpplibs.cmdline;
+
+Cross-compilation, bare metal and devices
+
+- `mcpp build --target ` — one flag; the toolchain payload for that target is resolved and installed automatically
+- Targets from `x86_64-linux-gnu` to Cortex-M, Cortex-A and RISC-V bare metal; the full table is under [Platform Support](#platform-support)
+- Freestanding targets carry no operating system: the C library, startup code, memory layout and emulator travel with a board-support package rather than with mcpp
+- Runners reach an artifact that cannot run on the build machine — `mcpp run --runner flash`, `--list-runners`, `mcpp why runners`
+- `mcpp new --template riscv-virt-rt` — a board template a package ships, instantiated by name
+- Cross-compilation over openkal: a portable program builds for a target whose kernel interface and C library come from packages
+
+
+
+
+Heterogeneous builds and accelerators
+
+- `[build] accel = "cuda12.9+{sm_89}, vulkan1.2"` — a build names one or more device backends, and `cfg(accelerator = "cuda")` is true in that build
+- Five programming models have rule packages today: CUDA, HIP, SYCL, Vulkan/SPIR-V and Ascend C
+- Device translation units are compiled by their own compiler and join the ordinary link; the host/device boundary is generated rather than written twice
+- A constrained glob selects device sources: `{ glob = "src/kernels/**/*.cu", accel = "cuda12.9+{sm_89}" }`
+- Nothing in the engine holds a vendor name, so a sixth backend is a package rather than an engine change
+
+
+
Package & dependency management
@@ -289,6 +315,16 @@ import mcpplibs.cmdline;
+
+Extending the build
+
+- `build.mcpp` — a build program for a step mcpp has no rule for, speaking a directive protocol that is versioned rather than guessed
+- `mcpp::action` declares work with explicit inputs and outputs, so a generated file takes part in the incremental graph instead of sitting outside it
+- A rule package carries that step to other projects: it declares a rule module, and a consumer selects it as a feature
+- Payloads, runtime adapters and board-support packages are ordinary packages — a tool, a driver or a board is installed by the resolver that installs a library
+
+
+
Developer experience
@@ -297,7 +333,9 @@ import mcpplibs.cmdline;
- `mcpp test [pattern] [-- args]` — auto-discover and run tests (filter by name; `--list`, `--timeout `, `--message-format json`)
- `mcpp search` — search package indices
- `mcpp add / remove / update` — dependency management
-- `mcpp why [toolchain|runtime|deps]` — explain resolved build decisions
+- Profiles and features on the command line: `--release` / `--profile ` on `build` and `run`, `--features ` on `build`, `run` and `test`
+- `mcpp why [toolchain|runtime|deps|runners]` — explain resolved build decisions; `--format json` for a machine reader
+- `mcpp emit sbom` — a CycloneDX bill of materials for the resolution just recorded
- `mcpp --offline` / `MCPP_OFFLINE=1` — use only already available local state
- `mcpp explain E0001` — detailed error-code explanations
- `mcpp self doctor` — environment self-diagnosis
@@ -306,10 +344,10 @@ import mcpplibs.cmdline;
## Benchmark
-Building **mcpp itself** — 137 module interface units, 57k lines, every one of
-them `import std;` — with four engines handed the **same compiler binary**.
-Each cell is the median of **3 samples** and how many times faster it is than
-cmake. Every column comes from **one run**.
+Building **mcpp itself** — 137 module interface units and 57k lines at the
+pinned workload, every one of them `import std;` — with four engines handed the
+**same compiler binary**. Each cell is the median of **3 samples** and how many
+times faster it is than cmake. Every column comes from **one run**.
| scenario | `mcpp` | `mcpp +opt` | `mcpp (old)` | `cmake` | `xmake` |
@@ -324,38 +362,29 @@ cmake. Every column comes from **one run**.
`mcpp` = mcpp@2026.8.13.1, the build under test · `mcpp +opt` = the SAME binary as `mcpp`, with the opt-in key `[build] bmi_schedule = "on"` (off by default) · `mcpp (old)` = mcpp@2026.8.11.3, the previously published release.
Linux x86_64 · i9-13900K · gcc 16.1.0 · n=3 · pinned workload `a749e9f` ·
-cmake 4.4.2 / xmake 3.1.0 · `-` would mean not measured, and there is none here ·
-min/max sit within 4% of every median above 1s ·
+cmake 4.4.2 / xmake 3.1.0 · min/max sit within 4% of every median above 1s ·
data: [`standard-20260814-linux-x86_64`](bench/results/standard-20260814-linux-x86_64/).
* **Cascade suppression accounts for the `touch-hub` and `edit-comment` rows.**
- cmake and xmake decide by timestamp and rebuild every downstream unit. mcpp
+ cmake and xmake decide by timestamp and rebuild every downstream unit; mcpp
compares the BMI the compiler has just produced against the previous one and
skips the cascade when the interface is unchanged. This is default behaviour
- and requires no configuration. The `mcpp (old)` column measures the previous
- release at 81.72s, level with cmake, so the effect is new in this revision.
-* **`edit-body` measures the case where the cascade is genuinely owed** — and
- whether an edit owes one depends on where the body lives:
-
- | the function body is in… | editing it | this row |
- |---|---|---|
- | a `.cppm`, and the edit **moves lines** | GCC records declaration positions, so the BMI changes → cascade owed | **what is measured: 1.1x, and 2.9x with `+opt`** |
- | a `.cppm`, edited **in place** (same line count) | GCC does not serialise non-template bodies → BMI unchanged → no cascade | ~200x, like `touch-hub` |
- | a separate `.cpp` implementation unit | that file has no BMI at all → no cascade, on every compiler | ~200x |
-
- The perturbation here inserts a statement, so it takes the first row: every
- engine has to rebuild the importers, and one that did not would be skipping
- work. `+opt` does not skip it either — it does the same work 2.9x faster.
- Splitting interface from implementation is the sturdiest of the three, because
- it does not depend on GCC's body handling or on avoiding line shifts.
- Measured in
+ and requires no configuration. `mcpp (old)` measures the previous release at
+ 81.72s, level with cmake, so the effect is new in this revision.
+* **`edit-body` is the control, and mcpp is deliberately not fast on it.** The
+ perturbation inserts a statement into an interface unit, which moves the
+ source positions GCC records, so the BMI changes and every importer is owed a
+ rebuild — an engine that were fast on that row would be skipping work it owes.
+ Whether an edit owes a cascade depends on where the body lives: an in-place
+ edit of the same length, or a body in a separate `.cpp`, owes none and lands
+ near the 200x rows. Measured in
[`.agents/docs/2026-08-15-module-edit-granularity.md`](.agents/docs/2026-08-15-module-edit-granularity.md).
* **`bmi_schedule` is opt-in and disabled by default** (`auto` resolves to off).
- It moves code generation off the critical path, so it helps only where a
- cascade is required: `cold` 86.69s → 35.73s, `edit-body` 80.87s → 29.83s. On
- the two rows where mcpp already skips the cascade it yields no improvement.
- An incorrect scheduling change fails silently rather than loudly, so the
- default is not changed on the evidence of a single machine.
+ It moves code generation off the critical path, so it pays only where a
+ cascade is owed — `cold` 86.69s → 35.73s, `edit-body` 80.87s → 29.83s, and
+ nothing on the two rows mcpp already skips. An incorrect scheduling change
+ fails silently rather than loudly, so the default is not changed on the
+ evidence of a single machine.
**[Methodology, pinned versions, and the full data →
`bench/README.md`](bench/README.md)** · [简体中文](bench/README.zh-CN.md)
@@ -370,29 +399,31 @@ the right toolchain payload is resolved and installed automatically.
**Hosts** (where mcpp itself runs): Linux x86_64 / aarch64, macOS arm64, Windows x86_64.
-**Targets** (what `--target` accepts; this table mirrors the in-code vocabulary):
+**Targets** (what `--target` accepts; the rows and their tiers are the ones in
+`modules/toolchain-model/src/triple.cppm`, which is also what `mcpp toolchain
+list` reports for this machine):
-| Target | Convention toolchain | Status |
+| Target | Convention toolchain | Tier |
|---|---|:---:|
-| `x86_64-linux-gnu` | gcc *(Linux default)* or llvm | yes |
-| `x86_64-linux-musl` | gcc 16, fully static | yes |
-| `aarch64-linux-musl` | gcc 16, fully static — cross from x86_64 (qemu-verified) or native | yes |
-| `x86_64-windows-gnu` | gcc 16 MinGW-w64 — native on Windows, cross from Linux (wine-verified) *(Windows default without Visual Studio)* | yes |
-| `x86_64-windows-msvc` | `msvc@system` (detected VS/BuildTools) or llvm ¹ *(Windows default with Visual Studio)* | yes |
-| `aarch64-macos` | llvm *(macOS default)* | yes |
-| `riscv64-none-elf` | llvm 22 — bare metal, no OS; needs no per-host cross payload ² | yes |
-| `riscv32-none-elf` | llvm 22 — bare metal, no OS; needs no per-host cross payload ² | yes |
-| `thumbv6m-none-eabi` | llvm 22 — Cortex-M0/M0+/M1, bare metal ² | yes |
-| `thumbv7m-none-eabi` | llvm 22 — Cortex-M3, bare metal ² | yes |
-| `thumbv7em-none-eabihf` | llvm 22 — Cortex-M4F/M7F, hard float ² | yes |
-| `thumbv8m.main-none-eabi` | llvm 22 — Cortex-M33/M55, soft float ² | yes |
-| `armv7a-none-eabi` · `armv7a-none-eabihf` | llvm 22 — Cortex-A 32-bit, bare metal ² | yes |
-| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22 — builds and links; no emulator run recorded | planned |
-| `riscv64-linux-musl` | — | planned |
-| `aarch64-linux-gnu` | — | planned |
-| `x86_64-macos` | — | planned |
-
-verified — CI builds **and executes** the artifact end-to-end (qemu/wine included) | planned
+| `x86_64-linux-gnu` | gcc *(Linux default)* or llvm | verified |
+| `x86_64-linux-musl` | gcc 16, fully static | verified |
+| `aarch64-linux-musl` | gcc 16, fully static — cross from x86_64 (qemu) or native | verified |
+| `x86_64-windows-gnu` | gcc 16 MinGW-w64 — native on Windows, cross from Linux (wine) *(Windows default without Visual Studio)* | verified |
+| `x86_64-windows-msvc` | `msvc@system` (detected VS/BuildTools) or llvm ¹ *(Windows default with Visual Studio)* | verified |
+| `x86_64-windows-musl` | llvm 22 — a PE with a musl C library, which no gcc emits; the system comes from the dependency graph | preview |
+| `aarch64-macos` | llvm *(macOS default)* | verified |
+| `riscv64-none-elf` · `riscv32-none-elf` | llvm 22 — bare metal, `xim:picolibc-riscv` ² | verified |
+| `thumbv6m-none-eabi` · `thumbv7m-none-eabi` | llvm 22 — Cortex-M0/M0+/M1, Cortex-M3 ² | verified |
+| `thumbv7em-none-eabihf` · `thumbv8m.main-none-eabi` | llvm 22 — Cortex-M4F/M7F hard float, Cortex-M33/M55 soft float ² | verified |
+| `armv7a-none-eabi` · `armv7a-none-eabihf` | llvm 22 — Cortex-A 32-bit, the first row with an MMU ² | verified |
+| `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 |
+
+`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
+· `planned` registered in the vocabulary and nothing wired yet — a build for
+such a target is refused rather than attempted.
> Linux release binaries are fully static musl builds for x86_64 and aarch64
> (`x86_64-linux-musl` and `aarch64-linux-musl`).
@@ -413,19 +444,27 @@ verified — CI builds **and executes** the artifact end-to-end (qemu/wine inclu
> cross-compilers by construction, so any host that can install the LLVM
> payload produces these targets. The C library, startup code, memory layout
> and emulator travel with a board-support package rather than with mcpp — see
-> [docs/13 — Bare-Metal and Freestanding Targets](docs/40-baremetal.md).
+> [40 — Bare-Metal and Freestanding Targets](docs/40-baremetal.md).
## Documentation
-- [Getting Started](docs/01-getting-started.md) — install → new → build → run in 5 minutes
-- [Examples](docs/03-examples.md)
-- [Packaging & Release](docs/10-pack-and-release.md)
-- [Toolchain Management](docs/20-toolchains.md)
-- [Building from Source](docs/90-build-from-source.md)
-- [mcpp.toml Guide](docs/04-mcpp-toml.md)
-- [Workspaces](docs/07-workspace.md)
+[`docs/`](docs/README.md) is the manual. A chapter's first digit says which part
+it belongs to, and the index carries the reverse lookup — from a manifest key or
+a command in front of a reader, to the chapter that owns it.
+
+| Part | Start at |
+|---|---|
+| `0x` fundamentals | [01 Getting Started](docs/01-getting-started.md) · [04 The mcpp.toml Manifest](docs/04-mcpp-toml.md) · [09 Commands by Scenario](docs/09-commands-by-scenario.md) |
+| `1x` publishing | [10 Packaging an Application for Release](docs/10-pack-and-release.md) · [11 Publishing a Library to mcpp-index](docs/11-publishing-a-library.md) · [12 Distributing a Prebuilt Library](docs/12-binary-distribution.md) |
+| `2x` toolchains and targets | [20 Toolchain Management](docs/20-toolchains.md) · [21 The Target Triple](docs/21-the-target-triple.md) · [24 Cross-Compilation Over openkal](docs/24-openkal-cross.md) |
+| `3x` extending mcpp | [30 Build Programs: `build.mcpp`](docs/30-build-mcpp.md) · [31 Authoring a Rule Package](docs/31-authoring-a-rule-package.md) · [34 Authoring a Board-Support Package](docs/34-authoring-a-bsp.md) |
+| `4x` devices and accelerators | [40 Bare-Metal and Freestanding Targets](docs/40-baremetal.md) · [41 Reaching a Device](docs/41-devices.md) · [42 Heterogeneous Builds](docs/42-heterogeneous-builds.md) |
+| `5x` contracts for programs | [50 Machine-Readable Output](docs/50-machine-output.md) · [51 Supported Versions and Compatibility](docs/51-supported-versions.md) · [the specifications](docs/specs/README.md) |
+| `9x` mcpp itself | [90 Building from Source and Contributing](docs/90-build-from-source.md) · [92 Releasing mcpp](docs/92-release.md) |
-Full options for any command are available via `mcpp --help`.
+Every directory under [`examples/`](examples/) is a project that builds, and
+[03 — Examples](docs/03-examples.md) says which one teaches what. Full options
+for any command are available via `mcpp --help`.
**AI-assisted learning**: send the following prompt to an AI coding assistant to get up to speed with mcpp quickly:
@@ -441,8 +480,8 @@ Real projects built with mcpp — `import`-able C++23 modules and the toolchain
| Project | Description |
| --- | --- |
-| [mcpp](https://github.com/mcpp-community/mcpp) | mcpp itself — 43+ C++23 modules, fully self-hosted |
-| [xlings](https://github.com/openxlings/xlings) | Toolchain & package-management foundation mcpp builds on |
+| [mcpp](https://github.com/mcpp-community/mcpp) | mcpp itself — written in C++23 modules, fully self-hosted |
+| [xlings](https://github.com/openxlings/xlings) | Toolchain and package-management foundation mcpp builds on |
| [tinyhttps](https://github.com/mcpplibs/tinyhttps) | Minimal C++23 HTTP/HTTPS client with SSE streaming |
| [llmapi](https://github.com/mcpplibs/llmapi) | Modern C++ LLM API client (OpenAI-compatible) |
| [imgui-m](https://github.com/mcpplibs/imgui-m) | Dear ImGui as a C++23 module package |
@@ -481,7 +520,7 @@ then follow the guide to help me submit a contribution to mcpp.
Dependencies and sources of inspiration:
-- [xlings](https://github.com/d2learn/xlings) — toolchain / package-management foundation
+- [xlings](https://github.com/openxlings/xlings) — toolchain / package-management foundation
- [mcpplibs.cmdline](https://github.com/mcpplibs/cmdline) — CLI framework
- [ninja](https://github.com/ninja-build/ninja) — underlying build engine
- [xmake](https://github.com/xmake-io/xmake) — cross-platform build tool
diff --git a/README.zh-CN.md b/README.zh-CN.md
index c2e374f3..c029f543 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -20,20 +20,23 @@
## 核心特性
-- **C++23 模块原生支持** — `import std` 自动处理,文件级增量构建,模块依赖自动分析,零手动配置
-- **纯模块化自举** — mcpp 自身由 43+ 个 C++23 模块组成,用自己构建自己,模块系统经实战验证
-- **开箱即用** — 一条命令安装,内置 GCC 16 / LLVM 20 工具链,自动下载到隔离沙盒,不污染系统
-- **集成依赖管理** — SemVer 约束解析、锁文件、跨项目 BMI 缓存、自定义包索引
-- **多包工作空间** — Workspace 统一锁文件与版本管理,适合大型项目
+- **模块化构建系统** — 专注 C++ 模块:`import std` 自动处理,文件级增量构建,模块依赖自动分析,零手动配置
+- **构建插件与异构硬件编程** — `build.mcpp` 与规则包扩展构建;CUDA、HIP、SYCL、Vulkan/SPIR-V 与 Ascend C 各是一个规则包
+- **包管理与模块化库生态** — SemVer 约束、锁文件、跨项目 BMI 缓存、自定义索引;[mcpplibs](https://github.com/mcpplibs) 的库两行引入即可 `import`
+- **工具链管理与通用交叉构建** — `family@version` 按需安装;`--target` 让同一次构建换到 Windows、macOS、Cortex-M 或 RISC-V 裸机,一份源码经 openkal 触及多个有操作系统的目标
+- **环境与运行时** — xlings 提供的用户态环境:工具链与依赖都留在隔离沙盒里,runner 把产物送上板子或模拟器
+- **纯模块化自举** — mcpp 完全由 C++23 模块接口单元写成,并用它自己构建自己
## 为什么选择 mcpp
-mcpp 专门为 **C++23 模块化开发** 打造。如果你想在项目中使用 `import std`、模块接口单元(`.cppm`)、模块分区等现代 C++ 特性,mcpp 在 Linux、macOS ARM64 和 Windows x86_64 上能为你提供便捷且友好的开发体验:
+mcpp 专门为 **C++23 模块化开发** 打造。如果你想在项目中使用 `import std`、模块接口单元(`.cppm`)、模块分区等现代 C++ 特性,mcpp 在 Linux、macOS ARM64 和 Windows x86_64 上能为你提供便捷且友好的开发体验。
-- **默认模块化** — `mcpp new` 创建的项目模板直接使用 C++23 模块,`import std` 开箱即用
-- **文件级增量构建** — 基于 P1689 dyndep 的三层优化(前端脏检查 + 逐文件扫描 + BMI restat),只重编真正变化的模块
-- **一键创建 & 构建** — `mcpp new hello && cd hello && mcpp build`,工具链自动安装,无需手动配置编译器和构建系统
-- **模块化生态** — [mcpplibs](https://github.com/mcpplibs) 提供一系列可直接 `import` 的 C++ 模块化库,支持自定义包索引
+C++ 通常把这五件事分给五个工具,而 mcpp 用一条命令承担全部五件。第二行是每一列
+在既有认知里通常对应的东西。
+
+| mcpp | 通用构建系统 | 构建插件 | 包管理 | 工具链管理 | 环境与运行时 |
+|---|---|---|---|---|---|
+| **最接近的** | CMake + Ninja | CMake modules、xmake rules | vcpkg、Conan | rustup、nvm | conda、Nix |
> [!NOTE]
> **早期版本** — mcpp 仍在积极开发中,接口和行为可能在后续版本调整。
@@ -233,7 +236,7 @@ import mcpplibs.cmdline;
- 三层增量优化:前端脏检查 + 逐文件 P1689 dyndep + BMI copy-if-different restat
- 指纹化 BMI 缓存:按编译器/标志/标准库哈希,跨项目共享
- Ninja 后端:自动生成 build.ninja,并行编译
-- compile_commands.json 自动生成(clangd / ccls 即用)
+- compile_commands.json 自动生成(clangd / ccls 即用);`mcpp build --configure-only` 可在编译普通源码之前先刷新它
- C 语言一等支持:`.c` 文件自动检测,混合 C/C++ 项目
- 用户自定义 cflags / cxxflags / ldflags / c_standard
@@ -251,6 +254,29 @@ import mcpplibs.cmdline;
+
+交叉构建、裸机与设备
+
+- `mcpp build --target ` — 一个开关;该目标所需的工具链载荷会自动解析并安装
+- 从 `x86_64-linux-gnu` 到 Cortex-M、Cortex-A 与 RISC-V 裸机,完整的表见[平台支持](#平台支持)
+- freestanding 目标不带操作系统:C 库、启动代码、内存布局与模拟器随板级支持包走,而不随 mcpp 走
+- runner 用来触及构建机器上跑不了的产物 —— `mcpp run --runner flash`、`--list-runners`、`mcpp why runners`
+- `mcpp new --template riscv-virt-rt` — 由包自带的板级模板,按名字实例化
+- 基于 openkal 的交叉编译:一个可移植程序,为内核接口与 C 库都来自包的目标构建
+
+
+
+
+异构硬件构建与加速器
+
+- `[build] accel = "cuda12.9+{sm_89}, vulkan1.2"` — 一次构建可以点名一个或多个设备后端,该构建里 `cfg(accelerator = "cuda")` 为真
+- 目前有规则包的编程模型有五个:CUDA、HIP、SYCL、Vulkan/SPIR-V 与 Ascend C
+- 设备翻译单元由它自己的编译器编译,产物进入普通链接;主机与设备之间的边界是生成的,不是写两遍的
+- 带约束的 glob 用来挑出设备源码:`{ glob = "src/kernels/**/*.cu", accel = "cuda12.9+{sm_89}" }`
+- 引擎里不含任何厂商名,因此第六个后端是一个包,而不是一次引擎改动
+
+
+
包管理与依赖
@@ -285,6 +311,16 @@ import mcpplibs.cmdline;
+
+扩展构建
+
+- `build.mcpp` — 为 mcpp 没有现成规则的那一步写的构建程序,说的是一套带版本号的指令协议,而不是靠猜
+- `mcpp::action` 用显式的输入与输出声明一份工作,于是生成物参与增量图,而不是待在图外
+- 规则包把那一步带给别的项目:包声明一个 rule 模块,消费者以 feature 的形式选中它
+- 载荷、运行时适配包与板级支持包都是普通的包 —— 一个工具、一个驱动或一块板子,由安装库的那个解析器安装
+
+
+
开发体验
@@ -293,7 +329,9 @@ import mcpplibs.cmdline;
- `mcpp test [pattern] [-- args]` — 自动发现并运行测试(按名字过滤;`--list`、`--timeout `、`--message-format json`)
- `mcpp search` — 搜索包索引
- `mcpp add / remove / update` — 依赖管理
-- `mcpp why [toolchain|runtime|deps]` — 解释已解析的构建决策
+- 命令行上的 profile 与 feature:`--release` / `--profile `(`build`、`run`),`--features `(`build`、`run`、`test`)
+- `mcpp why [toolchain|runtime|deps|runners]` — 解释已解析的构建决策;`--format json` 供程序读取
+- `mcpp emit sbom` — 为已记录的那次解析产出一份 CycloneDX 格式的 SBOM
- `mcpp --offline` / `MCPP_OFFLINE=1` — 仅使用已存在的本地状态
- `mcpp explain E0001` — 错误码详细解释
- `mcpp self doctor` — 环境自诊断
@@ -302,8 +340,8 @@ import mcpplibs.cmdline;
## 性能对比
-用**四个构建引擎**编译 **mcpp 自己** —— 137 个模块接口单元、57k 行、每一个都
-`import std;` —— 并且**给它们同一个编译器二进制**。每格是 **3 轮的中位数**,以及
+用**四个构建引擎**编译 **mcpp 自己** —— 锁定的工作负载有 137 个模块接口单元、
+57k 行,每一个都 `import std;` —— 并且**给它们同一个编译器二进制**。每格是 **3 轮的中位数**,以及
相对 cmake 的倍率。所有列出自**同一次跑**。
@@ -319,31 +357,23 @@ import mcpplibs.cmdline;
`mcpp` = mcpp@2026.8.13.1,被测的这一版 · `mcpp +优化` = **和 `mcpp` 同一个二进制**,开了 opt-in 的 `[build] bmi_schedule = "on"`(默认关闭) · `mcpp (旧版)` = mcpp@2026.8.11.3,上一个已发布版。
Linux x86_64 · i9-13900K · gcc 16.1.0 · n=3 · 锁定的工作负载 `a749e9f` ·
-cmake 4.4.2 / xmake 3.1.0 · `-` 表示未测,本表没有 ·
-所有大于 1s 的中位数 min/max 都在 ±4% 以内 ·
+cmake 4.4.2 / xmake 3.1.0 · 所有大于 1s 的中位数 min/max 都在 ±4% 以内 ·
数据:[`standard-20260814-linux-x86_64`](bench/results/standard-20260814-linux-x86_64/)。
* **`touch-hub` 与 `edit-comment` 两行由级联抑制决定。**
cmake 与 xmake 按时间戳判断,重编全部下游单元;mcpp 将编译器刚产出的 BMI 与上
一份比较,接口未变则不触发级联。这是默认行为,无需任何配置。`mcpp (旧版)` 一列
测得上一个发布版为 81.72s,与 cmake 同量级,因此该效果在本版本中才生效。
-* **`edit-body` 量的是级联确实欠着的那种改动** —— 一次改动欠不欠级联,取决于函数体
- 写在哪里:
-
- | 函数体所在 | 改动它 | 对应本行 |
- |---|---|---|
- | `.cppm`,且改动**移动了行号** | GCC 在 BMI 里记录声明位置,BMI 随之改变 → 欠级联 | **本行所测:1.1x,`+优化` 2.9x** |
- | `.cppm`,**原地等长**修改 | GCC 不序列化非模板函数体 → BMI 不变 → 不级联 | 约 200x,与 `touch-hub` 同档 |
- | 独立的 `.cpp` 实现单元 | 该文件根本不产生 BMI → 不级联,且跨编译器成立 | 约 200x |
-
- 这里的扰动插入一条语句,因此落在第一行:所有引擎都必须重建导入者,更快只能意味着
- 漏做。`+优化` 也不漏做,只是把同一份工作加快 2.9 倍。三者中**接口与实现分离最稳**,
- 因为它既不依赖 GCC 对函数体的处理方式,也不依赖你避免行号移动。实测见
+* **`edit-body` 是对照行,mcpp 在这一行有意不快。** 扰动往接口单元里插入一条语句,
+ GCC 记录的声明位置随之移动,BMI 因而改变,每一个导入者都欠一次重建 —— 在这一行
+ 跑得快的引擎,漏掉的是它欠下的工作。一次改动欠不欠级联取决于函数体写在哪里:
+ 原地等长的修改,或者写在独立 `.cpp` 里的函数体,都不欠级联,落在约 200x 的那一档。
+ 实测见
[`.agents/docs/2026-08-15-module-edit-granularity.md`](.agents/docs/2026-08-15-module-edit-granularity.md)。
* **`bmi_schedule` 为 opt-in,默认关闭**(`auto` 解析为 off)。它将代码生成移出关键
- 路径,因此仅在级联必需时有效:`cold` 86.69s → 35.73s、`edit-body` 80.87s →
- 29.83s;而在 mcpp 本已跳过级联的两行上没有收益。调度错误的表现是静默失效而非
- 报错,因此不以单台机器的证据变更默认值。
+ 路径,因此只在级联确实欠着时才有收益 —— `cold` 86.69s → 35.73s、`edit-body`
+ 80.87s → 29.83s,而在 mcpp 本已跳过级联的两行上没有收益。调度错误的表现是静默
+ 失效而非报错,因此不以单台机器的证据变更默认值。
**[方法、锁定的版本、完整数据 → `bench/README.zh-CN.md`](bench/README.zh-CN.md)** ·
[English](bench/README.md)
@@ -356,22 +386,30 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
**宿主**(mcpp 本身运行在哪):Linux x86_64 / aarch64、macOS arm64、Windows x86_64。
-**目标**(`--target` 接受什么;本表与代码内词汇表同源):
+**目标**(`--target` 接受什么;表里的行与它们的档位取自
+`modules/toolchain-model/src/triple.cppm`,也就是 `mcpp toolchain list` 为本机
+报告的那一份):
-| Target | 约定工具链 | 状态 |
+| Target | 约定工具链 | 档位 |
|---|---|:---:|
-| `x86_64-linux-gnu` | gcc(*Linux 默认*)或 llvm | 是 |
-| `x86_64-linux-musl` | gcc 16,全静态 | 是 |
-| `aarch64-linux-musl` | gcc 16,全静态——x86_64 交叉(qemu 实测)或原生 | 是 |
-| `x86_64-windows-gnu` | gcc 16 MinGW-w64——Windows 原生,Linux 交叉(wine 实测)(*无 Visual Studio 时的 Windows 默认*) | 是 |
-| `x86_64-windows-msvc` | `msvc@system`(探测 VS/BuildTools)或 llvm ¹(*有 Visual Studio 时的 Windows 默认*) | 是 |
-| `aarch64-macos` | llvm(*macOS 默认*) | 是 |
-| `thumbv7em-none-eabi` · `thumbv8m.base-none-eabi` · `thumbv8m.main-none-eabihf` | llvm 22——可构建可链接;未记录模拟器运行 | 计划中 |
-| `riscv64-linux-musl` | — | 计划中 |
-| `aarch64-linux-gnu` | — | 计划中 |
-| `x86_64-macos` | — | 计划中 |
-
-是——CI 端到端构建**并真实执行**产物(含 qemu/wine)| 计划中——尚未验证
+| `x86_64-linux-gnu` | gcc(*Linux 默认*)或 llvm | verified |
+| `x86_64-linux-musl` | gcc 16,全静态 | verified |
+| `aarch64-linux-musl` | gcc 16,全静态——x86_64 交叉(qemu)或原生 | verified |
+| `x86_64-windows-gnu` | gcc 16 MinGW-w64——Windows 原生,Linux 交叉(wine)(*无 Visual Studio 时的 Windows 默认*) | verified |
+| `x86_64-windows-msvc` | `msvc@system`(探测 VS/BuildTools)或 llvm ¹(*有 Visual Studio 时的 Windows 默认*) | verified |
+| `x86_64-windows-musl` | llvm 22——带 musl C 库的 PE,没有 gcc 能产出它;系统由依赖图供给 | preview |
+| `aarch64-macos` | llvm(*macOS 默认*) | verified |
+| `riscv64-none-elf` · `riscv32-none-elf` | llvm 22——裸机,`xim:picolibc-riscv` ² | verified |
+| `thumbv6m-none-eabi` · `thumbv7m-none-eabi` | llvm 22——Cortex-M0/M0+/M1、Cortex-M3 ² | verified |
+| `thumbv7em-none-eabihf` · `thumbv8m.main-none-eabi` | llvm 22——Cortex-M4F/M7F 硬浮点、Cortex-M33/M55 软浮点 ² | verified |
+| `armv7a-none-eabi` · `armv7a-none-eabihf` | llvm 22——Cortex-A 32 位,第一条带 MMU 的行 ² | verified |
+| `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 |
+
+`verified` 该行的镜像已被构建**并运行**过,qemu 与 wine 都算 · `preview` 可构建
+可链接,未记录过模拟器运行 · `planned` 已登记在词表中,尚未接线 —— 面向这类目标
+的构建会被拒绝,而不是被尝试。
> Linux release 二进制为 x86_64 与 aarch64 的 musl 全静态构建
> (`x86_64-linux-musl` 与 `aarch64-linux-musl`)。
@@ -384,18 +422,30 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
> 完全自包含、不需要 Visual Studio、`import std` 可用。无需安装或配置,裸 Windows 上
> `mcpp new && mcpp build` 直接可用。而 `mcpp.toml` 里显式写的 `[toolchain]` 永远按你
> 写的执行——mcpp 只修正自己选的默认值,不改你的。
+>
+> ² 裸机的那些行不带操作系统:clang 与 lld 天生就是交叉编译器,因此任何能安装
+> LLVM 载荷的宿主都能产出这些目标。C 库、启动代码、内存布局与模拟器随板级支持包
+> 走,而不随 mcpp 走 —— 见
+> [40 — 裸机与 freestanding 目标](docs/zh/40-baremetal.md)。
## 文档
-- [快速开始](docs/zh/01-getting-started.md) — 5 分钟完成 install → new → build → run
-- [示例项目](docs/zh/03-examples.md)
-- [发布打包](docs/zh/10-pack-and-release.md)
-- [工具链管理](docs/zh/20-toolchains.md)
-- [从源码构建](docs/zh/90-build-from-source.md)
-- [mcpp.toml 指南](docs/zh/04-mcpp-toml.md)
-- [工作空间](docs/zh/07-workspace.md)
+[`docs/zh/`](docs/zh/README.md) 是手册。章节号的第一位说明它属于哪一部分;索引
+另有一份反向查表 —— 从读者眼前的一个 manifest 键或一条命令,查到拥有它的那一章。
+
+| 部分 | 从这里开始 |
+|---|---|
+| `0x` 基础 | [01 快速开始](docs/zh/01-getting-started.md) · [04 mcpp.toml 工程文件指南](docs/zh/04-mcpp-toml.md) · [09 按场景选命令](docs/zh/09-commands-by-scenario.md) |
+| `1x` 发布 | [10 发布打包](docs/zh/10-pack-and-release.md) · [11 发布一个库到 mcpp-index](docs/zh/11-publishing-a-library.md) · [12 分发预编译库](docs/zh/12-binary-distribution.md) |
+| `2x` 工具链与目标 | [20 工具链管理](docs/zh/20-toolchains.md) · [21 目标三元组](docs/zh/21-the-target-triple.md) · [24 基于 openkal 的交叉构建](docs/zh/24-openkal-cross.md) |
+| `3x` 扩展 mcpp | [30 构建程序:`build.mcpp`](docs/zh/30-build-mcpp.md) · [31 编写规则包](docs/zh/31-authoring-a-rule-package.md) · [34 编写板级支持包](docs/zh/34-authoring-a-bsp.md) |
+| `4x` 设备与加速器 | [40 裸机与 freestanding 目标](docs/zh/40-baremetal.md) · [41 抵达一台设备](docs/zh/41-devices.md) · [42 异构硬件构建](docs/zh/42-heterogeneous-builds.md) |
+| `5x` 给程序的契约 | [50 机器可读输出](docs/zh/50-machine-output.md) · [51 受支持的版本与兼容性](docs/zh/51-supported-versions.md) · [规范](docs/specs/README.md) |
+| `9x` mcpp 自身 | [90 从源码构建与参与贡献](docs/zh/90-build-from-source.md) · [92 发布 mcpp](docs/zh/92-release.md) |
-任意命令的完整选项可通过 `mcpp --help` 查阅。
+[`examples/`](examples/) 下的每一个目录都是一个能构建的工程,
+[03 — 示例项目](docs/zh/03-examples.md) 说明哪一个教什么。任意命令的完整选项
+可通过 `mcpp --help` 查阅。
**AI 辅助学习**:你可以将以下提示词发给 AI 编码助手,让它帮你快速了解 mcpp:
@@ -411,7 +461,7 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
| 项目 | 说明 |
| --- | --- |
-| [mcpp](https://github.com/mcpp-community/mcpp) | mcpp 自身 —— 43+ 个 C++23 模块,完全自举 |
+| [mcpp](https://github.com/mcpp-community/mcpp) | mcpp 自身 —— 由 C++23 模块写成,完全自举 |
| [xlings](https://github.com/openxlings/xlings) | mcpp 依赖的工具链与包管理底座 |
| [tinyhttps](https://github.com/mcpplibs/tinyhttps) | 极简 C++23 HTTP/HTTPS 客户端,支持 SSE 流式 |
| [llmapi](https://github.com/mcpplibs/llmapi) | 现代 C++ LLM API 客户端(OpenAI 兼容) |
@@ -451,7 +501,7 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
项目依赖和灵感来源:
-- [xlings](https://github.com/d2learn/xlings) — 工具链 / 包管理底座
+- [xlings](https://github.com/openxlings/xlings) — 工具链 / 包管理底座
- [mcpplibs.cmdline](https://github.com/mcpplibs/cmdline) — CLI 框架
- [ninja](https://github.com/ninja-build/ninja) — 底层构建引擎
- [xmake](https://github.com/xmake-io/xmake) — 跨平台构建工具
diff --git a/docs/12-binary-distribution.md b/docs/12-binary-distribution.md
index 6b44891f..15b2e1be 100644
--- a/docs/12-binary-distribution.md
+++ b/docs/12-binary-distribution.md
@@ -320,7 +320,7 @@ no test in this suite produces a `.dylib` to measure the edit on.
### Debug information is removed
-See [docs/02](10-pack-and-release.md) for the flags, the per-shape table, and
+See [docs/10](10-pack-and-release.md) for the flags, the per-shape table, and
`--debug-symbols`. The rule that matters for a *library* package: a static
archive is only ever `--strip-debug`ed, because `--strip-all` removes the
archive symbol index and the consumer's link then fails with `archive has no
diff --git a/docs/24-openkal-cross.md b/docs/24-openkal-cross.md
index 14ac6f31..e48a22a1 100644
--- a/docs/24-openkal-cross.md
+++ b/docs/24-openkal-cross.md
@@ -313,7 +313,7 @@ the paths where a project overrides the contract explicitly.
## Reference
-[docs/14 — The Target Side](22-target-side.md) for the five layers, the four
+[docs/22 — The Target Side](22-target-side.md) for the five layers, the four
origins and the rules. [SPEC-002](specs/target-side.md) for the normative
statement of the capability grammar.
diff --git a/docs/specs/manifest-semantics.md b/docs/specs/manifest-semantics.md
index 2854fe0d..fcc9f411 100644
--- a/docs/specs/manifest-semantics.md
+++ b/docs/specs/manifest-semantics.md
@@ -10,7 +10,7 @@
| **最低实现版本** | 条件化形状:mcpp **2026.8.29.1**(`[target..build-dependencies]` 起齐备);目标轴:mcpp **2026.9.6.4** |
| **作者/维护** | mcpp-community |
| **相关设计文档** | `.agents/docs/2026-09-07-mcpp-toml-unified-semantics-design.md`
`.agents/docs/2026-06-04-manifest-schema-ownership.md`
`.agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md` |
-| **相关使用文档** | [docs/05 —— mcpp.toml 字段参考](../04-mcpp-toml.md) |
+| **相关使用文档** | [docs/04 —— mcpp.toml 字段参考](../04-mcpp-toml.md) |
## 规范用语
diff --git a/docs/specs/target-side.md b/docs/specs/target-side.md
index d17eac9c..b4a837eb 100644
--- a/docs/specs/target-side.md
+++ b/docs/specs/target-side.md
@@ -8,7 +8,7 @@
| 最后修改 | 2026-08-24 |
| 对应实现 | mcpp >= 2026.8.24.2 |
| 相关设计文档 | `.agents/docs/2026-08-24-target-side-design.md` |
-| 使用文档 | [docs/14 - 目标侧](../zh/22-target-side.md) |
+| 使用文档 | [docs/22 - 目标侧](../zh/22-target-side.md) |
本规范定义一次构建的目标侧由哪些层构成、每一层可以由谁供给、
供给者与需求者如何声明,以及引擎据此执行的规则。
diff --git a/docs/zh/12-binary-distribution.md b/docs/zh/12-binary-distribution.md
index 48ca5c7a..9030f58a 100644
--- a/docs/zh/12-binary-distribution.md
+++ b/docs/zh/12-binary-distribution.md
@@ -286,7 +286,7 @@ Mach-O 上打包器会读出 `LC_RPATH` 并在包会携带它时告警;自动改
### 调试信息会被剥掉
-参数、分档表与 `--debug-symbols` 见 [docs/02](10-pack-and-release.md)。
+参数、分档表与 `--debug-symbols` 见 [docs/10](10-pack-and-release.md)。
对**库**包最要紧的一条:静态归档只做 `--strip-debug`,因为 `--strip-all` 会删掉
归档的符号索引,消费方链接时会报 `archive has no index; run ranlib to add one`。
diff --git a/docs/zh/24-openkal-cross.md b/docs/zh/24-openkal-cross.md
index 632291dc..91257635 100644
--- a/docs/zh/24-openkal-cross.md
+++ b/docs/zh/24-openkal-cross.md
@@ -265,6 +265,6 @@ g++: error: unrecognized command-line option '-fuse-ld=…/ld.lld'
## 参考
-[docs/14 — 目标侧](22-target-side.md) 给出五个层、四种来源与规则。
+[docs/22 — 目标侧](22-target-side.md) 给出五个层、四种来源与规则。
[SPEC-002](../specs/target-side.md) 给出能力语法的规范性陈述。
diff --git a/modules/toolchain-model/src/triple.cppm b/modules/toolchain-model/src/triple.cppm
index 8b01aa08..2a605a65 100644
--- a/modules/toolchain-model/src/triple.cppm
+++ b/modules/toolchain-model/src/triple.cppm
@@ -199,10 +199,11 @@ std::optional parse(std::string_view s);
//
// tier semantics (Rust-style):
// verified — CI builds AND executes the artifact end-to-end (qemu/wine count)
+// preview — it builds and links; no execution has been recorded for the row
// planned — registered intent; payload or CI row not wired yet
struct TargetInfo {
std::string_view canonical; // "x86_64-linux-musl"
- std::string_view tier; // "verified" | "planned"
+ std::string_view tier; // "verified" | "preview" | "planned"
std::string_view note; // display annotation: "static" / "PE" / ""
// Convention toolchain pin for `--target ` with no explicit
// [target.X] toolchain override. Empty = no convention (host default).