Repository navigation
Commit 7d7aabf
authored
2026.11.13.1: toolchain payloads as data, build directories by lineage, UEFI through openkal, test feedback (mcpp#927), end-to-end cases in C++ with places and capabilities (mtest 0.8.0), canaries (#930)
* wip: a row's GCC payload as data (gcc-triple); the COFF object format
* tests/support and the first C++ end-to-end case; mtest 0.7.1
tests/support (mcpp.test.support) is the support of the end-to-end cases (design
2026-11-12 §5.9): a project as a table of files, mcpp run with the shared home
or a fresh one, the readings of a file's lines, a JSON document and an
executable's header (ELF, PE, Mach-O) made by this process. A case file imports
mcpplibs.mtest itself, which begins it at mtest's entry. tests/e2e/basics holds
02 (new, build, run) as the first case. mtest 0.7.1 is the dev-dependency and
the engine CI pins; the unit suite holds under it (169 programs, 2627 cases).
* e2e: the migration inventory of the 752 scripts and the keep baseline
tests/e2e/MIGRATION.tsv gives each script its subsystem (25 directory-style
names), its class (sink, merge, keep; design 2026-11-12 §5.9) and its planned
destination: e2e/<subsystem> for a merged case, unit/<file> for a sunk one,
keep for a script that stays. Assigned from each script's name, header and the
commands it runs.
merge 707, sink 12, keep 33.
tests/e2e/keep.tsv is the baseline of the keep class (emulators, a physical
device, upgrades and machine-state homes, CI-only and measurement legs); it may
only shrink.
* e2e: the test subsystem as C++ cases (38 scripts)
The 38 scripts MIGRATION.tsv assigns to `test` (mcpp test: discovery, selection,
reports, the JSON stream, timeouts, workspaces, --no-run and archives, the
launch description, the mtest protocol, the store, the fast path) are 39 cases
of the program e2e/test (tests/e2e/test/*.cpp, 12 files, one process each).
Each case is named by its script's sentence and keeps its number; its
`# requires:` words are its tags, and a case skips with "missing capability:
<word>" where the machine lacks one. The scripts are removed and MIGRATION.tsv
names each one's case.
Scripts: 15 16 17 18 72 78 90 94 152 153 154 155 157 158 159 160 164 178 213
673 745 854 855 856 865 989 993 1026 1034 1035 1037 1038 1050 1052 1065 1068
1069 1070.
Old run (the scripts, against this branch's mcpp, Linux x86_64, gcc and llvm in
~/.mcpp): 37 passed, 1 skipped (94 requires macos; started directly it exits 0
on Linux, its otool leg being conditional). Sequential time 227 s.
New run (`mcpp test e2e/test`): 38 passed, 0 failed, 1 skipped (94, missing
capability: macos). 72 s of runs, 12 programs at once.
tests/support gains: has(word) and needs({...}) (run_all.sh's capability
probes; an unknown word fails the case), on_path, glob (a shell's `*` per
component), has_line/no_line (grep -E per line), lacks, contains_nocase,
count_lines, ninja_log_outputs, mcpp_command (a mcpp for an e2e::session) and
project::scaffold (`mcpp new` in the case's directory).
Every assertion is kept. Where a reading changed:
- 159: `grep -qv '"test":"01-b/0"'` held for any line without the record; the
case asserts what it meant, that no line has it (stronger).
- 989: the evidence the script printed after a failure (file, xattr,
codesign) is a diagnostic, not an assertion; not carried over.
- 1070 O8: the script compared the output after the shell had stripped its
trailing newline; the case accepts the text with or without it.
- 993 is two cases: A-E, and the named-runner leg, which keeps
`requires:python3` because its runner is a Python program.
- python3 (993 A-E, 1026, 1035, 1037, 1050, 1065, 1070) read JSON for the
scripts; the cases read it with nlohmann::json and no longer require it.
- 90: its `requires: gcc` stood on line 8, which run_all.sh does not read, so it
ran on every host; the case does not require gcc either.
- 1052 sourced _toolchain_env.sh without using it; dropped.
- 178 and 1068 drive concurrent mcpp processes through e2e::session instead of
`&`, a fifo and kill. 1026 still unpacks with tar, 1038 still uses git, 94
still reads otool.
* e2e: the build-program subsystem as C++ cases (66 scripts)
The 66 scripts MIGRATION.tsv assigns to `build-program` (build.mcpp: its
directives and protocol, its environment and cache, dependencies' programs,
host tools and host modules, build plugins and the build programs' graph,
accelerator requests and the probe channel, payload overrides) are 66 cases of
the program e2e/build-program (tests/e2e/build-program/*.cpp, 6 files: accel,
cross, deps, graph, hosttools, protocol). Each case is named by its script's
sentence and keeps its number; its `# requires:` words are its tags.
Scripts: 58 89 92 110 111 112 125 139 143 144 145 168 181 186 187 189 193 194
309 310 311 312 334 600 605 612 617 618 622 624 625 626 632 633 651 665 683
693 698 712 720 775 779 800 802 804 823 824 826 831 859 873 874 877 902 903
904 905 906 908 910 958 997 1011 1036 1051.
168 and 804 keep their scripts: ci-aarch64-fresh-install.yml runs them by
path. The other 64 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 65 passed,
1 failed (779). New run (`mcpp test e2e/build-program`): 65 passed, 1 failed
(779), 0 skipped. 779 fails on this host in both runs for one reason outside
mcpp: it builds its plugin with the `gcc` PATH names, here an xlings subos gcc
whose shared objects record NEEDED libc.so (the linker script), which the
loader cannot load; /usr/bin/gcc records none. Under the script the plugin
built and `mcpp run` failed loading it; under `mcpp test` PATH names another
subos gcc that does not find crti.o.
tests/support gains: home kinds `linked` (the case's home with the shared
registry linked, as `ln -s ~/.mcpp/registry`), `inherited`
(_inherit_toolchain.sh) and `payloads` (the same without config and subos),
made under the system's temporary directory (glibc refuses an install
directory over 255 bytes, which a case's own directory exceeds); case_home,
shared_home, newest_payload, payload_root, family_version (and llvm_version,
gcc_version, musl_gcc_version, mingw_cross_version: _toolchain_env.sh),
repository, listing, newest_file, write_executable, run, has_line_nocase,
last_line, last_starting, count_matching.
Every assertion is kept. Where a reading changed:
- `mcpp run 2>&1 | tail -1` is read as the last line of the program's
standard output (the shell's interleaving put the program's line last).
- 168: `file`'s "statically linked" and readelf's interpreter are read from the
ELF header: no PT_INTERP and no DT_NEEDED.
- 802: readelf's NEEDED is read from the header; 804: the .comment strings are
read from the file's bytes (`clang version <llvm version>`, `GCC: (...)
16.1.0`), the llvm version being the one _toolchain_env.sh chooses (23.1.3
here, the literal the script grepped).
- 310: `strings -a` and `nm -C` become the file's bytes and `nm -C` when PATH
has it.
- 720, 877, 903: python3 read JSON for the scripts; the cases read it with
nlohmann::json and no longer require python3. 1011 required python3 and
did not use it; the case does not require it.
- 600, 605, 612, 626, 1036 and the other `mcpp new` fixtures keep `mcpp new`;
fixtures the scripts wrote whole are written whole.
- 698's Windows long-path reading runs powershell where the script did.
* e2e: the deps subsystem as C++ cases (49 scripts)
The 49 merge scripts MIGRATION.tsv assigns to `deps` (path, git and index
dependencies, how a dependency is named and resolved, mcpp.lock, tool
packages a graph declares twice, floors a dependency states, patches and
`embed`, entry targets, the advice an edge states) are 49 cases of the
program e2e/deps (tests/e2e/deps/*.cpp, 12 files: advice, edges, entry,
floors, git, graph, identity, lock, names, path, resolve, tools). Each case is
named by its script's sentence and keeps its number; its `# requires:` words
are its tags.
Scripts: 09 12 24 27 31 32 33 61 62 63 79 161 162 163 165 196 203 326 603 627
628 629 661 679 682 713 816 821 822 844 939 943 944 945 946 948 980 990 1001
1002 1018 1027 1032 1046 1048 1060 1066 1071 1074. No workflow runs any of
them by path; all 49 are removed. 23_remove_update.sh is a sink row and stays
for the sink step.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 49 passed.
New run (`mcpp test e2e/deps`): 49 passed, 0 failed, 0 skipped.
Every assertion is kept. Where a reading changed:
- python3 read JSON or rewrote a lock for 679, 682, 844, 1002, 1027 and 1071;
the cases do it in C++ (nlohmann::json, the FNV-1a digest, the lock
entry's lines) and 844, 939 (which did not use it) and 1071 no longer
require python3. 844's os.utime is std::filesystem::last_write_time.
- 628 read `mcpp why toolchain --format json` with jq only when jq was on
PATH; the case always reads it.
- `ls -1 <store> | wc -l` (627) counts the store's own entries;
`touch -d '30 days ago'` (945) sets the time 30 days back.
- 822 took its own release from `mcpp --version | awk '{print $2}'`, the case
from the second word of that line.
- 816's `LC_ALL=C grep $'\r'` and `$'\x1b'` are byte searches of the output.
- 713 skips when git is missing, as the script did (exit 77); the other git
cases skip the same way.
- Diagnostics the scripts printed on failure (`<(find ...)`, `diff`) are not
kept; the failing comparison shows both sides instead.
- Homes the scripts made with `MCPP_HOME=$TMP/home` (627, 628, 629) are fresh
case homes; `_inherit_toolchain.sh` homes are `home::inherited`.
docs/specs and docs/06 still name 163, 603, 627, 628 and 679 as the tests of
their rules; this change does not touch docs/.
* e2e: the actions subsystem as C++ cases (37 scripts)
The 37 scripts MIGRATION.tsv assigns to `actions` (actions a build program
declares and their roles, checks and their stamps, `prepare`, generated files,
device sources and the actions that consume them, rule packages, project build
hooks, what an action writes and reads, action progress) are 37 cases of the
program e2e/actions (tests/e2e/actions/*.cpp, 10 files: checks, commands,
declared, devices, generated, hooks, outputs, prepare, progress, rules). Each
case is named by its script's sentence and keeps its number; its `# requires:`
words are its tags.
Scripts: 51 107 124 142 188 313 314 315 317 606 607 608 609 610 611 613 619
623 630 631 648 659 660 780 790 791 792 793 798 799 808 827 896 897 982 1013
1015. No workflow runs any of them by path; all 37 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 36 passed,
1 failed (790). New run (`mcpp test e2e/actions`): 36 passed, 1 failed (790),
0 skipped. 790 fails on this host in both runs for the reason 779 does (batch
2): its install.sh links a shared library with the `gcc` PATH names, an xlings
subos gcc whose objects record NEEDED libc.so, a linker script the loader
cannot load ("invalid ELF header" when the program runs, under the script);
under `mcpp test` PATH names a subos gcc that does not find crti.o.
tests/support gains archive_members and archive_symbols: an `ar` archive's
members and its symbol index, read by this process (System V/GNU and BSD).
Every assertion is kept. Where a reading changed:
- 608's `ar t` and `nm` are read from the archive (its members and its
symbol index); the script read nm only when PATH had it.
- 606 read `mcpp why toolchain --format json` with jq; 808, 897 and 982 read
JSON with python3 without requiring it. The cases use nlohmann::json.
- 896's mtime in nanoseconds (python3) and 142/188/660's `stat %Y` are
std::filesystem::last_write_time; 827's `find -newer <ref>` compares the
destination's time with the one it had.
- 1013 and 1015 keep their Python drivers: the actions they declare run
Python workers, and 1013 drives the build through a terminal and a process
group. Each case writes its script's driver unchanged and runs python3 on
it; both keep requiring python3.
- 317's Ctrl-C leg sends SIGINT through `sh -c` as the script did (Unix
only, as before).
- 659's `type -P true` is the `true` PATH names; 799's `pwd -P` is the
canonical path.
- `mcpp run 2>&1 | grep '^X=' | tail -1` is the last such line of the
program's standard output.
* e2e: the features subsystem as C++ cases (37 scripts)
The 37 scripts MIGRATION.tsv assigns to `features` (feature activation, the
--strict schema gate, feature defines and their propagation, capabilities and
their providers, feature-deps, target-conditional configuration, feature-gated
sources, default features and forwarding, layer predicates) are 37 cases of
the program e2e/features (tests/e2e/features/*.cpp, 5 files: activation,
conditional, defaults, layers, sources). Each case is named by its script's
sentence and keeps its number; its `# requires:` words are its tags.
Scripts: 67 71 80 81 82 83 85 86 100 106 108 119 126 127 128 138 146 149 195
247 308 328 601 615 637 654 677 710 711 714 739 813 895 1007 1012 1019 1045.
No workflow runs any of them by path; all 37 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 37 passed.
New run (`mcpp test e2e/features`): 37 passed, 0 failed, 0 skipped.
Every assertion is kept. Where a reading changed:
- python3 and jq readings of JSON are nlohmann::json.
- 108 reads the host architecture from the compiler's predefined macros
instead of `uname -m`.
- 739's shell glob over the whole failed build (`*"sdk.h"*"No such file"*`)
is the same search: "sdk.h", then one of the three phrases after it.
- 813's `grep -rl` over the home's std-module records reads the records in
sorted order, so "the first record holding the flag" is one record on
every run (the script took find's order).
- sed's line edits (1012, 711, 813) are the same edits on the manifest
text; a fixture line an edit needs and does not find fails the case.
* e2e: the pack subsystem as C++ cases (37 scripts)
The 37 scripts MIGRATION.tsv assigns to `pack` (the packing modes and the
closures they carry, library packages and the gate a prebuilt package keeps,
formats a package provides, the report and the axes of a package, packs over
workspace members) are 37 cases of the program e2e/pack
(tests/e2e/pack/*.cpp, 4 files: formats, library, members, modes). Each case
is named by its script's sentence and keeps its number; its `# requires:`
words are its tags.
Scripts: 30 215 216 242 243 244 245 249 250 251 252 253 254 261 264 265 638
649 652 662 669 689 722 723 801 828 829 841 867 868 869 870 871 954 1008
1043 1061. No workflow runs any of them by path; all 37 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 36 passed,
1 failed (669). New run (`mcpp test e2e/pack`): 36 passed, 1 failed (669),
0 skipped. 669 fails on this host in both runs at the step 779 and 790 fail
at (batches 2 and 4): it links a prebuilt library with the `g++` PATH names.
Under the script that is an xlings subos g++ whose library records NEEDED
libc.so, so the pack refuses it; under `mcpp test` the subos g++ does not
find crti.o.
tests/support gains readers for what the scripts asked of binutils, tar,
zip and python3: ELF sections and symbol tables (defined or all), DT_SONAME
and whether a search path is DT_RPATH or DT_RUNPATH; a tar archive's
members and their extraction (by the machine's tar); a zip's central
directory; SHA-256.
Every assertion is kept. Where a reading changed:
- `file`, readelf, nm and the scripts' python3 ELF readers (read_tag,
has_symtab, elf_facts) are read from the files by these readers; 243's
`ar t` is read from the archive (the script read it when ar worked).
- python3 readings of JSON (723, 841, 869, 870, 954) are nlohmann::json;
954's tarfile/zipfile/hashlib readings are tar's member list, the zip
central directory and SHA-256.
- 871 compares compile intervals on the .ninja_log's absolute clock (each
edge ends at its output's recorded mtime and begins its duration
earlier), allowing 50 ms for the write that precedes a compiler's exit.
`mcpp test` runs ninja twice (the program, then the tests) and each run
appends with its own relative clock, so the script's comparison of the
relative columns paired edges of different runs; on this host its awk
compared them as strings and found nothing either way. Two compiles run
at once here overlap by about 300 ms.
- `find | head -1` is the first path in sorted order.
- sed edits of a manifest (244's abi tag, 828, 1043, 1061) are the same
edits on its text; 244's runs per line, as sed's does.
- `cp -r` copies (638) keep links as links.
* e2e: the build subsystem as C++ cases (45 scripts)
The 45 scripts MIGRATION.tsv assigns to `build` (a new project built and
run, incremental rebuilds, libraries, convention mode, C and assembly
sources, profiles, package-owned and per-target flags, include directories,
response files and link inputs, the C driver, link flags as written, paths
with spaces or no UTF-8 spelling, serial and concurrent plans) are 45 cases
of the program e2e/build (tests/e2e/build/*.cpp, 4 files: basics, flags,
link, sources). Each case is named by its script's sentence and keeps its
number; its `# requires:` words are its tags.
Scripts: 02 04 07 08 25 26 50 54 68 70 73 87 104 105 109 115 116 117 123
141 148 179 190 191 238 246 316 620 765 776 777 795 861 882 894 907 909 929
938 991 1003 1006 1047 1054 1062. No workflow runs any of them by path; all
45 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 45 passed.
New run (`mcpp test e2e/build`): 45 passed, 0 failed, 0 skipped (907 passed
on its rerun after a fixture edit in the case was corrected).
Every assertion is kept. Where a reading changed:
- `file`, readelf, nm (238's `nm -D --defined-only`), `ar t` (07) and
`strings -a` (929) are read from the files by tests/support's readers;
929's `strings` is the printable runs of four or more bytes, as strings
prints them.
- python3 readings of JSON (765, 894) are nlohmann::json; 148's python3
fixture generators are C++ that writes the same files.
- `sleep 1; touch f` (04, 104) moves the file's time two seconds on.
- 938 starts the program from a thread of the case while it relinks, as the
script did from a background subshell.
- `find | head -1` is the first path in sorted order.
* cfg: interface, the kernel-abi layer's provided interfaces
A layer key answered by membership in the resolved kernel-abi provider's
[kernel-abi] provides-interfaces, as accelerator is a set. A provider that
states no list provides every interface; a graph without the layer provides
none. A package selects its sources by what the implementation beneath it
provides and names no implementation. The provider lookup is one function,
shared with the requires-interfaces check.
* design: toolchain payloads as data (section 16)
* design: gcc 16.2.0 and the 17 trunk snapshot (section 16.4)
* toolchain: payloads as data, and any package of the index as a toolchain
Which package a family's toolchain is for a target on a host is the
[[toolchain.payload]] table of data/builtin.toml (name, aliases, family,
serves, hosts with exclusion, frontend directory and names, builds-for,
glibc-fixup, family-version), and [[toolchain.offer]] what a host is offered.
mcpp.toolchain.payloads reads both directions: to_xim_package,
identify_xim_payload, available_toolchain_indexes and the payload names
normalize_spec accepts. No payload is named in compat, and registry names none.
xim:<name>@<version> accepts any package: it is installed and resolved as
path:<root> with the table its .mcpp-toolchain.json states (schema 2: family,
prefix, sysroot, tools, each inside the payload); the family is the
descriptor's or its C++ driver's.
check_payload_names.py counts the table's payload names in the sources
against a baseline, as check_system_names.py counts systems; the refactor
lowers the system-name baseline.
* docs: the payload table, a family's versions and trunk snapshots, any package as a toolchain
* design: workspace concurrency, the build directory named by its lineage, and its size (section 17)
* build directory named by its lineage; lock wait in Finished; split debug in clean --report; debug = "lines"
A configuration's build directory is named by its lineage (compiler family,
target, standard library family, standard, configuration flags). The evolving
parts (mcpp, compiler build, standard library version, runtime contract) are
recorded inside it. An upgrade keeps every path the commands name, so ninja
compiles only what the release changed. Before, the directory took the whole
fingerprint's name, and its takeover changed every member's commands. A
compiler build that differs empties the directory in place.
Finished states the time spent waiting for a build directory. clean --report
gives split debug information its own column. [profile] debug = "lines"
keeps the line tables alone (-g1). Design 2026-11-12 section 17.
* the graph runs mcpp through the build directory's link to it
A build directory's graph names mcpp as engine/epoch-<N>/mcpp, a hard link
(else a copy) the planner makes the running engine under the directory's lock.
mcpp's own path holds its version under xlings, and every command that ran it --
the capped compile, BMI comparison, link wrapping, staging, module bindings --
changed with each release. Measured on a two-member workspace, an engine that
differs in its version alone: 18 of 153 objects and BMIs compiled again before,
none after. The link's path carries kCacheEpoch. Fast-path admission accepts a
link to this engine; a self-contained home is stated for the programs mcpp
starts, since it cannot be derived from the link's place. In-flight compiler
records are dropped when another engine wrote them.
* tests: the build directory's lineage, its engine link and an upgrade that compiles nothing
e2e 1000 gains G-I: the directory named by its lineage and recording the
fingerprint, the graph running mcpp through engine/epoch-<N>/mcpp, the same
mcpp from another path compiling nothing, and a compiler change emptying the
directory in place. 878 states that the program a graph runs is the engine
that wrote it. 960 and the accel cases read the new configuration-change
warning. unit/test_graph_engine covers the link.
* design: workspace test feedback, mcpp#927 (section 18)
* e2e: the cli subsystem as C++ cases (17 scripts)
The 17 scripts MIGRATION.tsv assigns to `cli` and that migrate here (--help
and --version, the error paths of a manifest and a source, usage errors kept
off standard output, the exit-code contract, --jobs and the `--` boundary,
--toolchain on run, test and pack, package templates and a transactional
scaffold, a build's output and status streams, a terminal's status row, the
control contract, and mcpp under signals) are 17 cases of the program e2e/cli
(tests/e2e/cli/*.cpp, 5 files: commands, output, signals, templates,
terminal). Each case is named by its script's sentence and keeps its number;
its `# requires:` words are its tags, and 936's `# resources: machine` its
resource.
Scripts: 01 05 48 69 202 204 231 329 340 671 842 843 864 866 936 941 1016. No
workflow runs any of them by path; all 17 are removed. 845 (--play-game
restores the terminal) is kept as a script: its case finds the prepare action
alive three seconds after a Ctrl-C ended mcpp, which criterion G3 forbids,
while the script's own G3 check compares a directory name with pgrep's list
of pids and cannot fail. MIGRATION.tsv records it; it waits for the engine
owner.
Old run (the 18 scripts, against this branch's mcpp, Linux x86_64): 18 passed.
New run (`mcpp test e2e/cli`): 17 passed, 0 failed, 0 skipped.
tests/support gains `process`: a program held by its pid, signalled alone,
whose standard streams are a pseudo-terminal, one pipe for both streams, a
file, /dev/null or a pipe whose reader has ended, and which starts with every
signal at its default; `processes()` reads /proc; `strip_escapes` removes CSI
sequences.
Every assertion is kept. Where a reading changed:
- `2>&1 | cat` and `2>&1 | tee` (864, 866) are both standard streams of mcpp on
one pipe of `process`; the shell's `&`, `kill` and `wait` (340, 936 H, 1016)
and Python's subprocess with `restore_signals` (941) are `process`; 941 B's
`returncode == -SIGPIPE` is the signal `process` reports.
- Python's pseudo-terminals (843, 866 D) are `process` with a terminal; 843's
screen model is C++ with the same rules (carriage return, line feed, CSI A,
J, K), over the code points of the UTF-8 the terminal received.
- 842 R5's Python timer reads the pipe in 50 ms steps and takes the time at
which the `error: build failed` line first appears and at which mcpp ends.
- python3 readings of JSON (866 B, 936) are nlohmann::json; 936's
`normcase(normpath(...))` comparisons are `lexically_normal` path
comparisons, which do not fold case on Windows.
- 69's `sed | grep -oE '"targets":[...]'` is a regular expression over the log
after `\"` is unescaped; 01's awk reading of mcpp.toml is a regular
expression.
- `sleep 1; touch mark; find -newer mark` (231) is the clock's time taken
after a second's wait, compared with each object's write time.
- 05's build with no manifest runs in a directory outside the repository, and
202 runs in a project of its own: a case's directory lies inside the
repository, whose mcpp.toml mcpp would find, and `mcpp pack --pkg-format
bogus` builds the project it finds before it refuses the value (the script,
run from tests/e2e, built the repository).
* e2e: the modules subsystem as C++ cases (30 scripts)
The 30 scripts MIGRATION.tsv assigns to `modules` (several modules and
partitions built and run, the p1689 scanner and ninja's dyndep, module units
in .cpp files, transitive and indirect imports, the std module compiled once,
what the scanner reads, `[build] module_extensions` and the lib root that
follows it, BMIs and their cache and staging, the split schedule, and modules
across programs, members and homes) are 30 cases of the program e2e/modules
(tests/e2e/modules/*.cpp, 5 files: basics, bmi, extensions, programs,
scanner). Each case is named by its script's sentence and keeps its number;
its `# requires:` words are its tags, and the `# resources: machine` of 170,
234 and 1039 their resource.
Scripts: 03 19 20 21 49 113 118 167 170 171 217 218 234 236 260 263 634 635
636 639 807 847 849 857 858 898 924 940 1039 1064. No workflow runs any of
them by path; all 30 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 29 passed,
1 failed. 218 fails on its reading alone: it looks for the graph in
`target/*/<fingerprint>/build.ninja`, and the build directory is named by the
configuration's lineage since this branch's engine (2da705cd); with that one
line reading the lineage instead, the script passes.
New run (`mcpp test e2e/modules`): 30 passed, 0 failed, 0 skipped.
tests/support gains `another_home` (a further home of a case, for a script
that exported MCPP_HOME again) and `file_identity` (`stat -c '%i %Y %s'`).
Every assertion is kept. Where a reading changed:
- 218 finds the graph in the directory `--print-fingerprint` names as
`Lineage:`; its assertion that module_extensions leaves the fingerprint
unchanged is kept.
- python3 readings (924's module maps, 1039's newest std-module record, 49's
entry.json) are C++ and nlohmann::json; 234's inline replay check is C++
(write times and tests/support's sha256), and its sections 6, 8 and 9 run
tests/e2e/helpers' Python checks as the script did.
- 1039's two background builds are two threads of the case, each running
mcpp. 1039 is a case of bmi beside 170 and 171: it removes the shared
home's std entry they stage from, and its `machine` resource keeps them
apart only within one program (in another program, 171 found the entry
gone); 858's `export MCPP_HOME` to a second and a third home is
`another_home`.
- 171's `stat -c '%i %Y %s'` is `file_identity`, `chmod 444` is a permission
change, `cmp` a comparison of the bytes, and its Windows holder the same
PowerShell command run as an `e2e::session`; 170's `cmp` likewise.
- `touch` of an existing file (170, 171, 234, 858) sets its write time to now;
`sleep 1; touch marker; find -newer marker` (218, 847) is the clock's time
taken before a second's wait, compared with the file's write time.
- `ls -d target/x86_64-linux-*/` and `./target/*/*/bin/app` (20, 21, 849) are
globs; `[ -d "$LLVM_ROOT" ]` (118, 217) is the llvm payload's directory;
635's `head -c 3 | od` compares the first three bytes; 639's `printf '%b'`
sources are C++ string literals.
- The commands that name no project (858's cache commands, 171's `mcpp
stage`) run in a directory outside the repository, as the scripts' did: a
case's directory lies inside the repository, whose mcpp.toml mcpp would
find.
* a toolchain named with --toolchain opts a planned row in
The planned refusal says no toolchain is published for the row; a build that
names one on the command line has one, as a build whose [target.<row>] section
names one does. MCPP_TOOLCHAIN, a standing preference, does not opt in. A
platform's conformance run (openkal-platform's i686-linux-musl row) named its
toolchain with --toolchain and was refused.
* the planned opt-in counts MCPP_TOOLCHAIN; a carrier reads its row's object format and suffix
--toolchain is stated as MCPP_TOOLCHAIN, so the two are one statement and both
opt a planned row in. The carrier of a system a platform defines took its
section directive and program suffix from the system's name: a PE row such as
x86_64-uefi was assembled as ELF and named without .efi. Both now come from the
row (Triple::object_format, artifact_naming).
* cenv answers by the row's container; cfg(object-format = ...) selects by it
* tests: case directories under the system's temporary directory
A case made its files in mtest::temp_dir(), which `mcpp test` places under
the repository's target/ (test-protocol/<run>/<case>). mcpp run there in a
directory with no manifest of its own found the repository's mcpp.toml
above it and built the repository: one such run took over the released
engine's build directory.
tests/support gains `case_dir()`: the directory of the case under the
system's temporary directory (`mcpp-e2e-case-XXXXXXXX`), made on first use.
`project` and `project::scaffold` make their files there, and the cases of
the earlier subsystems use it in place of mtest::temp_dir() (222 uses in 54
files; no case depended on the old location). The case's homes and its
directory are filed under the runner's directory of the case, which mtest
removes when the case ends: a directory whose case has ended is removed at
the next use of the support, and the rest when the program ends.
* e2e: the cache subsystem as C++ cases (22 scripts)
The 22 scripts MIGRATION.tsv assigns to `cache` (the global build cache
across projects and keyed by what an entry was compiled against, its modes
and maintenance commands, an entry's layout and a hit's std BMI, the fast
path and what it compares, a configuration's build directory over time,
`clean --stale` and `clean --report`, the sweeps by age and by budget, and
what a build writes once) are 22 cases of the program e2e/cache
(tests/e2e/cache/*.cpp, 3 files: entries, fastpath, directories). Each case
is named by its script's sentence and keeps its number; its `# requires:`
words are its tags. No script declared a resource.
Scripts: 172 173 174 184 212 324 609 645 832 878 879 935 959 998 999 1000
1005 1022 1028 1029 1030 1031. No workflow runs any of them by path; all 22
are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 20 passed,
2 failed. 174 fails on an engine defect of this branch: `mcpp build
--cache=off` empties the build directory after the plan made its link to
the engine (`engine/epoch-<N>/mcpp`, design 2026-11-12 §17), and every step
then fails with "not found" (a project, `mcpp build`, then `mcpp build
--cache=off`). 832 failed once with a SIGSEGV of mcpp in nlohmann's lexer
while resolving the toolchain in the shared home, during a concurrent run;
it passed when run again.
New run (`mcpp test e2e/cache`): 22 passed (21 ok, 174 an expected
failure), 0 failed, 0 skipped. 174 carries
`known_gap{"build --cache=off removes the engine link the graph runs",
"2026-12-15"}`: every assertion is kept, the run reports it as xfail, and
from 2026-12-16 it is judged as any case.
Every assertion is kept. Where a reading changed:
- python3 readings (174's `cache list --json` and entry.json, 935's result
record, 998's programs.json) are nlohmann::json; 174's strip of the
`[build] cache` override writes the manifest without it; 1029's os.utime
walk sets the write times of the entry's files and directories.
- `touch -d '<n> days ago'` and `touch -t 200001010000` (609, 1000, 1005,
998) set write times; 1000's `find -mtime -1` compares the write time with
a day ago; 174's `touch -d @1000` sets the time 1000 s after the epoch.
- `sleep` is a wait of the case; 935's two background builds are a thread
of the case and the case itself, each running mcpp.
- 878's and 1000's engine at another path is the program under test
copied there and started by its path with the same environment; 878's
`grep -v '^engine='` and awk rewrite of `$mcpp`, and 879's awk ageing of
`depSources=`, are line rewrites of the record and the graph.
- `ar t` (998) is `archive_members`; `stat -c %i` (998) is the inode of
`file_identity`; `readelf -S` (998, 999), `readelf --debug-dump` (173) and
`gdb` (999) run as the scripts ran them, where the machine has them; git
(1028) likewise.
- 1022's `cp -a` is a copy of the tree that keeps its links and its write
times; 1031's `cp -R` is a recursive copy.
- 645's "NOT MEASURED" exit 0 when an unchanged second build is not
replayed is a skip naming the recorded request; its machine-default leg
without a second installed version ends the case as passed, as the
script's exit 0 did. 324's NOTE (the warm build not replayed) printed and
asserted nothing, and is left out.
- 1029's cache commands, which name no project, run in the case's
directory, outside the repository.
* a defined row states its link emulation without its machine; design: UEFI converges into openkal (section 19)
* design: the measured UEFI result and the interface-selected optional interfaces (section 19)
* a provider states the link, base and runner of its identities over either base
* a runner named by the environment replaces the requirements of the one it replaces
* build: make the graph's engine link again after a cold build clears the directory
A cold build (--cache off, --no-cache) removed the output directory after
the plan had named its link to the engine, so every step of the graph
failed on a mcpp that was not there. The link is made again where the
link spec is regenerated. Where the running engine's file was replaced
while it ran, the link is copied from the running image.
* e2e: cache 174 is judged as any case again
`build --cache off` makes the graph's engine link again after the cold build
clears the build directory (ac9fba36), so 174 no longer carries
`known_gap{"build --cache=off removes the engine link the graph runs",
"2026-12-15"}`; its MIGRATION.tsv row drops the gap's note. Every assertion
is unchanged.
Run (this branch's mcpp, Linux x86_64):
- `mcpp test e2e/cache`: 22 passed (174 an ordinary pass), 0 failed,
0 skipped.
- `mcpp test e2e/features`: 37 passed, 0 failed, 0 skipped; 739, which
failed on the same defect, passes.
* e2e: the workspace subsystem as C++ cases (25 scripts)
The 25 scripts MIGRATION.tsv assigns to `workspace` (the members a command
takes, what a member inherits from its workspace at every position of the
graph and however it was fetched, where a workspace's statements are written
and what they reach, and the workspace as one graph per configuration) are 25
cases of the program e2e/workspace (tests/e2e/workspace/*.cpp, 4 files:
members, inheritance, statements, graph). Each case is named by its script's
sentence and keeps its number; its `# requires:` words are its tags. No
script declared a resource.
Scripts: 35 122 321 770 772 773 774 805 806 833 834 836 839 850 851 852 853
860 872 880 885 893 900 911 912. No workflow runs any of them by path; all 25
are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 25 passed,
0 failed, 0 skipped.
New run (`mcpp test e2e/workspace`): 25 passed, 0 failed, 0 skipped.
tests/support gains `cdb_arguments` (the words of a compile database's entry
for a file: its `arguments`, else its `command` split), which 321, 770, 774,
805 and 893 read and the cdb subsystem will.
Every assertion is kept. Where a reading changed:
- python3 readings of compile_commands.json (321's entry_of, 770's count and
before, 774's and 805's checks, 893's release entry) are `cdb_arguments`;
872's comparison of two build databases and its argument-file check are
nlohmann::json. 900's tomllib reading of the
archived manifest runs as the script ran it (python3 -I), the support
having no TOML reader.
- 805's `ls xim-x-gcc | sort -V | tail -1` of the case's inherited home is
`newest_payload("xim-x-gcc")`, the shared home's newest gcc, which the
inherited home links.
- `tar -xzf -O` (772, 900) is an extraction into the case's directory and a
read; `cmp` and `sha256sum` (772) compare bytes and `sha256`.
- `.ninja_log` readings (833's grep -c and tail -n +N, 851's and 852's awk,
872's compiles_after, 912's count) are `ninja_log_outputs`, counted from
the number of outputs recorded before; 860's start and end columns are the
log's rows read whole.
- `find target -mindepth 2 -maxdepth 2 -type d ! -name '.*'` (833) is a glob
of `*/*` kept to directories; `find -path '*/bin/*' -name` (850, 872, 880,
893) is the first file of that name under a `bin` directory; 872's newest
release `.ninja_log` is the graph directory beside the dev one.
- 834's wall time measured by a python3 wrapper is the outcome's duration.
- 885's awk extraction of a ninja build statement and its indented lines is
a line walk of build.ninja; its `gsub(...) > 1` per line is a count per
line.
- 880's and 885's home with `[xlings] home` naming the shared registry is a
fresh home of the case holding that config.toml.
- `sleep 1.1` (833) is a wait of the case; `touch` (839) sets the write time
to now; 850's and 880's `sed` rewrites of a manifest are string rewrites.
- 772's released client ($MCPP_BOOT) runs when the variable names a released
mcpp other than this one, as the script's did; otherwise that leg is left
out, as the script's SKIP line printed and asserted nothing.
* e2e: the run subsystem as C++ cases (19 scripts)
The 19 scripts MIGRATION.tsv assigns to `run` (the environment `run` and
`test` give a program, the runner of a target and its named and
feature-chosen runners, what `run` hands back of the program, and what a
runner receives) are 19 cases of the program e2e/run (tests/e2e/run/*.cpp,
4 files: environment, runners, program, contract). Each case is named by its
script's sentence and keeps its number; its `# requires:` words are its tags.
No script declared a resource; 927 carries `resource{"machine"}`, since it
appends to the shared home's config.toml for one run and restores it, and it
is the only case of the subsystem that needs the machine.
Scripts: 62 65 74 114 156 166 330 333 337 339 656 672 674 684 862 863 883
927 1024. No workflow runs any of them by path; all 19 are removed.
Old run (the scripts, against this branch's mcpp, Linux x86_64): 18 passed,
1 failed. 62 fails on this machine's host `gcc` (the xlings shim, gcc
16.1.0 built from source): the plugin it links with `gcc -shared` records
DT_NEEDED `libc.so` (the linker script's name), so the program's dlopen()
reaches the private glibc's `libc.so` and fails with "invalid ELF header";
the case fails the same way. An environment failure, not an engine one.
New run (`mcpp test e2e/run`): 18 passed, 1 failed (62, as above),
0 skipped.
Every assertion is kept. Where a reading changed:
- `ls target | head -1` (330, 339, 656, 672, 674) is the first entry of
target/ whose name does not start with a dot, as `ls` lists them.
- python3 readings (333's launch description and SBOM, 1024's descriptions,
record rewrite and NDJSON stream) are nlohmann::json.
- 883's drive.py (pty.fork) is `process` on a pseudo-terminal of a new
session; its subprocess return codes of a signal death (negative in
Python) are the shell status and the signal `process` reports; its check
that the terminated program is gone (`os.kill(pid, 0)`) is the absence of
/proc/<pid>; the piped run is the command's input.
- `dd ... seek=18 conv=notrunc` and `od -j18 -N2` (330) write and read the
two bytes at offset 18; `chmod -x` / `+x` (863) remove and restore the
execute permissions; `file`'s interpreter (74) is the ELF header's
PT_INTERP; `realpath` (62) is the canonical path.
- `cmp` of captured streams (862) compares the outcome's standard output and
standard error with the program's own; `head -c`, `tail -c`, `tail -n 2`
and the line after `Running` are string prefixes, suffixes and the
following line.
- `sed -i` rewrites of a manifest or a lock (330, 333, 339, 674) write the
rewritten text, or the text kept before the append; 337's `awk` of
`features=` in .build_cache is the last such line, the record holding one.
- `uname -m` (684, 927) runs as the scripts ran it; 927's PATH prefix and
RECORD are the run's environment; 166's look at `$HOME/.mcpp` is kept.
- `touch` (114, 330) sets the write time to now; `sleep 1.1` (114) is a wait
of the case; `$TMP/runner.log` truncation (330) truncates the file.
* e2e: the cdb subsystem as C++ cases, first part (8 scripts)
Eight of the scripts MIGRATION.tsv assigns to `cdb` (the root compile
database a build writes and when, `build --configure-only`, and `emit
build-database`'s S1 document) are 8 cases of the program e2e/cdb
(tests/e2e/cdb/*.cpp, 3 files: root, configure, emit). Each case is named by
its script's sentence and keeps its number; its `# requires:` words are its
tags. No script declared a resource. The other 15 scripts of the subsystem
are still scripts: 11 to follow, and 783 784 785 786, which
ci-linux-e2e.yml's llvm step runs by path and holds to the lines they print.
Scripts: 47 76 77 211 688 781 782 937. No workflow runs any of them by path;
all 8 are removed.
Old run (the 23 scripts of the subsystem, against this branch's mcpp, Linux
x86_64): 23 passed, 0 failed, 0 skipped.
New run (`mcpp test e2e/cdb`): 8 passed, 0 failed, 0 skipped.
Every assertion is kept. Where a reading changed:
- jq (47) and python3 readings of the databases and envelopes (76, 211,
688) are nlohmann::json; 47's skip when jq is absent is gone, the reading
needing nothing of the machine. 688's schema check runs
tests/e2e/_json_schema_subset.py with python3 -I, as the script ran it.
- 688's tree_digest (os.walk and hashlib) is the case's listing of the tree
and `sha256` of each file; its WORK_DIR (the planning pass's directory)
is removed by the case, as the script's trap removed it; its cold home is
the case's inherited home; `cp -R` is a recursive copy.
- 781's `touch .before-build2; sleep 1; find -newer` is the clock's time
taken before a second's wait, compared with each graph's write time;
`diff` and `cmp` (781, 937) compare the files' text.
- 937's `grep -v '^compile_commands = '` rewrite leaves `[build]` without the
key, as the script's did; `unset MCPP_COMPILE_COMMANDS` removes the
variable from each run.
- `touch` (782, 937) sets the write time to now; `printf >>` appends.
* e2e: the cdb subsystem as C++ cases, second part (11 scripts; 4 kept)
The other scripts MIGRATION.tsv assigns to `cdb` (what `emit
build-database` describes when planning fails or defers, the words of a
flag, what a rule generates, the private work directory, the sets of a
workspace's database and of the build programs, and what emit watches) are
11 further cases of the program e2e/cdb, now 19 cases in 5 files (root,
configure, emit, planning, sets). Each case is named by its script's sentence
and keeps its number; its `# requires:` words are its tags. No script
declared a resource.
Scripts: 731 736 787 788 789 809 815 817 840 913 921. No workflow runs any
of them by path; all 11 are removed.
Kept: 783 784 785 786. ci-linux-e2e.yml's llvm step ("Compile database e2e
that needs llvm") runs each by path and holds it to the lines it prints,
since no e2e shard has llvm; they move when that step runs the e2e/cdb cases
instead. MIGRATION.tsv marks them `keep` with that reason, and keep.tsv lists
them.
Old run (the 23 scripts of the subsystem, against this branch's mcpp, Linux
x86_64): 23 passed, 0 failed, 0 skipped.
New run (`mcpp test e2e/cdb`): 19 passed, 0 failed, 0 skipped.
Every assertion is kept. Where a reading changed:
- python3 readings of envelopes, databases and compile_commands.json (736,
787, 788, 789, 809, 815, 817, 840's check.py and root.py, 913, 921) are
nlohmann::json and `cdb_arguments`; 736's walk of every list of strings
is a recursive walk of the document.
- 817's tree_digest is the case's listing and `sha256`; its config.toml
rewrite (`grep -v '^binary' | sed '/^\[xlings\]/d'`, then [xlings] binary)
is the same line filter; its xlings stub is written as the script wrote it.
- 921's os.utime(ns=10**18) sets that time on each host lock through the
file clock; the comparison of time and text is kept.
- 913's `find -maxdepth 2` is a relative depth of at most two; its realpath
comparison is `weakly_canonical`.
- 731's pipeline (`mcpp emit ... | { readlink /proc/self/fd/0; cat; }`) runs
as the script's shell ran it, by `sh -c`: the reader's pipe is a property
of that pipeline.
- `touch` (736) sets the write time to now; `printf >>` appends.
* e2e: the cdb subsystem as C++ cases, third part (4 scripts; the llvm step runs the cases)
The four scripts kept for ci-linux-e2e.yml's llvm step (one database per
configuration, the database replayed from its directory, a module
interface's language, and the std unit with its emit agreement, provides and
build-id) are 4 cases of the program e2e/cdb, in a new suite `toolchains`
(tests/e2e/cdb/toolchains.cpp); e2e/cdb is now 23 cases in 6 files. Each case
is named by its script's sentence and keeps its number. No script declared a
resource.
Scripts: 783 784 785 786, all removed. MIGRATION.tsv marks them merged;
keep.tsv no longer lists them.
The hermetic job's step "Compile database e2e that needs llvm" now runs
`"$MCPP_BOOT" test e2e/cdb --case toolchains` with the PATH it set before and
the llvm the job installed before, and holds the run to the `ok` line of each
of the four cases and to "4 passed, 0 failed, 0 skipped": a skip or a failure
fails the step, as a missing PASS line did. The bootstrap drives the test so
that the test build reuses the step "Build PR mcpp from source"; the mcpp
under test is that build's binary (MCPP_BIN_MCPP). The step no longer installs
python3, which no case reads with. This step is unverified in CI.
Old run (783-786, against this branch's mcpp, Linux x86_64): 4 passed, 0
failed, 0 skipped.
New run (`mcpp test e2e/cdb`): 23 passed, 0 failed, 0 skipped
(`--case toolchains`: 4 passed).
Every assertion is kept; the lines the step required are each a case's
assertions. Where a reading changed:
- The capability words: 783 and 784 declared `gcc`, which the probe reads as
g++ on PATH; they run no g++ of the PATH but the home's gcc@16.1.0 toolchain,
and the hermetic container that runs them has no g++ on PATH (it ran them by
path, where the word was not read). 786 declared `python3`, which only its
readings used. The four cases require `llvm`.
- python3 readings of compile_commands.json and of the S1 document are
nlohmann::json; 784's and 785's subprocess replays of an entry's arguments
in its directory are `e2e::run` with that argv and directory; 784's
commonpath test is a relative path from the canonical project that does not
leave it.
- 786's `list(ide.toolchains.values())[0]` is the first toolchain in the
document's key order, the document having one.
* e2e: the index subsystem as C++ cases (34 scripts)
The 34 scripts MIGRATION.tsv assigns to `index` (what the index commands
state and the [indices] forms, a project's local path index and a home's own
index, the routes an index takes into a build, when an index is refreshed and
what a failed resolution says, and what a project asks of xlings) are 34 cases
of the program e2e/index (tests/e2e/index/*.cpp, 5 files: listing, local,
routes, refresh, xlings). Each case is named by its script's sentence and
keeps its number; its `# requires:` words are its tags. 614 declared
`resources: machine` and carries resource{"machine"}; it is the one case of
the subsystem that does, and mtest serialises that resource only within one
test program, which `mcpp test e2e/index` runs per file.
Scripts: 06 11 22 42 43 44 52 53 60 75 76 93 120 121 147 169 173 185 192 210
327 331 335 614 730 732 733 734 735 737 803 812 949 1067. No workflow runs any
of them by path (two workflow comments name 53); all 34 are removed. The index
173 is this subsystem's; the cache 173 was migrated before.
Sinks: MIGRATION.tsv marked 06, 42, 43, 44 and 93 to sink into unit tests
(test_emit_xpkg, test_indices, test_lockfile, test_xpkg_parse). No earlier
batch sank a script into a unit, and each of the five asserts a command's
output or file effect (`emit xpkg` and its --output, `index list`, `index pin`
and `index unpin` rewriting mcpp.toml, `xpkg parse` and its --json and
--format json), which the unit layer reaches only through the parsers. They
are e2e cases of e2e/index::listing; their rows say so.
Old run (the 34 scripts, against this branch's mcpp, Linux x86_64): 34 passed,
0 failed, 0 skipped.
New run (`mcpp test e2e/index`): 34 passed, 0 failed, 0 skipped.
Every assertion is kept, including those on files a script wrote itself (43's
and 44's lockfiles). Where a reading changed:
- python3 readings (93's parse documents, 733's effects and codes, 735's first
diagnostic, 803's FNV-1a of the declared address) are nlohmann::json and a
64-bit FNV-1a loop; 733's `grep -w` stays a word match over each effect and
each code.
- `touch -d '30 days ago'` (173, 949) sets the write time thirty days back.
- Line rewrites keep their filters: 733's, 734's and 812's config.toml
(`grep -v '^binary'`, `sed '/^\[xlings\]/d'`, 734 also `^auto_refresh` and
`^\[index\]$`, then [xlings] binary), 22's repo line, 169's and 735's
dependency lines. The xlings stubs (52, 60, 732, 733, 734, 812) are written
as the scripts wrote them; 812's unquoted heredoc is its text with the index
directory substituted.
- 732's background mcpp and `kill -TERM` are a process of the case and its
signal; `kill -0` runs by `sh -c`; `bounded 60` is the command's 60 s
timeout; `date +%s` is the steady clock.
- 185's `grep -A 4 "<first 40 characters of the last error>"` reads those
characters as a fixed string (grep read them as a basic expression) and
looks at each match and the four lines after it.
- `find` counts and searches (120's build.ninja outside .build-mcpp, 147's
accel_*.o, 737's ld-linux-* loaders) are the case's directory walks and
globs; 614's `ls | sort -V -r` orders the versions by numeric components.
- 737's `mv a b` into an existing directory b keeps that meaning (the second
shaping moves the payload into the first one's directory).
- 22's cold home and its inherited home are two homes of the case (the script
inherited into the cold one after its checks); `cache dir` is compared with
the second. 52's fake registry links the shared home's payloads and 803's
home links the shared home's registry, where the scripts named
$HOME/.mcpp.
- 1067's `cmp -s` is a byte comparison and its `grep borrower` looks for the
borrowing home's directory name.
- Skips: 730's SKIP (exit 77, no compat descriptor) and 614's SKIP lines
(which exited 0 and passed) are skips of the case naming the same reason.
- 06's lua check runs where lua is on PATH, as the script's did; 22's git runs
as the script ran it.
* e2e: the self subsystem as C++ cases (17 scripts)
The 17 scripts MIGRATION.tsv assigns to `self` to merge (a home's
configuration and its index migration, a project's xlings environment, a
SubOS without its description, the doctor offline, [index.repos.<name>] in an
existing home, the shell's active SubOS, the vendored xlings, a home selection,
the doctor's resource checks, a SubOS selection that is root-local, a
suggested command's quoting, and a home at a spaced or non-ASCII path) are 17
cases of the program e2e/self (tests/e2e/self/*.cpp, 4 files: config, subos,
machine, homes). Each case is named by its script's sentence and keeps its
number; its `# requires:` words are its tags. 205 and 931 declared
`resources: machine`; they carry resource{"machine"} and are the two cases of
machine.cpp, since mtest serialises that resource only within one test
program and `mcpp test e2e/self` runs each file as one.
Scripts: 10 38 39 46 88 151 205 221 237 604 681 686 687 919 920 931 932, all
removed, with tests/e2e/_home_selection.py and _doctor_network.py, which only
919 and 920 ran. No workflow runs any of them by path.
Kept (unchanged, keep.tsv): 27 30 846 914 915 916 917 918, machine state and
upgrades of a home, as MIGRATION.tsv states.
tests/support gains `local_server` and `connection`: a TCP server on
127.0.0.1 at a port the system chooses, each connection handled on its own
thread (POSIX; constructing one on Windows fails the case). 920 uses it for
the HTTP resource that refuses, stalls and trickles and for the TLS peer that
refuses the handshake, which _doctor_network.py served with http.server and
socketserver.
Old run (the 17 scripts, against this branch's mcpp, Linux x86_64, with
MCPP_HOME=~/.mcpp and MCPP_VENDORED_XLINGS set as the e2e job sets them): 17
passed, 0 failed, 0 skipped.
New run (`mcpp test e2e/self`): 17 passed, 0 failed, 0 skipped.
Every assertion is kept. Where a reading changed:
- 919's and 920's Python programs are the cases themselves: subprocess runs
are `e2e::run` with the same environment, working directory and timeouts;
919's four concurrent writers are processes of the case; Path.resolve() is
weakly_canonical; json readings are nlohmann::json. 919's write-fault leg
keeps its two branches (Windows: a read-only locator; POSIX: a 0500 origin,
and no proof where the account bypasses directory permissions).
- 920's provider is the first of MCPP_NETWORK_PROVIDER, MCPP_VENDORED_XLINGS
and XLINGS_BIN that names a file, else the shared home's registry/bin/xlings;
without one the case skips naming them (the script raised KeyError, and on a
machine whose XLINGS_BIN names the xlings bin directory it failed to start
that directory). On Windows the case skips: its oracle is the POSIX server.
- python3 readings (151's entry checks, 237's runtime and rewritten
description, 681's entry_url, same_json and re.sub rewrites) are
nlohmann::json and std::regex with the same patterns.
- `cmp -s` (151, 681) is a byte comparison; `cp -R` (205) is a
recursive copy keeping links; `ln -s` makes links; `mkdir` of an empty
directory (221) is made by writing and removing a file in it.
- 205's field11 (`sed -n 's/^[[:space:]]*\[11\][[:space:]]*//p' | tail -1`) is
the last such line; 931's `eval` of the suggested command runs it by
`bash -c` with the program under test in place of `mcpp`.
- 687's MCPP_INHERIT_CONFIG=0 is the inherited home with its copied
config.toml removed; its `PATH=/usr/bin:/bin command -v xlings` is a look
for xlings in those two directories; `env -u` removes the variable.
- 237's MCPP_HOME_REAL default is $HOME/.mcpp, as the script's; 88's and 205's
$MCPP_HOME is the shared home.
- The python3 word stays a tag and a requirement of 151, 919 and 920, as the
scripts declared it.
* a compiler without C++ modules refuses the graph's C++ sources before the first compile
GCC before 14 has neither -fmodules nor P1689 scanning (-fdeps-format),
through which every C++ unit is built; DJGPP's GCC 12.2.0 failed at the
first scan with four unrecognised options. The capability now follows the
version, and prepare refuses a graph with C++ units on such a compiler,
naming it, the first C++ source and the remedy. C units build as before.
* an interrupt asks each program to end before ending its unit; waits poll with the lock free
Ctrl-C ended ninja's unit outright, which does not reach the commands ninja
starts in process groups of their own, and an action outlived mcpp (e2e 845).
Each program is now asked to end (terminate), given up to 2 s, and what still
runs is then ended with its unit. The waits of a started program poll with
the wait lock released between polls: a blocking or timed wait held it, and
the request to end the program, which takes it, waited behind the program
(an __action-stamp ignored SIGTERM for as long as its action ran).
Measured: an interrupted build leaves no action running.
* test programs by package, the test summary by segment, an index reader that waits, the size of a directory left beside (mcpp#927)
- [test] programs = "package": the files a package discovers are one
program, named by the first glob's fixed directory; "directory" notes
the files it leaves as programs of their own, and a setting that changes
no program is a degradation --strict refuses (2026-11-12 §18 P1).
- The test summary states plan, programs, build and run, adding up to the
total (§18 P2).
- A process that skips the index refresh another holds, and has no index,
waits for that refresh instead of reading none (§18 P3).
- The configuration-changed warning gives the size of the directory left
beside and the command that drops it (§18 P6).
* cli: mcpplibs.cmdline 0.0.4; actions return their status and the run dispatches at any depth
The adaptation 0.0.1 required is gone: a wrapper that captured each
action's status through a shared local, and a dispatch table in each parent
command (emit, xpkg, cache, index, self). Each subcommand states its own
action; a parent named without a subcommand is answered by cmdline (the
subcommands, on standard error, status 2).
* design: implementation record for 2026.11.13.1 (§15.3, §18.3, §19.3)
* docs: test programs by package, the test summary by segment, the size of a test build; CHANGELOG 2026.11.13.1
* release: 2026.11.13.1
* build: the engine link reads a replaced image where procfs presents it, without naming a system; system-names baseline shrinks
* canaries: tests/canary (c, cxx), the matrix with its measured expectations, the nightly workflow (design 2026-11-12 §5.7)
* AGENTS.md: the canaries and their matrix
* spec test B1.2: programs = "package", and the note of "directory"
* test parts named by their path below the program's directory; a test program's other files take part in the object census
A package's program holds files of several directories: tests/b.cpp and
tests/unit/b.cpp were two parts named tests/b and two compile edges of
obj/b.cpp.ddi, which ninja refused. A part is named by its file's path below
the common directory of the program's files (the stem in a program of one
directory, as before), recorded for the fast path and the archive; the
census that disambiguates object paths counts a test program's other files.
Unit test: a GCC's modules by its version.
* docs: a package's parts by their path; design record
* e2e: test programs by package and the note of directory (1081, 1082), the size of a directory left beside (1083), a command without its subcommand, the summary by segment
* run: a program that ends during an interrupt ends its unit with it
The request asks each program to end and ends the units of those still
running after two seconds; a program that ended within them was no longer
live, and what it had started in its unit outlived mcpp (the shared compiler
cap's cancellation check: the compiler under the driver stayed alive).
* e2e: the openkal subsystem as C++ cases (45 scripts; 6 kept beside their cases)
The 45 scripts MIGRATION.tsv assigns to `openkal` (the openkal stack as
packages, the target side's layers, the target base, ok images, platform
packages, target sets and the legs of a pack, and the packages of images)
are 45 cases of the program e2e/openkal (tests/e2e/openkal/*.cpp, 8 files:
stack, layers, bases, images, machine, platforms, sets, trees). Each case is
named by its script's sentence and keeps its number; its `# requires:` words
are its tags (1033 declared none and has none). 951 declared
`resources: machine`; it carries resource{"machine"} and is the case of
machine.cpp.
Files: stack 269 285 286 287 289 1073; layers 305 743 950 994 1056 1059;
bases 925 926 930; images 942 947 953 960 996 1009 1033; machine 951;
platforms 952 1020 1021 1025 1040 1049 1057; sets 963 964 983 984 985 986
987; trees 955 956 957 961 962 992 995 1078.
Removed (39): 269 305 743 925 926 930 942 947 950 951 952 953 955 956 957
960 961 962 963 964 983 984 985 986 987 994 995 996 1009 1020 1021 1025 1040
1049 1056 1057 1059 1073 1078, and tests/e2e/_sysroot_fixture.sh, which only
925 and 930 sourced.
Kept beside their cases (6), as batch 2 kept 168 and 804: 285 287 289
(openkal-cross.yml runs them by path and holds them to their lines), 286
(openkal-cross.yml, ci-aarch64-fresh-install.yml and
.github/tools/check_published_arm64_cn_ecosystem.sh run it by path), 992
(cross-build-test.yml runs it by path for its wine leg) and 1033
(host-requirements.tsv requires its result on linux, macos and windows, and
check_e2e_coverage.py reads that from the scripts' reports). They go when
those steps run the cases. MIGRATION.tsv says so in their rows. No workflow,
doc or coverage list names a removed script.
Old run (the 45 scripts, against this branch's mcpp, Linux x86_64, with
MCPP_HOME=~/.mcpp and MCPP_VENDORED_XLINGS set as the e2e job sets them): 43
passed, 2 failed, 0 skipped. 287 failed on this machine's PATH llvm-nm, an
xlings shim that cannot load libz.so.1 (with it off PATH the script passes).
925 failed at S2: "the stated base reused the default output directory".
New run (`mcpp test e2e/openkal`, suite by suite): 44 passed, 1 failed, 0
skipped. 925 fails at the same assertion; with S2's and S4's directory
assertions made non-fatal, those two are the only checks of its 36 that fail.
Since 2026.11.13.1 a build directory is named by its lineage and the base is
part of the runtime contract, which evolves: a stated sysroot, and a sysroot
rewritten in place, take over the default base's directory. The assertions
are kept.
Every assertion is kept. Where a reading changed:
- `file -b` readings (286, 287, 289, 1073) are the header read here:
"statically linked" is no interpreter and no needed library, "LSB
executable" is ET_EXEC, "PE32+" a wide PE; 1073's "FreeBSD" is EI_OSABI 9
or a FreeBSD note. readelf -l/-d/-h (286, 925, 926, 930, 1040, 1057) are
the interpreter, RUNPATH, needed libraries and class from that header;
996's `readelf -S | grep ' C '` is SHF_COMPRESSED read from the section
headers; 947's `readelf -h` entry is e_entry, its symbol address still the
llvm payload's llvm-nm.
- nm (287, 950) is the symbol tables read here; 287 no longer depends on
the nm on PATH. 287's objdump is the one the script chose.
- python3 and jq readings (305, 942, 951, 952, 953, 955-964, 983-987, 994,
1025) are nlohmann::json, std::regex and the header reader; 994's
subprocess compiles are `e2e::run` in the case directory; 1025's re.sub is
the first match replaced; 957's and 985's tarfile readings are the
machine's tar; 957's zipfile reading of contents is `python3 -I -m zipfile
-e` (python3 is its declared word).
- 925's make_glibc_sysroot (_sysroot_fixture.sh) is the case's function with
the same layout and linker-script rewrite; `touch -d '2001-01-01'` sets
that local time; the S3 sed rewrites each __GLIBC_MINOR__ line. Its
machine-default leg appends [target.x86_64-linux-gnu] to the case's
inherited home's config.toml, where the script appended to the shared
home's and restored it.
- 1009's MCPP_HOME and 1020's and 1021's _inherit_toolchain.sh homes are the
case's fresh and inherited homes.
- `env -i HOME PATH=/usr/bin:/bin` runs the launcher by env -i; `cmp`/cksum
(942, 951) compare bytes; `grep -A<n>` (951, 1021) looks at the line and
the n after it; `tail -c` (952) reads the file's last bytes; 992's dd of
one byte writes the copy with that byte changed, and its trap's chmod -R
u+w is the case's guard (956 has it too).
- `uname -m`/`uname -s` are the build's architecture and system; a script's
SKIP (exit 77) is a skip of the case with the same reason; 285's guard
SKIP lines (one exited 0) are skips; its guards keep the script's
whole-text order (`*"kernel-abi"*"graph"*`); 289 skips when no row built.
* a compiler without C++ modules builds the C++ units that need none
A GCC before 14 (DJGPP's 12.2.0) has neither -fmodules nor P1689. Its units
that declare and import no module are compiled as translation units without
module flags, from mcpp's own scan; th…1 parent fd2a93d commit 7d7aabf
1,008 files changed
Lines changed: 54175 additions & 80401 deletions
File tree
- .agents
- docs
- skills/mcpp-release
- .github
- actions
- e2e-cases
- e2e-mtest
- mcpp-unit
- use-built-mcpp
- tools
- workflows
- bench
- tests
- docs
- specs
- zh
- modules
- buildmcpp
- graph
- manifest
- src
- toolchain-model
- data
- src
- versioning
- src
- src
- build
- prepare
- cli
- fallback
- freestanding
- home
- pack
- platform
- provider
- xlings
- runtime
- run
- toolchain
- tests
- canary
- cxx
- src
- tests
- c
- include
- res
- src
- e2e
- actions
- android
- baremetal
- basics
- build-program
- build
- cache
- cdb
- cli
- deps
- features
- helpers
- index
- macos
- modules
- openkal
- pack
- runtime
- run
- self
- targets
- test
- toolchain
- web
- windows
- workspace
- matrix
- scripts
- support
- src
- unit
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Large diffs are not rendered by default.
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 | | |
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
45 | 49 | | |
46 | 50 | | |
47 | 51 | | |
| |||
193 | 197 | | |
194 | 198 | | |
195 | 199 | | |
| 200 | + | |
196 | 201 | | |
197 | 202 | | |
198 | 203 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
118 | | - | |
| 118 | + | |
| 119 | + | |
119 | 120 | | |
120 | 121 | | |
121 | 122 | | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
127 | 138 | | |
128 | 139 | | |
129 | 140 | | |
| |||
138 | 149 | | |
139 | 150 | | |
140 | 151 | | |
141 | | - | |
| 152 | + | |
142 | 153 | | |
143 | 154 | | |
144 | 155 | | |
| |||
189 | 200 | | |
190 | 201 | | |
191 | 202 | | |
192 | | - | |
| 203 | + | |
193 | 204 | | |
194 | 205 | | |
195 | 206 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 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 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
| |||
55 | 57 | | |
56 | 58 | | |
57 | 59 | | |
58 | | - | |
| 60 | + | |
59 | 61 | | |
60 | 62 | | |
61 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 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 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
91 | | - | |
| 91 | + | |
| 92 | + | |
92 | 93 | | |
93 | 94 | | |
94 | 95 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
6 | 8 | | |
7 | 9 | | |
8 | 10 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
0 commit comments