Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
642 changes: 642 additions & 0 deletions .agents/docs/2026-11-15-ecosystem-gap-supplement-design.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .agents/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
---
```

362 records.
363 records.

## By subject

Expand Down Expand Up @@ -52,6 +52,7 @@ Records that declare one. Everything else is listed by date below.

### cross-platform

- [生态缺口补充方案:一个 C 程序走遍全部目标所暴露的通用问题](2026-11-15-ecosystem-gap-supplement-design.md) — active
- [生态方案:跨平台通用基础设施(mcpp + openkal + xim),以一个 C89 工程为验证](2026-11-10-cross-platform-ecosystem-design.md) — active

### design
Expand Down Expand Up @@ -201,6 +202,7 @@ Records that declare one. Everything else is listed by date below.

### 2026-11

- [生态缺口补充方案:一个 C 程序走遍全部目标所暴露的通用问题](2026-11-15-ecosystem-gap-supplement-design.md) — active
- [发布的发布面与 macOS 链接的 SDK 契约(2026-11-14 补充)](2026-11-14-release-publication-and-macos-sdk-design.md) — active
- [CI 与发布的时间成本:按测量调度的测试、分片、并行发布](2026-11-14-ci-and-release-time-cost-design.md) — active
- [The three-plane platform architecture: mcpp × openkal × xim](2026-11-12-three-plane-platform-architecture-design.md) — superseded by [2026-11-12-ecosystem-architecture-optimization-design.md](2026-11-12-ecosystem-architecture-optimization-design.md)
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/ci-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ jobs:
set -euo pipefail
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
mkdir -p "$RUNNER_TEMP/unit"
# LINKS BOUNDED BY THE BOOTSTRAP'S SCHEDULE. Before 2026.11.16.1 a link
# under the detached schedule (bmi_schedule = "on") took no permit, and
# ninja ran six per compiler: a change every test links relinked the 26
# tests at once, at 1 to 1.8 GB each, and the runner ran out of memory.
# Such a bootstrap builds the archive with the ordinary schedule, whose
# job count bounds links; a later one holds a permit for each link.
v=$("$MCPP" --version 2>/dev/null | awk '{print $2; exit}')
if [ "$(printf '%s\n' 2026.11.16.1 "$v" | sort -V | head -1)" != 2026.11.16.1 ]; then
export MCPP_BMI_SCHEDULE=off
echo "the bootstrap $v holds no permit for a link: the archive is built with bmi_schedule off"
fi
# shellcheck disable=SC2086 -- the flag is one word or none
# The profile `ci` (mcpp.toml): -O0 and no debug information. The
# default profile of this package is release (-O2); the tests built
Expand Down
126 changes: 126 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,132 @@
> Each `## [<version>]` section is that release's notes. Entries are written in English
> from 2026.9.28.3 on; earlier entries remain as written.

## [Unreleased]

## [2026.11.16.1] - 2026-11-16

### Added

- `x86_64-linux-android` runs in the Android Emulator on an x86_64 Linux
machine, once enabled: dirun (>= 0.4.0) boots the API 34 system image
through the image package's profile, and the target's runs do not overlap.
A provider's runner states `exclusive` (G7).
- `mcpp::runner_spec` states a runner in a build program as the table of
data: `.arg()`, `.require()`, `.hosts()`, and `host`, `longlived`,
`exclusive`, `enabled` (protocol 19). `{pkg:<name>}` names a program outside
a package's `bin/`, installed by the first run. A name stated by
`runner_spec` and by the token calls is refused (R2).
- A runner states `enabled = false` when its first use fetches a whole machine
image or an emulator, or leaves state outside the build tree and the package
store. `mcpp run` refuses it with status 2 and `mcpp test` leaves the tests
built and not run, naming `[runner-not-enabled]`, the packages its first use
installs and the ways of enabling it; nothing is installed first. A table
without `cmd` in `config.toml` or the project enables it (`runner = { enabled
= true }`), `MCPP_TARGET_<TRIPLE>_RUNNER_ENABLED=true` enables it once, and
`mcpp run --runner <name>` enables the runner it names. A runner whose `cmd`
a nearer tier replaces is enabled unless stated (R1).
- The Windows rows on an x86_64 Linux machine state Wine as their runner
(`xim:wine@11.0`, `host = "same-arch"`), not enabled by default (X1).
- A named runner takes the table of the default runner (`runners.<name> = {
cmd = [...], longlived = true, enabled = false }`) and merges by the same
rule; `longlived` states in data what `runner_longlived` states in a build
program. A provider's runner states `host` and `enabled` (R2).
- The execution table (`[[platform.executes]]`, SPEC-016 §2.4): a machine runs
the programs of another architecture where it is stated, `linux-x86_64`
executing `i686`. There a `same-arch` runner is built for the artifact's
architecture and started on this machine, and a launcher whose package lacks
this machine's architecture takes one the machine executes: an i386 image
runs on x86_64 Linux through the i686 runner (design 2026-11-15 N23).
- A row's `companions` (`.wasm` on wasm32-emscripten): removed before each
link, staged when the link wrote one, not an error when absent. A clean pack
with `-sSINGLE_FILE=1` failed, naming the missing module, and an earlier
link's module was packed beside a launcher that embeds its own (N33).

- `mcpp::pack_program("<program>")` (build-program protocol 19): a pack-format
provider states the program of the package being packed that it assembles
from, and `mcpp pack --pkg-format <format>` with no program named packs that
program. A program named on the command line outranks it; providers that
state different programs, or a program the build does not have, are refused
by name (design 2026-11-15 N30).
- A pack whose format provider submitted nothing lists, in the refusal, the
warnings the provider stated in that pass (`mcpp::warning`, `mcpp::report`)
as its reasons (N30).

### Changed

- A wasm32-emscripten build whose project states no `[abi] exceptions` uses
WebAssembly exception handling, the row's `exceptions = "wasm"`: a C++
exception is caught, and `setjmp`/`longjmp` follow the same model. The SDK's
default left catching off. `exceptions = "js"`, `true` or `false` states
another model or none (N26).
- A package that carries its runner (`--mode self-contained`) starts only the
runner it carries for this machine, and a machine it holds none for exits
with status 126, naming the machine and the systems the package holds. The
POSIX and the Windows launcher fell back to a runner on `PATH`, which ran
another release's runner (design 2026-11-15 N20).
- The POSIX launcher names the system by the system table's `uname` column, so
a FreeBSD machine finds `libexec/<runner>/freebsd-<arch>/`; it looked for
`FreeBSD-<arch>` (N20).

### Fixed

- A single file named for a system where the platform's runner does not exist
(`--systems macos` for an x86_64 image whose platform names no
`macos-x86_64` runner host) is refused before the build, by the reading
`--systems all` applies to leave it out; it was refused after the build.
- `mcpp test --guide` reads the frameworks from the resolved graph and plans,
scans and builds nothing; at a workspace root it names the frameworks of
every member. It planned and built the whole workspace first (#932).
- `mcpp test` over several members plans a configuration group's programs
together, as `mcpp build --workspace` does. Each member's programs were
planned alone, with that member's features for the packages it shares, and
rebuilt a shared module into the group's directory: three compiles of one
unchanged module in a run, and on Windows a failed rewrite of a module
another process had mapped (#936).
- Under `bmi_schedule = "on"` with GCC a link holds a permit of the build's
compiler limit. Ninja runs six jobs per compiler there and only compilers
were bounded, so links that became ready together all ran at once: mcpp's
26 test programs relinked together at 1 to 1.8 GB each and a 16 GB machine
ran out of memory. A POSIX link is published through a temporary name by
`mcpp link-output --temporary`, one command, so the permit is held until the
artifact is in place.
- A `sources` entry without a pattern that names no existing file is an error
naming its table, and a warning in a package fetched into a store. It was
skipped, and the build failed at an importer of its module (#938).
- On Windows a runner named with a directory and no suffix (`../tools/x`)
takes the system's suffixes as a bare name does; it could not be started.
A `.bat` or `.cmd` program, which the system starts through its command
interpreter (the system table's `command-scripts`), is refused an argument
holding `%`, `^`, `&`, `|`, `<`, `>`, `"` or a line end, which the
interpreter rewrote or ran as a command (R2).
- A name a build program, this machine or the project states for a program
(`mcpp::artifact_name("exe", "{name}")`) is followed on a built-in row as on
a platform's: a program named `cmd.exe` was `cmd.exe.exe` on
x86_64-windows-gnu and `cmd.exe` on x86_64-windows-musl (design 2026-11-15
N9).
- A linked program is written under a temporary name that keeps its extension
(`<dir>/.<file>`, then renamed). DJGPP's driver names a second file after the
output's extension, so `<out>.tmp` left two programs in `bin/` on
i586-dos-djgpp (N9).
- The carrier of an image whose row the built-in table keeps planned and the
project's platform lists with its toolchain is built (an i686 image's
i686-linux-musl runner); it was refused as a row this mcpp builds no program
for (N23).
- `[resources]` and the application manifest are compiled only for a PE row
whose execution system reads them, the system table's `resources = true`
(the built-in `windows` entry); the UEFI row, a PE image the firmware
loads, failed the build with them (N16).
- Two `[build-dependencies]` requests of one package that no version
satisfies are refused as such, naming both requesters, the merge's reason and
the `override` line. The engine tried to rename the second copy's modules,
which does not separate a build program's modules, and failed on the
requester's sources with a message about staging (a project asking for
`mcpp:plugins` 0.21.0 over a platform asking for 0.20.0: `stage: no source
files found under '<platform>'`) (design 2026-11-15 N29).
- `mcpp pack --all-programs` of an ok image gives every program its own pair of
launchers, `bin/<name>` and `bin/<name>.exe`; only the chosen program had them
(N11).

## [2026.11.15.1] - 2026-11-15

### Added
Expand Down
24 changes: 21 additions & 3 deletions docs/04-mcpp-toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,15 @@ belongs at the package/feature boundary, not on an individual target.
> mcpp 2026.8.18.1 the two were byte-identical, so there was no spelling for
> "nothing" and any file left under `src/` was swept in.

> **A path without a pattern names one file** (2026.11.16.1+). A `sources`
> entry with none of `*`, `?`, `[`, `{` names a file, and a file that does not
> exist is an error naming the table that states it (`[build]`,
> `[target.'<sel>'.build]`, `[features.<f>]`): it was skipped, and the build
> failed at an importer of the module the file provides. In a package fetched
> into a store, which its consumer cannot change, it is a warning. A file an action or
> a build program of the build writes is the build's. A pattern that matches
> nothing selects nothing, as a target's selection may.

> **`**/` stands for whole directories.** In `sources` and every glob that
> shares its grammar, `**/` matches zero or more directories and what follows
> it starts a path segment: `**/main.cpp` names every `main.cpp` and not
Expand Down Expand Up @@ -1063,6 +1072,8 @@ with two behaviours has to be.
With GCC and `bmi_schedule = "on"`, ordinary compilation and detached code
generation share the same total `jobs` limit (2026.11.7.1+). Additional
scheduling and join tasks do not increase the number of compiler processes.
A link shares the same limit (2026.11.16.1+): links that become ready together
run as many at once as compiles do, not all at once.
Cancelling the build ends its ordinary and detached compilers. An immediately
following build waits for their cleanup before starting another compiler;
partial outputs are rebuilt. A missing object from a completed module is
Expand Down Expand Up @@ -1546,7 +1557,8 @@ runner = { cmd = ["okrun", "{}"], host = "same-arch", exclusive = false }
| `tools.<name>` | the package that builds a tool for this machine, or `{ program = "<name>" }` |
| `link` | `driver`, `flags`, `start`, `script`, `inputs`, `exe`, `shared`, `seal` |
| `carrier` | `{ package, section, main }`: the program `--mode self-contained` builds |
| `runner` | an argv, or `{ cmd, host, exclusive }`: `host = "same-arch"` runs an artifact only on a machine of its architecture |
| `runner` | an argv, or `{ cmd, host, hosts, exclusive, requires, enabled, longlived }`: `host = "same-arch"` runs an artifact only on a machine of its architecture; a table without `cmd` adjusts the runner below it, as `{ enabled = true }` enables a runner its data states `enabled = false` (2026.11.16.1) |
| `runners.<name>` | the same argv or table, for a named runner (the table form 2026.11.16.1) |

`cfg(platform = "<name>")` selects by platform; `platform` itself is not
accepted under such a selector. The same keys are read from
Expand Down Expand Up @@ -1679,6 +1691,10 @@ anything compiles.
- Built-in profiles: `release` (-O2) / `dev`, `debug` (-O0 -g) / `dist` (-O3 + strip;
**LTO is not enabled by default**). `[profile.<built-in name>]` can override a
built-in definition wholesale.
- **The order of the flag lists.** Each of `cflags`, `cxxflags` and `ldflags`
reaches a command as `[build]`'s, then the profile's, then
`[target.<selector>.build]`'s: where a compiler or a linker takes the last of
two words, the target block's wins (measured on 2026.11.16.1).
- **`ndebug` and `trim_paths` (2026.10.24.1+)** follow the industry's convention: on
for `release` and `dist`, off for `dev`, `debug` and a custom profile that does not
state them. `ndebug` defines `NDEBUG` for every unit, so `assert` compiles out, as in
Expand Down Expand Up @@ -2056,8 +2072,10 @@ generates the resource script automatically.
| `version-info` | bool | `false` opts out of the generated version resource |
| `[resources.version-info]` | table | `company`, `product`, `description`, `copyright`, `original-filename`, `internal-name` |

**Only PE targets *compile* this.** On Linux and macOS the section is
*inapplicable*: no resource units, no diagnostics, byte-identical build. You do
**Only targets whose programs Windows runs *compile* this**: PE rows whose
execution system is Windows. Elsewhere the section is *inapplicable* --- Linux,
macOS, and a PE row another system loads, such as `x86_64-uefi` (2026.11.16.1):
no resource units, no diagnostics, byte-identical build. You do
**not** need (and cannot use) a `cfg(windows)` predicate — write it once,
unconditionally.

Expand Down
4 changes: 3 additions & 1 deletion docs/05-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ mcpplibs.os = { version = "0.4.4", override = true }
the defaults, and the project may choose another.
- **When it is needed.** Two version requests that no SemVer merge reconciles
are refused naming both, and the refusal ends with the line that overrides
the package. A library that pins one release of a package every consumer
the package. Two requests from `[build-dependencies]` tables are refused the
same way and are never renamed apart: a build dependency is a module of a
build program, and one build loads one version of it (2026.11.16.1). A library that pins one release of a package every consumer
shares (one that provides a target-side layer or a kernel ABI) is noted to
the projects that use it, and `mcpp publish` refuses the pin in a library:
a range leaves the choice to the project.
Expand Down
9 changes: 8 additions & 1 deletion docs/08-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,10 @@ The selection narrows what a pattern or `-p` names and adds nothing to it.
the package's programs first, as `mcpp build` does, and gives each test
`MCPP_BIN_<NAME>`: the path of program `<name>`, upper-cased, every character
other than a letter or a digit written `_` (`MCPP_BIN_MY_TOOL` for `my-tool`).
A test that runs the program it tests reads it from there.
A test that runs the program it tests reads it from there. Under several
members the programs of a configuration group are planned together, as
`mcpp build --workspace` plans them, so a package they share is built with
one configuration (2026.11.16.1+).

`--no-programs` (2026.11.13.1+) builds none of them: the programs are planned
for their names, and each test takes each program from the environment's
Expand Down Expand Up @@ -548,6 +551,10 @@ A framework states its guide to writing tests on its entry, `test-guide = "<file
package and of its version. `mcpp test --guide` names each test framework of the
package with its guide and repository, and the commands that list, select and
explain cases; with none, it states the contract above and how to take mtest.
The frameworks are declared data: the guide reads them from the resolved
graph, the dev-dependencies included, and plans, scans and builds nothing,
wherever it is asked (2026.11.16.1+). Where several members are selected, a
workspace root or `--workspace`, it names the frameworks of all of them.
A failed run ends with `how these tests are written: mcpp test --guide (...)`,
and a record of `--message-format json` carries `guide`. A person or an agent
about to write a test, or reading why one failed, starts there.
Expand Down
9 changes: 7 additions & 2 deletions docs/10-pack-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -831,8 +831,13 @@ The other legs are planned one after another and built at once
directory, picks the architecture directory from `uname -m` (`x86_64`, `amd64`
→ `x86_64`; `aarch64`, `arm64` → `aarch64`), hands a Git Bash, MSYS or Cygwin
shell to `bin/<name>.exe`, and starts the runner on the image there with its
own arguments, looking for the runner first in `libexec/<runner>/<os>-<arch>/`
and then on `PATH`. The Windows launcher is the engine's program built for
own arguments. A package that carries its runner starts the one in
`libexec/<runner>/<os>-<arch>/`, and on a machine it holds none for exits with
status 126, naming the machine and the systems it holds; a package that carries
none finds the runner on `PATH`. `<os>` is the system whose `uname -s` word the
system table records (`linux`, `macos`, `freebsd`, ...). Under `--all-programs`
every program of the package has its own pair of launchers, `bin/<name>` and
`bin/<name>.exe`. The Windows launcher is the engine's program built for
`x86_64-windows-gnu`: it finds the package from its own path, picks the image
of the machine's architecture, and starts the runner the same way; no `.cmd` is
written. Where the build's platform has a hosted arrangement for that row, the
Expand Down
Loading
Loading