The action lists have no size limit, and ${mcpp.self} names the engine (2026.9.13.1) - #629
Merged
Merged
Conversation
The bundled mcpp module held an action's six list fields in fixed arrays (8192 bytes of serialised JSON for inputs and outputs, 16384 for command) and refused a declaration that did not fit. The bound was in bytes, so a consumer's checkout depth decided whether a list of 44 resource files was accepted (HuxerUI#130 measured the margin at 45 bytes), and outputs is the one list an author cannot shorten: an output the program does not name cannot be built, and there is no depfile for outputs. The arrays were fixed because one constraint was read as two. The module must not import std, and its exported interface must name no std type; neither forbids the heap, and <cstdlib> was already in the global module fragment. The six arrays become one growable std-free buffer over realloc. The exported surface and the protocol version are unchanged, and the payload of every action that fit before is byte-identical, so the cache key is untouched. The overflow marker keeps its wire form and now means allocation failure; the engine's message says so. e2e 659 declares 200 inputs and 200 outputs and a 19200-byte command and reads the action's edge out of build.ninja; under 2026.9.12.4 the same fixture is refused.
…able copy
An action's command is an argv with no shell, so a build program had no
portable way to copy a file: cp is absent on Windows, cmd /c copy is a
shell and an 8191-character limit, and a copier carried by a package is a
host-tool sub-build for one copy. The engine is the one program present
wherever a build runs, and mcpp stage --verify content --output <dst> <src>
is the copy every stage_file edge already performs. ${mcpp.self} joins the
argv substitution family and is replaced by the engine's absolute path, as
the check wrapper already bakes it in.
mcpp stage's argument shape is a contract from here on; its help text now
states the real default (content). e2e 660 copies the linked program
through the token on every shard; under 2026.9.12.4 the token stays
literal and the edge fails.
…ecord for the four upstream asks
…dge line check_inline_command_lengths read each build line as a proxy for the command. That is right for a rule that expands $in and $out and wrong for an action rule, whose command is a literal argv and whose inputs and outputs exist only so that ninja can order and re-run it. e2e 659's first run on the Windows shard refused an action with 200 outputs and 200 inputs with the whole edge line counted as argv. The guard now measures a literal command's own text. The refusal names the edge by its first output and the count of the rest, instead of printing every output. e2e 659 declares 600 inputs and 600 outputs, so the edge line crosses the POSIX 128 KiB limit as well as Windows's 32767 and the guard is measured on every shard; its negative leg declares a 140800-byte literal command and expects the refusal to name the edge.
…arget The Windows job of this workflow was green with one sandbox lineage and red with the next on the same sources and the same runner image (PR #629: lld could not find -lntdll and its neighbours). On a Windows host openkal-windows's build program generates no import libraries, so where lld finds them is a property of the host, the sandbox or the payload, and nothing in the job said which. This step prints it.
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
Implements P1-P4 of §9.2 in
.agents/docs/2026-09-13-four-upstream-asks-from-a-ui-framework.md(mcpp repository), one release, 0.9.0.dist-apk(dist/apk.cppm), P1 --options::manifest_template. A project file, package-root-relative, rendered through six tokens substituted verbatim wherever they appear:{{application_id}},{{label}},{{activity}},{{lib_name}},{{min_sdk}},{{target_sdk}}.{{application_id}}and{{activity}}are required always;{{lib_name}}joins them at level 0. A missing required token, or an unknown{{...}}token, is refused at plan time (applies = false,reasonset, printed to the child's own stderr exactly as this member's other refusals are) naming the token -- the missing-token message also namesassets/mcpp-run.json, whichadb-runreads to start the application. The built-in default ismanifest_xml's 0.8.0 output expressed with these tokens; level 0 with no template renders byte-identical to 0.8.0's. The template is declared withmcpp::rerun_if_changed. The unknown-token check is deliberatelydist-apk's own and not proposed fordist-web(a page template may legitimately carry{{ }}for a front-end framework); the asymmetry is recorded in a comment inapk.cppm.dist-apk, P2 --options::java_sourcesbecomesstd::vector<std::string>. Onejavacover every root's.javafiles, oned8.rerun_if_changed_globis declared only for a root undermcpp::manifest_dir(); a root outside it (a dependency's own tree) is not walked by the glob fingerprint regardless, its files are alreadyjavacaction inputs, and its version is already in the build's fingerprint.dist-web(dist/web.cppm), P3. The two copy steps become argv{ "${mcpp.self}", "stage", "--verify", "content", "--output", dst, src }; the plan-timecreate_directoriesis removed (stagecreates the parent); the POSIX-only note leaves the header and the README row; the header states${mcpp.self}/mcpp stage's argument shape as the engine contract since 2026.9.13.1.dist-apkanddist-web(floor2026.9.13.1for both, one sentence each on why);mcpp.tomlandsrc/plugins.cppmto0.9.0;.github/workflows/ci.ymlMCPP_VERSIONto2026.9.13.1.Tests
tests/apk-consumeris extended (not a new, HuxerUI-specific fixture):build.mcppreads two environment variables (APK_CONSUMER_TEMPLATE,APK_CONSUMER_LEVEL1) to reach five configurations without a second project. New fixtures:manifest-template-good.xml(auses-permissionand areceiver),manifest-template-missing-appid.xml,manifest-template-bogus-token.xml, a project-local Java root (tests/apk-consumer/java) and a sibling directory outside the fixture (tests/apk-consumer-external-java) reached by an absolute pathbuild.mcppcomputes, standing in for a path dependency's own Java tree.check-apk-features.shruns criteria (a)-(e) and is wired into CI beside the existing "dist-apk produces a signed APK, level 0 and level 1" step.tests/web-consumer/check-web-idempotent.shis a new sibling ofcheck-web-plan.sh(which is unchanged and still passes) asserting a second, no-op pack copies nothing; wired in beside the existing dist-web step.Criteria and local results (engine: a build carrying this batch's engine half, reporting
2026.9.12.4but with${mcpp.self}and the unbounded action lists -- the release will be2026.9.13.1)AndroidManifest.xmlas a checked-in fixture; built this branch's code andcmp'd the twomcpp pack --format apkwithmanifest-template-good.xml;aapt2 dump xmltreeon the linkedbase.apkandroid.permission.INTERNETandorg.mcpp.apkconsumer.SampleReceiver{{application_id}}build.mcppbinary directly (see note below);mcpp packitself also refused, "no action claimed --format 'apk'"the manifest template does not use '{{application_id}}', and assets/mcpp-run.json -- which adb-run starts the application from -- is written from the same value{{bogus}}the manifest template names an unknown token '{{bogus}}' -- expected one of application_id, label, activity, lib_name, min_sdk, target_sdkmcpp pack --format apkwithAPK_CONSUMER_LEVEL1=1;dexdump -l plainon the producedclasses.dexorg.mcpp.apkconsumer.MainActivity;(project root) andorg.mcpp.apkconsumer.ExternalHelper;(external root)dist-websecond packmcpp pack --format webruns 1s apart; every staged file's mtime comparedbuild.ninja's command line confirmed as<engine path> stage --verify content --output <dst> <src>tests/web-consumer/check-web-plan.shPASS: dist-web produces a static directory, node runs it, 1-2-3tests/all-rules-compilemcpp build && mcpp runall-rules-compile okmcpp.tomlvssrc/plugins.cppm0.9.0Note on (c)/(d):
mcppdiscards a build program's captured stdout/stderr when it exits 0 (build_program.cppm: the capture is surfaced only on a non-zero exit or a timeout), and aplan_forrefusal never makesbuild.mcppexit non-zero --submit()returnstrueforapplies = falseexactly as every other refusal in this member does, which is the behavior the design record asks this change to match ("printing to stderr exactly as its other refusals do"). So the refusal TEXT itself is checked by re-invoking the already-compiledbuild.mcppbinary directly with the documentedMCPP_*build-program contract (docs/30) reconstructed from a prior, real build's resolved payload paths --check-apk-features.sh's own header explains this. The FUNCTIONAL refusal (the overall command failing) is additionally checked through the ordinarymcpp packCLI, which fails with "no action claimed --format 'apk'" in both cases.Note on the web idempotency check: the design record's
ninja -nleg was not usable as written -- this repository's.ninja_logis written by mcpp's own internal ninja, and the systemninja(a different version) on this machine reads that log, decides it is stale, and reports every edge as pending regardless of whether anything reran. The design record's alternative (unchanged mtimes) was used instead and is whatcheck-web-idempotent.shasserts.Not verified
adb-run) -- this sandbox has neither an emulator nor an attached device, consistent with the existing CI step's own note.mcpp2026.9.13.1 does not exist yet, so this PR's CI will be red on the fetch-the-release step until that engine release ships. Do not lower the pin to make CI green.Design record:
.agents/docs/2026-09-13-four-upstream-asks-from-a-ui-framework.md(mcpp repository), §3, §4, §9.2 (P1-P4).Local full suite
tests/e2e/run_all.shagainst a build of this branch before the guard fix: 353 passed, 37 skipped, 1 failed, the failure being 659 at N=600 refused by the old guard on Linux (the reverse leg of the guard fix). 659 passes against the build that carries the fix; 188, 313, 631 and 660 pass on both. The first Windows shard run of this PR (headef96d829) measured the guard defect; head88bd3136carries the fix.