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
feat(xlings): tools gain a tier, and a dependency's declaration is installed
## Two halves of one defect
`[xlings.workspace]` had one list. A board-support package naming both an
emulator (to run) and a debug probe (to reach hardware) installed both for every
consumer, including one that only wanted the library to compile — while package
dependencies have had `[dependencies]` / `[build-dependencies]` /
`[dev-dependencies]` since the beginning.
[xlings.workspace]
"xim:qemu-arm" = "9.2.4-1" # unchanged behaviour
"xim:probe-rs" = { version = "0.24.0", when = "run" }
[feature-xlings.hardware]
"xim:probe-rs" = "0.24.0" # never fetched otherwise
| when | installed by | reaches a consumer |
|---|---|---|
| (omitted) | every verb that builds | yes |
| build | every verb that builds | yes |
| run | `mcpp run`, `mcpp test` | yes |
| dev | only the package that declared it, as the root | no |
Omitting `when` is the pre-2026.9.4.2 behaviour exactly, so no manifest changes.
The tier rides the entry rather than a second table, the shape `[dependencies]`
already uses; `[feature-xlings.<f>]` is `[feature-deps.<f>]`'s spelling.
## ⚠️⚠️ And provisioning now spans the graph, because looking did already
The previous release extended the runner's bare-name LOOKUP to every package in
the graph and left provisioning at the root. That is a search of directories
nothing installed into — a lookup that can only fail. Both are one expression
now, so they cannot drift.
Provisioning happens in two passes for an ordering reason: the root's list is
needed before the graph exists (a toolchain may depend on it), and a
dependency's is not known until resolution. The second pass provisions only what
the first did not, under its own stamp, so an unchanged project still pays for
no xlings round trip.
## ⚠️ The hazard a tier introduces, and the gate for it
`mcpp build` installs less than `mcpp run` needs, and the run fast path exists
precisely to skip the pass that would install the difference. The build cache
records whether a build left a run-tier tool unprovisioned; `try_fast_run`
declines such an entry, exactly as it declines one that records a runner.
## The criterion tests what mcpp REQUESTS, not what got installed
Verifying a tier by installing needs a clean machine and a network. It is also
the wrong object: what the tier changes is the SET, and `MCPP_NO_AUTO_INSTALL=1`
refuses to provision while naming exactly that set. `tests/e2e/335` reads both
commands on one project, so the criterion carries its own denominator — an mcpp
that provisioned nothing, or a manifest that never parsed, fails the `run` half.
## Discoverability
`mcpp why runners` lists what this project supplies, beside everything else it
reports resolving; `mcpp run --list-runners` is the same read alone. No stamped
one-time hint: a cache hit does not re-run, so it would be silent on the build
that needed it most.
97/97 unit (5 new); e2e 88, 130-131, 327, 332-335 green.
0 commit comments