You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(modules): nine subsystems, and the build.mcpp contract among them
The unit under `modules/` is a SUBSYSTEM, not a file. One file per package
would turn a directory listing into nine manifests and call it architecture.
libs json + toml -- text-format parsers, vendored and in-house
log leveled diagnostics
versioning mcpp.version + mcpp.version_req
source-kind the source-file role table
dyndep ninja dyndep emission
platform the OS abstraction: fs, process, axis, shell, terminal
manifest mcpp.toml and the xpkg descriptor, plus the vocabulary they
are written in
toolchain-model what a toolchain IS -- triple, model, dialect, cppfly,
fingerprint, linkmodel
buildmcpp the build.mcpp CONTRACT -- program_protocol, directives,
provisions, tool_store
Dependencies between them are declared and enforced rather than assumed, so
`mcpp test -p <member>` builds each one ALONE and a subsystem relying on
something it does not declare fails there while compiling fine in the root.
## Three cuts, one mistake
Every group-level cycle was produced by a countable set of back-edges, and each
one was a module named for where it sat rather than for what it did.
`src/platform/xlings/` was never platform: it is the xlings integration, which
by its nature knows about packages, indexes and manifests. Its modules were
named `mcpp.platform.xlings` while their namespace already said
`mcpp::xlings`; both now agree, in `src/xlings/`.
`runtime_binding.cppm` imported the whole of `mcpp.config` to read ONE path out
of it, which put the OS layer above configuration, xlings and the package
manager. The caller passes the path; the file describes the xlings runtime
contract rather than the OS, so it moved to `src/runtime/` with its only
dependent.
`toolchain.fingerprint` imported `toolchain.detect` for a type -- `Toolchain`
-- that `toolchain.model` defines and `detect` merely re-exports. The import
named a CONSUMER of the type rather than its provider and dragged toolchain
detection behind it. That one is load-bearing: every module on the
build-program side needs `fingerprint` for `hash_file`, so while that edge
pointed at `detect` the whole build.mcpp contract sat transitively above the
package manager. One import line was the entire distance.
## What stays in src/, and why it is not a defect
`build_program.cppm` and `hostprogram.cppm` -- the half that compiles and runs
the program -- need `toolchain.registry` and `toolchain.stdmod`, which reach
into detection, the compiler families and xlings. A thing that compiles and
runs a program is a toolchain consumer by construction; moving it would
relocate the dependency rather than remove it.
## Tests at two altitudes
Each package carries its own tests, and the root keeps a suite that crosses
layers. A subsystem test going red means the contract changed; the root's
cross-check going red means the contract held and a consumer drifted.
`tests/unit/test_subsystem_contracts.cpp` is the new one: it asserts that the
protocol number the bundled module announces is SUBSTITUTED rather than spelled
(so the announced value cannot drift from the checked one), and that the
compiled binary's version equals the root manifest's.
⚠️ `mcpp test -p <member>` exits 0 for a member with no tests, so CI's loop is
green either way and "has no tests" reads exactly like "tests pass".
`check_modules_wiring.sh` prints which members have none, and does not fail on
them.
`versioning` is not called `version`: that word is a reserved key inside a
dependency specification, so `[dependencies.mcpp] version = { path = … }`
parses as the mcpp dependency's version field and fails with an error about the
wrong table.
against the smallest possible blast radius, before anything with a cut in it.
1078
+
1079
+
### 12.7 Where the boundary actually fell, and why
1080
+
1081
+
Three edges had to be cut, and each was the same mistake wearing different
1082
+
clothes: a module named for where it sat rather than for what it did.
1083
+
1084
+
| Cut | What it was |
1085
+
|---|---|
1086
+
|`src/platform/xlings/` → `src/xlings/`| Not platform. The xlings integration, which by its nature knows about packages, indexes and manifests. Its module names said `mcpp.platform.xlings` while its namespace already said `mcpp::xlings`; the rename made the two agree. |
1087
+
|`runtime_binding.cppm` → `src/runtime/`| Imported the whole of `mcpp.config` to read **one** path out of it, which put the OS layer above configuration, xlings and the package manager. The caller passes the path now. |
1088
+
|`fingerprint → toolchain.detect`| The type it needed, `Toolchain`, is defined in `mcpp.toolchain.model`; `detect` merely re-exports it. The import named a **consumer** of the type rather than its provider, and dragged toolchain detection behind it. |
1089
+
1090
+
That third one is the load-bearing one. Every module on the build-program side
1091
+
needs `fingerprint` for `hash_file`, so while that edge pointed at `detect` the
1092
+
entire build.mcpp contract sat transitively above the package manager and could
1093
+
not be separated from it. One import line was the whole distance.
1094
+
1095
+
**What stays in `src/`, and why it is not a defect.**`build_program.cppm` and
1096
+
`hostprogram.cppm` — the half that compiles and runs the program — need
1097
+
`toolchain.registry` and `toolchain.stdmod`, which reach into detection, the
1098
+
compiler families and xlings. A thing that compiles and runs a program is a
1099
+
toolchain consumer by construction. Moving it would relocate the dependency
1100
+
rather than remove it, and the boundary between the contract and its executor
1101
+
is the honest place to stop.
1102
+
1103
+
### 12.8 Tests at two altitudes
1104
+
1105
+
Each package under `modules/` carries its own tests, and the root keeps a suite
1106
+
that crosses layers. The pair produces two distinguishable failures:
1107
+
1108
+
| Red | Meaning |
1109
+
|---|---|
1110
+
| the subsystem's own test | the contract changed |
1111
+
| the root's cross-check | the contract held and a consumer drifted |
1112
+
| both | the change was made once and propagated — the normal case |
1113
+
1114
+
The subsystem tests are also built in a configuration the root suite never
1115
+
produces: the package **alone**, with only its declared dependencies present. A
1116
+
subsystem that has quietly come to rely on something it does not declare
1117
+
compiles in the root build and fails there, which is the reason to run both
1118
+
rather than pick one.
1119
+
1120
+
⚠️ `mcpp test -p <member>` exits 0 for a member with no tests, so the CI loop is
1121
+
green either way and "has no tests" reads exactly like "tests pass".
1122
+
`check_modules_wiring.sh` therefore prints which members have none. It does not
1123
+
fail on them: a package of vendored parsers legitimately has nothing of its own
0 commit comments