Repository navigation
Build cost and foreign toolsets, measured on a Windows workspace: engine items E1-E6 and plugin items P1-P5 #734
Description
Activity
- changed the title
[-]Build cost measured on a five-member Windows workspace: a path dependency compiled once per member, no toolset description for build programs, pack without -p, no tree stage, no PE fast path[/-][+]Build cost and foreign toolsets, measured on a Windows workspace: engine items E1-E6 and plugin items P1-P5[/+]on Sep 28, 2026 - added 15 commits that reference this issue
on Sep 28, 2026 Closing: every engine item is released, and the plugin items are released in mcpp.plugins 0.17.0. The validation project builds, runs and packs with the released engine.
Engine
Item Released in Outcome E1. A path dependency compiled once 2026.9.29.1 (#738); corrections in 2026.9.29.2 (#739), 2026.9.29.3 (#740), 2026.9.29.4 (#741) The approach of 2026.9.28.3 (a shared member planned as the root of a nested build) is removed: it cost 2^n plans on a chain of members. A workspace is now one build graph per configuration ( .agents/docs/2026-09-29-workspace-build-graph-design.md): the selected members are planned together under a virtual root, a member used by several members is compiled once, and each member's products are inbin/<member>/. The three patch releases correct what such a plan reads from its members (design section 17.1).E2, E2b. The resolved toolset and the C++ runtime contract, stated to build programs 2026.9.28.3 (#734's pull request) mcpp::tool(role),abi_tool(role),tool_env(),toolset_identity(),msvc_instance_dir(),ninja_program(),cxx_runtime(),msvc_crt_linkage()(protocol 14).E3. mcpp pack -p2026.9.28.3 A member is packed from the workspace root. E4. Placing a directory tree as one edge 2026.9.28.3 One process places the files beside a program. E5. The project fast path on PE and Mach-O 2026.9.28.3 The fast path runs on macOS, Windows and SDK-sysroot targets. E6. The library's exported surface 2026.9.28.3 Phase 1 of SPEC-008: mcpp packnames the library interface; W1 to W3 are warnings.Plugins (mcpp.plugins 0.17.0, mcpp-community/mcpp-plugins#37)
- P1:
mcpp.plugins.toolsetpasses the resolved toolset to deps-vcpkg and deps-cmake (a Visual Studio instance, a managed MSVC toolset, or the clang rows through a derived triplet). - P2: a self-contained program's ports use the static CRT triplet; naming the dynamic triplet under it is refused.
- P3: the derived triplet and its chain-loaded toolchain file hold no path, so the vcpkg ABI hash does not depend on the user's home.
- P4: no change, as stated in the issue.
- P5: remains a measurement, as stated in the issue; no design was made.
Readings (the validation project on windows-2025, fast-release, vcpkg binaries cached)
2026.9.28.2 (this issue) 2026.9.29.4 mcpp build --workspace, the CI step1726 s (run 36378870254) 1124 s (the released 2026.9.29.4, Sunrisepeak/GalTranslPP run 36549033403); of which the build after planning 1020.7 s times the core library is compiled 3 (core, cli and gui each compile it) 1 build directories in the members' own directories each member builds in its own 0 With the released 2026.9.29.4 the same workflow also passes
mcpp run -p GPPCLI,mcpp pack -p GPPCLI/GPPGUI --format release, the release layout check and the CLI started fromRelease; each member's resource script (GPPCLI.rc, GPPGUI.rc, Updater.rc) is compiled for that member, and the updater GPPGUI ships throughartifactsis inbin/gui/beside the GUI program. The run on the pull request's branch before the release (36537600583) gave the same results, with the step at 1131 s.Ecosystem
- mcpp-index pins the released engine and passed its full sweep on every platform (ci: validate every member with mcpp 2026.9.29.3; latest_mcpp -> 2026.9.29.3; ignore the workspace-root build outputs mcpplibs/mcpp-index#488 for 2026.9.29.3, run 36525705026; ci: validate every member with mcpp 2026.9.29.4; latest_mcpp -> 2026.9.29.4 mcpplibs/mcpp-index#489 for 2026.9.29.4, run 36548558591).
- xim-pkgindex names the release as
latest(bump(mcpp): track 2026.9.29.2 as latest openxlings/xim-pkgindex#906, #907, #908). - The released engine passes a sandbox verification (
xlings subos use <n> --sandbox, CN mirror): workspace planning once, the fast path, a shared member compiled once across two configurations, a tests-only member importingstd, and a consumer of mcpp.plugins 0.17.0. The sandbox's tracer intermittently returnedEFAULTto ordinary reads on this host, with either engine version; that is reported as subos --sandbox: proot 5.4.0 intermittently returns EFAULT (Bad address) to ordinary reads openxlings/xlings#635.
Open elsewhere: #732 (two members that provide a module of one name cannot be built in one
--workspaceplan).The post-release acceptance also showed two defects outside this issue's items, recorded for the next patch release: a build program's cache key varies with the selection, because the graph document lists every requester in the plan, the virtual root included, so
-p,mcpp packandmcpp emit build-databasererun the build programs a--workspacebuild ran; andmcpp emit build-databaseplans each member separately, so the core library appears in three sets with three different argument lists.From 2026.9.29.4 on, a change to the engine is cross-verified with the validation project before it merges: the project's CI builds mcpp from the pull request's branch, and the pull request merges only when that run and its own CI both pass.
- P1:
Context
A downstream validation project is used: a five-member Windows workspace with a core library of 76 translation units, a Qt GUI, 22 vcpkg ports and one CMake project, built with mcpp 2026.9.28.2 and mcpp:plugins 0.16.0. The project is evidence, not a requirement. An item is listed here only if its need survives the removal of that project; project-specific items are listed at the end with the reason they stay with the project.
The design, with alternatives, compatibility and one criterion per item, is recorded in
.agents/docs/2026-09-28-build-cost-foreign-toolsets-and-library-surface-design.md(under review).Principles applied:
build.mcpp, with a stated default.Readings
mcpp build --workspace, vcpkg binaries cachedcli(compiles core again)gui(compiles core a third time; plus GUI, ElaWidgetTools, Qt code generation)mcpp emit build-database, same runmcpp pack --format release, two membersmcpp run -p clion Windowsee6a47d)portfile.cmakemcpp packof a library exportingAlphaandBetawith no lib rootsources = []Engine (mcpp)
E1. A path dependency is compiled once per consuming member
--workspaceand separate-pinvocations build each member as its own graph, so a shared library member is compiled once per consumer: 3 times here, about 570 s of 1726 s. The global cache excludes path packages (their sources are mutable), and a source stamp of the package root cannot key them either, because a library's inputs are not confined to its root: core compiles../3rdParty/...modules.Proposal. Build a path dependency as a keyed sub-build, the way host tools already are:
<workspace>/target/.members/<package>/<key>/, keyed by the existing per-package build key, which excludes the consumer.The alternative is one workspace graph (Cargo's model), recorded in the design.
Criterion.
--workspacecompiles each library unit once, and a following-p <second program>compiles none of them.-pbuilds of different programs both succeed.E2. The resolved toolset, stated to build programs
Build programs can read
toolchain_dir()andcompiler(), but not the tools of the row or their environment. Plugins therefore let vcpkg and CMake detect Visual Studio on Windows, and on Linux they reconstruct mcpp's clang privately (program_compilers).The engine already holds the facts:
envOverrides(INCLUDE/LIB/PATH);msvcToolsDir,windowsSdkRootand their versions.Proposal. Build-system-neutral accessors, carried as
MCPP_*variables:mcpp::tool(role)cc,cxx,ld,ar,rc,asmcpp::abi_tool(role)cl,link,lib,rcof the resolved toolset and SDK on the MSVC ABI, and the same astool(role)elsewheremcpp::tool_env()INCLUDE/LIB/PATHon the MSVC ABI (synthesised for the llvm row by the cl.exe row's function), empty elsewheremcpp::toolset_identity()msvc 14.44.35207; sdk 10.0.26100.0mcpp translates nothing into any foreign build system's terms. Upgrade cost: every build program runs once more.
Criterion.
msvc@14.44.35207resolved,abi_tool("cxx")andtool_env()name the managed toolset.tool("cxx")is clang++ andabi_tool("cxx")is the sysroot'scl.exe.E2b. The C++ runtime contract, stated to build programs
Objects a plugin produces must follow the program's CRT contract. Today:
VCPKG_CRT_LINKAGE dynamic, and the standard triplets are dynamic;/MD.A project with
cxx_runtime = "self-contained"is therefore expected to mismatch. This is read from the sources and not yet measured.Proposal.
mcpp::cxx_runtime()andmcpp::msvc_crt_linkage()(static/dynamic/ empty), the valuesplace-dlls --crtalready receives.Criterion. First a reading with plugins 0.16.0: a
self-containedproject plus one vcpkg port on Windows. The item is withdrawn if it links.E3.
mcpp pack -pbuild,runandtesttake-p;packmust be run from the member directory.Criterion.
mcpp pack -p <member> --format <f>at the root equals the same command in the member directory.E4. Placing a directory tree as one edge
mcpp stagecopies one file per action. This costs 2675 processes here, and the validation project's upstream wrote its own--copy-treetool.Proposal.
mcpp stage --tree <src> --output <dir> --manifest <f> --depfile <f>:The alternative, a layout stated in the pack format, is recorded in the design. The recommendation is the primitive, because it also serves builds.
Criterion.
E5. The project fast path on PE and Mach-O
try_fast_buildrequires a stored ELF run-timePassverdict for every artifact (validated_artifact_snapshot), so on Windows and macOS every build and run plans again (#400; e2e 645 and 821).Proposal. Record
NotApplicablefor formats without a validator and accept it on the fast path. This is sound on PE because the check that matters there is theplace-dllsedge, which ninja runs on every relink.Criterion.
E6. The library's exported surface: a specification, and warnings at its readers
The lib root (
src/<tail>.<ext>or[lib].path) has two readers:mcpp pack <lib>, which publishes the lib root's module closure.A source consumer may import any exported module, so the build-time warning
lib target without conventional lib roothas no reader in the build. Meanwhilemcpp packof a library exporting modules without a lib root publishes it as headers-only and exits 0, and its "Withheld (nothing)" row is false.An error is not proposed now:
[lib], which older engines refuse.Phase 1.
[lib].path, what is published and withheld, the legitimacy of a headers-only interface, and the recommended facade (one primary interface that re-exports withexport import).mcpp packwarns and names each exported module the package will not contain.mcpp buildwarning stays for the package being built, reworded to state the consequence at pack time.Phase 2 (conditions only). An error with an explicit headers-only declaration becomes possible when two conditions hold:
min_mcppreads the new key.Criterion (phase 1).
Alpha/Betalibrary packs with a warning naming both modules, and "Withheld" lists both.[lib].path, both modules are published and no warning appears.Plugins (mcpp-plugins, to be filed there as P1 to P5 once E2 is settled)
P1. A toolset option for deps-vcpkg and deps-cmake.
build.mcpp:toolset = resolved | detected;compiler = abi_native | row;generator = ninja | default.resolved:VCPKG_CHAINLOAD_TOOLCHAIN_FILE(vcpkg then does not loadvcvars);CMAKE_<LANG>_COMPILER;program_compilersbecomes the Linux instance ofresolved.resolvedwithabi_native.detectedstays selectable.resolvedthere is an error.P2. CRT linkage from the contract.
VCPKG_CRT_LINKAGEandCMAKE_MSVC_RUNTIME_LIBRARYcome from E2b and can be overridden. Proceed only if E2b's reading confirms the mismatch.P3. vcpkg ABI-hash hygiene under
resolved. The hash covers the triplet, the compilers, the chain-loaded file and the values ofVCPKG_ENV_PASSTHROUGH, and paths contain the user's home. Therefore:VCPKG_ENV_PASSTHROUGH_UNTRACKED;toolset_identity()goes into a triplet comment;$ENV{}.Criterion: two homes with the same pinned toolset compute the same hash.
P4. Binary sources. No change.
VCPKG_BINARY_SOURCESalready passes through, and the plugin documentation states it.P5. Reuse of a CMake dependency's build across runs. First measure ElaWidgetTools' share of gui's 690 s. No design until that reading exists.
Outside mcpp and the plugins
-pinstead of--workspace, Updater as anartifactsdependency, hoisted[target.windows.build]valuesreleaseprofilemcpp packTool,Dictionary) in coreOrder
toolset = resolvedon the Visual Studio-masked row andpack -p.