Skip to content

Releases: mcpp-community/mcpp

v2026.10.3.1

Choose a tag to compare

@github-actions github-actions released this 02 Oct 20:42

This release is identical to 2026.10.2.1 in code; the bump exists to publish a
fresh GitHub Release and a fresh index entry, because the 2026.10.2.1 release
ran while the canary runners on ubuntu-24.04 were unavailable and the publish
itself could not complete. It carries no behavioural change and is wire-
and-cache-compatible
with 2026.10.2.1: a record written by 2026.10.2.1 is
still admitted by 2026.10.3.1, and a record written by 2026.10.3.1 is admitted
by 2026.10.2.1 (the engine field exists in both; its value 2026.10.x matches
its own self).

CI

  • Bootstrap fast path. bootstrap-mcpp skips the xlings tarball fetch,
    extract and self install when the restored cache already holds the pinned
    xlings version. The check is the one case the guard would otherwise miss:
    a stale xlings cache from before the pin was bumped, or a binary that no
    longer runs because its dynamic loader is gone. On Linux it shaved the
    Run ./.github/actions/bootstrap-mcpp step from ~5 s to ~2 s on the
    bootstrap-mcpp invocation per job, summed over ~30 jobs per run; on Windows
    from ~68 s to ~30–60 s. The fast path addresses xlings by $XL_BIN_PATH
    rather than by the bare name, because Git Bash re-derives PATH from the
    Windows environment on every child shell and drops the mixed-separator
    entry the export PATH writes; xlings self install would write it back
    via [Environment]::SetEnvironmentVariable, but the fast path skips that
    step.
  • Windows e2e MinGW prewarm guard. Each ci-windows-e2e shard now
    probes the two locations run_all.sh looks at for g++.exe before
    invoking mcpp toolchain install mingw 16.1.0, so a sandbox restored
    from the build job's prewarm does not pay the install call again.
    Measured 68 s → 45 s on shard 1/3; the other two shards were already
    cheaper. The install is still run when the probe finds no payload, so
    a missing-prewarm shard is unchanged.

Operator-facing note (transient)

  • release.yml carries a skip_canaries workflow_dispatch input,
    documented in the workflow, kept on the main branch during the
    ubuntu-24.04 runner outage of 2026-10-02. The canaries design (WS10) is
    intact: the input is if: ${{ !inputs.skip_canaries }} on the
    canaries job, and the gate is otherwise unchanged. This release's
    GitHub Release was created via the input. The input is removed by the
    commit that follows the next successful canary run.

v2026.10.1.3

Choose a tag to compare

@github-actions github-actions released this 01 Oct 18:51
4d81d06

This release gives every tool a build uses a source that can be declared,
decided by a build program, and read back (mcpp#755;
.agents/docs/2026-10-01-tool-and-toolchain-sources-design.md). A project that
writes none of the new keys builds exactly as before, and its output is
unchanged.

Added

  • [xlings.overrides] states where a declared payload comes from, in the
    root manifest (also under [target.'cfg(..)']), as
    MCPP_XLINGS_OVERRIDE_<NS>_<NAME>, or in ~/.mcpp/config.toml. An
    overridden payload is not provisioned and does not reach the offline gate;
    mcpp::xpkg_dir answers the root it implies, and the new
    mcpp::xpkg_program and mcpp::xpkg_source answer the program it named and
    override. A stated version is checked against every requirement a package
    of the graph made. A dependency that writes the table is refused: which
    payloads a package needs is its own statement, where they come from is the
    project's.
  • provision = "on-request" installs a payload when a build program asks for
    it
    , with mcpp::xpkg_request. Every request of one invocation is installed
    together and only the programs that asked run again, so a build whose program
    names its own tool downloads nothing. mcpp emit build-database installs
    nothing and records MCPP_BUILD_DATABASE_PAYLOAD_DEFERRED.
  • A toolchain named by path: [toolchain] <key> = { path = "<dir>", prefix, sysroot, family, launcher, tools }, or MCPP_TOOLCHAIN=path:<dir>. mcpp
    probes the drivers in the tree, identifies them, drives them with its own
    link model, and writes nothing into the tree. The driver and each stated tool
    enter the fingerprint by content, and a build records them beside its output,
    so the fast paths decline once one of them changed.
  • [toolchain] bootstrap names the toolchain that compiles and runs build
    programs when it should not be the one building the project.
  • [toolchain] <key> = { configure = "build.mcpp" } hands the build
    toolchain to the root build program: it runs once in a toolchain phase, where
    mcpp::phase() is "toolchain", and states the toolchain with
    mcpp::toolchain(key, value). That phase may state nothing else.
  • A build reports its sources. A source that is not the ecosystem's gets a
    line of its own (Using … [custom · mcpp.toml:22], Bootstrap …), the
    Finished line summarises them, and the record is written to
    resolution.json. mcpp why sources, mcpp why tool <name> and
    mcpp why payload <ns:name> report it, including as mcpp.why.sources under
    --format json.
  • --managed-only / MCPP_MANAGED_ONLY=1 refuses a build whose toolchain,
    payload or plugin tool came from anywhere but the ecosystem, naming each.
  • Protocol 15 for build programs: xpkg_source, xpkg_program,
    xpkg_request, xpkg_pending, phase, decision and toolchain.

Changed

  • mcpp why toolchain states the source of the toolchain and the origin the
    resolution recorded, in place of a sentence listing every way one can be
    chosen.

Fixed

  • A build program's compile command goes through a response file when it
    outgrows the channel it travels.
    The command carries one
    -fmodule-file=<name>=<path> per host module the program imports, with
    absolute paths, and on Windows it reaches a shell that tolerates 8191 bytes: a
    program importing fifteen modules reported only The command line is too long., naming neither the length nor the cause. The file is written in the
    grammar its driver reads -- single quotes for clang and GCC, which treat a
    backslash as an escape, Windows quoting for cl and clang-cl -- and stays beside
    the program for a failed compile to show.
  • A manifest key this engine does not know says which engine the package
    needs.
    A package written for a newer mcpp was refused with unknown key '<key>' and nothing about the version, because the engine floor is checked on
    the document that very parse failed to produce. The refusal now names the
    floor, this engine and the upgrade, which is what a reader meets first after a
    plugin collection raises it.
  • which() resolves a name that is also a shell builtin. command -v true
    prints true, not a path, so a bare-name payload override of such a name was
    refused as not found on a machine carrying /usr/bin/true.

v2026.10.1.2

Choose a tag to compare

@github-actions github-actions released this 01 Oct 05:43
23c9590

This release implements the design for a pack's build and a compile that does
not depend on the member selection
(.agents/docs/2026-10-01-pack-drive-and-selection-independent-compile-design.md),
and resolves mcpp#751 and mcpp#753. A project that imports a module of a
dependency, and every project on an ELF target, is compiled once more after the
upgrade, because its commands change (both under Changed).

Fixed

  • A unit is compiled by the same command in every selection of a
    workspace.
    The selections of one configuration share a build directory,
    and three facts about the whole graph reached the commands of members they
    did not concern, so each switch between --workspace and -p recompiled
    them (mcpp#751; GalTranslPP 3.1.3 recompiled its core for 3m22s):
    • a module name that two members provide moved every BMI of that name below
      its provider's directory, and told the importers so, only when the graph
      held both providers;
    • a file that a member lists from outside its own directory was owned by the
      workspace's virtual root, whose object census depended on how many members
      listed it;
    • a member that builds a shared library put -fPIC on every unit of the
      graph.
      A command now depends on the unit's package, the packages it reaches, the
      features the selection activates for it and the declarations a selected
      member holds as the root, and on nothing else in the graph. mcpp build --workspace followed by mcpp build -p <member> compiles nothing, and so
      does any alternation of selections (e2e 872).
  • mcpp pack states the build it performs. Its build wrote no package
    line, no status row and no Finished, so a release job whose pack
    recompiled mcpp showed Planning for six minutes (mcpp#753). A pack is now
    stated as mcpp build states one, with Finished before the first Packing
    line and one Finished for a pack over several configurations; the second
    pass of a dispatched --format is stated in the same way (e2e 871).
  • [build] jobs bounds every command that compiles. mcpp test and
    mcpp pack ran ninja's default number of jobs whatever [build] jobs,
    --jobs or MCPP_JOBS said, which on a machine with little memory exceeded
    the bound the key exists to enforce. The backend reads the job count from
    the plan, and the reclaim of stale bmi_schedule = "on" tokens, which ran
    only under mcpp build, runs before the first drive of every build
    directory (e2e 871).
  • A pack fills the global dependency cache with the dependencies its
    build compiled, as mcpp build and mcpp test do.

Changed

  • Every package's BMIs lie below the package's directory, except the root
    package's.
    The rule is the one object files have followed since mcpp#233:
    gcm.cache/<package>/<module>.gcm (pcm.cache with clang). A unit that
    imports a module of another package reads one module map of the modules it
    reaches through its imports: a mapper file with GCC, and an argument file
    @<build directory>/modmap/<package>-<hash>.modmap of -fmodule-file= or
    /reference lines with clang or MSVC. A project whose modules are all its
    own is laid out, and every command spelled, as before. A package whose
    module name another package of the graph provides is served from the global
    dependency cache again; 2026.9.30.2 compiled it in the project.
  • Every unit of an ELF target that is not freestanding is compiled with
    -fPIC,
    whether or not the graph links a shared library, as rustc's
    default relocation model does on these targets: an object compiled once
    serves a program and a shared object. Mach-O compilers produce
    position-independent code by default, PE has no such flag, and nothing
    changes on those targets or on freestanding ones. The global dependency
    cache keys these entries by the flag, so each is compiled once more and
    then served again.

v2026.10.1.1

Choose a tag to compare

@github-actions github-actions released this 30 Sep 23:49
80d1fde

This release implements the plan for member selection, build programs prepared
once, a pack over several members, and the output streams
(.agents/docs/2026-09-30-member-selection-and-build-program-cost-plan.md),
and resolves mcpp#748, mcpp#749 and mcpp#750. Two changes are observable by
scripts: narration moves to standard error, and a mcpp run whose build failed
exits 101 (both under Changed).

Changed

  • Narration is written to standard error, and a command's result to
    standard output.
    The narration is what says what a command is doing: the
    status lines that begin with a verb (Resolving, Compiling, Finished,
    Running, Packed, Downloading and the others), the progress bars, the
    status row of a terminal, and the blank line after Running. The result is
    what the command was asked to produce: the program's output under mcpp run,
    a document or a listing, and for mcpp test each verdict, the test result
    line and the workspace result line. Releases up to 2026.9.30.2 wrote the
    narration to standard output. A script that read a status line from standard
    output changes: mcpp build | tee log becomes mcpp build 2>&1 | tee log. A
    CI log, which captures both streams, is unaffected. The live status row is
    drawn on standard error, whether or not standard output is a terminal
    (e2e 864 to 866).
  • A mcpp run whose planning or build failed exits 101, as cargo run
    does. It exited 1 or 2, which a program that returns 1 also does. The
    program's own status still passes through, a refused start keeps 125 to 127,
    and build, test and pack keep their statuses. A program or runner that
    itself returns 101 reads as a failed build (e2e 863).
  • mcpp test over several members plans once per configuration. The
    members of one configuration are one plan with each member's tests, so a
    package they share is compiled once with the union of their features and its
    build program runs once; then each member's tests run in [workspace] members order with that member's runtime directories, continuing past a
    member that fails. The JSON stream adds a group_build record before a
    group's first test and a build_group field in each member's summary, whose
    build_ms is the group's build time; each test record adds build_ms, the
    build time of its own binary. --workspace-timeout bounds the runs and
    --build-timeout the build (e2e 854 to 856).
  • A workspace's build programs share what they import and compile at the
    same time (mcpp#748).
    The bundled mcpp module and each host module are
    compiled once per key: the host compiler, the standard and every flag of the
    compile, the imported BMIs and the interface's content. The engine's module
    and the host modules of index packages are kept in the global cache, where
    mcpp cache gc collects them; a host module of a path or git dependency or
    of a workspace member is kept under the workspace's
    target/.build-mcpp/host-modules/. The members' programs are compiled
    together, up to the job count, and run in the order they always ran, so the
    plan is the one a serial build writes. On a fixture of #748's shape the whole
    build takes 0.33 to 0.48 s instead of 0.89 to 1.22 s (e2e 857 to 861).

Added

  • A repeated -p selects every member it names (mcpp#750). build,
    test, pack and mcpp emit build-database read one selection: a set of
    members in [workspace] members order, whatever the order of the -p
    values. A value that names no member is refused before planning, and the
    refusal lists the members. mcpp run executes one program and refuses a
    second -p, naming both; --workspace together with -p is refused
    (e2e 852, 853).
  • --exclude <member> removes members from a whole-workspace selection
    (--workspace, or a virtual root without -p), on build, test, pack
    and mcpp emit build-database. It is refused with -p, for a name that
    matches no member, and when it leaves no member (e2e 853).
  • mcpp pack over several members (mcpp#749). --workspace, a repeated
    -p and --exclude pack several members with one plan per configuration and
    one build; each member is staged in a tree of its own, its build program sees
    its own pack_stage_dir(), and a dispatched format runs one second pass per
    configuration. A positional target, several --target values, an --output
    that is a file, a member no provider of the requested format acts for, and
    two members that would write one destination are refused before anything is
    compiled. A member whose distribution step fails is reported by name and the
    others are packed; the members are reported in [workspace] members order,
    and ${mcpp.target_file:<name>} names the target of the member an action is
    for. mcpp pack -p <member> keeps its meaning. The JSON envelope adds
    data.stages and a member field per artifact when several members are
    packed (e2e 867 to 870).

Fixed

  • A repeated -p no longer keeps only its last value (mcpp#750).
    mcpp test -p a -p b tested b alone and exited 0.
  • Starting a child process is safe for concurrent callers. A pipe created
    by one thread could be inherited by a child another thread started, and the
    first thread's reader then waited for end of file until the unrelated child
    exited. Pipes are created close-on-exec on Linux; on macOS and Windows the
    creation of a pipe, the start of the child and the parent's close of the
    write end form one critical section. The registry of children for signal
    forwarding is serialised and holds 256 entries.
  • mcpp run -q writes exactly the program's standard output. It began
    with an empty line (e2e 862).

v2026.9.30.2

Choose a tag to compare

@github-actions github-actions released this 30 Sep 11:08
8e00a18

This release answers five reports on 2026.9.30.1 while building xlings
(.agents/docs/2026-09-30-build-wall-time-progress-count-and-hang-plan.md): a
build that did not exit after its status row stopped, a status row whose count
was mostly bookkeeping, planning that preceded every edit's compile by three
seconds, mcpp#744, and mcpp#732. On a clean build of xlings the command starts
ninja at 1.3 s instead of 4.5 s; an edit of one source builds in 8.0 s instead
of 10.5 s; a build with nothing to do is unchanged at 0.05 s.

Fixed

  • A build no longer hangs after ninja. The stack animation could spawn
    pieces onto cells it already held once its stack reached the right edge
    short of its target, and its loop then never ended while the status row held
    the terminal's line lock; the build joined the row's thread and waited for
    ever (about 1% of interactive builds). Every loop of the animation now grows
    the stack or ends. A property test drives every animation over 2000 seeds and
    every game over 500 under a watchdog; it fails on the previous animation.
  • The status row counts the work of the build. A clean build of xlings
    counted 1195 steps, of which 503 placed files the global cache serves and 460
    were dependency scans, and read 967/1195 when its first compile began. The
    cache pass is reported by its Cached lines and not counted; the scans that
    wait on no action run first, shown as Scanning f/t; and Building f/t
    counts the compiles, links, archives and actions: 0/232 at the first compile
    of the same build. The fast path runs the same passes (e2e 842, 843).
  • A vendored xlings is replaced from the newest source (mcpp#744).
    MCPP_VENDORED_XLINGS when set, otherwise the newer of the xlings released
    with mcpp and the xlings on PATH; the note that no newer source is
    available was false when a newer xlings was on PATH. Updating and Note
    are each stated once per process (e2e 846).
  • A module name is unique within one program, not within one build
    (mcpp#732).
    An artifacts program, or a workspace member that shares no
    program with another, may provide a module of the same name as another
    program of the build. An import is resolved in the importing package's
    closure; two BMIs of one name lie below their packages' directories, and each
    compile is told which one a name means (a module map for GCC,
    -fmodule-file= for Clang, /reference for MSVC). Two providers that one
    program links are refused, naming that program's package, and one file
    reached as two packages is recognised whatever its spelling. When every name
    has one provider, build.ninja and compile_commands.json are byte-identical
    to 2026.9.30.1's (e2e 847, 848).
  • A BMI served from the global cache waits for the modules it imports that
    the build compiles.
    A module that a package's build program generates lies
    below the consumer's target directory and is compiled in every build, also
    when the rest of the package is staged from the cache (xpkg's lua_stdlib,
    imported by its cached executor). Nothing ordered a consumer of the staged
    BMI after that compile, so a fresh build could compile the consumer first and
    fail with failed to read compiled module. The stage edge of such a BMI now
    waits for those BMIs (e2e 849).
  • A selected workspace member declares as the root. Since 2026.9.29.1 a
    workspace is planned from a virtual root that declares only its members, and
    the rules that grant the root's own declarations a privilege read that
    root's edges alone. A rooted workspace's own package, and a member selected
    with -p, again hold the position each held when planned as its own root:
    its path or git override of a dependency that another package requests
    by another kind wins (the release canary on mcpp-language-server, whose root
    package overrides openkal-linux by path, was refused with "Pick one");
    linkage on its dependency edges is honoured; the identity its declarations
    adopt is written back for the lock names; and its registry dependencies are
    considered for the index refresh. Two selected members that disagree about
    one dependency's checkout (its kind or its reference) or its link form are
    refused, naming both (e2e 850).
  • The clang MSVC row states the compiler version (mcpp#746). The version
    of the toolset's cl.exe, read from its file version as clang reads it, is
    passed as -fms-compatibility-version on every command and enters every
    key. Without it the driver chose the version itself and wrote it into each
    BMI, and a std module compiled under one runner image's cl.exe was served
    to another image whose cl.exe differed under the same toolset directory
    name (std.pcm was compiled for ... msvc19.51.36260 ... msvc19.51.36257).
    A clang build for *-windows-msvc rebuilds once after the upgrade.

Changed

  • Planning walks each package tree once. A source pattern with an empty
    literal prefix walked the whole package tree: the 127 patterns of
    compat.libarchive, expanded about three times per plan, opened its 35
    directories 13,406 times. A walk is now kept per tree for the command and
    revalidated by its directories' modification times. A planned edit of one
    xlings source opens 1,749 package directories instead of 30,372, and its
    scan phase takes 43 ms instead of 0.91 s.
  • The version of the vendored xlings is asked once. It is kept per
    process, and under the home keyed by the binary's path, size and
    modification time, so a command that loads its configuration no longer runs
    xlings --version (0.35 s) when the binary has not changed.
  • Planning states where its time goes. Each phase of planning, and each
    step of its last phase, logs its duration under build/stage in the log
    file, which --verbose or MCPP_LOG_LEVEL=info enables. The backend's own
    stage lines are recorded in the file under the same condition.

v2026.9.30.1

Choose a tag to compare

@github-actions github-actions released this 30 Sep 00:28
cc24173

This release revises what a build prints, from a report on mcpp build in the
xlings repository with 2026.9.29.5
(.agents/docs/2026-09-30-build-output-refinement-design.md): a package is
named when it does work, the live display is one status row drawn in one
write, and a bare key of a path dependency is no longer reported. It also
records one lock entry per identity again.

Added

  • A screen in the status row. Beside the phase, 24 braille cells play one
    of four animations, chosen per command: a chomper whose position is the
    progress, a snake that eats a food in the colour of each package that
    starts, Tetris on its side whose stack is the progress, and an emitter whose
    ions build the progress bar. Each takes its tempo from the build: slow
    while mcpp works, faster as steps finish, still while the build waits.
    MCPP_PROGRESS names one, or plain (the row without the screen) or off
    (no live row) (e2e 843; mcpp.ui.dots_screen).
  • --play-game[=NAME] on build, run and test plays snake, stack
    or runner on the screen while the build runs, steered from the keyboard at
    a speed of its own. The best round is stated after Finished. Keys are
    read without echo; Ctrl-C still stops the build, and the terminal's mode is
    restored when the build ends or is interrupted. Where standard input or
    output is not a terminal, or mcpp runs as a background job, one line says
    why and the build proceeds (e2e 845). An arrow with a modifier is read as
    the arrow, and a sequence for any other key is skipped whole
    (TerminalKeys unit tests).
  • last N running. Once ninja has no step left to start (its %u
    reaches 0), the status row states how many steps remain, all of them
    running.

Fixed

  • The live display no longer flickers. A frame reached the terminal in two
    or three writes (stdout is line-buffered, and a line above the region was
    erase, text, redraw), and every frame erased the region before drawing it:
    in one build of xlings, 184 of 202 frames left in two parts. A frame now
    leaves in one write that overwrites the rows in place, with autowrap off for
    the status row, so a terminal that draws East Asian ambiguous characters
    wide clips the row instead of wrapping it. The row is first drawn half a
    second into the command, so the first lines of output no longer push it
    down (e2e 843).
  • A dependency the global cache serves is named when its units are
    placed.
    Its staging steps ran in a pass of their own that nothing read,
    so such a package never completed and the folded dependency line waited
    for ninja to exit (27 s late in a first build of xlings), after the root's
    line (e2e 842).
  • The phase returns to planning after the build programs. The status
    line read Running build programs for as long as planning continued.
  • A planned build leaves a committed mcpp.lock unchanged. Since
    2026.9.29.1 a planned build of a workspace root wrote the dependencies of a
    [dependencies.<ns>] table under a second spelling with another hash,
    beside the entries already there; the lock now holds one entry per
    identity, spelled as 2026.9.28.3 spelled it, and a lock that holds two is
    reduced to one by the next planned build (e2e 844).

Behaviour changes

  • A package is named once, when it does work: Compiling <package> when
    the first of its steps that is not a dependency scan finishes, or when its
    first check or prepare action starts, and Cached <package> (N units)
    when the global cache places its units. The line states no outcome and does
    not change; nothing is folded, and a package with nothing to do has no line
    (--verbose names it Fresh, and states each package's steps and span as
    Compiled) (e2e 842).
  • A package inside the project is named by its short name, version and
    directory
    (platform v0.1.0 (modules/platform)); any other by its
    identity, with (index <name>), (git <kind> <ref>) or its relative
    directory. On a terminal the name's colour states the source: the official
    index cyan, another index magenta, git blue, the project's own packages the
    default colour.
  • A build program has a line when it runs or fails; a reused program has
    one under --verbose. The folded build.mcpp N dependencies line is gone.
  • A failure names its package: error: build failed in <package>.
  • Finished states how the time was spent from one minute, where it did
    from ten seconds; below a minute it states the profile and the total alone.
    The longest step, when it took at least a quarter of the build, is named
    by its source file for a compile (longest slow: src/main.cpp 1m08s), not
    by its object file. A build with nothing to do states the profile's
    descriptor as a full build does (Finished dev [unoptimized + debuginfo] in 0.02s): the step record's header carries it for the fast path.
  • The status row is aligned with the verbs ( Building 612/707 · 0:35),
    its phases are Planning, Running, Building, Stopping and Checking,
    and no blank row separates it from the output.
  • A bare key of a path or git dependency states only the short name. Its
    adoption of the namespace the manifest declares is no longer reported; a
    key that states a namespace the manifest contradicts is reported once per
    consumer manifest, named by its path, with the TOML that states the
    declared identity (e2e 679, 713; package-identity §4.2).
  • The bundled xlings is 2026.9.30.1.

v2026.9.29.5

Choose a tag to compare

@github-actions github-actions released this 29 Sep 16:47
ff04535

This release completes the workspace build graph in the commands around the
build, from the validation project's post-release run of 2026.9.29.4: build
programs are reused across selections and run dependencies first, the build
database and --configure-only plan by configuration, and the output names
what it reports. A build now reports each step when its outcome is known, and
shows what runs while it runs (.agents/docs/2026-09-29-build-progress-display-design.md).

Added

  • A build reports each step once, with its outcome. A package's line is
    written when every step the graph assigns to it has run (done with the
    span of its steps, read from ninja's log), when the global cache supplied it
    (cached N units), when a step of it failed (failed), or when the build
    ends. A package with nothing to do has no line. The packages the command was
    asked to build are listed; their dependencies are folded into one line, and
    a dependency that fails is named. --verbose lists every package and prints
    each step as ninja reports it (e2e 842).
  • A status line states the build. On a terminal the steps still running
    and one status line, Building 612/1203 · 14:32 · gpp.gui: vcpkg install 6:10, are drawn below the output and updated in place; the status line
    names the longest-running check or prepare action, which the engine's
    action wrapper reports when it starts. In a log (CI, a pipe) only final lines
    are written, and the status line is written when the log has been silent for
    a minute (e2e 842, 843).
  • Finished states the whole command's time, and for a command of ten
    seconds or more how it was spent (plan, programs, build) and the step
    that took at least a quarter of the build.

Fixed

  • A failed step is reported when it fails. Its diagnostics were printed
    after ninja exited, that is, after every step still running had finished:
    a compile error beside a twenty-minute vcpkg install appeared twenty minutes
    late (e2e 842).

  • macOS and Windows terminals are terminals. Terminal detection was
    compiled only under __unix__, which Apple's compilers do not define, so
    the download bar and colours were drawn on Linux alone. A Windows console
    now receives mcpp's lines as UTF-16, so ·, → and non-ASCII paths appear
    as written whatever its code page.

  • A member's build program is reused whichever members a command selects.
    Its graph document listed every requester in the plan, the virtual root
    included, so the program's re-run key followed the selection: -p, mcpp pack and mcpp emit build-database reran the programs a --workspace
    build had run (7 to 15 s each in the validation project). A program's
    document now lists the requests made inside its own closure (e2e 839).

  • A member's build program runs after those of the members it depends on.
    They ran in discovery order, so a member's program could run before its
    dependency's had applied its directives (e2e 839).

  • mcpp emit build-database and mcpp build --configure-only plan a
    workspace by configuration, as the build does.
    They planned each member
    separately, so a package two members use was described once per member,
    each time with other arguments (the validation project's core library three
    times). A member that is a program is described as one, and its tests as
    tests. A configuration whose plan fails is planned member by member, so a
    member's failure still affects that member only (e2e 840; SPEC-005 v1.6).

  • A command that plans several configurations writes the root
    compile_commands.json once
    , as the union of their databases. Each
    configuration replaced it, and under mcpp build --workspace, whose
    configurations build at the same time, the file was the last one's (e2e 840).

Behaviour changes

  • A build program has one line, which names its package and states its
    outcome
    : build.mcpp <package> ran <time>, cached or failed, in
    place of build.mcpp compiling <package> and running <package>, and of
    up to date <package> (cached) (e2e 839, 842). The programs of the
    requested packages are listed, and those of their dependencies are folded
    into one line.
  • Compiling <package> is written when the package's steps have run, with
    their outcome, rather than for every direct dependency before ninja starts;
    the per-dependency Cached <package> (N units) line is --verbose output,
    as cached N units (e2e 842).
  • A selected member is announced by its directory in a --workspace
    build, also where another member depends on it (e2e 839).
  • mcpp pack summarises many outputs. A format that reports more than
    eight outputs is reported by the entry each lies in below their common
    directory, with a count; --verbose names every output, and
    --message-format json lists every one as before (e2e 841).
  • Build database set names (SPEC-005 v1.6). A set is named by its package;
    a document of several configurations prefixes each name with the
    configuration's build directory name, instead of <member>/.

v2026.9.29.4

Choose a tag to compare

@github-actions github-actions released this 29 Sep 08:42
832f623

This release links a program that a workspace member ships through
artifacts with the link line of its own package's closure, and expands
${mcpp.bin_dir} to where the declaring member's binaries land. The validation
project's post-release build of 2026.9.29.3 found the first, and its
cross-verification against this release's pull request the second.

Fixed

  • A program shipped through artifacts links with its own closure's line in
    a workspace plan.
    It was linked with the plan's line, which pools the
    dependencies' link flags and not a member's; a library that the program's
    package states through its build program (mcpp::link_lib) was therefore
    missing, and the link failed with undefined references (e2e 838). The
    program now has a link group of its own that holds its closure's line and
    runtime contract and places nothing, since the members that ship the program
    place it.
  • ${mcpp.bin_dir} in an action a workspace member's build program declares
    is that member's product directory.
    It was the plan's bin/, where a
    member's binaries are not, so an action that named a file beside the
    member's program (a .pdb, say) read or wrote the wrong place (e2e 838 A4).

v2026.9.29.3

Choose a tag to compare

@github-actions github-actions released this 29 Sep 05:04
c73725b

This release completes the runtime placement of a program that a workspace
member ships through artifacts. The validation project's post-release build
of 2026.9.29.2 found it.

Fixed

  • A program shipped through artifacts no longer waits for a runtime file
    that a workspace plan never places.
    Its link edge depended on the plan's
    own deploy set, which a workspace plan does not place (bin/ holds products
    only), so a workspace whose members declare runtime files stopped with
    "missing and no known rule to make it" (e2e 833 G9).
  • The runtime files of such a program are beside it. A member's runtime
    set includes the closure of every package whose program the member ships
    through artifacts, so the program finds its own runtime files in the
    member's product directory, as it did beside a root's program in bin/
    (e2e 833 G9).

v2026.9.29.2

Choose a tag to compare

@github-actions github-actions released this 29 Sep 02:37
b8ddb55

This release corrects what a workspace plan reads from its members. The plan's
root is a virtual root that holds the values shared by the whole graph; five
statements that a member makes about itself were read from that root in
2026.9.29.1 and were therefore empty, and a member's product directory lacked
the shared libraries that its libraries need. The mcpp-index sweep of
2026.9.29.1 found the first two.

Fixed

  • A member's own relative [indices].path is anchored at the member. It
    was resolved against the workspace root, so a member that declares its own
    index (47 members of mcpp-index do) found no package. Two members that name
    one tree with different relative paths are now one configuration (e2e 120).
  • A member whose tests import std is tested with the std module built.
    The decision read the entry files of the root's targets only, and the virtual
    root has none; a member whose only sources are its tests failed to compile
    them (e2e 836).
  • A member's [resources] and windows_code_page reach its own images.
    Each selected member's resources are compiled against the member's directory
    and include directories, under res/<member>/, and embedded into the
    member's programs and shared libraries only (e2e 837). A quoted #include
    or resource file in a script is now found through the include directories as
    the resource compiler finds it, in every build.
  • A member's product directory holds every graph-built shared library of its
    closure.
    Only the libraries a member's own units link were placed, so a
    library that another library needs (libffi under libwayland-client) was
    missing and the program did not start (e2e 835 L3).
  • [build] linkage is a value of the plan. It chooses the C runtime that
    every object is compiled against, so members that differ in it are separate
    configurations, and the plan takes it from its members.
  • A member's manifest is checked as a root's. An unknown capability under
    the reserved mcpp: prefix is refused, and a cfg() predicate mcpp cannot
    evaluate and the other schema warnings are reported, for each selected
    member (e2e 836).