2026.9.28.1: #717-#726 in one round (in progress) - #727
Open
speak-agent wants to merge 42 commits into
Open
speak-agent wants to merge 42 commits into
speak-agent wants to merge 42 commits into
Conversation
…720) The lib root was placed at the head of the host-module compile list before the list was sorted by imports, so a lib root that imports a sibling unit was compiled first and failed. The lib root is now the first node of the same sort; it is emitted first whenever it imports nothing of its package, so existing packages keep their order. e2e 807 covers an explicit and a conventional lib root.
The read site printed the E0006 text as error: at the start of every run that read such an index, including runs that then resolved every package elsewhere and exited 0. The fact is now recorded without printing: - a run that fails carries the E0006 text in the message that stops it; - a run that refreshed an index and met a floor ends with one tip: line, printed after the command's output (closing notices, mcpp.ui), and an envelope reports it as the note MCPP_INDEX_REQUIRES_NEWER_MCPP; - mcpp self doctor lists every index whose floor this mcpp does not meet; - a tree recorded as unusable answers no later lookup either; - the E0006 upgrade note starts on its own line. Unit tests for the read site, the hint, the notices; e2e 812.
…d -p resolves the package first A rooted workspace (root [package] + [workspace]) built as itself never set state.wsManifest / state.runtimeWorkspaceRoot, so a member reached through the root's own [dependencies] path entry was loaded as an ordinary path dependency: its x.workspace = true entries were refused or silently unresolved, and it received none of [workspace.package] / [workspace.build]. The workspace context is now set in that branch too, before any dependency is loaded, matching the member-switch branch and satisfying "a member is a member however it is reached" (graph.cpp's depIsMember). -p, --package <NAME> promised a package but both resolvers (manifest.cpp's inline loop and project.cppm's resolve_member_dir) matched only a member's directory basename or path. They are now one function in project.cppm, resolving in order: a member's qualified name (<namespace>.<name>), then its bare package name (refused, naming every match, when shared by two or more members), then its directory path or basename (kept as a fallback; a duplicate basename keeps today's first-match selection but now warns naming the others). A value that is one member's package name and a different member's directory selects the package, with a warning naming the other member. SPEC-004 §9 item 1 names the missing position and states that -p resolves the package identity first; docs/07 §5.3 (en, zh) and the four `-p` help strings state the resolution order. Tests: six unit tests for the resolver in test_workspace_inheritance.cpp (package/path/basename all selecting one member, same name under two namespaces, package-outranks-directory with a warning, duplicate-basename warning, the "not found" listing, and the unaffected no-filter cases); e2e 805 (a rooted workspace's own path dependency, with a non-latest pinned version, an omitted package.version, a [workspace.build] flag, and a shared root [toolchain], served from a project-local index so the build touches no network) and e2e 806 (-p's three resolution steps end to end). Both e2e scripts fail on the released 2026.9.27.1 binary and on 2026.9.26.1, and pass with this fix. Co-authored-by: speak-agent <agent@mcpp-community>
Two or more source paths for one deploy destination no longer collide at planning. `add_deploy` (src/build/plan.cppm) merges them into one `DeployFile` entry instead of refusing a second source path; a `BuildPlan` whose deploy has exactly one source still produces the byte-identical `build.ninja` line it always has. The merged destination becomes one `stage_file` edge with every source as an input (src/build/ninja_backend.cppm), and `mcpp stage` places it once all sources agree byte-for-byte (`stage_files`, src/build/stage.cppm), otherwise failing and naming every source and the destination. `cmd_stage` (src/cli/cmd_build.cppm) accepts one or more source positionals. One destination, one writer: `place-dlls`'s post-link DLL placement (src/pack/pack.cppm `place_runtime_dlls`, invoked from src/cli/cmd_publish.cppm `cmd_place_dlls`) now receives the set of names the merged deploy list already places beside a program, computed in ninja_backend.cppm and passed through a per-edge `$placed` ninja variable so the generated graph stays stable. `place_runtime_dlls` skips those names instead of overwriting them, comparing content and reporting a difference as a warning. SPEC-007 R4.2 and R4.3 are amended to state the content check and the single-writer rule; docs/04 (en, zh) is corrected to match. e2e 810 covers the merge and the build-time refusal; e2e 811 (`# requires: windows`, not run here) covers the single-writer rule; e2e 646's collision case is updated for the new build-time message. Unit tests cover `stage_files`'s multi-source behaviour and the deploy-list skip/warn logic in `place_runtime_dlls`. Co-Authored-By: Claude <noreply@anthropic.com>
…onment as it found it and starts in the registry's home (#726) On Windows, build_command_prefix prepended the registry's subos/default/bin to the process PATH and set XLINGS_HOME process-wide, and ran xlings in mcpp's working directory. After a build installed a payload, every action found xim:llvm's cl/link/lib/rc shims in front of MSVC's tools, and vcpkg's compiler detection failed; a project with a .xlings.json at its root also received the shims of mcpp's toolchain and payloads in its own SubOS. ScopedInvocationEnv now applies XLINGS_HOME, the scope variables and the PATH prefix for the invocation and restores them afterwards, and the Windows prefix starts with `cd /d "<home>" &&`, as the POSIX prefix does. Refs #726.
…uild program's own diagnostic survives; emit writes no project file W3 (src/build/plan.cppm): the compile-unit loop now skips SourceKind::Device graph units, so build.ninja carries no dead cxx_object edge for a rule-claimed device source, and compile_commands.json / the S1 document agree without their own filter (unit_invocations already excluded only NASM; nothing else needed to change). The source stays in `watch` and still reaches the package's build program through MCPP_DEVICE_SOURCES, since both read the manifest's sources glob directly, not plan.compileUnits. Checked every other consumer of plan.compileUnits (prepare/plan.cpp's dependency-cache collection keys by path, not by index, so it is unaffected beyond a smaller artifact set for a package with device sources). W4: a package whose build program failed under `emit`'s plan_only records MCPP_BUILD_DATABASE_PROGRAM_FAILED and applies none of its directives (state.cppm: new PrepareState::programFailedPackages, set at both call sites in target_side.cpp and features.cpp). The device-source orphan check in target_side.cpp now skips such a package outright, instead of reading every device source as unclaimed and failing the whole member. Also: notes a phase recorded before prepare_build's own failing return are no longer silently dropped (driver.cpp: a thread_local sink in the same per-run-sink style as mcpp::build::refusal, exported as mcpp::build::take_notes_on_failure); cmd_build.cppm's emit failure path folds any such note into the one diagnostic SPEC-005 R5.2 allows a wholly- failed member (path stays the member's mcpp.toml, exactly one entry), so the true cause is not lost behind a downstream symptom without violating that invariant. hasProgram's existing exists(build.mcpp) check is now correct by construction, since a failed program's package never reaches it. W5 (src/build/prepare/xlings.cpp): ensure_project_index_dir's two calls under a private work_dir are collapsed into the one call the ownerRoot==workRoot branch always made, targeting workRoot in every case. Previously the runtime-environment half (deps/subos/workspace) went to runtimeSelection.ownerRoot, which is always the real project root regardless of emit's private work_dir -- so `emit build-database` wrote <root>/.mcpp/.xlings.json into a project that declares [xlings] deps. SPEC-005: R3.7 names device sources beside NASM units (both absent from S1 and compile_commands.json, for different reasons -- NASM is a compile unit excluded from export, a device source is never a compile unit at all). R5.2 gains the sentence that a check whose premise is a build program's directives does not run for a package whose program failed in this pass. R2.1 needed no change. Tests: e2e 808 (device source: no dead ninja edge, absent from both databases, and the rule still compiles it and the build still runs), e2e 809 (a device source plus a build.mcpp that does not compile: PROGRAM_FAILED with path build.mcpp, no device-source mention, package still described), e2e 817 (688's project-tree digest repeated on a stub-xlings fixture with [xlings] deps: byte-identical tree, no .mcpp/.xlings.json, no write-project effect, and the private work directory does gain one naming the dependency). Each fails against the released 2026.9.27.1 binary and passes on the fresh build. Full regression: all 15 emit/build-database e2e scripts, 798, and four more that exercise the compile-unit loop (asm/GAS, NASM, object-path-collision, multi-module) all still pass; `mcpp test` (130 unit tests) passes.
Sunrisepeak
added a commit
to Sunrisepeak/GalTranslPP
that referenced
this pull request
Sep 27, 2026
…into feat/eight-reports
A set's ide.generated lists each output of its package's source-role actions (header or source, with the step's id, inputs, arguments and work directory) and each generated include directory its units name, with the path the document names and the path a mcpp build of the same selection writes. The profile version is 0.3.0 (Sunrisepeak/ mcpp-language-server#28); compile_commands.json is unchanged. SPEC-005 R3.12, docs 50; e2e 815, e2e 688 reads the new version.
- ProgressBar prints one start line and one finish line when stdout is not a terminal: no carriage return, no erase sequence, no repaint per frame; an item that does not complete says so instead of reporting it done. - The index refresh runs through xlings interface update_packages and draws its progress and download events with the same renderer; an xlings that emits none shows its status line as before, and its terminal text no longer reaches the output. - The clone of a git dependency passes --progress and draws the download phase, read as it is redrawn (run_streaming_bounded gains an opt-in rule that a lone carriage return ends a line); the output is kept whole for the failure message, and a clone silent for fifteen minutes is stopped. - The sandbox bootstrap's hand-drawn spinner is the shared bar. e2e stubs accept the interface refresh; unit tests for the git progress parser, both render modes and the line splitting; e2e 816; docs 09.
[target.<selector>.build] dialect_cxxflags is now accepted, parsed into a new ConditionalConfig member kept apart from BuildInputs (the key is graph-wide, not a per-package additive input), and merged into the same BuildConfig::dialectCxxflags every consumer already reads: the std BMI prebuild, the scan, every TU and the fingerprint. Only the root of the build renders it; a dependency's own value (conditional or not) reaches no command and is now excluded from its own fingerprint contribution, matching the rule that a key enters a fingerprint only where it reaches a command. The build-program directive is deliberately not added. SPEC-004 SS3.1 and SS9 item 10 state the new rule; docs/04 and docs/zh/04 document the conditional form (since 2026.9.28.1). Unit tests cover parsing, the emptiness gate, and root-only resolution order. e2e 813 covers reach into the std BMI/scan/TUs, a non-matching selector, the A-B-A std BMI rebuild, and a dependency's key being fingerprint-inert; it fails on the released 2026.9.27.1 binary and passes on the fresh one.
The one-writer rule passed the deploy list's names to place-dlls on its command line. The plan's deploy set reads runtime search directories that a prepare action fills, so it differs between the first and the second plan; the command changed and every build after the first re-ran the placement (e2e 797, found by a differential run against 2026.9.27.1). place-dlls now treats a DLL beside the program that it did not place, and that a runtime search directory also offers, as another writer's; the command is the one 2026.9.27.1 wrote.
Every MSVC-ABI row now receives the same CRT model: cl spells it /MT or /MD, clang++ targeting *-windows-msvc spells it -fms-runtime-lib=static or =dll. One helper (msvc_abi_crt_word, dialect.cppm) decides the word for the translation units, the std/std.compat BMIs and the link command alike, closing #649 E10 (a compile-only flag never reached the clang driver's own link-time choice of -defaultlib:). toolchain-coupled (the dynamic CRT, with the toolset's own vcruntime140.dll/msvcp140.dll staged beside the artifact) is now the default for every role on this ABI (dist::msvc_abi_default_contract, ContractStatement::msvcAbiDefault). A toolset with no VC\Redist\MSVC directory defaults to host-coupled silently and refuses an explicit toolchain-coupled, naming the missing directory (prepare/plan.cpp). A free-form CRT word in cxxflags/dialect_cxxflags is checked against the resolved model: agreeing is a warning, contradicting is a refusal (dialect::check_crt_word, wired in prepare/scan.cpp). The toolset's redistributable directory is carried as its own Toolchain field (msvcRedistDir), populated for cl from vc_redist_dir and for the LLVM row from its sysroot's tools directory (vc_redist_dir_for_tools_dir) rather than from linkRuntimeDirs, which holds LLVM's own runtime directories on that row. The staging gate and the mcpp run/test search path both read this field, gated on the MSVC ABI rather than on which compiler is in use. mcpp pack carries the staged DLLs by default; an explicit --mode system now resolves a defaulted (never-declared) toolchain-coupled contract to host-coupled instead of refusing. e2e 703 is inverted to the new default; e2e 814 covers the LLVM row's import table, staged DLL, clean-PATH run, self-contained round trip, BMI switching (A, B, A) and pack modes (Windows-only, unverified here). Unit tests cover the CRT-word derivation, the free-form-word check, the MSVC-ABI default/redistributable resolution and a compute_flags-level property test across both rows; the link-line half of that test is gated on mcpp.platform.is_windows, since link_shape resolves LinkShape::PeLld only when current_link_host() is Windows and a Linux-built mcpp cannot reach that branch regardless of the plan's target triple. docs/20, docs/zh/20 and SPEC-006 record the new default and the upgrade; docs/04 needed no change (it only points at docs/20). 131/131 unit tests pass on Linux (gcc 16.1.0 and llvm 22.1.8 rows); docs structure/style checks pass.
…; place-dlls comment matches its command; docs/20 names the --mode system refusal
Verbatim extraction along the sections its own banners already name: prebuilt dependencies, link forms, make_plan, the C++ runtime checks, graph/schedule, declared build-graph actions, assembly units, Windows resources, the global dependency cache, mcpp.lock, runtime provider overrides, ABI enforcement, resolution.json, and the empty-link check. Longest resulting function: 356 lines (step13_build_graph_actions).
…steps The worklist step for one item is split into identity resolution, the already-resolved / identity-adoption handling (with its own version-merge sub-step), acquiring a fresh dependency's source and manifest, and finalizing it (recording the package, recursing into children); the per-item locals that cross those boundaries move into a phase-local struct, WorklistItemCtx, the same PrepareState pattern one level deeper. The post-loop cycle check is its own function. Preamble closures that captured only `state` (or nothing) become static file-scope functions, called with an explicit PrepareState& where they used to close over it; this also fixes the one comment that had gone factually stale (activateFeatures's group banner said 'defined as local lambdas, not file-scope functions' -- now they are file-scope, and are still safe because a static function carries no external linkage into the module's exported interface). Longest resulting function: 300 lines (step4b_identity_version_merge).
…) into sub-steps Along the sections its own banners already name: feature activation, device extensions and rule application, the graph's [xlings.workspace] provisioned before build.mcpp, host-module registration, host-tool provisioning, the dependencies' build programs, and capability binding. aggregatedRequest (needed by two of these sections) is promoted from a local lambda to a file-scope function of PrepareState&. Two scoping braces that had no matching close within their own section (opened to wrap several sections at once) are dropped as redundant once the content is distributed across separate functions, each of which supplies its own scope. Longest resulting function: 425 lines (step6_provision_host_tools).
…b-steps Gather the graph's target-side candidates into a phase-local struct (TargetSideGather, the PrepareState pattern one level deeper), then resolve and realise [c-abi], broadcast the include set, check kernel-abi interfaces and layer requirements, apply the layer-conditional config (L1b), decide each dependency's link form (#519), define the graph_package_entry closure, run the root build.mcpp (L3), require every device source to reach an action, and handle re-run inputs (R1.3). Two scoping braces that wrapped several sections at once are dropped as redundant, as in the two previous files. Longest resulting function: 404 lines (step9_kernel_abi_interfaces_and_requirements).
…-steps The closures phase4a_graph_load assigns onto state (each captures only state) split into two groups: split/identity closures, and candidate selection closures. LoadedDep is hoisted to file scope so it stays visible to state.loadVersionDep, which remains where it was. state.loadVersionDep itself (509 lines) is not split further: its six local closures (readLuaContent, findRawInstalled, installedLayoutMatchesIndex, revisionIsCurrent, findCompleteInstalled, markInstalled) mutually capture nine-odd shared locals by reference: hoisting them to free functions would mean threading all of that through explicit parameter lists for one closure, judged higher risk than benefit within this round; noted as a residual in the T6 report.
CI (PR #727): - e2e 190/191 select the program by name; bin/ also holds the staged redistributable DLLs on an MSVC-ABI row (#718). - A DLL found in a runtime search directory yields to a declared deploy of the same name, and a difference is warned at planning, where a successful build shows it (SPEC-007 R4.3, e2e 811; e2e 818 is its Linux-hosted form). - build.mcpp that imports only a build rule compiles in the build directory under GCC (e2e 807). - The runtime environment half of .xlings.json goes to the runtime's owner again; only plan_only redirects it to the planning directory (e2e 205, 817). Review: - Every set of a package names what its build program generates (R3.12). - A dependency's contradicting CRT word is refused; debug CRT words are refused; the agreeing-word message names the key of the dynamic model; the dependency cache key carries the CRT word. - PE deploy destinations compare without case; MSVC version directories compare numerically; a missing deploy source is named as missing. - A failed index refresh draws "did not complete"; an automatic refresh that exhausts its retries warns; the floor tip names the version and the install-aware upgrade. - -p compares paths as paths; the help names the qualified form.
…p) into sub-steps Split at phase1's own banner boundaries: the closures phase1 assigns onto state, the target/--static override resolution, and the device axis plus the L1 conditional-section merge. phase2_define_toolchain_resolver is not split: it is a single stored closure, state.resolve_target_toolchain, whose ~1000-line body is one sequential toolchain-resolution flow with dozens of interdependent locals -- the same category of residual as state.loadVersionDep in graph_load.cpp, noted in the T6 report. step1_target_and_static_overrides (524 lines) is a smaller residual of the same kind.
…phase3_xlings_before_graph Both were found over ~400 lines by the same sweep that produced the seven functions #722 named (the issue's own "any other function under src/build/prepare/ over ~400 lines"), and split cleanly along their own banners: phase0's own "Workspace handling" section becomes step0_workspace_handling (108 lines; phase0 itself drops to 372); the host-toolchain closures phase3 assigns onto state, plus the index-refresh section, become step3_define_host_tc_closures_and_refresh_index (298 lines; phase3 itself drops to 235).
…o CI yet .github/tools/check_function_sizes.sh runs clang-tidy's readability-function-size (LineThreshold=400) over the compile database mcpp produces for its own LLVM build (mcpp build --toolchain llvm@22.1.8), restricted to files under src/build/prepare/. clang-tidy is not part of the plain xim:llvm payload; it ships in the sibling xim:llvm-tools package at the same version, which the script locates under the xlings store. Measured: at 7ccbc8d (before this round's split) it reports 10 functions over 400 lines -- the seven #722 named, plus phase0_manifest_and_workspace, phase11_scan and phase3_xlings_before_graph, found by the same sweep. After this round's split it reports 6: step6_provision_host_tools (421), phase4a_graph_load (518, its loadVersionDep closure), phase11_scan (773, untouched), step9_kernel_abi_interfaces_and_requirements (401), step1_target_and_static_overrides (522) and phase2_define_toolchain_resolver (1006, untouched) -- see the T6 report for why each remains. Not wired into CI next to check_file_lengths.sh: it does not yet pass, so adding the workflow step now would land a gate red on day one. Wire it once the remaining residuals are split in a follow-up; the file gate stays the only enforced one for this round, per the design's own fallback for a working tool over an incomplete split.
…under the file-length limit after the merge; byte-identical on the seven fixtures)
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request is in progress. It carries the whole round planned in
.agents/docs/2026-09-27-eight-reports-by-home-and-one-optimisation-plan.md: issues #717, #718, #720, #722, #723, #724 and #725, together with #726, the fix this pull request was opened for. The final description, with the full test table, replaces this one before merge.Landed so far
-presolves a package's identity first, then the directory spellings.emitwrites no.mcpp/.xlings.jsoninto the project.[target.<selector>.build] dialect_cxxflags, a graph-wide key read from the root only.mcpp self doctorreports it.Still to come
toolchain-coupledis the MSVC-ABI default.The version is 2026.9.28.1, in
mcpp.tomlandMCPP_VERSIONboth. The first CI run of #727 failed because onlymcpp.tomlhad moved.