Repository navigation
Releases: mcpp-community/mcpp
Release list
v2026.10.31.3
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_debuginfounder[profile.<name>]("unpacked"|"off",
docs/04 §2.9)."unpacked", the default ofdevanddebug, 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_pathsoff. e2e 999.mcpp clean --report: the bytes of each configuration's objects, BMIs and
products and of each.build-mcpparea.mcpp cache clean --toolsand host tool entries incache list,prune,
gcandverify.- Collection by last use after a build: at most once a day, the build
cache's package, host tool andstdentries unused for[cache] unused_days
(config.toml; default 30,0keeps 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, andnoatimemounts keep no reads); a package hit no longer rewrites
itsentry.json. A build that replays its graph without planning marks the
entries its configuration names inuses.listthrough the sweep after it. mcpp self config --cache-unused-days <N> --keep-days <N>writes the two
retentions into config.toml;mcpp self configshows 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 buildormcpp testsucceeds, at
most once a day, the build directories of the project that no configuration
used for[build] keep_daysdays (config.toml; default 3,0keeps all) are
removed, with the generated-source and build-program directories none uses.
Changed
- Debug information is compressed on ELF (
-gz=zlibon the compile and the
link;--compress-debug-sections=zlibon an ok image'sld.lldlink): 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 theld.lldits 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 nold.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=nonein a package's flags turns it off. - A static library holds its own package's objects (docs/12): a member's
lib<target>.aheld 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 --stalealso 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
undermcpp test --workspace, complete undermcpp 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 = 1on 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 itstarget/. -
mcpp clean --stalekeeps 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
prepareaction builds (SPEC-013
§4.6, mcpp#702). Every link edge waits for the phony of each package with a
prepareaction, and the ordering guard checks that it does. Under a platform
with seal steps the link writes beneathobj/platform/and aplatform_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'sclangd.okb,
whose libraries aprepareaction 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
Added
mcpp test <name> --print-launch(featuretest-launch, docs/50 §9):
the one test the pattern names, built asmcpp testbuilds it and described
asmcpp testwould start it -- its runner, the environment the run adds, the
arguments after---- in themcpp.launchenvelopemcpp run --print-launchprints, withtestnaming 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 itsmcpplibs.ospins 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 andmcpp pack
stopped. It is now built over the platform mcpp's openkal rows pin, the one
itsmcpplibs.oswas released against. e2e 995 packs a project that selects
openkal-platform 0.19.0 with its Windows launcher.
v2026.10.31.1
Added
-
featuresinmcpp --protocol-version. A client gates on a feature
rather than on a version number:target-setsandpkg-format-listname 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'smcppfield
carries the same list beside the version, so oneself envgives a client
both, and a job's envelope says when either has changed. -
programinmcpp self env --format json: the file that is running. A
tool that ranmcppthrough 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 onPATHor a set path,
withXLINGS_PROJECT_DIRremoved (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
throughsh, which copies the system's program to the single-file cache once,
checks its sha256 and executes it.mcpp run --pkg-format polyglotstarts it
throughsh;mcpp emit xpkgrefuses it; it is not in a--pkg-formatlist
(e2e 992). -
mcpp run --pkg-format single-fileruns the single file directly; it was
refused as an unknown format.tar,zipanddirare refused as containers
that are not programs. -
A tree's POSIX launcher names macOS's quarantine mark. A package a browser
downloaded carriescom.apple.quarantineon every file, and macOS refuses to
start an unsigned runner so marked; the launcher says so before it starts the
runner, withxattr -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-pof each write its own key and hash. The key is the
workspace's spelling: its[dependencies],[workspace.dependencies], then
the members in the ordermemberslists them (docs/07, e2e 990). - A file
[build] ldflagsnames 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):^0is< 1.0.0,^0.0is< 0.1.0,~1is
< 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,\uXXXXand\UXXXXXXXX. The writer escapes every control
character in turn (\uXXXXwhere TOML has no short form), so a normalised
manifest that holds one is still TOML;\0is this reader's extension and is
never written.
v2026.10.30.1
Added
- Target sets (SPEC-015). A
--targetwhose first segment is a system
word names every row of the target table with that system and environment:
openkalisx86_64-openkalandaarch64-openkal,linux-muslthe musl
rows this mcpp builds. Each member is a leg.buildbuilds every leg (planned
one after another, built at once);runruns the first leg this machine
reaches, its own architecture first;testruns the legs it reaches, reports
the others built and not run, and fails when no test ran. Themcpp.build
envelope addslegs, the events theirtriple;self envaddslegs
(e2e 963).[build] targettakes one target. - Tests built once, run elsewhere.
mcpp test --no-run --archive <file>
writes every leg's tests withmcpp-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.jsonbeside 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'sdeps-cmake
compiles a cross build's subproject from it. [platform] runner.hosts, carrier entries anddescriptor_keys. A
platform states the<os>-<arch>pairs its runner exists for, any number of
carrier entries each for the pairs in itshosts(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_oninmcpp-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-openkalpacks that architecture;--target openkalpacks both into
one tree. The platform no longer widens a pack:[platform] universal,
[pack] universaland--no-universallose 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-containedlibexec/<runner>/<os>-<arch>/. The image embedded
in a carrier belongs to--pkg-format single-fileonly. A set of native
programs is a package per leg (e2e 955, 983, 984). - The Windows launcher is a program.
bin/<name>.exereplaces
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. --systemstakes<os>,<os>-<arch>andall, and[pack] systems.
--pkg-formattakes 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 onand oneNot coveredline per pair the package does not reach;mcpp.packadds
pkgFormatsandrunsOn. 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'slinux
block on Linux and itswindowsblock on Windows, and an ok image the block
of whichever system built it. Each row now names its blocks in order:
wasm32-emscriptenreadsemscripten, thenlinux;<arch>-openkal
openkal, thenlinux(a platform may state its own); iOSios, then
macosx; bare metalnone. Version resolution, the synthesised manifest
and the index refresh read the same block (unitDescriptorKeys). - 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] exceptionstakes the model (#851)."wasm"selects
WebAssembly's exception handling (-fwasm-exceptions),"js"the
JavaScript model;truekeeps the JavaScript model it has selected since
2026.9.12.3. The model is part of every cache key (e2e 981).mcpp emit xpkgnames 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 architecturesruns_ongives:
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 sayruleswarned on every build, and the
bare name went to one of them. It is now published for neither:
MCPP_DEP_AMBIGUOUS_RULESlists the qualified names, each qualified name
keeps its own directory, anddep_dir("rules")ends the build program naming
both (e2e 980). - Programs start in the Android app sandbox again (#857). Under
run-as
(SELinuxrunas_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 withO_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.gzof more than 4 GiB was refused as "an input
over 4 GiB", bymcpp packand 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 ofmcpp testandmcpp test --archivecarries what was being done
and the kernel layer's value.
v2026.10.29.1
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 theokrunbuilt for this machine whatever
the execution host:run_under = ["qemu-aarch64"]foraarch64-openkalon
x86_64 handed qemu the x86_64 okrun ("Invalid ELF image for this
architecture"). Asame-archrunner 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 underlibexec/<runner>/<os>-<arch>/(SPEC-013 §10).mcpp runand
mcpp testofaarch64-openkalpass underqemu-aarch64on x86_64 Linux
(e2e 960). With nothing named the refusal stays;--no-runnerkeeps 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 testwith no--targetruns 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 testprints the refusal in full, once. The reason a target is
not run here was recorded silently and the summary printed its first line,
so therun_underline 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 --targetstates how the target runs here. It reported
the runner resolving to this machine'sokrunforaarch64-openkaland
said nothing of the refusal. It now prints the refusal on arunline
(JSONrunner.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 --targetrecords 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 baremcpp runprintedfingerprint changed (… → …), full rebuild;mcpp run --targetwas 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-containedbuilt 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-formatslists that target's formats. The
plan ignored--targetand listed this machine's; for an ok-image target
it now listsokb, which the platform's plugin provides (SPEC-014 §6;
e2e 961).mcpp emit xpkgdescribes a package for the system its program runs on.
Every triple that was not Windows or macOS was writtenlinux: a
wasm32-emscriptenpackage became a Linux entry witharchs = {"wasm32"}.
An entry'sxpm.<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-containedorstatic) the carrier's, which
the package now records inmcpp-package.jsonsystems(and themcpp.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 throughdist-web, or the platform's
universal package (SPEC-014 §4.5, §8; e2e 961, 962).--pkg-format single-fileis refused on a WebAssembly target. A single
file is an executable of one system; the pack tried awasm32-linux-musl
launcher. The refusal comes before anything compiles and namestar,
zip,dirand a provider's format (SPEC-014 §1.5; e2e 962).- The release requests its index bump with the permissions its token has.
bump_index.shstarted 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 typexpkgindex-updatenamingxim:mcpp, which the
index'sxpkgindex-update-request.ymlpasses 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 onlykal_*and runs in executable memory its loader
publishes, an okb holds ok images only, and the web is reached through the
wasm32-emscriptentarget and thedist-webprovider, openkal API code
through openkal-emscripten.
v2026.10.28.1
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.pluginsin 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
programsvariant 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 frommainfirst with
the build's own failure (exit 1, seen once on CI in e2e 936 H). The end of
mainnow ends mcpp as the request asks, after the watcher has finished. mcpp emit xpkg --packagenames 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 underbin/beside the
root's convenience script. The entry registered the first package's
directory for every system, so on Windows xvm pointed at abin/the
package does not have. It now writesbindir[os.host()]from a table of
each system's directory when they differ (SPEC-014 §8).mcpp emit xpkg --packagestates 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-namespacerefuses it for a published package.- The install of an
emit xpkg --packageentry finds the package. It
looked the archive's directory up withos.dirs("<name>-0.1.3-*"), and
libxpkg'sos.dirslists<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 whatmcpp packnames 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 asmcpp pack -odoes,
where it kept the first input's, so a merged package is described as
every other is. mcpp emit xpkgrefuses a word that is not an option. Each archive is
its own--package;--package a.zip b.zip(a shell glob's expansion)
describeda.zipalone and leftb.zipout without a word.- The release proposes its index bump again.
bump_index.shwaited 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,--mergetakes its container from[pack] formatbefore-o, and
--linkage staticmoves this machine's row and each-linux-gnu--target
to musl while a project's own[build] targetis packed as it is.
v2026.10.27.1
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], orfalse), 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 namesexeandshared, thesealsteps), 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-openkalandaarch64-openkalneed no declaration. The target
table selectsmcpplibs.openkal-platformfor 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,okldandokrun, the image's flags,
its link and seal,{name}.okeandlib{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/okimageis
removed).- The host graph's platform. With
[host] platformbuild 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,sealandrun_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 listhas a PLATFORM column. mcpp testbuilds 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 Linuxstaticis the
musl row),--mode system|vendored|self-contained(what the machine
provides;--mode staticis--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,signanduniversalare
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>,
withlib/<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-containedadds the runner of each system of
--systemsand each architecture underlibexec/, 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-universalpacks
the selected target alone. e2e 955. - Single files.
--pkg-format single-filewrites 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 onPATH, and an archive
is the same bytes on every host. A zip's entries are deflated (gzip) or
stored (none). mcpp-package.jsonand 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 bjoins
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.packenvelope gainslinkage,mode,pkgFormat,legs,
systemsand each file artifact'ssha256. A platform's plugin may provide
package formats (pack-format,artifactactions) in the platform phase. mcpp.lockrecords the packages a platform added, and those only they
reach, withorigin = "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.
Theplanevent andresult.jsoncarryconfiguration.platformand
hostPlatform.
Changed
--pkg-formatis the package format ofmcpp packandmcpp run.
--formatthere is the earlier spelling and prints a warning; on every other
command--formatis the output encoding.- The reason
image-tool-absentisplatform-tool-absent; new reasons
platform-not-a-platform,platform-target-unserved,
platform-version-groupandplatform-phase-directive. - A layer the target's platform added is reported
platform, and the
platform follows on aPlatformline (Host platformfor 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), andmcpp pack --merge
refuses its images beside the others. --pkg-format tarwrites.tar.gzon every host, Windows included, with
mcpp's own zlib, for a program's package and a library's alike; notaror
gzipprogram is run.--compress nonewrites.tar.x86_64-openkalandaarch64-openkalselect 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.llditself finds the linker asld.lld,lld
orlld-linkbeside the compiler (a Windows payload shipslld-link, run
with-flavor gnu);mcpp explain <reason>explains eachplatform-*
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 anmcpp: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
undermcpplibs.os; a shared image reaching it was left withkal_*
undefined, and CPython could not load an extension built onmcpplibs.os
(undefined symbol: kal_process_close). Akernel-abiprovider 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...
v2026.10.26.2
Added
-
x86_64-openkalandaarch64-openkal: one ok image per architecture
(openkal SPEC clause 12). The image is compiled for no operating system
with the convention's flags, linked byld.lldinto an ET_DYN entered at
ok_main(bin/<name>.oke, orlib<name>.oksofor an image another
loads), and published only afterokld notehas written its requirements
note andokld checkhas checked it.okrunruns 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 nookldin the graph is refused with
the machine-readable reasonimage-tool-absent.
The tools a workspace's root package declares are its own, as any root's
are.
mcpp pack --mode self-containedreads 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
suppliesmain, 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 itsrequired_features, and itswindows_entryis
inherited.start = "<symbol>"on a program, an entry, a row or[test],
andmcpp:link-startfrom 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 anEntryline
and in the result record'sentry. A file a target names asmainon 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/platformis
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 theinvocationpackage, the
constraints of a run inmcpp.effects, every program mcpp starts in
mcpp.run, the loader environment and the run-time search in
src/runtime, command-line spelling inmcpp.build.command_line, the
platform keys and the Apple SDK intoolchain-model, the terminal and key
input insrc/ui, the home's lock and trust check inmcpp.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
withmcpplibs.os'ssplit_listandjoin_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 receivescmd.exe /d /s /c "<command>"as one string. Every
started program reads an empty standard input unless it is the program
mcpp runbecomes, 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 sizetook 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.rcwith no number is
now a failed unit, said as such. - A WebAssembly link writes its
.wasmagain. 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 toapp.js.tmpwrote no
app.wasm, the edge's implicit output was missing, andmcpp packof a
wasm32-emscriptenprogram (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 failedimport std.compatin 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 updatemoves 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--offlinebuild
("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 compiledmcppmodule 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 = trueunder 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...
v2026.10.26.1
Added
- The build stream and the result record.
mcpp build --message-format jsonwrites NDJSON events (plan,waiting,progress,diagnostic,
artifact) and ends with themcpp.buildenvelope. Every build writes
target/<triple>/<fp>/result.json(running, thenok,failedor
cancelled) andtarget/.last-result.json: the command, the caller
(MCPP_CLIENT), the artifacts with howmcpp runstarts each one, and the
compilers' diagnostics with path and range.mcpp run --print-launchprints
themcpp.launchenvelope instead of starting the program. --deny <effects>andMCPP_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 deniedwrite-global-cacheuses the project's own cache instead.
--offlinecounts 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) andmcpp pack --list-formats(mcpp.pack.formats). mcpp --home <dir>runs one command with<dir>as its home, as
MCPP_HOME=<dir>does.[build] compile_commandsnames where the project's compile database is
published, withMCPP_COMPILE_COMMANDSfor one run and aconfig.toml
default.- A C++ runtime island is declared and verified.
requirestakes a table,
{ layer = "mcpp:c++-abi=libstdc++", visibility = "private" }(for the
c++-abilayer 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,
itsstdmodule and-stdlib=libstdc++. Build programs receive the resolved
MCPP_CXX_STDLIBandMCPP_CXX_ABI_ROOT. - Colour on a pipe when
CLICOLOR_FORCEorFORCE_COLORasks for it;
NO_COLORandMCPP_NO_COLORstill 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.jsonis written only when
[build] compile_commandsnames 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 whoseDT_NEEDEDnames one, replaces
the search forstdc++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:glibcandabi:cxxstdlibread the resolved target side, as
requires = ["mcpp:c-abi=glibc"]and["mcpp:c++-abi=…"]do (the
compatibility readingabi-capability).- mcpp's next-step lines read
help:(they readhint:). - A manifest's rejection is located at the key it names
(mcpp.toml:6:11: error: [build].sources …), and envelope diagnostics carry
that location aspathandrange. cache listreads 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 asself doctorleaves the home as it
found it). Measured with xlings 2026.10.26.1:emit build-database71 ms,
toolchain list44 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,--homeselects one run's home, and a hook in a
project's store findsxlings.
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 runreleases 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_cacheandmcpp.lockare replaced atomically; a lock another
run wrote meanwhile is not overwritten ([lockfile-concurrent]).- A build ended by SIGINT, SIGTERM or SIGHUP leaves its record
cancelledand
exits128 + N. - A libstdc++
stdmodule source namedstd.ccis compiled as a module
(-x c++-module).
v2026.10.24.1
Added
- The target base.
[target.<triple>].sysroottakes 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, thestdmodule, the artifact's loader
and run path, closure validation, the fingerprint, the reports,mcpp packand
the runner. Statements come from--sysroot/MCPP_TARGET_<TRIPLE>_SYSROOT/
MCPP_SYSROOT, then the project, then[target.<triple>]inconfig.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, soglibc@systembuilds 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.38against 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
buildmcpp packstates the highest glibc the program and its packed
libraries need and writes it toHOST-REQUIREMENTS. Using sysroot glibc 2.31 ← third_party/sysroot [custom · mcpp.toml:12],
mcpp why sysroot, andbase/executioninresolution.json.
--managed-onlynames a stated base. A new refusal code,target-base.SDKROOTnames 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 whoselibSystem.tbd
lacks the row's slice is refused naming it. The clang*-windows-msvcrow
takes{ path }for an unpacked Visual Studio tree or a/winsysrootlayout.- The execution host.
[target.<triple>].run_under(also inconfig.toml,
overridden byMCPP_TARGET_<TRIPLE>_RUN_UNDER,noneclears) is composed in
front of the runner. Runners receiveMCPP_RUN_TRIPLE,MCPP_RUN_KIND,
MCPP_RUN_MODE,MCPP_RUN_ID,MCPP_RUN_CLOSURE(a versioned JSON closure)
andMCPP_SYSROOT; their exit status is the program's. An Apple artifact on a
host that is not macOS is refused before anything runs, namingrun_under. - A toolchain phase may state
sysrootbesidespec. - Profile keys
ndebugandtrim_paths, on by default forreleaseanddist.
trim_pathsmaps each package's sources to<name>@<version>/…and the store,
home, build directory, toolchain and sysroot to/mcpp/…, and writes
source-map.gdbinit/.lldbinitwhen there is debug information. It
applies to whatbuild,runandpackproduce;mcpp testkeeps paths,
because a test binary finds its fixtures through__FILE__. x86_64-ios-simisverified: CI builds it on Apple silicon and runs it on
an Intel Mac's simulator.
Changed
releaseanddistdefineNDEBUGand trim source paths.assert
compiles out there, and an artifact no longer carries the directories of the
machine that built it (mcpp#832). Writendebug = falseortrim_paths = falseunder the profile to keep the previous artifact; the first shipping
build after the upgrade says so once. Both profiles rebuild once;devdoes
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 themcpplibs.mwrapadapter 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
specwithprefix,launcher,familyor
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 precompilestd; mcpp now names that cause and the two
ways out after the error.