Commit c04dd06
committed
feat(pack):
`tar` and `dir` answer the same question `msi` and `appimage` answer -- what
shape does the output take -- so they are values of one flag rather than the
beginning of a second one. The split that keeps every other format out of the
engine is: `mcpp pack` owns the mechanism and the one universal format, and
every other format lives in a package that `mcpp pack` dispatches to.
The universal format is what it already produces: an archive that extracts and
runs, universal in the only sense that matters here -- it needs no knowledge of
anyone else's release. Everything past it does. dpkg's control fields,
AppImage's runtime, WiX's schema, Apple's notarisation, Android's signing
scheme: each one bound into the engine couples an mcpp release to a release mcpp
does not control. The project already made this argument for languages, where
Slang is supported without being named in the engine, and a distribution format
has less claim to a name in the engine than a language does.
Three additions, each FORMAT-NEUTRAL, which is the test for whether something
belongs in the engine at all:
- A staged tree an artifact action can consume. `mcpp pack` already computes
one -- the dependency closure after the strip policy, the debug-symbol split
and `include`/`exclude` -- and then compressed it and the directory was
gone, so a `.deb`, an AppImage, a `.app` and an `.msi` each had to rebuild
it. `${mcpp.stage_dir}` exposes it.
- The rest of `[package]` in the build program: `MCPP_PKG_VERSION` /
`_DESCRIPTION` / `_LICENSE` / `_AUTHORS` / `_REPO` and the matching
`mcpp::package_*()`. Every installer states a version; without these a
project restates it in the member's own options, where the copy drifts from
`[package]` with nothing able to detect it.
- `--format` resolving its value through the graph. A package declares with
`mcpp::provides_pack_format("<name>")`; `--format <name>` finds the provider
among the resolved dependencies. The refusal for an unknown value names what
IS available rather than a constant, and arrives before anything is
compiled.
DECLARE UNCONDITIONALLY, SUBMIT CONDITIONALLY. This is the load-bearing rule of
the dispatch and the one a member author is most likely to get wrong, because a
member that gets it wrong still works for whoever wrote it -- they always pass
their own format. The declaration must not be gated, or the engine can never
answer "which formats does this graph provide"; the submission must be, or a
plain `mcpp build` grows an edge it must not have. A format nothing submitted
for is refused by name rather than reported as a pack that produced no package.
`mcpp pack --format <name>` PREPARES TWICE, AND NOTHING IS RE-DERIVED BETWEEN
THE PASSES. An artifact action is a ninja edge and the staged tree is produced
after the link, so the tree cannot be an input of the pass that built it. The
first pass collects declarations and refuses an unknown format; the build and
the staging follow; the second pass sets `pack_format` and `pack_stage_dir` and
builds the submitted edge. Its triple and staged path come from what the first
pass and `make_plan` already answered -- `stagingRoot` is a function of the
resolved triple, and a second derivation of it before prepare is the shape where
two answers agree on every machine the author has.
build.ninja's header line gains a fourth field, `dist=`, and the fast paths
require it to read `none`. The format is deliberately NOT in the fingerprint --
putting it there would cost a full recompile to package an already-built tree --
so the two graphs share a directory, and `target/<triple>/<fp>/build.ninja` is
shared mutable state two fast paths replay. That is the third instance of the
failure `graph=` and `accel=` each already record. The criterion is a unit test
rather than an end-to-end assertion: measured on 2026-09-11, a plain build after
the pack pass regenerates the graph even with the field ignored, so an
end-to-end check would pass whether or not the field works and would keep
passing if it were deleted.
`${mcpp.stage_dir}` REFUSES rather than expanding to nothing, in two places: a
build that is not packaging, and a role other than `artifact`. An empty path is
still a token the command accepts, and the tool then reads the build directory
root, which exists -- so the mistake produces a plausible artifact instead of a
diagnostic. The measured prototype is a valid, empty, 52 KB installer with
nothing said about it.
An action that names the placeholder automatically gains a dependency on
`<staged tree>.stage-manifest` -- a sibling, never a member, so it never travels
inside anyone's installer. The engine adds it because the use implies it:
without it the edge is dirty only when a link output changes, and a closure that
grew a dependency's shared library while the program's own bytes did not would
leave the previous distributable in place, reported as up to date.
Build-program protocol v9 (`mcpp:pack-format=`). The row carries a non-empty
`tag`, so the declaration is replayed from the build program's cache record --
the pass that reads the set is `mcpp pack`, which is never a project's first
build, and an unpersisted declaration would be absent exactly when a user names
a format.
Tests: 6 unit tests for the staged-tree contract, the graph-shape field in both
of its two readers, and the directive row's persistence; one e2e holding the
four properties of the dispatch, each with the wrong answer it excludes.
Docs: `docs/10` for the `--format` axis, `docs/30` for the three-category
taxonomy and the new placeholders and accessors, `docs/31` for the six
constraints a distribution member owes its consumer. Both languages.--format <name> dispatches to a package, and no distribution format lives in the engine (2026.9.11.1)1 parent 2d10281 commit c04dd06
30 files changed
Lines changed: 2496 additions & 54 deletions
File tree
- .agents/docs
- docs
- zh
- modules
- buildmcpp/src
- manifest/src
- versioning/src
- src
- build
- cli
- pack
- tests
- e2e
- unit
Lines changed: 801 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
46 | 50 | | |
| 51 | + | |
47 | 52 | | |
48 | 53 | | |
49 | 54 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
8 | 73 | | |
9 | 74 | | |
10 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| 143 | + | |
143 | 144 | | |
144 | 145 | | |
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
149 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
150 | 192 | | |
151 | 193 | | |
152 | 194 | | |
| |||
421 | 463 | | |
422 | 464 | | |
423 | 465 | | |
424 | | - | |
425 | | - | |
426 | | - | |
427 | | - | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
428 | 475 | | |
0 commit comments