Skip to content

Releases: mcpp-community/mcpp

v2026.11.2.2

Choose a tag to compare

@github-actions github-actions released this 02 Nov 09:31
8d099cb

The built-in openkal platform is 0.21.0; an embedded dependency's link
requirements are its embedder's; a response file a link flag names is an input
of the link (design 2026-11-02, part 3 rev 4).

Changed

  • The built-in <arch>-openkal platform is mcpplibs.openkal-platform@0.21.0
    (docs/35): openkal 0.21, whose loader maps an image's instructions from its
    file (kal_exec_map_part); openkal-linux 0.22, openkal-macos 0.19,
    openkal-windows 0.17; openkal-musl 0.26 with mimalloc; openkal-llvm-runtime
    0.17; openkal-image 0.3. The Windows launcher mcpp pack builds over it
    depends on mcpplibs.os 0.2.7, the release written against openkal 0.21.
  • An embedded dependency's link flags and libraries are its embedder's
    (docs/05). The embedder supplies the definitions, over the operating-system
    layer it chose, and states what they link against; the dependency's
    [build] ldflags and [runtime] libraries described the library it builds
    on its own. openkal-musl embeds compat.mimalloc without its prim.c, and a
    Windows program built on Linux was linked against psapi, user32 and
    advapi32, which no sysroot of that build has. e2e 1002.

Fixed

  • A response file a link flag names (@<file>), and every file it names,
    are inputs of the link.
    A build program that lists the archives a
    prepare action makes in one (link_flag("@<file>")) left the link up to
    date over rebuilt archives. The hermetic link check reads such a file in
    place: with one on its command line the driver handed the linker a response
    file of its own, which -### names and does not write, and the check refused
    the link as taking the system's C library. e2e 1006.
  • The cache entry of a package whose units this build does not all need is
    filled
    (bmi cache populate failed ... expected build output missing). The
    engine package's mcpp.core was compiled only where something imported it,
    and a build plugin importing mcpp alone left its entry unfilled on every
    build. A unit whose outputs fill an entry is built whether or not a link or
    an importer of this build asks for it. e2e 910.
  • A release's SHA256SUMS names every archive, the four builds and the
    source; it named Linux x86_64 alone.

v2026.11.2.1

Choose a tag to compare

@github-actions github-actions released this 01 Nov 22:21
16a0c1a

A dependency's source is used without being copied: a dependency edge carries
patches, and a dependency is compiled into its consumer (design 2026-11-02,
part 3 §14). With them, mcpp#875 and mcpp#881.

Added

  • patches = [...] on a dependency edge (docs/05). Unified diffs, named
    relative to the declaring manifest, applied in order to the verified source by
    mcpp's own exact applier (no fuzz, no search, no conversion of line endings;
    one leading directory of each path removed; renames, copies and binary changes
    refused). A patched library is another library, a variant: its identity is
    (package, version, patch set), its source a tree of its own under the mcpp
    home (cache/src/<package>/<version>+p<digest>), and its build, BMIs and
    cache entries its own. Version and git dependencies; a path dependency with
    patches is refused. mcpp.lock records the variant and each patch's path and
    SHA-256 (variant, patches); mcpp emit sbom states them as the component's
    pedigree. A variant's tree is collected by cache gc, prune and after a
    build as the cache's entries are. e2e 1001.
  • embed = true and exclude = [...] on a dependency edge (docs/05). The
    dependency's sources, less exclude, are compiled into the consumer with the
    consumer's flags and, for those files, the dependency's include directories
    and compile flags; the dependency builds nothing of its own in that
    configuration, and a program that reaches it without its embedder is refused
    naming both. exclude without embed is refused at the manifest. e2e 1002.
  • One program, one variant of a package: two programs of one configuration
    may each use their own patch set; a program that reaches two variants of a
    package, or a variant and the package without patches, is refused naming
    them. e2e 1003.

Fixed

  • A build with a toolchain outside every provider found no ninja (mcpp#875):
    the ninja mcpp installed into its own home is used after the one beside the
    compiler's provider and before PATH, and MCPP_NINJA names the same one. With
    none, the build is refused naming where it looked, instead of "cannot start
    'ninja'". e2e 1004.
  • clean --stale said every directory was current after keeping one
    (mcpp#881): directories kept for being newer than the window are counted, and
    the summary says how many and within what window. The default window of
    --older-than is [build] keep_days (3 days), the window of the sweep after a
    build, instead of one day. The SubOS remedies name the home:
    XLINGS_HOME=<registry> xlings self doctor --fix. e2e 1005.

Design

  • Design 2026-11-02 part 3 (ok image performance parity), approved; part 2
    rev 11 and 12 corrections.

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