Skip to content

Commit 6937759

Browse files
committed
feat: 2026.9.5.3 -- a feature-selected rule collection, mcpp.rules.* naming, and a variant switch the fast path no longer replays
A host-module package contributes every module interface unit among its feature-resolved sources, the lib root first; only listed sources take part. This is what lets mcpp:plugins carry mcpp.rules.cuda and mcpp.rules.spirv, selected by features. The rule packages leave examples/; examples 09 and 10 consume the index package. The prefix is mcpp.rules.* for rules and mcpp.tools.* for utilities; mcpp.build.* is the engine's own module family and the specification is corrected accordingly. The graph header records whether --accel/--no-accel chose the variant, and the fast paths decline a graph an override wrote: a plain build after --no-accel used to report Finished in 0.00s and run the CPU variant. The device-source table is by compiler, not by vendor: 18 extensions, none in the default globs, all a hard error before. Chapter 20 is renamed to Heterogeneous Builds; docs 05 and 07 gain the collection and naming sections in both languages.
1 parent 3a9656e commit 6937759

50 files changed

Lines changed: 2538 additions & 1302 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/docs/2026-08-29-build-rule-package-spec.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
> reading anything: `role = "check"` could not be written portably, and that is
2727
> fixed here (§9, open question 4).
2828
>
29-
> ⚠️ A sixth is about this document's own method. The descriptor count was
29+
> A sixth is about this document's own method. The descriptor count was
3030
> first taken from `mcpp-community/mcpp-index`, while `mcpp index list` reports
3131
> `mcpplibs/mcpp-index` as the default — the right question asked of the wrong
3232
> object. The conclusion survived (one rule package either way) and the number
@@ -223,7 +223,12 @@ behaviour without using a new API, is left uncovered deliberately.
223223

224224
### I8. `mcpp.*` is reserved for official plugins, and using it produces a warning
225225

226-
Official rule packages are named `mcpp.build.<x>`. In C++ this creates no
226+
Official rule packages are named `mcpp.rules.<x>` and official build-time
227+
utilities `mcpp.tools.<x>`; both live in the collection `mcpp:plugins`
228+
(repository `mcpp-community/mcpp-plugins`), each member selected by a feature
229+
(2026-09-05 revision; the first revision of this section said `mcpp.build.<x>`,
230+
which is the engine's own module family -- `mcpp.build.plan`,
231+
`mcpp.build.prepare` -- and was withdrawn for that reason). In C++ this creates no
227232
relationship with the bundled `mcpp` module — a dot in a module name carries no
228233
hierarchy — but to a reader the prefix reads as an endorsement, and that is a
229234
supply-chain statement.
@@ -537,7 +542,12 @@ hazard.
537542
10.1 row 4: the `mcpp.rules.*` decision is withdrawn. It was unimplementable
538543
under the rule that the module name is the bare package name. It is replaced
539544
by `mcpp.build.*` as a **module name** prefix — the distinction the original
540-
decision lacked.
545+
decision lacked. *Revised 2026-09-05:* with I1 implemented the objection no
546+
longer holds, and `mcpp.build.*` names the engine's own modules; the prefix
547+
is `mcpp.rules.*` for rules and `mcpp.tools.*` for utilities, as I8 now
548+
states. A package may contribute every module interface unit among its
549+
feature-resolved sources (mcpp 2026.9.5.3+), which is what lets one
550+
collection carry them all.
541551
2. `docs/05-mcpp-toml.md:1605` and its Chinese counterpart: the paragraph
542552
stating that the module name is the package's `name`, and that `grpc-rules`
543553
is therefore illegal, is rewritten under I1 and I2.
@@ -611,7 +621,7 @@ own.
611621
needs no shell, no `touch`, and no per-platform spelling. A command that
612622
already writes its own stamp is unaffected — existing files are left alone.
613623

614-
⚠️ **The obvious assertion does not discriminate.** Measured: ninja does NOT
624+
**The obvious assertion does not discriminate.** Measured: ninja does NOT
615625
fail when a declared output goes unproduced. It leaves the file absent and
616626
re-runs that edge on every subsequent build, so the build stays green and
617627
the only symptom is work silently redone. `tests/e2e/312` therefore asserts
@@ -1176,7 +1186,7 @@ subsystem that has quietly come to rely on something it does not declare
11761186
compiles in the root build and fails there, which is the reason to run both
11771187
rather than pick one.
11781188

1179-
⚠️ `mcpp test -p <member>` exits 0 for a member with no tests, so the CI loop is
1189+
`mcpp test -p <member>` exits 0 for a member with no tests, so the CI loop is
11801190
green either way and "has no tests" reads exactly like "tests pass".
11811191
`check_modules_wiring.sh` therefore prints which members have none. It does not
11821192
fail on them: a package of vendored parsers legitimately has nothing of its own

.agents/docs/2026-09-05-heterogeneous-build-ecosystem-design-v2.md

Lines changed: 351 additions & 0 deletions
Large diffs are not rendered by default.

.agents/docs/2026-09-05-multi-device-ecosystem-design.md

Lines changed: 146 additions & 146 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 316 additions & 228 deletions
Large diffs are not rendered by default.

docs/01-examples.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ examples.
2727
| 02 | [`examples/02-with-deps`](../examples/02-with-deps/) | Adds the `mcpplibs.cmdline` dependency to parse command-line arguments | `[dependencies]`, SemVer, `mcpp.lock` |
2828
| 03 | [`examples/03-pack-static`](../examples/03-pack-static/) | Produces a fully static release package via `mcpp pack --mode static` | `[target.<triple>]` and `[pack]` configuration |
2929
| 08 | [`examples/08-build-rules`](../examples/08-build-rules/) | Two rule packages and a project that uses both | `host-module = true`, `[build-dependencies]`, `mcpp::action` with `role = "check"` |
30+
| 09 | [`examples/09-cuda-kernel`](../examples/09-cuda-kernel/) | A CUDA kernel behind a seam module, with a CPU fallback | `accel`, constrained source globs, `mcpp::action` with `role = "object"`, `cfg(accelerator = …)` |
31+
| 10 | [`examples/10-vulkan-compute`](../examples/10-vulkan-compute/) | The same computation as a Vulkan compute shader, on a GPU or on the CPU | `mcpp.rules.spirv` from `mcpp:plugins`, `mcpp::action` with `role = "source"`, generated headers, a software driver as a payload |
3032

3133
## Suggested Reading Order
3234

docs/05-mcpp-toml.md

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,12 @@ the package/feature boundary, not on an individual target.
222222
> would leave nothing to compile for that device and say so only at the link).
223223
> Under `--no-accel` the glob is left out, which is how one project yields its
224224
> CPU-only variant. Under an `--accel` that does not cover the constraint the
225-
> build is refused naming both (`accel-mismatch`). Device-kind files (`.cu`,
226-
> `.hip`) the effective set matches are never compiled by the engine; they
227-
> reach the build program as `MCPP_DEVICE_SOURCES`, where the rule package the
228-
> project imports turns each into an `mcpp::action`.
225+
> build is refused naming both (`accel-mismatch`). Device-kind files the
226+
> effective set matches — CUDA and HIP, the GLSL stages, HLSL, OpenCL C and
227+
> Metal, listed in full in [20 — Heterogeneous Builds](20-heterogeneous-builds.md) — are never
228+
> compiled by the engine; they reach the build program as
229+
> `MCPP_DEVICE_SOURCES`, where the rule package the project imports turns each
230+
> into an `mcpp::action`.
229231
230232
```toml
231233
[build]
@@ -278,7 +280,7 @@ relinked, whether something else in the process already provides that library.
278280
/ the executable's own directory (PE) finding it again after the build
279281
directory moves.
280282

281-
⚠️ **This is not `[target.<triple>].linkage`** (§2.7.1). That key answers the
283+
**This is not `[target.<triple>].linkage`** (§2.7.1). That key answers the
282284
same-sounding question about the **C library** (a musl `-static` link, MSVC's
283285
`/MT`). The two are not independent, and the direction matters: a fully static
284286
image has no interpreter, so it cannot load a shared object at all. On a target
@@ -311,7 +313,7 @@ way mcpp's build of a package and a third party's copy of the same library can
311313
resolve to **one file** instead of two — which a package cannot state if
312314
declaring it forces the package to stop being consumable as a static library.
313315

314-
⚠️ A descriptor that writes `soname` on a non-shared target cannot be read by
316+
A descriptor that writes `soname` on a non-shared target cannot be read by
315317
mcpp releases before 2026.8.28.2 — the whole manifest fails to load, not just
316318
the key. Publishing one to an index therefore waits for that floor to move.
317319

@@ -360,7 +362,7 @@ reason it has no way to see.
360362

361363
```toml
362364
[build]
363-
# ⚠️ The relative ORDER of the two kinds is load-bearing: the internal overlay
365+
# The relative ORDER of the two kinds is load-bearing: the internal overlay
364366
# must precede the public headers for this package's own build. That is why
365367
# this is a SUBSET of `include_dirs` rather than a second list — two arrays
366368
# cannot express one order.
@@ -666,7 +668,7 @@ toolchain's libraries first.
666668
both the C and C++ compile channels. It reaches every TU in the package — module
667669
interface units included — so it also reaches the compiler's own P1689 module scan.
668670

669-
> ⚠️ **It does not make a macro-guarded `import` acceptable.** mcpp runs its own
671+
> **It does not make a macro-guarded `import` acceptable.** mcpp runs its own
670672
> lexical pre-scan before the compiler ever sees the file, and that scanner
671673
> rejects an `import` inside **any** `#if` / `#ifdef` block without evaluating the
672674
> condition:
@@ -817,7 +819,7 @@ package that also publishes device builds is selected.
817819

818820
The value is compared against the `accel` field of any prebuilt artifact the
819821
build consumes, and a build asking for none is satisfied by every artifact. See
820-
[20 — Accelerators](20-accelerators.md).
822+
[20 — Heterogeneous Builds](20-heterogeneous-builds.md).
821823

822824
### 2.4 `[lib]` — Library Root Module Convention
823825

@@ -924,15 +926,15 @@ selector normalizes to exactly one identity:
924926
There is no ordered fallback or fuzzy, index-wide search by short name:
925927

926928
```toml
927-
# Correct — dotted selector
929+
# Correct — dotted selector
928930
[dependencies]
929931
chriskohlhoff.asio = "1.38.1"
930932

931-
# Correct — namespace sub-table (preferred for several packages from one org)
933+
# Correct — namespace sub-table (preferred for several packages from one org)
932934
[dependencies.chriskohlhoff]
933935
asio = "1.38.1"
934936

935-
# Wrong — a bare name never reaches the `chriskohlhoff` namespace
937+
# Wrong — a bare name never reaches the `chriskohlhoff` namespace
936938
[dependencies]
937939
asio = "1.38.1"
938940
```
@@ -1210,7 +1212,7 @@ sysroot = "xim:newlib-riscv@4.4" # a different C library
12101212
sysroot = "" # no C library at all
12111213
```
12121214

1213-
⚠️ **An absent key and an empty one are different answers.** Absent inherits the
1215+
**An absent key and an empty one are different answers.** Absent inherits the
12141216
target table's C library. Present-and-empty is the **zero-libc tier**: no C
12151217
library is resolved, no include or library path is added, and the link carries
12161218
only what the project and its dependencies supply. `#include <stdio.h>` stops
@@ -1227,7 +1229,7 @@ A build program can ask which C library **payload** supplies the sysroot:
12271229
Both are empty on the zero-libc tier. See
12281230
[13 — Bare-Metal and Freestanding Targets](13-baremetal.md).
12291231

1230-
⚠️ **That is not the same question as "which C library did the target side
1232+
**That is not the same question as "which C library did the target side
12311233
resolve to".** `target_libc()` names the payload mcpp installed, and that value
12321234
is an *input* to target-side resolution — a package in the dependency graph can
12331235
supply the C library instead, in which case the resolved `c-abi` is not what
@@ -1613,7 +1615,7 @@ error: `toolkitnew` requires cuda.driver >= 13.0, and this machine has 12.4.
16131615
version; `cuda.driver` is data passing through, and a backend mcpp has never
16141616
heard of compares the same way.
16151617

1616-
⚠️ **A floor nobody answered is silent.** A machine that never declared what it
1618+
**A floor nobody answered is silent.** A machine that never declared what it
16171619
has is not a machine that fails the floor — it is one nobody asked. Turning
16181620
"we do not know" into "no" is the failure mode this exists to avoid, and it is
16191621
asserted directly: `tests/e2e/603_version_floor.sh` builds a project whose floor
@@ -1644,7 +1646,7 @@ backend-openblas = { implies = ["use_blas"] }
16441646
compat.openblas = "0.3"
16451647
```
16461648

1647-
⚠️ **`"^0.3.0"` and not `"0.3.x"` or `"0.3"`.** Measured against a package the
1649+
**`"^0.3.0"` and not `"0.3.x"` or `"0.3"`.** Measured against a package the
16481650
index certainly carries, with a **build** as the criterion:
16491651

16501652
| Written | Result |
@@ -1654,12 +1656,12 @@ index certainly carries, with a **build** as the criterion:
16541656
| `cmdline = "0.0"` | resolves, then `install path missing after fetch` |
16551657
| `cmdline = "0.0.x"` | `E_NOT_FOUND`, naming the package — which exists |
16561658

1657-
The three outcomes are worth distinguishing, because two weaker criteria each
1659+
The three outcomes are worth distinguishing, because two weaker criteria each
16581660
admit a form that does not work: "no `E_NOT_FOUND`" admits the two-segment
16591661
prefix, and "resolves" admits it as well. Only building against the real index
16601662
settles it.
16611663

1662-
⚠️ This matters more here than in `[dependencies]`. A feature whose
1664+
This matters more here than in `[dependencies]`. A feature whose
16631665
implementation cannot be fetched is a feature that does not exist, and a project
16641666
using a **path** dependency during development never consults the index — so the
16651667
failure appears only after publication, to somebody else.
@@ -1988,7 +1990,7 @@ statement of intent and a CI-matrix hint, shown by `mcpp why`, never a gate.
19881990
Distinct from an artifact's `accel` field on purpose. A declaration is written
19891991
by hand and may be aspirational; `accel` is measured from the build that
19901992
produced a binary and is what a consumer is refused against. See
1991-
[20 — Accelerators](20-accelerators.md).
1993+
[20 — Heterogeneous Builds](20-heterogeneous-builds.md).
19921994

19931995
### 2.13 `[xlings]` — the project's environment
19941996

@@ -2244,6 +2246,37 @@ internal fork are all legitimate and indistinguishable from here.
22442246
The lib root must be at `src/<name>.cppm` (or wherever `[lib] path` points); a
22452247
missing one is reported as *"host module 'x': no interface unit at …"*.
22462248
2249+
**A package may offer several rules, selected by features** (mcpp 2026.9.5.3+).
2250+
Every module interface unit among the package's resolved `[build] sources` —
2251+
including the sources a feature adds — is compiled as a host module under the
2252+
name it declares, the lib root first. A feature unit may import the lib root;
2253+
units are otherwise compiled alone, so they import `std`, `mcpp` and nothing
2254+
else. Only listed sources take part: the inferred `src/**` of a package that
2255+
declares no `sources` is not consulted, so a rule package published before this
2256+
release exposes exactly what it exposed then.
2257+
2258+
```toml
2259+
# the collection's manifest
2260+
[build]
2261+
sources = ["src/plugins.cppm"] # export module mcpp.plugins;
2262+
2263+
[features]
2264+
rules-cuda = { sources = ["rules/cuda.cppm"] } # export module mcpp.rules.cuda;
2265+
rules-spirv = { sources = ["rules/spirv.cppm"] } # export module mcpp.rules.spirv;
2266+
```
2267+
2268+
```toml
2269+
# a consumer
2270+
[dependencies.mcpp]
2271+
plugins = { version = "0.1.0", features = ["rules-spirv"], host-module = true }
2272+
```
2273+
2274+
The module set is the feature set: a unit whose feature is not active is not
2275+
compiled, and importing it fails as an unknown module. `mcpp:plugins` is the
2276+
collection the mcpp project maintains (repository `mcpp-community/mcpp-plugins`);
2277+
its members are named `mcpp.rules.<x>` for rule packages and `mcpp.tools.<x>`
2278+
for build-time utilities.
2279+
22472280
*Build-time only:* a `host-module = true` dependency is **not** compiled into
22482281
or linked with the target, and neither is anything it depends on. It exists to
22492282
run during `build.mcpp` and nowhere else. (Before 2026.8.5.2 it was also built

0 commit comments

Comments
 (0)