Skip to content

feat(run): --features 与 --profile,以及那个不看 feature 的构建缓存条目 - #552

Merged
Sunrisepeak merged 2 commits into
mainfrom
feat/run-features-profile
Sep 4, 2026
Merged

feat(run): --features 与 --profile,以及那个不看 feature 的构建缓存条目#552
Sunrisepeak merged 2 commits into
mainfrom
feat/run-features-profile

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

feat(run): --features and --profile, and the cache entry that ignored them

The one scenario the design could not run

mcpp build and mcpp test have always taken --features and --profile.
mcpp run did not, so it could only execute whatever a previous build
happened to leave behind: there was no spelling of mcpp run that ran a release
artefact, or one built with a feature on.

That is the axis the whole device surface is built around. A board-support
package expresses its two environments — an emulator, a debug probe — AS
features, so

mcpp run --features hardware

is the command a developer types when the board arrives on their desk. It did
not exist, which made the design's own scenario a manifest edit rather than a
command.

--release and --dev come along as the shorthands the other verbs accept, and
--list-runners takes the same axes: which runners a project HAS depends on the
features, so reporting them without it answers a question nobody asked.

⚠️⚠️ And the pre-existing defect it uncovered

The build cache entry is keyed on (target, profile, cache mode). The OUTPUT
DIRECTORY is keyed on a fingerprint that includes the features. So an entry
written by mcpp build --features loud pointed at the loud directory, and the
next plain mcpp build matched it and reported success in 0.00s — serving a
featured artefact to a request that had none.

Measured: three consecutive builds of one project printed quiet, LOUD,
LOUD. The third had no feature on.

The entry now records its feature set, normalised so a,b and b a are one
request, and both fast paths compare it. Absent on older caches, which reads as
"no features" — correct for a request that has none, a miss otherwise, which is
the safe direction.

⭐ The run fast path additionally declines whenever either new axis is set, for
the reason --cache and --profile already bypass cmd_build's: the cached
graph was generated without them, and accepting a flag to ignore it is worse
than refusing it.

tests/e2e/337 covers both halves and fails without this change.

97/97 unit; e2e 87, 88, 130-131, 327, 332-337 green.

… them

## The one scenario the design could not run

`mcpp build` and `mcpp test` have always taken `--features` and `--profile`.
`mcpp run` did not, so it could only execute whatever a previous `build`
happened to leave behind: there was no spelling of `mcpp run` that ran a release
artefact, or one built with a feature on.

That is the axis the whole device surface is built around. A board-support
package expresses its two environments — an emulator, a debug probe — AS
features, so

    mcpp run --features hardware

is the command a developer types when the board arrives on their desk. It did
not exist, which made the design's own scenario a manifest edit rather than a
command.

`--release` and `--dev` come along as the shorthands the other verbs accept, and
`--list-runners` takes the same axes: which runners a project HAS depends on the
features, so reporting them without it answers a question nobody asked.

## ⚠️⚠️ And the pre-existing defect it uncovered

The build cache entry is keyed on (target, profile, cache mode). The OUTPUT
DIRECTORY is keyed on a fingerprint that includes the features. So an entry
written by `mcpp build --features loud` pointed at the loud directory, and the
next plain `mcpp build` matched it and reported success in 0.00s — serving a
featured artefact to a request that had none.

Measured: three consecutive builds of one project printed `quiet`, `LOUD`,
`LOUD`. The third had no feature on.

The entry now records its feature set, normalised so `a,b` and `b a` are one
request, and both fast paths compare it. Absent on older caches, which reads as
"no features" — correct for a request that has none, a miss otherwise, which is
the safe direction.

⭐ The run fast path additionally declines whenever either new axis is set, for
the reason `--cache` and `--profile` already bypass `cmd_build`'s: the cached
graph was generated without them, and accepting a flag to ignore it is worse
than refusing it.

`tests/e2e/337` covers both halves and fails without this change.

97/97 unit; e2e 87, 88, 130-131, 327, 332-337 green.
…ator

⚠️⚠️ EVERY ASSERTION IN 332 WAS ABOUT WHAT THE IMAGE PRINTED, AND THAT IS NOT
ENOUGH ON THIS ARCHITECTURE.

`SYS_EXIT` (0x18) takes its reason code in r1 DIRECTLY; the `{reason, code}`
block is `SYS_EXIT_EXTENDED` (0x20), which exists because a 32-bit r1 cannot
carry both a reason and a status. A board that confuses them prints every
expected line and then reports the WRONG status.

Measured in mcpplibs/openarch while this branch was open: an example printed
`both tasks observed preemption` and `mcpp run` exited 1. Every check on its
output passed.

⚠️ And the status has to come from the emulator rather than from the tail of a
pipeline: `$?` after `qemu | head` is head's, which is always 0 — the shape
that would have made this assertion vacuous in exactly the case it is for.

docs/13 records it as an AArch32 fact rather than an A-profile one, and names
both measurements.

e2e 332 green: four rows boot AND exit 0.
@Sunrisepeak
Sunrisepeak merged commit b96b61c into main Sep 4, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants