Skip to content

Releases: mcpp-community/mcpp

v2026.10.31.3

Choose a tag to compare

@github-actions github-actions released this 31 Oct 02:43
ce4dd4b

A large workspace keeps its bytes once and builds each output once (mcpp#874;
design 2026-10-31, workspace space and reuse). Measured on mcppls (6 members,
61 test programs, clang): bin/ 4 914 MiB -> 1 217 MiB, the tests' links
75 s -> 32 s.

Added

  • split_debuginfo under [profile.<name>] ("unpacked" | "off",
    docs/04 §2.9). "unpacked", the default of dev and debug, leaves the
    debug information of the units compiled in the build directory beside their
    objects (-gsplit-dwarf) on an ELF target with a loader, ok images included;
    each program holds a skeleton that names it, and a test no longer carries a
    copy of its closure's debug information. An index package's units keep it in
    the object, since their objects enter the build cache. It applies with
    trim_paths off. e2e 999.
  • mcpp clean --report: the bytes of each configuration's objects, BMIs and
    products and of each .build-mcpp area.
  • mcpp cache clean --tools and host tool entries in cache list, prune,
    gc and verify.
  • Collection by last use after a build: at most once a day, the build
    cache's package, host tool and std entries unused for [cache] unused_days
    (config.toml; default 30, 0 keeps all) are removed. A use sets the time of
    the entry's record file, at most once in 12 hours (an entry's files never
    change, and noatime mounts keep no reads); a package hit no longer rewrites
    its entry.json. A build that replays its graph without planning marks the
    entries its configuration names in uses.list through the sweep after it.
  • mcpp self config --cache-unused-days <N> --keep-days <N> writes the two
    retentions into config.toml; mcpp self config shows them (docs/25).
  • A warning before a build when the file system it writes to has less than
    2 GiB free.
  • The sweep after a build: after mcpp build or mcpp test succeeds, at
    most once a day, the build directories of the project that no configuration
    used for [build] keep_days days (config.toml; default 3, 0 keeps all) are
    removed, with the generated-source and build-program directories none uses.

Changed

  • Debug information is compressed on ELF (-gz=zlib on the compile and the
    link; --compress-debug-sections=zlib on an ok image's ld.lld link): a test
    program of mcppls 96.0 -> 35.7 MiB, with no measurable cost in compile time.
    Only with a toolchain whose link reads them: an LLVM toolchain is asked by
    one real link of an empty unit through the ld.lld its driver runs
    (-print-prog-name=ld.lld) with --compress-debug-sections=zlib, a GCC by
    its driver (-gz=zlib -###), once, and the answer kept under the probe cache.
    Measured on windows-2022: the xim LLVM 23.1.3 ships no ld.lld, its driver
    runs the image's own LLVM from PATH, and that one has no zlib.
    Mach-O, PDB, MinGW, WebAssembly and freestanding targets are unchanged;
    -gz=none in a package's flags turns it off.
  • A static library holds its own package's objects (docs/12): a member's
    lib<target>.a held the objects of its whole closure (mui: 11 433 archive
    members for 2 666 objects, 5.3 GB). Programs of the build link objects and are
    unaffected; a consumer outside mcpp links each dependency's archive.
  • A configuration keeps one build directory over time (design 2026-10-31
    §8). A new fingerprint of a lineage (compiler family, target, standard
    library family, standard, configuration flags) takes over the directory the
    lineage used last, and ninja rebuilds the edges whose commands changed: an
    mcpp upgrade that leaves the flags as they were compiles no unit again, where
    it opened a full new directory and rebuilt everything (mcppls: 2.9 GB per
    configuration per upgrade). A predecessor of another compiler build is
    removed. e2e 1000.
  • mcpp clean --stale also removes .build-mcpp/out/<hash> and
    .build-mcpp/host/<group>
    that no kept configuration names in its
    uses.list.

Fixed

  • A host tool has one store key whatever the command and the member. Its
    dependency closure is read from the tool package's manifests, as its build
    resolves them; it was read from the graph the command had walked -- empty
    under mcpp test --workspace, complete under mcpp build, and holding the
    dev-dependencies of the member under test -- so the tool's path changed and
    every action that ran it ran again (69 of 99.6 compile-minutes repeated in
    mui's run). e2e 997.

  • An action that leaves its output as it was rebuilds nothing below it
    (restat = 1 on action rules). e2e 998 G.

  • A shared library staged as an equal copy is placed again by a link, and a
    build program beside its record is a link to its group's binary.

  • The build programs' graph builds its own build programs beside its group
    (<group>.<depth>), not in a copy of the host workspace under its target/.

  • mcpp clean --stale keeps a rooted workspace's own build. It removed the
    members' build directories from before the workspace was the unit of build,
    and a workspace that lists itself (members = ["."]) had its own
    configuration directory removed as one of them. e2e 1000 F.

  • A path host tool's superseded entries are removed when its tree changes.

  • e2e 998 covers these and the archive, the placement and the collection.

  • An ok image whose graph declares a prepare action builds (SPEC-013
    §4.6, mcpp#702). Every link edge waits for the phony of each package with a
    prepare action, and the ordering guard checks that it does. Under a platform
    with seal steps the link writes beneath obj/platform/ and a platform_seal
    edge publishes the artifact's name; the guard took that edge for the link and
    refused every such build as an emitter defect (mcppls-clangd's clangd.okb,
    whose libraries a prepare action builds). The emitter and the guard now name
    the link edge's output by one function. e2e 996 builds and runs such an image
    and finds the link edge waiting for the action.

v2026.10.31.2

Choose a tag to compare

@github-actions github-actions released this 30 Oct 16:39
295d21e

Added

  • mcpp test <name> --print-launch (feature test-launch, docs/50 §9):
    the one test the pattern names, built as mcpp test builds it and described
    as mcpp test would start it -- its runner, the environment the run adds, the
    arguments after -- -- in the mcpp.launch envelope mcpp run --print-launch prints, with test naming it. A debugger starts what it
    describes (an IDE debugging a test or one of its cases, design 2026-10-30
    part 2 §7.4). A pattern several tests contain resolves to the test named
    exactly; one naming several, --list, --no-run, --archive,
    --message-format json, several legs and several members are refused.

Fixed

  • The build's contract states the target side (${mcpp.contract},
    docs/30). Its words held the configuration's own flags only: a foreign build
    reading it for an ok image (<arch>-openkal) had neither the C library's and
    the C++ runtime's headers nor __OPENKAL__, and could not compile
    <stdio.h>. They now end with what every unit receives from beneath it: the
    platform's layers' include directories and flags, the engine's predefines
    (__MCPP_TARGET_<OS>__, __OPENKAL__) and the realised C environment. e2e 994
    compiles a C and a C++ unit for the target with the contract alone.
  • A package's Windows launcher builds whichever openkal-platform release the
    project selects
    (SPEC-014 §4.3). The launcher (bin/<name>.exe) is mcpp's
    program, and its mcpplibs.os pins one openkal release; it was built over the
    project's platform, so a platform whose version group names a newer openkal
    (openkal-platform 0.19.0) gave its graph two openkal releases and mcpp pack
    stopped. It is now built over the platform mcpp's openkal rows pin, the one
    its mcpplibs.os was released against. e2e 995 packs a project that selects
    openkal-platform 0.19.0 with its Windows launcher.

v2026.10.31.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 12:53
304487b

Added

  • features in mcpp --protocol-version. A client gates on a feature
    rather than on a version number: target-sets and pkg-format-list name the
    meanings 2026.10.30.1 introduced. A name is added when a client gates on it,
    and is never renamed or removed (docs/50 §4). Every envelope's mcpp field
    carries the same list beside the version, so one self env gives a client
    both, and a job's envelope says when either has changed.

  • program in mcpp self env --format json: the file that is running. A
    tool that ran mcpp through a version manager's shim in a project's directory
    reads which mcpp the shim chose for that project. docs/50 states how a tool
    runs mcpp for a project: in its root, by the name on PATH or a set path,
    with XLINGS_PROJECT_DIR removed (design 2026-10-30 part 2 §4.0).

  • One file for several systems: --pkg-format polyglot (SPEC-014 §5.3,
    #849). An ok image's single files of every leg and system are assembled into
    one <name>-<version>.exe: the Windows program leads with a POSIX shell
    script in its DOS header, the other systems' programs follow unchanged.
    Windows starts it as itself (Authenticode signs it); Linux and macOS start it
    through sh, which copies the system's program to the single-file cache once,
    checks its sha256 and executes it. mcpp run --pkg-format polyglot starts it
    through sh; mcpp emit xpkg refuses it; it is not in a --pkg-format list
    (e2e 992).

  • mcpp run --pkg-format single-file runs the single file directly; it was
    refused as an unknown format. tar, zip and dir are refused as containers
    that are not programs.

  • A tree's POSIX launcher names macOS's quarantine mark. A package a browser
    downloaded carries com.apple.quarantine on every file, and macOS refuses to
    start an unsigned runner so marked; the launcher says so before it starts the
    runner, with xattr -dr com.apple.quarantine <package> (SPEC-014 §4.3).

Fixed

  • A workspace's lock entry is keyed the same way whatever members a command
    plans
    (#868). Two members spelling one dependency two ways
    (tinyhttps.workspace = true, [dependencies.mcpplibs] tinyhttps.workspace = true) made -p of each write its own key and hash. The key is the
    workspace's spelling: its [dependencies], [workspace.dependencies], then
    the members in the order members lists them (docs/07, e2e 990).
  • A file [build] ldflags names by absolute path is a link input. A
    replaced prebuilt archive did not relink the program that names it; now every
    link that carries the word depends on the file (docs/04, e2e 991).
  • A partial caret or tilde requirement treats unwritten segments as
    wildcards
    (#866, NopeCharon): ^0 is < 1.0.0, ^0.0 is < 0.1.0, ~1 is
    < 2.0.0. A bare requirement is a caret, so the bare partial spellings "0"
    and "0.0" take the same bounds; before, they matched 0.0.0 alone (docs/05).
  • The TOML reader accepts every escape TOML 1.0 defines (#867, NopeCharon):
    \b, \f, \uXXXX and \UXXXXXXXX. The writer escapes every control
    character in turn (\uXXXX where TOML has no short form), so a normalised
    manifest that holds one is still TOML; \0 is this reader's extension and is
    never written.

v2026.10.30.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 04:57
13e2da0

Added

  • Target sets (SPEC-015). A --target whose first segment is a system
    word names every row of the target table with that system and environment:
    openkal is x86_64-openkal and aarch64-openkal, linux-musl the musl
    rows this mcpp builds. Each member is a leg. build builds every leg (planned
    one after another, built at once); run runs the first leg this machine
    reaches, its own architecture first; test runs the legs it reaches, reports
    the others built and not run, and fails when no test ran. The mcpp.build
    envelope adds legs, the events their triple; self env adds legs
    (e2e 963). [build] target takes one target.
  • Tests built once, run elsewhere. mcpp test --no-run --archive <file>
    writes every leg's tests with mcpp-tests.json; mcpp test --archive <file>
    in a checkout runs what this machine reaches and builds nothing. The archive
    is a tar compressed as its name states (.tar.gz, .tgz, .tar.zst,
    .tar.xz, .tar); another name is refused before the build (e2e 964). A
    leg of the host's own system runs from an archive as an openkal leg does
    (e2e 989, on every system); on macOS the first build of this release started
    none of them, giving each an environment entry of an empty name.
  • The build's contract, ${mcpp.contract}. Every configuration writes
    mcpp-contract.json beside its compile database: the compiler of each
    language with the words every unit is compiled with (target, sysroot,
    target-side headers, platform and profile flags), and the link driver, its
    words and the archiver. A build program's action names it with
    ${mcpp.contract} and reads it while ninja runs, so a foreign build system
    compiles for the target as the engine does; the engine's own C++ dialect and
    module words are not in it (e2e 982). mcpp-plugins 0.21.0's deps-cmake
    compiles a cross build's subproject from it.
  • [platform] runner.hosts, carrier entries and descriptor_keys. A
    platform states the <os>-<arch> pairs its runner exists for, any number of
    carrier entries each for the pairs in its hosts (an entry of a carrier
    model this mcpp does not build is skipped with a warning), and the
    descriptor blocks its targets read (SPEC-013 §1.1, §10, §11, §14).
  • runs_on in mcpp-package.json. Where a package runs is computed once
    when it is packed: self, the pairs it runs on with what it carries, and
    with.<runner>, the pairs it runs on given the machine's runner.

Changed

  • A pack holds the legs it names (SPEC-014 2.0). --target x86_64-openkal packs that architecture; --target openkal packs both into
    one tree. The platform no longer widens a pack: [platform] universal,
    [pack] universal and --no-universal lose their effect, with a message
    that names the new spelling ([pack] targets = ["openkal"]; docs/51).
    Images of a platform with a runner have one layout whatever the number of
    legs: bin/<name>, bin/<name>.exe, lib/<arch>/, and with
    --mode self-contained libexec/<runner>/<os>-<arch>/. The image embedded
    in a carrier belongs to --pkg-format single-file only. A set of native
    programs is a package per leg (e2e 955, 983, 984).
  • The Windows launcher is a program. bin/<name>.exe replaces
    bin/<name>.cmd: no "Terminate batch job" prompt, and the process is named
    after the package. The POSIX launcher hands over to it under Git Bash, MSYS
    and Cygwin.
  • --systems takes <os>, <os>-<arch> and all, and [pack] systems.
    --pkg-format takes a list of containers of one staged tree (tar,okb),
    built once (e2e 985). Single files cover legs × systems (e2e 986).
  • The pack summary names where the package runs. Runs on and one Not covered line per pair the package does not reach; mcpp.pack adds
    pkgFormats and runsOn. A tree that runs nowhere is refused.
  • A target reads the descriptor blocks its row names, never the build
    machine's (#851).
    A target that is not Linux, macOS or Windows read the
    block of the system mcpp ran on, so a Web build took a dependency's linux
    block on Linux and its windows block on Windows, and an ok image the block
    of whichever system built it. Each row now names its blocks in order:
    wasm32-emscripten reads emscripten, then linux; <arch>-openkal
    openkal, then linux (a platform may state its own); iOS ios, then
    macosx; bare metal none. Version resolution, the synthesised manifest
    and the index refresh read the same block (unit DescriptorKeys).
  • The Web link carries the profile's optimisation level (#851). emcc runs
    wasm-opt and minifies the JavaScript launcher at the link, which had no
    -O: a release Web program was linked at -O0. The hello world's
    JavaScript is 13 KB rather than 66 KB (e2e 981).
  • [target.<sel>.abi] exceptions takes the model (#851). "wasm" selects
    WebAssembly's exception handling (-fwasm-exceptions), "js" the
    JavaScript model; true keeps the JavaScript model it has selected since
    2026.9.12.3. The model is part of every cache key (e2e 981).
  • mcpp emit xpkg names where a package runs (#860). A
    self-contained universal package's entry named every system with every
    image architecture, so an Intel Mac installed a package that carries no
    runner for it. Each system's block names the architectures runs_on gives:
    the carried runners for a self-contained package, the pairs the platform's
    runner exists for otherwise.

Fixed

  • A name two dependencies answer to is no package's (#852). Two
    dependencies whose manifests both say rules warned on every build, and the
    bare name went to one of them. It is now published for neither:
    MCPP_DEP_AMBIGUOUS_RULES lists the qualified names, each qualified name
    keeps its own directory, and dep_dir("rules") ends the build program naming
    both (e2e 980).
  • Programs start in the Android app sandbox again (#857). Under run-as
    (SELinux runas_app) every program mcpp started failed with "not found"
    since 2026.10.26.2. A static strace showed one failing call, before any
    process was created: the root directory opened for reading, which the
    domain may search but not read. openkal-linux 0.18.3 opens every directory
    handle with O_PATH; mcpp is built on mcpplibs.os 0.2.3, which pins it, and
    openkal-platform 0.18.0 carries it to every ok image and hosted program.
  • A gzip stream over 4 GiB. zlib counts in 32 bits, and mcpp handed it the
    whole tree at once: a .tar.gz of more than 4 GiB was refused as "an input
    over 4 GiB", by mcpp pack and by a test archive alike. Input and output
    now pass in pieces of 1 GiB, so such a tree is one gzip member as any other.
  • A program that cannot be started is reported with the system's account.
    Every condition beyond not found, permission and not a program was reported
    as "error 5" (EIO), the system's own message dropped. The classification now
    covers invalid, no memory, directory, not supported and too large, and the
    report of mcpp test and mcpp test --archive carries what was being done
    and the kernel layer's value.

v2026.10.29.1

Choose a tag to compare

@github-actions github-actions released this 29 Oct 03:56
db3b815

Fixed

  • An image of another architecture runs through its execution host.
    openkal-platform's runner is { cmd = ["okrun", "{}"], host = "same-arch" },
    and its first word resolved to the okrun built for this machine whatever
    the execution host: run_under = ["qemu-aarch64"] for aarch64-openkal on
    x86_64 handed qemu the x86_64 okrun ("Invalid ELF image for this
    architecture"). A same-arch runner runs on a machine of the artifact's
    architecture, so with an execution host named ([target.<t>] run_under, or
    MCPP_TARGET_<T>_RUN_UNDER) a runner that is the platform's tool is now the
    platform's runner for that architecture: the carrier with nothing carried,
    built for this system, the program a self-contained universal package
    carries under libexec/<runner>/<os>-<arch>/ (SPEC-013 §10). mcpp run and
    mcpp test of aarch64-openkal pass under qemu-aarch64 on x86_64 Linux
    (e2e 960). With nothing named the refusal stays; --no-runner keeps its
    meaning; a platform with no carrier, or a system and architecture this mcpp
    builds no program for, is refused rather than handed this machine's tool. In
    a platform package, mcpp test with no --target runs a target of another
    architecture an execution host is named for, and reports the others built
    and not run, as before (measured on a copy of openkal-platform 0.17.3).
  • mcpp test prints the refusal in full, once. The reason a target is
    not run here was recorded silently and the summary printed its first line,
    so the run_under line it carries never reached a person (SPEC-013 §18,
    criterion 7). It is printed as a warning before the first test it leaves
    not run, once per build; the summary keeps the first line (e2e 960).
  • mcpp self env --target states how the target runs here. It reported
    the runner resolving to this machine's okrun for aarch64-openkal and
    said nothing of the refusal. It now prints the refusal on a run line
    (JSON runner.refusal), the architecture of the runner's program
    (runner.arch), and, with an execution host named, the other architecture's
    runner, whether the first run has built it (runner.built; self env
    builds nothing) and the execution host's program (runUnderProgram)
    (e2e 960).
  • mcpp test --target records its program build under the target. The
    package's programs, built before its tests, were recorded in the build cache
    as this machine's build, so the next bare mcpp run printed fingerprint changed (… → …), full rebuild; mcpp run --target was fixed for the same
    defect earlier (e2e 960).
  • A self-contained package of one ok image is a program of the image's
    architecture.
    mcpp pack --target aarch64-openkal --no-universal --mode self-contained built the carrier for this machine's architecture: an x86-64
    program carrying an aarch64 image, which runs on no machine. The carrier is
    now this system's program of the image's architecture, as a single file's
    is; a row this mcpp builds no program for is refused before the generated
    project is packed, naming the row (SPEC-014 §1.2; e2e 961).
  • mcpp pack --target <t> --list-formats lists that target's formats. The
    plan ignored --target and listed this machine's; for an ok-image target
    it now lists okb, which the platform's plugin provides (SPEC-014 §6;
    e2e 961).
  • mcpp emit xpkg describes a package for the system its program runs on.
    Every triple that was not Windows or macOS was written linux: a
    wasm32-emscripten package became a Linux entry with archs = {"wasm32"}.
    An entry's xpm.<system> installs a program of that system for machine
    architectures. A package of one leg is now described for the system and
    architecture of its program: the triple's for a Linux (other than Android),
    macOS or Windows target, and for an ok image packed with its carrier
    (--no-universal --mode self-contained or static) the carrier's, which
    the package now records in mcpp-package.json systems (and the mcpp.pack
    envelope's), e.g. ["linux-aarch64"]. A package whose program is no program
    of a system -- a WebAssembly module, an Android or iOS application, a
    bare-metal image, an ok image without its carrier -- is refused, naming the
    route that distributes it: a page through dist-web, or the platform's
    universal package (SPEC-014 §4.5, §8; e2e 961, 962).
  • --pkg-format single-file is refused on a WebAssembly target. A single
    file is an executable of one system; the pack tried a wasm32-linux-musl
    launcher. The refusal comes before anything compiles and names tar,
    zip, dir and a provider's format (SPEC-014 §1.5; e2e 962).
  • The release requests its index bump with the permissions its token has.
    bump_index.sh started the index's updater with a workflow_dispatch, which
    needs Actions: write; the release's token holds Contents and Pull requests
    (measured: a workflow_dispatch 403, a repository_dispatch 204). It now sends a
    repository_dispatch of type xpkgindex-update naming xim:mcpp, which the
    index's xpkgindex-update-request.yml passes on to the updater
    (openxlings/xim-pkgindex#1087).

Changed

  • SPEC-013 1.2 and SPEC-014 1.3 state the rules above, and how ok images
    relate to the web: an ok image is position-independent ELF for x86_64 and
    aarch64 that imports only kal_* and runs in executable memory its loader
    publishes, an okb holds ok images only, and the web is reached through the
    wasm32-emscripten target and the dist-web provider, openkal API code
    through openkal-emscripten.

v2026.10.28.1

Choose a tag to compare

@github-actions github-actions released this 28 Oct 19:06
d67b96f

Fixed

  • A package's cache key covers every package whose module it imports.
    A BMI is bound to the BMIs it read, and the key folded in only the
    dependencies a manifest states. A package can import a module its manifest
    does not name: the libraries of a build program import the engine
    interface (mcpp), which the engine adds to the build program's graph.
    Such a package was served an entry compiled against another BMI of that
    module and failed with "CRC mismatch" (measured: mcpp.plugins in a build
    of an ok image, over a cache earlier builds had filled). The provider of
    each imported module is now an upstream of the importer's key, a declared
    edge or not; a module this build generates for a dependency below its
    consumer's root is not one (e2e 849). e2e 959 serves a package an
    interface it does not declare at two versions; the upgrade lane gains a
    programs variant that builds an ok image over the previous release's
    cache.
  • A build ended by SIGTERM exits 143 however the race falls. The watcher
    that takes a request to end over ends the programs mcpp runs, writes the
    cancelled record, runs the cleanups and then ends mcpp as asked; the main
    thread, woken by its compiler's end, could return from main first with
    the build's own failure (exit 1, seen once on CI in e2e 936 H). The end of
    main now ends mcpp as the request asks, after the watcher has finished.
  • mcpp emit xpkg --package names each system's program directory. The
    packages of one program for several systems may place it apart: a Windows
    package holds the program at its root, a POSIX one under bin/ beside the
    root's convenience script. The entry registered the first package's
    directory for every system, so on Windows xvm pointed at a bin/ the
    package does not have. It now writes bindir[os.host()] from a table of
    each system's directory when they differ (SPEC-014 §8).
  • mcpp emit xpkg --package states the runner dependency with its
    namespace
    (deps = { "xim:okrun" }). A bare name turns ambiguous once a
    second index the home lists has a package of that name, and xim-pkgindex's
    check-dep-namespace refuses it for a published package.
  • The install of an emit xpkg --package entry finds the package. It
    looked the archive's directory up with os.dirs("<name>-0.1.3-*"), and
    libxpkg's os.dirs lists <base>/* without matching names: no xlings
    install of such an entry succeeded. The directory is now the downloaded
    archive's name without its extension, which is what mcpp pack names it
    after, so it holds for every system, architecture and later version; an
    archive renamed since it was packed, or a file xim does not unpack, is
    refused when the entry is written. mcpp pack --merge -o <archive> names
    the merged package's directory after its output as mcpp pack -o does,
    where it kept the first input's, so a merged package is described as
    every other is.
  • mcpp emit xpkg refuses a word that is not an option. Each archive is
    its own --package; --package a.zip b.zip (a shell glob's expansion)
    described a.zip alone and left b.zip out without a word.
  • The release proposes its index bump again. bump_index.sh waited for
    auto/bump-xim-mcpp-<version>, and the index's updater opens
    auto/bump-xim-mcpp; the proposal is now found by the marker in its body.
    A dispatch the token is refused for (it needs Actions: write on
    openxlings/xim-pkgindex) names the permission and the command a maintainer
    runs.

Changed

  • upgrade-lane-logs/ is no longer in the tree. A local run's logs were
    committed with 2026.10.17.1; the directory is ignored, as the lane writes
    it beside the workspace for CI to upload.
  • SPEC-014 1.2 states what 2026.10.27.1 already does: a package's name
    carries its mode's suffix, a single file's launcher exits 127 on its own
    failure, --merge takes its container from [pack] format before -o, and
    --linkage static moves this machine's row and each -linux-gnu --target
    to musl while a project's own [build] target is packed as it is.

v2026.10.27.1

Choose a tag to compare

@github-actions github-actions released this 28 Oct 11:39
e0b1133

Added

  • Platforms: the configuration of a class of targets as one versioned package
    (SPEC-013; docs/35). A package's [platform] section states the targets it
    serves, their toolchain and target base, the package for each target-side
    layer ([platform.layers], or false), the tools built for this machine
    ([platform.tools]), the compile flags ([platform.build] and its
    cfg(...) tables), the link ([platform.link]: driver, flags, start,
    script, inputs, the names exe and shared, the seal steps), the
    runner ({ cmd, host = "same-arch" | "any", exclusive }) and named runners,
    the carrier of a self-contained program, a plugin run in the platform phase,
    a version group ([platform.versions]) and the part a host triple takes
    ([platform.hosted]). The engine selects a platform for the target graph
    (--platform, MCPP_TARGET_<T>_PLATFORM, [target.<selector>] platform,
    config.toml, the target table) and for the host graph (--host-platform,
    MCPP_HOST_PLATFORM, [host] platform), merges the overrides a project and
    this machine write under the same key names, adds the layers and tools the
    graph lacks (a layer a package of the graph provides is left to it), links,
    seals and names the artifact, runs it, and reports every value with its
    source. cfg(platform = "<name>") selects by platform.
  • x86_64-openkal and aarch64-openkal need no declaration. The target
    table selects mcpplibs.openkal-platform for both rows, and the platform
    states what a project wrote by hand in 2026.10.26.2: llvm@23.1.3,
    openkal-musl, openkal-llvm-runtime, okld and okrun, the image's flags,
    its link and seal, {name}.oke and lib{name}.okso, the runner and the M3
    carrier. A project that names only its sources builds, runs, tests and
    packs an ok image. The engine holds no openkal code (src/okimage is
    removed).
  • The host graph's platform. With [host] platform build programs and
    their libraries are compiled as ok images of this machine's architecture,
    with one toolchain on Linux, macOS and Windows, and run by the platform's
    runner; a platform's own tools are always built natively. e2e 953; a
    platform package unrelated to openkal is e2e 952.
  • Protocol 17 (SPEC-007 R9.10): link_driver, link_input,
    link_input_from, artifact_name, seal and run_host, stated by a
    platform's plugin in the platform phase (mcpp::phase() is "platform")
    or by any build program in its build phase.
  • One configuration, without a build. mcpp self env --target <triple>
    (--host, --format json) reports the toolchain, the platform with each
    field's source, the tools, the runner and its program; mcpp toolchain install --target <triple> fetches the target's platform and builds its
    tools; mcpp toolchain list has a PLATFORM column.
  • mcpp test builds the package's programs first and gives each test
    MCPP_BIN_<NAME>, the path of the program <name>. In a platform package
    with no --target, it builds the tests for each of the platform's targets
    and runs those of this machine's architecture.
  • Packaging along three axes (SPEC-014; docs/10): --linkage static|shared (the program's dependency linkage; on Linux static is the
    musl row), --mode system|vendored|self-contained (what the machine
    provides; --mode static is --linkage static --mode self-contained),
    --pkg-format tar|zip|dir|single-file|<provided> (what is delivered; a
    format left unstated is a zip for a Windows program and a tar elsewhere) and
    the legs (--target, repeatable). Impossible combinations are refused before
    the build. [pack] linkage, format, compress, sign and universal are
    the project's defaults. e2e 954.
  • Universal packages. A platform with universal = true (openkal-platform)
    packs every one of its targets into one package, <name>-<version>-<platform>,
    with lib/<arch>/ per leg and generated launchers (bin/<name>,
    bin/<name>.cmd) that pick the machine's architecture and start the
    platform's runner; --mode self-contained adds the runner of each system of
    --systems and each architecture under libexec/, built over the platform's
    hosted arrangement on any machine (a macOS or Windows runner is packed on
    Linux with no SDK and no C run-time of that system); --no-universal packs
    the selected target alone. e2e 955.
  • Single files. --pkg-format single-file writes one executable: for a
    native program the engine's launcher carrying the staged tree as a zstd
    frame, extracted once into the user's cache under the payload's sha256 and
    run from there; for an ok image the platform's carrier running the image
    from memory, one file per system of --systems. e2e 956.
  • Compression in mcpp's own codecs. --compress gzip|zstd|xz|none[:level]
    (.tar.gz, .tar.zst, .tar.xz, .tar) with zlib, zstd and liblzma as
    dependencies of mcpp: no compressor is looked for on PATH, and an archive
    is the same bytes on every host. A zip's entries are deflated (gzip) or
    stored (none).
  • mcpp-package.json and what reads it. Every staged tree describes its
    package: platform, the three choices, legs, systems, how it starts, and each
    file with its role, leg and sha256. mcpp pack --merge a --merge b joins
    packages written on several machines (legs and systems united, one digest
    per path); mcpp emit xpkg --package <archive> --url <prefix> writes the
    xim-pkgindex entry, one asset for every system for a universal package with
    its runner as a dependency. [pack] sign = [argv] signs each program, image,
    library and loader before the container, and each single file. e2e 957.
  • The mcpp.pack envelope gains linkage, mode, pkgFormat, legs,
    systems and each file artifact's sha256. A platform's plugin may provide
    package formats (pack-format, artifact actions) in the platform phase.
  • mcpp.lock records the packages a platform added, and those only they
    reach, with origin = "platform", and names each entry's configurations in
    targets (absent for an entry of this machine's native configuration alone,
    so a lock of a natively built project is unchanged): a build for this machine
    and a build or pack for an ok image leave each other's entries as they were.
    The plan event and result.json carry configuration.platform and
    hostPlatform.

Changed

  • --pkg-format is the package format of mcpp pack and mcpp run.
    --format there is the earlier spelling and prints a warning; on every other
    command --format is the output encoding.
  • The reason image-tool-absent is platform-tool-absent; new reasons
    platform-not-a-platform, platform-target-unserved,
    platform-version-group and platform-phase-directive.
  • A layer the target's platform added is reported platform, and the
    platform follows on a Platform line (Host platform for the host graph).
  • A version of a dependency the index does not have is stated by mcpp: which
    index, its revision and age, and whether it was refreshed now and why not;
    the provider's own report is printed under -v.
  • A package shared by several configurations of one command is announced once.
  • With trim_paths, clang is given -ffile-reproducible (clang 17 and
    later): __FILE__ takes the target's separator where clang applies it. An ok
    image built on Linux and one built on macOS are the same bytes; one built on
    a Windows host is not, because clang keeps the \ it joined an included
    path with for a POSIX target (#850, upstream), and mcpp pack --merge
    refuses its images beside the others.
  • --pkg-format tar writes .tar.gz on every host, Windows included, with
    mcpp's own zlib, for a program's package and a library's alike; no tar or
    gzip program is run. --compress none writes .tar.
  • x86_64-openkal and aarch64-openkal select openkal-platform 0.17.3
    (openkal-musl 0.22.6, openkal-llvm-runtime 0.16.6): the C library takes a
    program's arguments when it is entered, which a hosted program on macOS did
    not receive.
  • A platform's link with ld.lld itself finds the linker as ld.lld, lld
    or lld-link beside the compiler (a Windows payload ships lld-link, run
    with -flavor gnu); mcpp explain <reason> explains each platform-*
    reason. A build program's cached run is replayed only while what it named
    below its output directory is there (e2e 958).

Fixed

  • A version the local index lacks is refreshed for when its install is
    refused.
    The walk refreshes the index when a descriptor is missing, and not
    when a descriptor is present without the version asked for (version tables
    are per system, so that absence is not proof). The provider's "not found" for
    the version is, and it now reaches the refresh policy (offline,
    [index] auto_refresh, the debounce, one sync per run); after a refresh the
    install is asked once more. A dependency on a version published after the
    index was last synced (openkal-llvm-runtime = "0.16.3") was refused until
    mcpp index update. e2e 949.
  • A kernel-abi package over the system's C library is placed into the shared
    image that reaches it.
    Every package providing an mcpp: layer was a
    boundary of static placement and went into programs only, which is right for
    a C library or a C++ runtime. On a hosted target openkal-linux (-macos,
    -windows) sits on the system's C library and is an ordinary static library
    under mcpplibs.os; a shared image reaching it was left with kal_*
    undefined, and CPython could not load an extension built on mcpplibs.os
    (undefined symbol: kal_process_close). A kernel-abi provider is now a
    boundary only when a C library of the graph sits on it. e2e 950.
  • A kernel-abi package over the system's C library leaves a hosted row's
    toolchain in place.
    The musl and windows-gnu rows name the payload that...
Read more

v2026.10.26.2

Choose a tag to compare

@github-actions github-actions released this 27 Oct 10:38
7d70482

Added

  • x86_64-openkal and aarch64-openkal: one ok image per architecture
    (openkal SPEC clause 12). The image is compiled for no operating system
    with the convention's flags, linked by ld.lld into an ET_DYN entered at
    ok_main (bin/<name>.oke, or lib<name>.okso for an image another
    loads), and published only after okld note has written its requirements
    note and okld check has checked it. okrun runs it on Linux, macOS and
    Windows and is the target's runner. Both programs come from the dependency
    graph: [build-dependencies] openkal-tools = { tools = ["okld", "okrun"] }.
    The C library and the C++ run-time inside an image are packages; the
    payload's libc++ is not offered to an image.
    A build that links an image with no okld in the graph is refused with
    the machine-readable reason image-tool-absent.
    The tools a workspace's root package declares are its own, as any root's
    are.
    mcpp pack --mode self-contained reads the declaration of openkal-tools a
    build resolves: after workspace inheritance and in the target's
    [target.'cfg(os = "openkal")'.build-dependencies].

  • A program's beginning can be a dependency's entry target, and a
    freestanding image states the symbol it is entered at
    (#845). A framework
    declares [targets.<n>] kind = "entry" with the translation unit that
    supplies main, and a program selects it with
    main = { from = "<dependency>/<entry>" } in place of a file of its own; a
    test suite does the same with [test] main. The entry is compiled in its
    package's context with its own flags, linked only into the programs that
    select it, gated by its required_features, and its windows_entry is
    inherited. start = "<symbol>" on a program, an entry, a row or [test],
    and mcpp:link-start from a board package's build program (protocol 16),
    name the ELF entry of a freestanding image; statements that disagree are
    refused naming each, the symbol must be defined (it was a linker warning and
    an image entered at address 0), and the build reports it on an Entry line
    and in the result record's entry. A file a target names as main on any
    row is no longer linked as an ordinary source on the other rows.

Changed

  • mcpp keeps no operating-system layer of its own. modules/platform is
    removed; mcpp reaches the operating system through mcpplibs.os 0.2.0, and
    what was mcpp's own meaning under that name now lives in the module that
    owns it: the environment of an invocation in the invocation package, the
    constraints of a run in mcpp.effects, every program mcpp starts in
    mcpp.run, the loader environment and the run-time search in
    src/runtime, command-line spelling in mcpp.build.command_line, the
    platform keys and the Apple SDK in toolchain-model, the terminal and key
    input in src/ui, the home's lock and trust check in mcpp.home.files.
    The one primitive kept per system is the lock every release of mcpp takes
    (src/native/posix, src/native/windows). A program is found on PATH
    without a shell, with PATHEXT's suffixes on Windows, and the current
    directory is not searched on Windows. A list of directories in an
    environment variable (PATH, the run-time library path) is read and written
    with mcpplibs.os's split_list and join_list: an empty entry is
    skipped, and a quoted entry on Windows is unquoted.
  • A program mcpp starts is named by its argument vector. Compiler probes,
    git, xlings, tar, strip, patchelf, readelf and vswhere are started without a
    command interpreter, with their additions to the environment given to them
    alone; the interpreter runs only commands that are text by nature (a
    [hooks] command, an MSVC environment script, and the command lines mcpp
    composes and records as text, as ninja's rules are). On Windows the
    interpreter receives cmd.exe /d /s /c "<command>" as one string. Every
    started program reads an empty standard input unless it is the program
    mcpp run becomes, and runs in a unit that ends with mcpp.
  • No signal handlers. A request to end (SIGINT, SIGTERM, SIGHUP, a
    console's control events) is taken over by mcpplibs.os and read by one
    watcher thread, which ends the units of the running programs, writes the
    interrupt files, restores the terminal and ends mcpp as the request would
    have; the exit codes of §5 are unchanged. A closed standard output ends mcpp
    in the same order (mcpp … | head: 141 to a shell on POSIX). Every program
    mcpp starts begins with SIGPIPE at its default, although openkal ignores it
    for its own writes.
  • mcpp does not write its own process environment. Variables mcpp states
    (its home, --offline, a hook's variables, xlings' home and PATH) are this
    invocation's statements, read by every lookup mcpp makes and given to the
    programs it starts; an xlings invocation's environment is the started
    program's, on Windows as elsewhere.

Fixed

  • A dependency upgraded to a cached version replaces the previous version's
    files.
    A package's BMI and objects are staged from the build cache at paths
    named by the module and the source, not by the cache entry, and staging under
    --verify size took a destination of the source's size as already staged.
    After an upgrade the previous version's files are at those paths, and a
    change that keeps their sizes (a constant) left them in place: the program
    ran the old code, and a unit compiled against the new graph failed with
    CRC mismatch (GCC). A destination that can be read is now compared by
    content under every mode; an equal size is accepted only for one that cannot
    be opened (a language server holding it). e2e 948.
  • A compile whose status could not be written fails at once. A detached
    module compile reports its status in <slot>.rc, which appears by a rename
    after the number is written. On a full disk the file appeared empty and the
    build waited for a number until its two-hour limit; an .rc with no number is
    now a failed unit, said as such.
  • A WebAssembly link writes its .wasm again. 2026.10.26.1 published every
    POSIX link through a temporary name, and Emscripten names the file it writes
    beside the output after the output: a link to app.js.tmp wrote no
    app.wasm, the edge's implicit output was missing, and mcpp pack of a
    wasm32-emscripten program (mcpp-plugins' dist-web) found no stem family.
    A WebAssembly link is in place, as a PE link is.
  • A module's indirect import is found whatever map its importer has (GCC).
    A unit that reaches another package's module is compiled with a module map,
    and a unit that imports only the root's modules with GCC's own mapper. A BMI
    records the paths of its imports as its compile's mapper spelt them, and the
    map named no repository, so a unit with a map that imported a root module
    compiled without one looked for that module's import beside the build
    directory (failed to read compiled module, Bad import dependency). The
    map now names the BMI directory as its repository ($root gcm.cache) and
    every path below it, as GCC's own mapper does; the dyndep step reads the
    same form. The same cause failed import std.compat in a unit with a map, in
    any project with a dependency. A dependency's BMI that an earlier mcpp put in
    the global build cache spells its imports the earlier way, so the cache key
    of a package that provides a module under GCC carries the spelling: such a
    package is compiled once more after the upgrade, and a package of object
    files keeps its entry.
  • mcpp update moves a branch dependency forward again. The record of
    the previous build was replayed although mcpp.lock had been cleared, so the
    next build kept the old commit and wrote it back. A recorded build is now
    replayed only under the lock it was planned with.
  • A git branch reached through another package is locked. mcpp.lock
    recorded only the branches the project itself declared, so a branch two
    edges away moved to its tip on every build and stopped an --offline build
    ("mcpp.lock records no commit for branch"). It is now recorded under the
    package's qualified name and read back as its anchor.
  • A root's checkout answers a dependency's request by version that no index
    carries.
    The root's path or git declaration of a package overrides a
    dependency's request for it by version, but the request was first given to
    the index to select, and a package the index did not carry yet (a root
    pinning a checkout ahead of a release) refused the build with "no package
    found for exact selector". The root's checkout now answers it; a
    requirement the checkout does not satisfy is still refused.
  • A build program compiled against an updated interface of the same mcpp
    version reads the new interface.
    The compiled mcpp module of build
    programs was kept by mcpp's version alone, so a build of mcpp between
    releases that added a directive served the previous interface. It is kept by
    the version and a digest of its text.
  • A request by version the local index cannot answer refreshes the index
    wherever it is in the graph.
    The refresh before the dependency walk asked
    only about the root's and the selected members' [dependencies], so a
    version named by a path dependency, by a package's own manifest or in a
    conditional table, and published after the local index was fetched, refused
    the build ("no package found for exact selector") with no refresh at all.
    The miss now asks the refresh policy (offline, [index] auto_refresh, the
    debounce, one sync per run) and the selection is made again; a namespace the
    index cannot refute still never refreshes.
  • A dependency written workspace = true under a target selector or behind
    a feature receives the workspace's source.
    Only [dependencies],
    [dev-dependencies] and [build-dependencies] were merged, so a git or path
    dependency of `[wo...
Read more

v2026.10.26.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 02:41
434b3fb

Added

  • The build stream and the result record. mcpp build --message-format json writes NDJSON events (plan, waiting, progress, diagnostic,
    artifact) and ends with the mcpp.build envelope. Every build writes
    target/<triple>/<fp>/result.json (running, then ok, failed or
    cancelled) and target/.last-result.json: the command, the caller
    (MCPP_CLIENT), the artifacts with how mcpp run starts each one, and the
    compilers' diagnostics with path and range. mcpp run --print-launch prints
    the mcpp.launch envelope instead of starting the program.
  • --deny <effects> and MCPP_DENY_EFFECTS. A run stops before the first
    effect its caller denied (init-mcpp-home, write-project,
    write-global-cache, network, exec-build-script), names it, and exits
    5; a denied write-global-cache uses the project's own cache instead.
    --offline counts as --deny network.
  • Queries. mcpp search [keyword] --format json (mcpp.search, every
    package when no keyword is given), mcpp index list --format json
    (mcpp.index) and mcpp pack --list-formats (mcpp.pack.formats).
  • mcpp --home <dir> runs one command with <dir> as its home, as
    MCPP_HOME=<dir> does.
  • [build] compile_commands names where the project's compile database is
    published, with MCPP_COMPILE_COMMANDS for one run and a config.toml
    default.
  • A C++ runtime island is declared and verified. requires takes a table,
    { layer = "mcpp:c++-abi=libstdc++", visibility = "private" } (for the
    c++-abi layer only), and may be written under [target.<sel>]. A declared
    island is linked with the foreign runtime after the program's own and checked
    after the link ([cxx-runtime-collision]); a build with a C++ shared library
    beside one is refused ([cxx-runtime-island]).
  • llvm with a libstdc++ from the graph. A package that provides
    mcpp:c++-abi=libstdc++ (gcc.libstdcxx) gives a clang build its headers,
    its std module and -stdlib=libstdc++. Build programs receive the resolved
    MCPP_CXX_STDLIB and MCPP_CXX_ABI_ROOT.
  • Colour on a pipe when CLICOLOR_FORCE or FORCE_COLOR asks for it;
    NO_COLOR and MCPP_NO_COLOR still win.
  • Docs: 52 — Driving mcpp from a Program or an Agent,
    SPEC-011 concurrency and
    SPEC-012 human output.

Changed

  • The project root's compile_commands.json is written only when
    [build] compile_commands names it.
    Each configuration's database stays in
    its build directory. A copy an earlier mcpp left is no longer updated, and the
    first build that finds it says so once.
  • A foreign C++ runtime is recognised by type. A runtime library named on
    the link line, or a shared library on it whose DT_NEEDED names one, replaces
    the search for stdc++ in the flags. One no package declares is verified as
    an island: a build that passes proceeds with a warning (undeclared-cxx-runtime),
    one that does not is refused.
  • abi:glibc and abi:cxxstdlib read the resolved target side, as
    requires = ["mcpp:c-abi=glibc"] and ["mcpp:c++-abi=…"] do (the
    compatibility reading abi-capability).
  • mcpp's next-step lines read help: (they read hint:).
  • A manifest's rejection is located at the key it names
    (mcpp.toml:6:11: error: [build].sources …), and envelope diagnostics carry
    that location as path and range.
  • cache list reads each entry's size from its record; compiler probes and the
    provider handshake are cached under the home, written by a run that plans
    and read by every command (a query such as self doctor leaves the home as it
    found it). Measured with xlings 2026.10.26.1: emit build-database 71 ms,
    toolchain list 44 ms.
  • A provisioning failure passes on xlings' diagnostic and no longer suggests
    xlings install.
  • xlings 2026.10.26.1 is bundled and is the vendored floor: an interface call
    ends with its capability, --home selects one run's home, and a hook in a
    project's store finds xlings.

Fixed

  • Two builds of one configuration no longer break each other (mcpp#839).
    A build directory has a lock from the moment its configuration is known; the
    second build waits, says whose run it waits for, and finds little left to do.
    mcpp run releases it before starting the program.
  • A linked file is replaced whole. A program started while a build links
    never runs a partial file (POSIX: link to a temporary name and rename;
    Windows: the old file is renamed aside).
  • catch (const std::exception&) matches across a second C++ runtime
    (mcpp#838): the foreign runtime is linked after the program's own, so the ABI
    layer binds to one runtime.
  • target/.build_cache and mcpp.lock are replaced atomically; a lock another
    run wrote meanwhile is not overwritten ([lockfile-concurrent]).
  • A build ended by SIGINT, SIGTERM or SIGHUP leaves its record cancelled and
    exits 128 + N.
  • A libstdc++ std module source named std.cc is compiled as a module
    (-x c++-module).

v2026.10.24.1

Choose a tag to compare

@github-actions github-actions released this 23 Oct 22:02
d5ec182

Added

  • The target base. [target.<triple>].sysroot takes a package, a locator
    (glibc@system, musl@system, macosx|iphoneos|iphonesimulator@system|<version>,
    msvc@…), a table { path = "<dir>", provides = [...] } relative to the file
    that writes it, or "". One statement is resolved once and every reader uses
    it: compile and link lines, the scan, the std module, the artifact's loader
    and run path, closure validation, the fingerprint, the reports, mcpp pack and
    the runner. Statements come from --sysroot / MCPP_TARGET_<TRIPLE>_SYSROOT /
    MCPP_SYSROOT, then the project, then [target.<triple>] in config.toml
    (the user's, then the system's), then the target row.
  • The C library a base supplies is measured: the driver says where the C runtime
    is, the preprocessor which library and version. Debian multiarch is supplied to
    a gcc that does not know it, so glibc@system builds with gcc and clang alike.
    A base older than the compiler's runtime requires is refused before compiling,
    naming both versions (libstdc++.so.6: GLIBC_2.38 against glibc 2.31).
  • A native artifact binds its base: PT_INTERP and RUNPATH name the base's loader
    and C library, and closure validation checks against it. The base's identity
    enters the fingerprint, so a sysroot rewritten in place rebuilds. For such a
    build mcpp pack states the highest glibc the program and its packed
    libraries need and writes it to HOST-REQUIREMENTS.
  • Using sysroot glibc 2.31 ← third_party/sysroot [custom · mcpp.toml:12],
    mcpp why sysroot, and base / execution in resolution.json.
    --managed-only names a stated base. A new refusal code, target-base.
  • SDKROOT names an Apple SDK on every host, so an SDK produced from one's own
    Xcode on Linux is located as one in Xcode is. An SDK whose libSystem.tbd
    lacks the row's slice is refused naming it. The clang *-windows-msvc row
    takes { path } for an unpacked Visual Studio tree or a /winsysroot layout.
  • The execution host. [target.<triple>].run_under (also in config.toml,
    overridden by MCPP_TARGET_<TRIPLE>_RUN_UNDER, none clears) is composed in
    front of the runner. Runners receive MCPP_RUN_TRIPLE, MCPP_RUN_KIND,
    MCPP_RUN_MODE, MCPP_RUN_ID, MCPP_RUN_CLOSURE (a versioned JSON closure)
    and MCPP_SYSROOT; their exit status is the program's. An Apple artifact on a
    host that is not macOS is refused before anything runs, naming run_under.
  • A toolchain phase may state sysroot beside spec.
  • Profile keys ndebug and trim_paths, on by default for release and dist.
    trim_paths maps each package's sources to <name>@<version>/… and the store,
    home, build directory, toolchain and sysroot to /mcpp/…, and writes
    source-map.gdbinit / .lldbinit when there is debug information. It
    applies to what build, run and pack produce; mcpp test keeps paths,
    because a test binary finds its fixtures through __FILE__.
  • x86_64-ios-sim is verified: CI builds it on Apple silicon and runs it on
    an Intel Mac's simulator.

Changed

  • release and dist define NDEBUG and trim source paths. assert
    compiles out there, and an artifact no longer carries the directories of the
    machine that built it (mcpp#832). Write ndebug = false or trim_paths = false under the profile to keep the previous artifact; the first shipping
    build after the upgrade says so once. Both profiles rebuild once; dev does
    not.
  • Bundle xlings 2026.10.24.1 and raise the vendored floor to it: a script
    package's alias quotes its path, so its hook loads under a home whose path
    holds a space (openxlings/xlings#699, mcpp#832).
  • The getting-started guide, the features page and the usage skill describe
    package-owned project templates and the mcpplibs.mwrap adapter convention
    (mcpp#835, #836).

Fixed

  • A stated sysroot hands the linker --sysroot (gcc's clean link specs drop
    the driver's), so a sysroot's linker scripts resolve inside it (cannot find /lib64/libm.so.6). The ecosystem's default build is unchanged.
  • A stated sysroot no longer produces an artifact that loads the host's loader
    and the SubOS farm's C library, and closure validation no longer compares a
    stated base with the ecosystem's glibc.
  • mcpp no longer writes into a sysroot it does not own (a toolchain named by
    path reports its vendor's).
  • A toolchain phase that states spec with prefix, launcher, family or
    tool.<role> is refused by name; those keys were dropped without a word.
  • A command printed for pasting quotes a path that holds a space
    (mcpp self init --force --home '/tmp/mc probe/home', mcpp#832).
  • A fresh home whose path holds a space builds the default project (with
    openxlings/xim-pkgindex#1032, #1035 and #1044). The programs a build graph
    runs (cxx, cc, ar, mcpp, …) and the toolchain paths on its command
    lines (-B, -isysroot, -ffile-prefix-map, …) are quoted for the host;
    they were escaped for ninja alone and split at the space. Under a path outside ASCII, gcc's
    module mapper cannot precompile std; mcpp now names that cause and the two
    ways out after the error.