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
{{ message }}
Repository navigation
prepare: split the longest phase functions into sub-steps #722
mcpp 2026.9.27.1 (#719) decomposes src/build/prepare.cppm: prepare_build becomes eleven phase functions over an explicit PrepareState, in implementation units of at most 2,500 lines each. The file boundaries are architectural; several phases are still single functions of about two thousand lines:
Phase function
File
Lines
phase13_finish
src/build/prepare/plan.cpp
2,293
phase4b_graph_worklist
graph.cpp
2,245
phase6_features_and_host_tools
features.cpp
1,953
phase9_target_side
target_side.cpp
1,869
phase4a_graph_load
graph_load.cpp
1,060
phase1_toolchain_spec_and_axes
toolchain.cpp
1,029
phase2_define_toolchain_resolver
toolchain.cpp
1,007
Each still keeps many locals of its own and defines closures that later steps of the same phase call, so a step inside a phase can be read, tested or changed only through the whole phase.
Proposal
Split each of these along the sections its own banners already name, the way the decomposition split prepare_build:
phase13_finish: prebuilt dependencies, link forms, make_plan, the runtime binding, assembly units, Windows resources, the global dependency cache, mcpp.lock, resolution.json, the empty-link check;
phase4b_graph_worklist: the worklist step for one item, identity adoption, the cycle check;
phase6_features_and_host_tools: feature activation, capability and ABI accumulation, host-tool provisioning, the dependencies' build programs;
phase9_target_side: capability binding, target-side resolution, link forms.
Each sub-step is a function over PrepareState& (or over a phase-local struct passed by reference), extracted verbatim; the checks the decomposition used apply unchanged: the golden fixtures byte-identical, the unit tests, the e2e suite, AddressSanitizer with detect_stack_use_after_return=1 for closures that outlive their step, and no new interface unit in mcpp.build.prepare's import chain (#721).
Acceptance
No function under src/build/prepare/ longer than about 400 lines, and the length gate extended to functions.
The pure pieces that fall out of the split gain unit tests alongside tests/unit/test_prepare_helpers.cpp.
Done in 2026.9.28.1 (#727). The long phase functions of src/build/prepare/ are split into sub-steps along the sections their own banners name, by the #719 method. Statements are moved, not reordered or rewritten. Where several steps of one phase share locals, those locals live in a phase-local context struct.
Scope. The seven functions this issue named are split, together with the three others over about 400 lines that the measurement found: phase0, phase3 and phase11. So are the six sub-steps and closures that were still over 400 after the first pass, among them phase2_define_toolchain_resolver (1,006 lines) and the loadVersionDep closure.
Where the code now lives.plan.cpp would have exceeded the 2,500-line file gate after the merge. The records half of P13, which writes mcpp.lock and resolution.json, moved to records.cpp.
The size check..github/tools/check_function_sizes.sh runs clang-tidy's readability-function-size over the compile database of an LLVM build. It reported 10 functions over 400 lines at the base and reports none now.
resolution.json, build.ninja and the emit build-database output of seven fixtures are byte-identical to the pre-split binary. The fixtures are: a single package, a workspace, a git dependency, a host tool, a build program, a musl cross target, and emit.
AddressSanitizer with detect_stack_use_after_return=1 reports nothing over those fixtures and a 20-script prepare-heavy e2e subset.
Activity
T6 (mcpp#722, W7 follow-on): split phase0_manifest_and_workspace and …
T6 (mcpp#722): add the function-size gate, verified but not wired int…
docs: #722 completed, the function-size gate in CI (changelog, 13.6)
2026.9.28.1: eight reports (#717, #718, #720, #722-#726), one downloa…
speak-agent commentedon Sep 27, 2026
Done in 2026.9.28.1 (#727). The long phase functions of
src/build/prepare/are split into sub-steps along the sections their own banners name, by the #719 method. Statements are moved, not reordered or rewritten. Where several steps of one phase share locals, those locals live in a phase-local context struct.phase0,phase3andphase11. So are the six sub-steps and closures that were still over 400 after the first pass, among themphase2_define_toolchain_resolver(1,006 lines) and theloadVersionDepclosure.plan.cppwould have exceeded the 2,500-line file gate after the merge. The records half of P13, which writesmcpp.lockandresolution.json, moved torecords.cpp..github/tools/check_function_sizes.shruns clang-tidy'sreadability-function-sizeover the compile database of an LLVM build. It reported 10 functions over 400 lines at the base and reports none now.mcpp build --toolchain llvm@22.1.8.Readings on the final tree:
resolution.json,build.ninjaand theemit build-databaseoutput of seven fixtures are byte-identical to the pre-split binary. The fixtures are: a single package, a workspace, a git dependency, a host tool, a build program, a musl cross target, andemit.detect_stack_use_after_return=1reports nothing over those fixtures and a 20-script prepare-heavy e2e subset.maintoo (ci-macos xcode-27: ld64.lld cannot parse arm64e.x1 in either available SDK (upstream, tracked) #669).