0.19.0: one resolver for a member's tool, payloads installed on request, and a toolchain a build program states (#41) - #43
Merged
Conversation
…nstalled on request, and a toolchain a build program states A member that runs a program answers one question before it can plan anything: which program. Each member answered it its own way -- an option here, a variable there, a silent PATH fallback in two members and a refusal of PATH in two others -- and a tool named in build.mcpp did not stop the declared payload from being downloaded. On the engine of mcpp#755 this release answers it once. - `mcpp.plugins.tool` (in `plugins-core`) resolves a member's tool in one order: the build program's choice, the member's legacy variable, the engine's override, then the declared payload. A choice never asks for the payload, which is what makes "the build program names its own tool" mean "the payload is not downloaded". Every answer is recorded with `mcpp::decision`, so a build reports the source and `mcpp why tool <name>` can answer, and one refusal text lists every way to name the tool. - Every member that runs a payload tool takes its option as a `tool::choice`, which a string constructs, so `o.cmake = "/usr/bin/cmake"` keeps working and the line that wrote it is what the build reports. `rules-cuda`, `rules-hip`, `rules-ascendc` and `dist-apk` gain the options they had none of. - `deps-cmake`, `deps-archive`, `deps-vcpkg`, `dist-appimage` and `dist-apk`'s `bundletool` declare their payloads `provision = "on-request"`: a build that names its own tool, or never reaches the tool, downloads nothing. - `rules-spirv` and `rules-slang` keep their PATH fallback behind a warning that names the choice to write instead, until 2027-04-01 (SPEC-007 R6.2). - `mcpp.plugins.toolchain` (feature `plugins-toolchain`) builds the statement a root build program makes in its toolchain phase: `layout`, `prefixed`, `compose`, `from_env_script`, `with_launcher`, `managed`, `env`, `configure` and `use`. - `mcpp.plugins.testing` states a payload's source, its program and the phase, so a case can describe an override or a pending payload. The engine floor is 2026.10.1.3, the release whose protocol 15 states where each tool and payload comes from. Closes #41 Test plan - `tests/plugin-logic`: 27 cases pass, including five for the resolver (a choice asks for no payload, an override answers, a pending payload is requested, the payload answers last, and the refusal lists every way to name the tool). - 11 consumer fixtures build against the engine branch, `all-rules-compile` compiles every rule and dist member, and the default build's output is unchanged. - New CI criterion `tool-sources` on all three platforms: with nothing named the member asks for the payload; with the build program naming a cmake the payload is not asked for and the source is reported; an override says the same; and `--managed-only` refuses both. The criterion reads `resolution.json`, so it holds on a runner that already has the payload.
…n be measured before it is released `MCPP_VERSION` states the engine this collection needs, and that release may not exist yet: 0.19.0 is written against mcpp#755 and names 2026.10.1.3 while it is still a branch. Every fetch and every sandbox cache key now reads `MCPP_BOOTSTRAP`, the release that is published, and `mcpp_source_ref` builds the engine under review with it. The two meet again when that release exists.
…n reads narration from stderr Two failures from the other platforms, with one cause each. The kit left the real build's `MCPP_XPKG_*` in the environment. These cases run inside a build program, which the engine gives one `_DIR`, `_PROGRAM` and `_SOURCE` per payload its package declares -- including `pending`, for a payload declared `provision = "on-request"` that nothing has asked for. Eight vcpkg cases therefore asked for `xim:vcpkg` and planned nothing wherever the payload was not installed, and passed wherever it was: 19 of 27 on macOS arm64 and on Windows, 27 of 27 here. The keys are derived from package names, so they are enumerated rather than listed. Measured both ways on the same binary with the inherited value present: 19 of 27 before, 27 of 27 after. The spirv fixture's step asserted on a status line while capturing stdout alone. mcpp writes narration to standard error from 2026.10.1.1, so the log was empty and the step reported that the rule had not run while it had.
…s it Darwin exports `environ` to a main program only and offers `_NSGetEnviron()` instead, so the macOS host-module compile failed with `use of undeclared identifier 'environ'`. Elsewhere the symbol is declared here rather than taken from <unistd.h>, whose declaration a module purview does not see.
…which of its payloads is asked for The examples named 0.8.0 and 0.11.0, which a reader copies into a project that then resolves a release without what the page describes. The reason dist-apk's tools are features also predated on-request provisioning: `xim:bundletool` is now asked for while a bundle is planned, and `xim:kotlin` is still installed with the feature that compiles Kotlin.
… values
While this rule took the payload directory and appended `/bin/clang++`, one
string served as both. A resolver answers the program, and the migration left
`-L` pointing at it: the SYCL consumer's build failed with
ld.lld: error: unable to find library -lsycl
in `compat.opencl`, because the search directory had become
`<root>/bin/clang++/lib`. The link search now uses the resolved root. Every other
migrated member was audited for the same swap: the toolkit members take `.root`
and the tool members take `.program`, and no other site appends a directory to a
program path.
The page named `compose`, which does not exist, and left out `newest_under`, `managed` and `with_family`. Every name on that line and in the tool section was then checked against the sources.
…file carries A build program writes the path a shell gave it, and on Windows `command -v cmake` answers `C:/Program Files/CMake/bin/cmake` for a `cmake.exe`. The resolver insisted on the exact spelling and refused a program the host itself would have run -- measured in CI, where the cmake consumer reported `options::cmake = "C:/Program Files/CMake/bin/cmake" (not found)` on a runner carrying cmake, and then failed to compile because the subproject it configures never built. A stated path now follows the same suffix rule as a discovered one, which `program_in` has always applied, and a case states it on a Linux row because the rule is the same on every host.
Inserting the toolchain feature put it between that comment and the feature it describes.
…o inline the iterator here
The range-for walked a `std::string` through `__gnu_cxx::__normal_iterator`, whose
`operator*` and `operator++` are `always_inline` and reach this module from the
`std` module. Once this file imported one module more, gcc 16.1.0 refused both:
error: inlining failed in call to 'always_inline'
'constexpr __gnu_cxx::__normal_iterator<...>::operator*() const'
note: called from here for (char c : message)
in `warn@mcpp.rules.qt` alone, while every other range-for in the collection
compiled. Measured on this machine with `MCPP_TOOLCHAIN=gcc@16.1.0`: two errors
before, none after, and the qt consumer builds. An index touches no iterator, so
it does not depend on what a BMI carries across a module boundary -- the same
hazard class the engine records for a clang 20.1.7 crash, where the error names a
file the change never touched.
…ection's floor The pin existed because the floor named a release that did not exist yet; it does now, so the ordinary path runs the engine this collection states and no dispatch is needed to validate it.
Both sides added a `plugin-logic` case at the same point, and the conflict boundary fell inside one of them. The resolution keeps every case from both: the six tool cases of this branch and the qrc case from #42, whose fix in `qrc_files` merged untouched. 29 of 29 cases pass with the released engine.
…mes from The page told the reader to comment the declaration out, which was the only way out while every declared payload was provisioned. An override states the same thing without removing the declaration, so a machine without that SDK still gets the ecosystem's.
…d users The mechanism was documented from each side separately -- the engine's keys in mcpp docs/23, the module's API in plugin-development.md, each member's options in its own page -- and nothing stated the two halves that have to meet: a `tool::choice` in a build program avoids a download only if the plugin declared that payload `provision = "on-request"`, because an eager payload is provisioned before any build program runs. That pairing is the question both audiences actually ask, and it was the original symptom behind mcpp#755. The page is written as scenarios: six for a project (write nothing, name the machine's tool, state it in the manifest, replace it for one job, share it across projects, build offline and audit), seven for an author (the three pieces, the pair, a branch-only payload, a tree rather than a program, a legacy variable, a member that only looks, and how to prove it without installing anything), and the design decisions behind them. It closes with a table of every official member that drives a tool, how it declares it, and why -- so the eager ones are not read as oversights. Figures are the lab's measurements, including that the saving is the one-time provisioning cost rather than a per-build one.
…re being written down The page had the three spellings as one trailing comment, which is not an account of them. Section 3.2 now gives each as code on `deps-vcpkg` -- a program, a tree, PATH, and the `vcpkg_root` spelling kept since 0.18.1 -- plus the relative-path rule, deciding from the environment inside the build program, and the fact that a stated choice which fails is refused rather than replaced. Compiling the example as a real build program found a defect in it: a feature makes a module available, not visible, so `tool::root` needs `import mcpp.plugins.tool;` while a plain string assignment does not. The page states that, with the compiler's own words. The refusal text it quotes was copied from a run.
It was a subsection beside "state it in the manifest" and "replace it for one CI job", so a reader had to recognise which of the six scenarios was theirs. It is now chapter 3, in the order the question is actually asked: the four spellings, where a relative path points, deciding inside the build program, what happens when a stated choice fails, why this avoids the download at all, which option each member reads and what it looks for under a root, and how to confirm nothing was downloaded. The former chapter 3 becomes chapter 4, the other ways. The per-member table is read from each spec: the option name, the program names, the directories searched under a root, and the variable the member still reads. Three members name a root rather than a program, which the table says so that a reader does not pass them a path to an executable.
speak-agent
marked this pull request as ready for review
October 1, 2026 20:41
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.
Summary
A member that runs a program answers one question before it can plan anything: which program. Each member answered it its own way — an option here, a variable there, a silent
PATHfallback in two members and a refusal ofPATHin two others — and a tool named inbuild.mcppdid not stop the declared payload from being downloaded. On the engine of mcpp-community/mcpp#755 this release answers it once, for every member.mcpp.plugins.tool(inplugins-core)One order, written once:
o.cmake = "/usr/bin/cmake",tool::root(dir),tool::on_path();MCPP_SLANGC,MCPP_GLSLC,MCPP_GLSLANG);[xlings.overrides],MCPP_XLINGS_OVERRIDE_<NS>_<NAME>,config.toml;provision = "on-request".A choice never asks for the payload, which is what makes "the build program names its own tool" mean "the payload is not downloaded". Every answer is recorded with
mcpp::decision, so a build reports the source andmcpp why tool <name>can answer, anddescribe_missingis the one refusal text that lists every way to name the tool.An option field is a
tool::choice, which a string constructs:o.cmake = "/usr/bin/cmake"keeps working, and thebuild.mcppline that wrote it is what the build reports.Members
deps-cmake,deps-archive,deps-vcpkg,rules-spirv,rules-slang,rules-sycl,dist-appimage,dist-wixandrules-qtresolve their tools through the resolver.rules-cuda,rules-hip,rules-ascendcanddist-apkgain the options they had none of:toolkit, and fordist-apkthe six payloads it runs.deps-cmake,deps-archive,deps-vcpkg,dist-appimageanddist-apk'sbundletooldeclare their payloadsprovision = "on-request": an ordinary Linux build no longer installsappimagetool, an--format apkbuild no longer installsbundletool, and a project that names its own cmake downloads none.rules-spirvandrules-slangkeep theirPATHfallback behind a warning that names the choice to write instead, until 2027-04-01 (SPEC-007 R6.2).mcpp.plugins.toolchain(featureplugins-toolchain)The builders a root build program states the build toolchain with, in the toolchain phase of a project whose
[toolchain]saysconfigure = "build.mcpp":layout,prefixed,compose,from_env_script(a vendor SDK'senvironment-setup-*),with_launcher,with_sysroot,with_tool,managed,env,configure(fn)anduse(d).Engine floor
2026.10.1.3, the release whose protocol 15 states where each tool and payload comes from. The per-member floors in the README and the member documents are raised for the members that use it.
Closes #41
Test plan
tests/plugin-logic: 27 cases pass, five of them new for the resolver — a choice asks for no payload, an override answers, a pending payload is requested and plans nothing, the payload answers last, and the refusal lists every way to name the tool.mcpp.plugins.testinggainedxpkg_source,xpkg_programandphaseso a case can state them.11 consumer fixtures build against the engine branch;
all-rules-compilecompiles every rule and dist member; a default build's output is unchanged.New CI criterion
tool-sourceson all three platforms: with nothing named the member asks for the payload; with the build program naming a cmake the payload is not asked for and the source is reported; an override says the same and is reported ascustom;--managed-onlyrefuses both. It readsresolution.json, so it holds on a runner that already has the payload installed.A defect this found in the resolver: it used the compile-time executable suffix, so a case stating a Windows row on Linux could not find
vcpkg.exe. Both spellings are tried on every host now, asrules-spirvhas always done.Two defects the other platforms found, each with a measurement:
mcpp.plugins.testingleft the real build'sMCPP_XPKG_*in the environment. These cases run inside a build program, which the engine gives one_DIR,_PROGRAMand_SOURCEper payload its package declares —pendingamong them, for a payload declaredprovision = "on-request"that nothing asked for. Eight vcpkg cases therefore asked forxim:vcpkgand planned nothing wherever the payload was not installed: 19 of 27 on macOS arm64 and on Windows, 27 of 27 on a machine that had it. Measured both ways on one binary with the inherited value present: 19 of 27 before, 27 of 27 after. The keys are enumerated, not listed, because they are derived from package names.environto a main program only, so the first fix failed to compile there;_NSGetEnviron()is used instead.The spirv fixture's step asserted on a status line while capturing stdout alone. mcpp writes narration to standard error from 2026.10.1.1, so the log was empty and the step reported that the rule had not run while it had.
An ecosystem reading this release produces:
xim:vcpkgno longer appears in the macOS or Windowsrulesjob logs, wheremainprovisionsxim:vcpkg@>=2026.7.27. The saving is real, and it is also what exposed the first defect above — installing one payload less makes a test environment's assumption visible.Order
This PR needs mcpp 2026.10.1.3, which is mcpp-community/mcpp#758. CI here is pinned to that release and goes green once it is published; until then it is validated by dispatching this workflow with
mcpp_source_ref=feat/build-sources, which builds the engine from that branch.