# The mcpp.toml Manifest Guide

`mcpp.toml` is the project configuration file for the mcpp build tool, analogous to Cargo's `Cargo.toml` or Node's `package.json`. Place it in your project root and `mcpp build` will discover and read it automatically.

## 1. Minimal Examples

mcpp is designed around **convention over configuration** — most fields have sensible defaults, so the simplest `mcpp.toml` is just a few lines:

### 1.1 Executable (minimal)

```toml
[package]
name    = "hello"
version = "0.1.0"
```

mcpp infers automatically:
- Source files: `src/**/*.{cppm,cpp,cc,c,S,s,asm}`
- Entry point: `src/main.cpp` → produces the `hello` binary
- Standard: C++23
- Modules: scans `export module ...` declarations and builds the dependency graph automatically

### 1.2 Library project (minimal)

```toml
[package]
name    = "mylib"
version = "0.1.0"

[targets.mylib]
kind = "lib"
```

lib-root convention: the primary module interface defaults to `src/mylib.cppm` (the last segment of the package name).

## 2. Full Field Reference

### 2.1 `[package]` — Package Metadata

```toml
[package]
name        = "myapp"              # Package name (required)
version     = "0.1.0"              # Semantic version (required)
standard    = "c++23"              # C++ standard (default c++23; can be set to c++20 / c++26)
description = "My awesome app"     # Description (optional)
license     = "MIT"                # License (optional)
authors     = ["Alice", "Bob"]     # Author list (optional)
repo        = "https://github.com/user/myapp"  # Repository URL (optional)
```

`standard` is the first-class setting for the C++ language standard. Recommended values:

- `c++23`: the default, suited to the current module-based default templates.
- `c++20`: the lowest level mcpp accepts — named modules are a C++20 feature, so nothing below it exists for this build model. Use it when an external constraint (an older in-house rule, a third-party API that stops at C++20) forces the level down. `import std;` still works there: it is a C++23 *library* feature, but GCC (≥ 15), Clang + libc++ (≥ 17) and the MSVC STL (from VS 2022 17.8) all provide the `std` module in C++20 mode as well. Note that C++23 library facilities (`std::print`, `std::expected`, …) are not available, including in the code generated by `mcpp new`.
- `c++26`: use when you need C++26 language features.
- `c++2a` / `c++2c`: compatibility aliases, normalized to `c++20` / `c++26` after parsing.
- `gnu++20` / `gnu++23` / `gnu++26`: use when you need a GNU dialect; this enters the fingerprint and the std BMI cache key.
- `c++latest`: resolves to the newest standard level the resolved toolchain supports. Good for local experimentation, but not recommended for release packages that require reproducibility.
- `c++fly`: `c++latest` **plus every experimental standard feature the resolved toolchain can enable** (language + standard library). On GCC ≥ 16 this turns on C++26 reflection (`-freflection`) and contracts; on Clang/libc++ it adds `-fexperimental-library`; unsupported gates are skipped with a printed summary. Deliberately toolchain-dependent — the bleeding-edge playground mode, never for published packages.

Two properties worth knowing:

- **The standard is module-graph-global.** The root package's `standard` applies to every
  translation unit in the build, dependencies included — a dependency's own `standard` is not
  used when it is being built as a dependency. This is not a simplification: BMIs are not
  compatible across levels (GCC rejects them with `language dialect differs`), so a single
  graph physically cannot hold two levels.
- **Levels never share caches.** The standard is part of the fingerprint, the `import std` BMI
  identity and the dependency build-cache key, so switching between `c++20` and `c++23` gives
  each level its own target directory and its own std BMI instead of a corrupt hit.

If the sources `import std;` at a level the resolved toolchain does not provide the `std`
module for, mcpp fails before compiling and names both the toolchain and the project level.

### 2.2 `[targets.<name>]` — Build Targets

```toml
# Executable (default; inferred automatically when src/main.cpp exists)
[targets.myapp]
kind = "bin"
main = "src/main.cpp"       # Optional, defaults to src/main.cpp

# Static library
[targets.mylib]
kind = "lib"

# Shared library
[targets.mylib]
kind = "shared"
soname = "libmylib.so.1"  # Optional: Linux/ELF ABI name; an alias of the same name is generated at runtime
```

`soname` is the ABI name for a shared library, analogous to `SOVERSION`/`SONAME` in
Autotools/CMake. On Linux, mcpp passes `-Wl,-soname,<name>` to the linker and
generates a `<name> -> lib<target>.so` alias in the output directory, so that
downstream programs can load the library via its standard ABI name through
`DT_NEEDED` or `dlopen()`. This field only applies to `kind = "shared"`, and the
value must be a filename basename.

Shared-library targets are currently supported only for Linux/ELF targets. A
`kind = "shared"` target for macOS or Windows (including a cross build) is
rejected before planning because mcpp does not yet model Mach-O install names
or PE import libraries. Use `kind = "lib"` for a static library on those
targets, or build the shared library for Linux.

#### Per-target keys

```toml
[targets.server]
kind     = "bin"
main     = "src/server.cpp"
defines  = ["BUILD_SERVER=1", "PORT=8080"]   # -D macros, applied to this target's entry only
cxxflags = ["-Wno-deprecated-declarations"]  # extra C++ flags for this target's entry (no -std=...)
cflags   = ["-DPURE_C"]                       # extra C flags for this target's entry

[targets.gui]
kind = "bin"
main = "src/gui.cpp"
required_features = ["gui"]                   # only built when feature `gui` is active
```

| Key | Meaning |
|---|---|
| `defines` | Preprocessor macros (`name` or `name=value`); desugar to `-D<x>` on both the C and C++ entry compile. |
| `cxxflags` / `cflags` | Extra compile flags for this target. Do **not** put `-std=...` here — use `[package].standard`. |
| `required_features` | The target is emitted only when **every** listed feature is active in the build; otherwise it is silently skipped. A gate only — it does not activate features (use `--features` / `[features].default`). **One exception, and it is not a second rule:** when this target is requested as a host tool (`tools = [...]`, §2.14), the target is what was *asked for*, so its `required_features` become the sub-build's *inputs*. Same field, one meaning — the resolution just runs in the opposite direction. |

> **Scope (important):** `defines` / `cxxflags` / `cflags` on a target apply **only to that
> target's exclusive entry source** (its `main`) — never to shared module/impl objects, which
> are compiled once and linked into every target (mcpp's compile-once model). They are the right
> tool when the flag only needs to affect a single binary's (or test's) own entry — for example a
> per-test contract evaluation semantic (`-fcontract-evaluation-semantic=observe`) for a test whose
> `main` exercises the violation, a feature macro the entry alone reads, or a local warning
> suppression. If a flag must reach **shared** code, it does not belong here — split into a
> [workspace](06-workspace.md) member or use `[features]`, or for a whole-build mode use a
> `[profile.*]` (`mcpp test --profile <name>` builds the whole test image, code-under-test
> included, under that profile).
>
> Unsupported keys under `[targets.<name>]` are reported as a warning (an error under `--strict`).

**Choosing where build configuration goes** — when more than one binary must differ:

| You want | Use |
|---|---|
| Different macros/flags on a binary's **own entry** | per-target `defines` / `cxxflags` (above) |
| Two products that differ in code they **share** | split into [workspace](06-workspace.md) members, each with its own `[build]` flags over a shared `lib` |
| To **select a variant** of a shared library (e.g. a backend) | `[features]` on that library (§2.8) — additive, reaches the library's own compile |
| A **whole-build mode** (sanitizers, contract semantics, opt level) | `[profile.<name>]` (§2.9) + `--profile`; also honored by `mcpp test --profile <name>` |

mcpp deliberately does not compile a shared source two different ways within one build: a source
maps to one object (and one BMI for modules), so divergence that must reach shared code belongs at
the package/feature boundary, not on an individual target.

### 2.3 `[build]` — Build Configuration

```toml
[build]
sources      = ["src/**/*.cppm", "src/**/*.cpp"]  # Source globs (default: src/**/*.{cppm,cpp,cc,c,S,s,asm})
include_dirs = ["include", "third_party/include"]  # Header search paths
include_dirs_after = ["*"]         # Header dirs searched AFTER system dirs (-idirafter)
c_standard   = "c11"              # Standard for C source files (default c11)
cflags       = ["-DFOO=1"]        # Extra C compile flags
cxxflags     = ["-DBAR=2"]        # Extra C++ compile flags (do not put -std=... here)
ldflags      = ["-lfoo"]          # Extra link flags
defines      = ["BIZ=1", "QUX"]   # Preprocessor macros for every TU (desugars to -D; reaches module scans)
cxx_runtime  = "self-contained"   # C++ runtime contract (§ below); static_stdlib is the old spelling
target       = "x86_64-linux-musl" # Default build target when no --target is passed
                                   # (≙ cargo build.target; e.g. "ship fully-static")
macos_deployment_target = "14.0"   # Minimum supported OS version for macOS artifacts (macOS only)
cache        = "global"           # Global dependency cache: global (default) | local | off (§2.10)
```

`include_dirs_after` (#249) lists header directories that are searched **after**
the toolchain's system directories (emitted as `-idirafter` on GCC/Clang, as
trailing `/I` under the MSVC dialect, and as plain `-I` for NASM
assembly units — neither has an equivalent, and neither has a system-header
chain to protect). Use it instead of
`include_dirs` when the directory is an extracted source-tarball root that
contains files whose names collide with standard headers — e.g. ffmpeg's
top-level `VERSION` file shadows libc++'s `<version>` on case-insensitive macOS
filesystems when the root is put on `-I`. With `include_dirs_after` the system
header always wins while the package's real headers (`<libavutil/frame.h>`)
remain findable. Entries support the same `*` glob convention as
`include_dirs`, and they propagate to dependent packages along the same edges —
consumers receive them as after-dirs, never upgraded to `-I`.

`macos_deployment_target` sets the minimum system version in the artifact's
Mach-O header (`LC_BUILD_VERSION minos`), i.e. the oldest macOS the binary can
run on. The precedence follows ecosystem convention: the `MACOSX_DEPLOYMENT_TARGET`
environment variable (an explicit per-invocation override, honored the same way by
cargo/rustc, cc, etc.) > this field (the project default, similar to SwiftPM's
`platforms:`) > the **built-in default `14.0`** (rustc-style — every target has a
baseline, and 14.0 is the floor of LLVM's official static libraries themselves).
This value enters the BMI fingerprint, so switching targets automatically rebuilds
the module cache.

### The C++ runtime contract (`cxx_runtime`)

`cxx_runtime` states what the produced artifact promises about the machine that
runs it. It is a **distribution** property, not a build one — it describes the
runtime dependency set, and the flags that deliver it differ per platform.

```toml
[build]
cxx_runtime = "self-contained"          # applies to every target (the default)

# or, per role:
[build.cxx_runtime]
default = "self-contained"              # binaries and shared libraries
tests   = "host-coupled"                # test binaries never leave this machine

# or, per target triple — beside `linkage`, which is the same axis:
[target.x86_64-linux-gnu]
cxx_runtime = "host-coupled"            # e.g. this build is for a distro package
```

| value | the artifact needs, at run time | typical use |
|---|---|---|
| `self-contained` (default) | no C++ runtime outside itself | shipping a binary |
| `toolchain-coupled` | the C++ runtime of the toolchain mcpp installed | local iteration |
| `host-coupled` | whatever the driver resolves by default (the system runtime) | distro packaging, `dlopen` plugins that must share a runtime with their host |

**Self-contained by default (portable by default)**: on macOS this statically
links LLVM's bundled libc++/libc++abi — the system libc++ would otherwise pin the
runnable version to the build machine's OS (older systems lack newer symbols, e.g.
the support symbols behind `std::print`), and only static linking can truly deliver
the `macos_deployment_target` floor. On Linux/MinGW it is `-static-libstdc++` (GCC)
or the whole-link `-static` (MinGW); on a Linux clang/libc++ toolchain it links
libc++.a/libc++abi.a/libunwind.a explicitly. A lower macOS floor (11–13) requires a
self-built libc++ archive (already verified to work, a data-level switch, available
on request).

`static_stdlib` is the older spelling and still works: `true` means
`self-contained`, `false` means `host-coupled`. An explicit `cxx_runtime` wins.

> **Current implementation limitation.** The parser recognizes `cxx_runtime`,
> but the current `[build]` unknown-key allowlist omits it. A normal build can
> therefore emit an unsupported-key warning, and `--strict` rejects the manifest.
> This is an implementation defect, not a different spelling or contract.

**A contract that cannot be honored is reported, never silently downgraded.** If a
toolchain ships no `libc++.a`, or a contract has no mechanism on that platform
(`self-contained` under the MSVC runtime would need `/MT`, which mcpp does not emit
yet), the build prints what it fell back to instead of quietly producing a
different artifact than the manifest asked for.

**Scope.** The contract governs the C++ runtime only. Static **libc** is a separate
axis (`linkage = "static"` / `--static`, e.g. a musl target), and the deployment
floor is a third (`macos_deployment_target`). Also, `host-coupled` means mcpp adds
nothing to embed a C++ runtime; it does not strip the toolchain rpath the link
carries for other reasons, so on ELF such an artifact may still find the
toolchain's libraries first.

> **macOS + `self-contained` and static initialization order.** Mach-O has no
> priority-ordered initializer section and libc++'s `<iostream>` carries no
> `ios_base::Init` guard of its own (unlike libstdc++ and the MSVC STL), so a
> stream initializer pulled out of `libc++.a` would otherwise run *after* the
> program's own global constructors — a global whose constructor touches
> `std::cout` would read an unconstructed stream and crash at process start. mcpp
> links a tiny generated object first to force the streams up; nothing is required
> of your code. See mcpp-community/mcpp#336.

`defines` takes **bare** macro names (no `-D`) and desugars each entry to `-D<x>` on
both the C and C++ compile channels. It reaches every TU in the package — module
interface units included — so it also reaches the P1689 module scan, which is what
makes a macro-guarded `import` resolvable. Assembly units pick it up too. It is a
build input like any other, so `[target.'cfg(...)'.build]` can carry it:

```toml
[build]
defines = ["APP_NAME=\"demo\""]

[target.'cfg(windows)'.build]
defines = ["USE_WIN32", "WINVER=0x0A00"]
```

Picking the right axis:

| You want the macro on… | Use |
|---|---|
| every TU of this package | `[build].defines` (here) |
| one binary's own entry source only | `[targets.<name>].defines` |
| a specific set of files | `[build].flags` with a `glob` + `defines` |
| every TU **and** every consumer's TUs | `[features.<name>].defines` (an interface contribution) |

`[build].defines` is private to the package: it does not propagate to consumers.

Unsupported keys under `[build]` are reported as a warning (an error under
`--strict`) rather than silently ignored.

Do not configure the C++ standard via `build.cxxflags = ["-std=..."]`. Instead use:

```toml
[package]
standard = "c++26"
```

mcpp applies the same standard to ordinary C++ compilation, module scanning,
`compile_commands.json`, and the standard library BMI build for `import std`.

**glob exclusion** (`!` prefix, mcpp 0.0.4+):

```toml
[build]
sources = [
    "src/**/*.cpp",
    "!src/**/*_test.cpp",       # Exclude test files
    "!src/**/*_fuzzer.cpp",     # Exclude fuzzers
]
```

**Per-glob flags** (mcpp 0.0.95+): `[build] flags` is an ordered array of
inline tables attaching extra compile flags to exactly the sources a glob
matches — the tool for SIMD dispatch TUs and vendored-code warning isolation:

```toml
[build]
flags = [
  { glob = "third_party/**",            cflags = ["-w"], cxxflags = ["-w"] },
  { glob = "src/simd/**/*.avx2.cpp",    cxxflags = ["-mavx2"], defines = ["HAVE_AVX2"] },
  { glob = "src/x86/**/*.asm",          asmflags = ["-DPREFIX"] },
]
```

Keys per entry: `glob` (package-root-relative, required) plus `cflags` /
`cxxflags` / `asmflags` / `defines` (no `ldflags` — linking has no per-TU
scope). Declaration order is application order: a later entry's flags land
later on the command line, so with GNU "last flag wins" a narrower glob
placed after a broader one overrides it. All matching entries apply. These
are private build flags — they never propagate to consumers. A glob that
matches no source file prints a warning (a typo'd glob must not silently do
nothing).

**Generated files** (mcpp 0.0.95+): `[generated_files]` maps a relative path
to file contents (TOML multiline strings supported). Entries are written
into the project tree before source globs expand — the same mechanism index
descriptors use to synthesize module wrappers — and the content enters the
fingerprint, so editing it rebuilds:

```toml
[generated_files]
"src/gen/wrap.cppm" = """
module;
#include <vendored.h>
export module wrap;
"""
```

Paths must stay inside the project root (`..` / absolute paths are parse
errors).

**Assembly sources** (mcpp 0.0.95+): `.S`/`.s` (GAS — preprocessed by the C
driver, covers ARM and AT&T-syntax x86) and `.asm` (NASM — Intel-syntax x86)
are first-class sources: default-globbed, fingerprinted, built incrementally
in parallel, and linked like any other object. The NASM output format is
derived from the target triple (`elf64`/`win64`/`macho64`/... — cross builds
just work), and `nasm` itself is resolved lazily only when `.asm` units exist:
`PATH` first, then the mcpp sandbox, then `xlings install nasm`; if none
yields nasm ≥ 2.16 the build **fails hard** (assembly is never silently
skipped). Limits: `.asm` targets x86 only (hard error elsewhere — gate the
files off other targets), `.S` is unavailable on the MSVC toolchain, and
`.asm` means NASM syntax (MASM sources should be `!`-excluded).

### 2.4 `[lib]` — Library Root Module Convention

```toml
[lib]
path = "src/capi/lua.cppm"    # Override the default lib-root location
```

Default convention: `src/<last segment of package name>.cppm` (e.g. package name `mcpplibs.cmdline` → `src/cmdline.cppm`).

### 2.5 `[dependencies]` — Runtime Dependencies

```toml
# Packages under the default package namespace (mcpplibs)
[dependencies]
gtest   = "1.15.2"              # Exact version
mbedtls = "3.6.1"
ftxui   = "6.1.9"

# Dotted selector: try mcpplibs.<path> first, then fall back to the sibling peer root.
# For example, imgui.core is tried in order as mcpplibs.imgui/core, then imgui/core.
[dependencies]
capi.lua = "0.0.3"
compat.gtest = "1.15.2"
imgui.core = "0.0.1"
imgui.backend.glfw_opengl3 = "0.0.1"

# Namespace sub-table form
[dependencies.mcpplibs]
cmdline   = "0.0.2"
tinyhttps = "0.2.2"
llmapi    = "0.2.5"

[dependencies.compat]
glfw = "3.4"                    # Explicit namespace, skips the mcpplibs-first candidate

# Path dependency (local development)
[dependencies]
mylib = { path = "../mylib" }

# Git dependency — pick exactly one of tag / branch / rev
[dependencies]
mylib = { git = "https://github.com/user/mylib.git", tag = "v1.0.0" }
applib = { git = "https://github.com/user/applib.git", branch = "develop" }

# Long-form dep spec: features and backend knobs
[dependencies]
imgui = { version = "0.0.3", features = ["docking"] }   # Request a feature of this dependency
widget = { version = "1.0", backend = "glfw_opengl3" }  # Sugar for: features=["backend-glfw_opengl3"]
```

`backend = "<impl>"` is **general-purpose convention sugar**: it desugars 1:1 into
requesting the dependency's `backend-<impl>` feature (a library that supports this
knob should declare a `backend-*` family in its own `[features]`). If the target
package declares `[features]` but does not include the requested feature (including
the result of backend desugaring), a warning is issued by default, and an error
under `mcpp build --strict`.

**Git dependencies and `mcpp.lock`**: a `tag` or `rev` already names a fixed point
in history, but a `branch` moves. The first build resolves the branch to a commit
and records it in `mcpp.lock`, and every later build rebuilds **that** commit — the
lock is authoritative, not a cache hint, so deleting `~/.mcpp/git` or moving to
another machine cannot quietly put you on a newer tip. Ask for the newer tip
explicitly:

```bash
mcpp update mylib     # drop the recorded commit; the next build re-resolves it
mcpp update           # same, for every dependency
```

Because the recorded commit is enough to decide what to build, a rebuild with the
clone already in `~/.mcpp/git` makes no network request at all and works under
`--offline`. Only two things need the network: resolving a branch that has no
commit in the lock, and cloning a commit that is not cached yet. A `git =` value
that names a local directory (or a `file://` URL) needs neither, so it is never
refused offline.

**SemVer constraints**:

```toml
[dependencies]
foo = "^1.2.3"      # >= 1.2.3, < 2.0.0 (caret, default)
bar = "~1.2.3"      # >= 1.2.3, < 1.3.0 (tilde)
baz = "=1.2.3"      # Exact match
qux = ">=1.0, <2.0" # Range combination
```

#### Namespace resolution rules

Every package has a two-part identity: a **namespace** and a **name**. How you write
a dependency key decides which namespaces mcpp will look in.

**A bare name resolves in exactly three places**, in order:

| # | Namespace | Example |
|---|---|---|
| 1 | `mcpplibs` — the default namespace | `cmdline = "0.0.2"` |
| 2 | `compat` — the wrapper namespace for third-party C/C++ libraries | `gtest = "1.15.2"` → `compat.gtest` |
| 3 | upstream packages that declare no namespace at all | `opencv = "4.10.0"` |

**Any other namespace must be written out in full.** There is no fuzzy, index-wide
search by short name:

```toml
# ✅ Correct — dotted selector
[dependencies]
"chriskohlhoff.asio" = "1.38.1"

# ✅ Correct — namespace sub-table (preferred when you have several from one org)
[dependencies.chriskohlhoff]
asio = "1.38.1"

# ❌ Wrong — a bare name never reaches the `chriskohlhoff` namespace
[dependencies]
asio = "1.38.1"
```

The third form fails with an error that lists the namespaces that were searched and,
when a package with that short name exists elsewhere, the exact line to write instead.

**Why not resolve bare names across every namespace?** Because dependency resolution
has to be reproducible. A global short-name search would mean that (a) two namespaces
owning the same short name are settled by index ordering, and (b) **adding an index
could silently change which package an existing dependency resolves to**. Requiring
the namespace keeps a `mcpp.toml` resolving to the same packages on every machine.

**For xpkg authors:** in an index descriptor, identity is the pair
`(package.namespace, package.name)`. The namespace is the dotted path; **`name` is
a single atomic segment**:

```lua
package = {
    namespace = "chriskohlhoff",
    name      = "asio",                 -- one segment; NOT "chriskohlhoff.asio"
}

package = {
    namespace = "mcpplibs.capi",        -- depth belongs here
    name      = "lua",
}
```

The filename is only a hint — a descriptor is found by its declared identity, so
`pkgs/c/chriskohlhoff.asio.lua` and `pkgs/z/anything.lua` resolve identically.
`<name>.lua` or `<namespace>.<name>.lua` are recommended (they hit mcpp's fast
path) but not required.

The older fully-qualified spelling (`name = "chriskohlhoff.asio"`) is still
accepted, so already-published descriptors keep working. `mcpp xpkg parse`
enforces the rule — run it in your index CI. Requires mcpp >= 0.0.106 and
xlings >= 0.4.69; full normative text in `docs/spec/package-identity.md`.

#### When mcpp refreshes the package index

`mcpp build` / `run` / `test` refresh the package index **only when a dependency
cannot be resolved from the local copy** — never merely because time has passed.
Concretely, a refresh happens when there is no local index at all, when a
dependency's descriptor is missing from it, or when a SemVer constraint matches
none of the versions it knows. A build whose dependencies all resolve locally
makes no network request, however old the local index is.

The consequence worth knowing: a constraint like `^1.2` resolves against the
**versions your local index knows**. If `1.3.0` was published upstream after
your last refresh, you will not see it until you ask:

```bash
mcpp index update     # sync the index
mcpp update           # sync, then re-resolve dependencies
mcpp index status     # what you have locally: state, age and revision
```

Controls, in order of precedence:

| Control | Effect |
|---|---|
| `--offline` (any command) | Never touch the network — no index refresh, no downloads, no toolchain auto-install, no `git ls-remote`/`clone`. Anything already installed still builds, including git deps whose commit is in `mcpp.lock` and whose clone is cached |
| `MCPP_OFFLINE=1` | Same, for a whole shell session or CI job |
| `[index] auto_refresh = false` in `~/.mcpp/config.toml` | Never refresh the index automatically; downloads still work |

`MCPP_NO_AUTO_INSTALL=1` remains accepted as the older, narrower spelling of
`--offline` (it gates only toolchain auto-install).

Run any command with `-v` to see the decision for each dependency and why.

### 2.6 `[dev-dependencies]` — Test Dependencies

```toml
[dev-dependencies]
gtest = "1.15.2"
```

`mcpp build` ignores these; `mcpp test` resolves and uses them. `mcpp test` automatically discovers `tests/**/*.cpp` and compiles them into test binaries. The runner is framework-agnostic: each file is an independent binary judged by exit code — a bare `main`, gtest (via `[dev-dependencies]` + `gtest_main`), or any other framework all work identically, and `-- args` are forwarded to every test binary (e.g. `-- --gtest_filter=...`). Note: synthesized test target names may contain `/` (`tests/00-a/0.cpp` → `00-a/0`), unlike `[targets.*]` names — the two namespaces are intentionally separate (test targets never enter the manifest or publishing). Tests are named by their `tests/`-relative path (`tests/00-a/0.cpp` → `00-a/0`), each test compiles in isolation (a broken test fails alone; package/dep breakage is reported as a build error instead), and `mcpp test <pattern>` / `--message-format json` filter and machine-format the run.

### 2.7 `[toolchain]` — Toolchain Configuration

```toml
[toolchain]
default = "gcc@16.1.0"

# Cross-compilation target override
[target.x86_64-linux-musl]
toolchain = "gcc@16.1.0"
linkage   = "static"
```

### 2.7.1 `[target.*]` — Platform-Conditional Dependencies & Flags

Scope dependencies and build flags to a platform with a `[target.<sel>]` table.
The selector `<sel>` has three forms:

| Selector | Meaning | Example |
|---|---|---|
| **bare OS alias** | a single OS / family — the concise, common form | `[target.windows]`, `[target.unix]` |
| **`cfg(...)` predicate** | a compound condition (arch / env / combinators) | `[target.'cfg(all(linux, not(arch = "aarch64")))']` |
| **exact triple** | one specific target (also carries `toolchain` / `linkage`) | `[target.x86_64-linux-musl]` |

Under a selector you may put platform-conditional **dependencies** and **build flags**:

```toml
# Concise bare-alias form — pull OpenBLAS and link it only on Windows.
[target.windows.dependencies.compat]
openblas = "0.3.33"
[target.windows.build]
ldflags = ["-Llib", "-llibopenblas"]

# cfg(...) for compound predicates (grammar: all/any/not over os/arch/family/env,
# plus the bare aliases windows/unix/linux/macos).
[target.'cfg(all(linux, not(arch = "aarch64")))'.build]
cxxflags = ["-march=x86-64-v2"]
```

`[target.windows]` is exactly equivalent to `[target.'cfg(windows)']` — the bare
aliases `windows` / `linux` / `macos` / `unix` are never valid target triples, so
there is no ambiguity. Use the bare form for a single OS/family; use `cfg(...)`
when you need arch/env conditions or combinators.

- **Keys**: `dependencies` / `dev-dependencies` / `build-dependencies` /
  `feature-deps.<feature>` (mcpp 2026.8.6.2+ — see §2.14; the feature is
  registered unconditionally, only its dependency set is scoped), and
  `build` with `cflags` / `cxxflags` / `ldflags` / `sources` (mcpp 0.0.95+ —
  conditional source globs, e.g. gating `src/x86/**/*.asm` behind
  `cfg(arch = "x86_64")`; `!`-exclusion globs work here too), plus `flags` and
  `include_dirs` / `include_dirs_after` (mcpp 0.0.102+).
- **What `build` accepts is exactly the set of *additive build inputs*** — the
  things that combine by appending and are consumed after the predicate is
  evaluated. `linkage`, `target`, and the profile knobs are deliberately not
  among them: they are *inputs to* target selection (conditioning `target` on
  a predicate evaluated against `target` is circular), or they need
  override-rather-than-append semantics.
- **Evaluated against the resolved target** — the `--target` triple for a cross
  build, otherwise the host. So a native Linux build never even *downloads* a
  `[target.windows]` dependency.
- **Precedence**: an exact-triple table wins over a `cfg`/alias table; multiple
  matching predicate tables have their flags concatenated. Conditional entries
  are appended **after** the unconditional `[build]` ones, so under GNU
  "last flag wins" a conditional rule overrides a broader unconditional one.
  That is what makes a per-OS **removal** expressible:

  ```toml
  [build]
  flags = [{ glob = "third_party/zlib/**", defines = ["HAVE_UNISTD_H=1"] }]

  # clang-MSVC has no <unistd.h>: undo the base define, add the windows one.
  [target.'cfg(windows)'.build]
  flags = [{ glob = "third_party/zlib/**",
             defines = ["NO_FSEEKO"], cflags = ["-UHAVE_UNISTD_H"] }]
  ```

- **A conditional `flags` entry that does not match the current target does not
  exist at all**, so it cannot produce a "glob matched no source file" warning.
  One manifest can therefore carry all three OSes' flag tables without any of
  them generating noise on the other two — the same way an inactive feature's
  entries simply are not there. A zero-hit glob in the *unconditional* table
  still warns, because there it is a real defect.
- **`toolchain` / `linkage` are exact-triple only** — they describe one specific
  cross target, so put them under `[target.<triple>]` (above), not under a bare
  alias or `cfg(...)`.

### 2.8 `[features]` — Features (Cargo-style, additive)

```toml
[features]
default = ["base"]        # Default activation set
base    = []
docking = ["extra"]       # Activating docking implies activating extra (transitive closure)
extra   = []
```

- Activation sources: the package's own `default` set ∪ explicit requests (the root
  package via `mcpp build --features a,b`; dependencies via the long-form dep spec's
  `features = [...]` / `backend = "..."` sugar).
- Each activated feature gets the macro `-DMCPP_FEATURE_<NAME>` during that package's
  compilation (the name is uppercased and non-alphanumerics become `_`, e.g.
  `backend-a` → `MCPP_FEATURE_BACKEND_A`).
- **strict validation**: when the target package declares a `[features]` table,
  requesting an undeclared feature produces a warning; an error under `--strict`. A
  package that does not declare `[features]` accepts any request (pure macro usage).

#### Table form — a feature that contributes more than implied features

A `[features]` entry may be written as a **table** instead of an array, letting the
feature carry package-owned preprocessor `defines`, feature-gated source globs
(`sources`, mcpp 0.0.95+ — the globs leave the default build and compile only when
the feature is active, exactly like an index descriptor's `features.<f>.sources`;
the highest-frequency shape for vendored libraries: *feature = a source set + a
define*), feature-gated per-glob compile flags (`flags`, mcpp 0.0.101+), and/or
capability `requires` / `provides` (see §2.8.1) alongside its implied features:

```toml
[features]
default    = []
# Array shorthand: just implied features.
docking    = ["extra"]
extra      = []
# Table form: contribute a package-owned define when active.
mpl2only   = { defines = ["EIGEN_MPL2_ONLY"] }
# Table form: a define + an implied feature.
fast_math  = { defines = ["APP_FAST=1"], implies = ["extra"] }
# Table form: feature-gated sources + per-glob flags that co-locate with them.
simd       = { sources = ["src/simd/**"], flags = [
                 { glob = "src/simd/**/*.avx2.cpp", cxxflags = ["-mavx2"] } ] }
```

- `defines` are **bare** macro names (no `-D`); each desugars to `-D<x>` on the
  package's own compile when the feature is active — exactly like `[targets.*]
  defines`. They are restricted by convention to the package's **own** namespaced
  macros: a feature does **not** inject free-form package-wide `cflags`/`ldflags`,
  which would break the additive feature-union model. Link flags come from a
  provider dependency (§2.8.1), not from a feature.
- The automatic `-DMCPP_FEATURE_<NAME>` is still defined for every active feature,
  so `defines` are additive to it.
- `flags` (mcpp 0.0.101+) is the same ordered array-of-inline-tables grammar as
  `[build].flags` (§2.3: `glob` required, plus `cflags`/`cxxflags`/`asmflags`/
  `defines`; the `[[features.<name>.flags]]` array-of-tables spelling is accepted
  too, like `[[build.flags]]`). When the feature is active the entries
  are appended **after** the base `[build].flags`, features in name order, so a
  feature rule wins over a broader base rule via "last flag wins"; when it is
  inactive the entries do not exist (no dead-glob warning). This is how a
  feature's group-specific flags co-locate with its `sources` instead of living
  as base rules whose globs go dead on feature-off builds. Unlike `defines`,
  feature `flags` are **private per-TU build flags** — they never propagate to
  consumers (same contract as `[build].flags`), so they stay inside the additive
  model: scoped by glob, deterministic order, no cross-package effect.

### 2.8.1 `provides` / `requires` — Capabilities (backend selection)

A **capability** is a shared abstract name (e.g. `blas`). A package can *provide*
one; a feature can *require* one instead of naming a concrete package, and the
resolver binds exactly one provider from the dependency graph. This is how you pick
one of several interchangeable backends (OpenBLAS / MKL / …) without baking a choice
into the library.

```toml
# A provider package satisfies a capability for any dependent that requires it.
[package]
name     = "compat.openblas"
version  = "0.3.0"
provides = ["blas", "lapack"]
```

```toml
# A consumer requires the abstract capability via one of its features.
[features]
use_blas = { defines = ["EIGEN_USE_BLAS"], requires = ["blas"] }

# When >1 provider is in the graph, pick one (else the build errors and lists them).
[capabilities]
blas = "compat.openblas"     # equivalently: mcpp build --cap blas=compat.openblas

[dependencies]
compat.openblas = "0.3.0"    # the provider must be a real dependency in the graph
```

Binding is **deterministic**:

| Providers of a required capability in the graph | Result |
|---|---|
| exactly one | bound automatically (no config needed) |
| a `[capabilities]` pin / `--cap` names one | the pin wins |
| zero | **error**: no package provides `<cap>` |
| two or more, unpinned | **error**, listing the candidates — never a silent guess |

The bound provider's link/include flags reach the consumer through normal
dependency mechanics; the capability layer is the *selection-and-validation* step
that turns a silently-wrong or missing backend into a loud configure-time error.

### 2.8.2 `[feature-deps.<name>]` — dependencies a feature pulls in

A dependency declared under `[feature-deps.<name>]` is **optional**: it is
resolved only when that feature is active (root `--features`, or a dependency
spec's `features = [...]`). A dependency in `[dependencies]` is always resolved;
optionality is expressed by *where* you declare it, not a flag.

```toml
[features]
use_blas         = { defines = ["EIGEN_USE_BLAS"], requires = ["blas"] }
backend-openblas = { implies = ["use_blas"] }

# Pulled ONLY when `backend-openblas` is active. Each entry is a full dependency
# spec (version/path/git + its own features).
[feature-deps.backend-openblas]
compat.openblas = "0.3.x"
```

This composes with capabilities (§2.8.1): a single `backend-openblas` feature
both **pulls** the provider (`compat.openblas`, which `provides = ["blas"]`) and
**turns on** the consumer switch (`implies = ["use_blas"]`, which
`requires = ["blas"]`). With one provider in the graph the capability binds
automatically — `features = ["backend-openblas"]` is all the consumer writes.

In an index package's Lua descriptor the same is written inline:

```lua
features = {
    use_blas         = { defines = { "EIGEN_USE_BLAS" }, requires = { "blas" } },
    ["backend-openblas"] = {
        implies = { "use_blas" },
        deps    = { ["compat.openblas"] = "0.3.x" },
    },
}
```

### 2.8.3 `[scan_overrides."<glob>"]` — Author-Asserted Scan Results

The default module scanner is a text-level pass that (deliberately) rejects
`import` statements inside conditional preprocessor blocks. Some legitimate
module units carry them — e.g. fmt's official `src/fmt.cc` guards
`import std;` behind `#ifdef FMT_IMPORT_STD`. When the file's import set is
known and stable, declare it instead of scanning:

```toml
[modules]
sources = ["src/**/*.cppm", "vendor/fmt.cc"]

[scan_overrides."vendor/fmt.cc"]
provides = ["fmt"]      # at most one provided module per unit
imports  = ["std"]
```

Files matched by the glob skip the text scan; the declared unit enters the
module graph directly. The declaration is **audited every build**: the
compiler's own P1689 scan of the file (the `.ddi` dyndep input) is compared
against it, and any divergence fails that compile edge with both sides
printed — a stale declaration cannot silently corrupt the graph. An override
glob that matches no source file is an error.

The same key exists in xpkg descriptors (index packages):

```lua
mcpp = {
    sources  = { "*/src/fmt.cc" },
    cxxflags = { "-DFMT_IMPORT_STD" },
    scan_overrides = {
        ["*/src/fmt.cc"] = { provides = { "fmt" }, imports = { "std" } },
    },
}
```

To extend the plan-vs-ddi audit to *every* module unit (not just overrides),
set `MCPP_VERIFY_MODGRAPH=1` when generating the build.

### 2.9 `[profile.<name>]` — Build Profiles

```toml
[profile.dist]
opt      = 3              # -O level (a number, or the string "s"/"z")
debug    = false          # -g
lto      = true           # -flto (note: some packaged gcc builds ship without the LTO plugin)
strip    = true           # -s at link time
# passthrough escape hatch (fixed keys, open values):
cflags   = ["-fno-plt"]
cxxflags = ["-fno-plt"]
ldflags  = []
```

- Selection & default: a bare `mcpp build` uses the **`dev`** profile (`-O0 -g`) — the
  mainstream convention (cf. Cargo/Meson/CMake/Zig/Bazel). **Release is opt-in:**
  `mcpp build --release` (shorthand) or `--profile release`. `--dev` is the explicit
  shorthand for dev. Same applies to `mcpp test --profile <name>` (builds the
  code-under-test plus the test binaries under that profile).
- **Per-project default** — `[build].default-profile = "<name>"` (alias: `profile`) sets
  the project's own default when no flag is passed. The typical use is a tool/library
  that should build optimized by default: `[build] default-profile = "release"`. Precedence:
  `--profile`/`--release`/`--dev` flag **>** `[build].default-profile` **>** global `dev`.
  (A project that defaults to dev should pass `--release` when producing a distributable.)
- 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.
- **Each profile owns its own build directory.** The resolved profile knobs
  participate in the fingerprint, so `target/<triple>/` holds one hash directory
  per profile and switching between them is incremental instead of a full
  rebuild. It also means the disk cost scales with the number of profiles you
  actually use.

### 2.10 `[build] cache` — The Global Dependency Cache

Compiled artifacts for dependencies fetched from an index are cached across
projects under `$MCPP_HOME/build-cache/v1/`. A dependency's artifacts do not
depend on who consumes them, so two projects with the same toolchain, profile and
dependency versions reuse one entry.

```toml
[build]
cache = "global"   # "global" (default) | "local" | "off"
```

| Mode | Reads the cache | Writes the cache | Clears the build dir first |
|---|---|---|---|
| `global` (default) | yes | yes | no |
| `local` | no | no | no |
| `off` | no | no | yes |

`local` builds every dependency inside this project's `target/` — useful to rule
the cache out while diagnosing something, and to give CI a no-sharing baseline.
`off` additionally clears this build's `target/<triple>/<fp>/` for a cold
rebuild; `--no-cache` is a deprecated alias for it.

Precedence: `--cache <mode>` **>** `MCPP_BUILD_CACHE` **>** `[build] cache` **>**
`global`. An unrecognized value is reported (an error under `--strict`) rather
than silently falling back to `global`.

What is **not** cached: `path` and `git` dependencies, at any depth, and
workspace members. Their sources can change without their `name@version`
changing, so no key over that identity could notice the change.

Inspection and reclamation:

```
mcpp cache dir                      # where the cache lives
mcpp cache list [--json]            # entries, sizes, last use
mcpp cache info <pkg>@<ver>         # one entry, including the key inputs it was built with
mcpp cache verify                   # every entry's file list against the disk
mcpp cache gc --max-size 5GiB       # LRU-collect package entries to a budget
mcpp cache gc --older-than 30d      # ...or by how long since they were last used
mcpp cache clean [--deps|--std|--all|--legacy]
```

The on-disk entry layout is versioned. An mcpp release that changes it retires
every older entry at once, so the first build after such an upgrade rebuilds
its dependencies and repopulates — nothing to clean by hand. 2026.8.3.4 did
exactly that: an entry's object paths are now addressed relative to the
*package*, never to the build directory of whichever project happened to
populate the entry first. `mcpp cache verify` additionally reports any entry
whose recorded addresses escape it, so a recurrence is auditable offline.

### 2.11 `[runtime]` — Host Runtime Capabilities

```toml
[runtime]
library_dirs = ["vendor/lib"]            # Directories baked into the artifact's RUNPATH (relative to the package root)
dlopen_libs  = ["libGL.so.1"]            # sonames dlopen'd at runtime (validated by doctor)
capabilities = ["opengl.glx.driver"]     # Host capabilities required (open namespace)
provides     = ["opengl.glx.driver"]     # Explicitly declares capabilities this package fulfills (strong provider)

# Explicit provider override (the "explicit" notch of the three-way knob)
[runtime."opengl.glx.driver"]
provider = "compat.glx-runtime"
```

- **Provider selection**: a package that declares `provides` (strong) takes precedence
  over one that merely lists a capability under `capabilities` (weak, backward
  compatible); `[runtime.<cap>] provider=` is an explicit override with the highest
  precedence, and pointing at a provider not present in the dependency graph produces
  a warning.
- The resolved result can be inspected via `mcpp why runtime`, `mcpp self doctor`, and
  the build artifact `target/<triple>/<fp>/resolution.json` (it is not magic by
  default).
- Capability naming convention: layered lowercase `domain.sub.role` (e.g.
  `opengl.glx.driver`, `x11.display`) and prefix-style `abi:<name>` (e.g. `abi:glibc`,
  which participates in toolchain ABI enforcement).

### 2.12 `[package] platforms` — Platform Declaration

```toml
[package]
platforms = ["linux", "macos", "windows"]
```

Declares the platforms the package supports (a CI matrix hint, shown via `mcpp why`).
The vocabulary is fixed by mcpp (which owns the target/triple system):
`linux | macos | windows`; unknown values produce a warning, and an error under
`--strict`.

### 2.13 `[xlings]` — Build Environment

```toml
[xlings]
deps  = ["make@4.4", "cmake@3.28", "python@3.13"]   # host build-tools to provision
subos = "dev"                                        # a named per-project sandbox

[xlings.workspace]                                   # pin tool versions (general form of [toolchain])
clang = "20.1.7"

[xlings.envs]                                        # env vars applied to the tool environment
OPENBLAS_NUM_THREADS = "1"
```

Declares the project's **build environment**, provisioned through xlings (which mcpp
is built on). The subsection names mirror xlings' own `.xlings.json` schema **1:1**, so
mcpp materializes them verbatim into `<project>/.mcpp/.xlings.json` (no translation
layer): `deps` (host build-tools), `[xlings.workspace]` (tool→version pins),
`subos` (a named sandbox), `[xlings.envs]` (env vars). Use it to declare host tools a
build needs (`make`/`cmake`/`protoc`/…), pin tool versions per project, or set
build-time env vars — without hand-editing `.xlings.json`. `[toolchain]` (§2.7) remains
the ergonomic shorthand for the compiler; `[xlings.workspace]` is the general form.

`subos` carries a second meaning on Linux: it selects **which C runtime the
build binds against**. A subos describes its own runtime (xlings 2026.8.5.1+),
and mcpp takes that as the authority rather than looking around for a libc — so
`subos = "el8"` and `subos = "trixie"` in two projects on one machine produce
artifacts targeting each one's glibc, with the compile side and the run side
guaranteed to agree. The binding is part of the toolchain fingerprint, so
switching it rebuilds rather than reusing the other subos' objects. A subos
older than that (or none at all) falls back to whatever the toolchain itself
was installed against; `mcpp build -v` prints which of the two happened. See
docs/08-toolchain-internals.md §2.1.

### 2.14 Host tools from a dependency (mcpp 2026.8.5.1+)

A package can build a binary its consumers need *at build time* — `protoc`, a
`grpc_cpp_plugin`, `flatc`, `moc`, a transpiler. Ask for it on the dependency:

```toml
[dependencies]
protobuf = { version = "35.1",   tools = ["protoc"] }
grpc     = { version = "1.83.0", tools = ["grpc_cpp_plugin"] }
```

Each name must be a `kind = "bin"` target of that package. mcpp builds it **for
the build machine** and hands `build.mcpp` its absolute path as
`MCPP_DEP_<PKG>_BIN_<TOOL>` — read it with `mcpp::dep_bin("protobuf", "protoc")`
(see [07 — build.mcpp](07-build-mcpp.md)).

Four properties worth knowing:

- **Always a host binary.** Under `mcpp build --target <triple>` the tool is
  still built for *this* machine, because a code generator has to run here. It
  is a separate, host-targeted sub-build — the tool package's own `[toolchain]`
  and its own dependency resolution apply, and none of it has to agree with
  your build. That is safe precisely because an executable has no ABI contact
  with your code.
- **One version axis.** The tool's version *is* the dependency's version, so
  a `protoc` that does not match its runtime is not expressible. (This is the
  problem with packaging the tool separately, and it is the failure mode that
  bites at run time rather than compile time.)
- **Default off.** Nothing is built unless someone asks; the cost is the
  consumer's to pay. A package gates the expensive part with
  `[features]` + `required_features` (protobuf's `protoc` needs libprotoc's
  ~157 extra TUs, which the runtime's users must not compile).
- **Cached globally**, keyed on package version × host toolchain × features ×
  its own dependency closure — built once per machine, not once per project.

#### `[tools.overrides]` — use a binary you already have

```toml
[tools.overrides]
"compat.protobuf:protoc" = "/usr/bin/protoc"
```

or, without editing the manifest (CI, distro packaging):

```bash
MCPP_TOOL_PROTOBUF_PROTOC=/usr/bin/protoc mcpp build
```

An override **skips the build entirely**. Every comparable system provides this
escape hatch (vcpkg's `VCPKG_HOST_TRIPLET`, CMake's `LLVM_NATIVE_TOOL_DIR`,
Qt's `QT_HOST_PATH`), and for the same reason: a tool that cannot be built from
source on this machine must not be a dead end. It is deliberately **not** part
of the cache key — an override is an escape hatch, not a reproducible input.

#### `host-module = true` — reusable build rules as packages

A rule (say "run protoc over these `.proto` files") should be written once, not
copy-pasted into every consumer's `build.mcpp`. Ship it as an ordinary mcpp
library package and import it:

```toml
[dependencies]
protobufgen = { version = "0.1.0", host-module = true }
```

```cpp
// build.mcpp
import mcpp;
import protobufgen;
int main() { return protobufgen::generate({"schema"}) ? 0 : 1; }
```

mcpp compiles that package's lib-root module **for the host, in the same
command as `build.mcpp`** — which is what makes the BMI usable at all, since a
module interface is only importable by a compile that agrees with it on
standard, dialect and compiler identity.

Rules are therefore versioned, testable and distributable through the package
manager you already have, written in **C++** — no second language, which is the
whole point of `build.mcpp` existing.

**The module name is the package's `name`.** mcpp registers the host module
under the dependency's bare `package.name` (not `<namespace>.<name>`), so a
rule package's name has to be a legal C++ module name: `grpcgen` works,
`grpc-rules` does not — the hyphen is fine in a package name and illegal in a
module name, and the mismatch surfaces as `module 'grpc_rules' not found`
rather than as a complaint about the name.

The lib root must be at `src/<name>.cppm` (or wherever `[lib] path` points); a
missing one is reported as *"host module 'x': no interface unit at …"*.

*Limit:* the rule interface is compiled alone, so it may import `std` and the
bundled `mcpp` module, but not a third package. A rule package is a leaf.

*Build-time only:* a `host-module = true` dependency is **not** compiled into
or linked with your target. It exists to run during `build.mcpp` and nowhere
else — the same separation Cargo draws with `[build-dependencies]`. (Before
2026.8.5.2 it was also built as an ordinary library, which made `import mcpp;`
inside a rule fail: the bundled module does not exist in that second compile.)

#### `reexport = true` — a library standing up a toolchain for its user (2026.8.6.2+)

Everything above is declared by whoever *uses* the tool. That is the wrong
place when the knowledge belongs to a library: gRPC's code generation needs
protobuf's `protoc`, and no user of a gRPC package should have to know that.

`reexport = true` hands an edge's build-time provisions — its `tools`, its
`host-module`, and the dependency's directory — to **this package's own
consumers**:

```toml
# inside the grpc package's manifest
[feature-deps.codegen]
"compat.protobuf" = { version = "35.1",   tools = ["protoc"],            reexport = true }
grpc-plugin       = { version = "1.83.0", tools = ["grpc_cpp_plugin"],   reexport = true }
grpcgen           = { version = "1.83.0", host-module = true,            reexport = true }
```

Its user then writes one line, and imports the rule:

```toml
[dependencies]
grpc = { version = "1.83.0", features = ["codegen"] }
```

```cpp
// build.mcpp
import mcpp;
import grpcgen;
int main() { return grpcgen::generate_all() ? 0 : 1; }
```

- **Off by default, and deliberately not the edge's `visibility`.** `visibility`
  already defaults to `"public"`, so riding it would let any dependency at any
  depth put entries into your build program's tool namespace without saying so.
  Handing something to your consumers is a supply-chain statement; it has to be
  written down.
- **One hop per declaration.** A re-exported provision reaches the consumers of
  the package that declared it. For it to travel further, the next package must
  re-export in turn — each package decides only what *it* hands on.
- **A feature may add a request to a dependency you already declare.** gRPC
  depends on protobuf unconditionally and its `codegen` feature adds
  `tools = ["protoc"], reexport = true` to that same edge. `tools` and
  `features` union, `host-module` and `reexport` OR together; `version` /
  `path` / `git` do not merge, so a feature still cannot silently override the
  unconditional entry's identity.
- **Visibility, not execution.** `dep_bin()` returns a path; whether anything
  runs is still the consumer's `build.mcpp`'s decision. Nothing changes about
  who builds the tool or how the tool store is keyed.
- **Unqualified names are resolved by a ladder, not by luck.** Once two
  libraries can re-export, both may offer the tail `protobuf`. The
  fully-qualified `MCPP_DEP_<NS>_<NAME>_BIN_<TOOL>` is always published; the
  bare spelling is bound to `mcpplibs.<x>`, else `compat.<x>`, else an
  unnamespaced `<x>`, else the single remaining candidate — and when it is
  contested mcpp says so instead of picking silently.

##### Older mcpp reading a manifest that uses this

An unrecognized dependency key is reported as a **degradation** and ignored
(mcpp 2026.8.6.2+), so a package written for a newer mcpp still loads and the
parts this reader understands still apply. Before that release it was a hard
load failure with a misleading message, which is why a published package could
not adopt a new key at all — the same property the index floor establishes:
data must not decide whether the program works.

Consequently a package that *relies* on `reexport` for its ergonomics still
needs a client new enough to implement it; what changed is that everything else
about that package keeps working on an older one.

##### Scoping a provision per platform

A package may declare a `bin` target on some platforms only. Because the
*library* now decides what is requested, an unconditional request turns an
unsupported platform into an error its user cannot edit away. Scope it:

```toml
[target.'cfg(not(windows))'.feature-deps.codegen]
"compat.protobuf" = { version = "35.1", tools = ["protoc"], reexport = true }
```

`[target.<sel>.feature-deps.<feature>]` (2026.8.6.2+) follows the same rules as
the other conditional dependency tables (§2.7.1). The **feature itself is
registered on every platform** — only what it pulls in is conditional — so
requesting it where no predicate matches is not an unknown-feature error.

### 2.15 `[resources]` — Metadata and Assets Embedded in the Artifact (2026.8.7.1+)

An exe icon and the version metadata Windows shows in a file's Properties dialog
are a path in `mcpp.toml`, nothing more:

```toml
[resources]
icon = "assets/app.ico"
```

That is the whole common case. `FILEVERSION`, `ProductName`, `FileDescription`,
`CompanyName` and `LegalCopyright` all default from `[package]`, and mcpp
generates the resource script for you.

| Key | Type | Meaning |
|---|---|---|
| `icon` | path | Embedded as the application icon (resource ordinal 1) |
| `files` | list of paths | Your own `.rc` scripts, compiled and **tracked** as build inputs |
| `extra-inputs` | list of paths | Inputs the `.rc` scanner could not see (see below) |
| `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
**not** need (and cannot use) a `cfg(windows)` predicate — write it once,
unconditionally.

**A declared file that does not exist fails the build — on every target.** A
resource is a build input like a source file; mcpp will not quietly ship a
binary without it. Validation is deliberately *not* PE-gated: whether a path
exists is a fact about your working tree, not about the target, so a typo in
`icon = "assets/app.ico"` is caught by your Linux or macOS build (and by their
CI jobs) instead of waiting for the Windows one. If you do not want an icon,
delete the line.

**Version fields.** `FILEVERSION` takes the four numeric segments of
`[package].version`, each of which must fit in 16 bits; the string fields keep
the version verbatim, so a form the numeric fields cannot hold (`1.0.0-rc1`)
still shows up in the Properties dialog.

#### Writing your own `.rc`

```toml
[resources]
files = ["res/app.rc"]
```

With `files` set, mcpp stops generating a version resource — you own the
resource ID space. Set `version-info = true` alongside it if you want both (and
mind the collision: there can be only one `RT_VERSION` at ordinal 1).

To start from the generated script instead of a blank file, copy it out of the
build directory (`target/<triple>/<fp>/res/<target>.mcpp.rc`) and list it in
`files`. The result is byte-identical, so moving from generated to hand-written
never changes what ships.

> **`VS_VERSION_INFO` needs `<windows.h>`.** In a hand-written script,
> `VS_VERSION_INFO VERSIONINFO` without `#include <windows.h>` files the version
> resource under a *string* name instead of ordinal 1. Every tool still reports
> `Type: VERSIONINFO`, but `GetFileVersionInfo` looks up the ordinal, so
> PowerShell's `FileVersionInfo` shows every field as empty. Either include
> `<windows.h>` or write `1 VERSIONINFO`. mcpp warns when it sees this shape;
> the script it generates uses the literal `1`.

#### Tracked inputs

mcpp reads the `.rc` for quoted `#include`s and for the files named by resource
statements (`ICON`, `RCDATA`, `MANIFEST`, …), and makes them build inputs, so
editing your icon relinks. Angled includes (`<windows.h>`) are the toolchain's
and are covered by the toolchain fingerprint instead.

A file name reached through a macro (`1 ICON APP_ICON`) is invisible to that
scan. mcpp names what it could not resolve and asks you to declare it:

```toml
extra-inputs = ["assets/app.ico"]
```

#### Anything else: `role = "object"`

For inputs that are not resource scripts — a blob embedded with `objcopy`, a
generated `.def`, a pre-built object — a build program can declare a build-graph
node whose outputs join the link:

```cpp
mcpp::action o;
o.id = "blob"; o.role = "object";
o.arg("./mkblob.sh").arg("blob.bin").arg("${mcpp.out_dir}/blob.o")
 .input("blob.bin")
 .output("${mcpp.out_dir}/blob.o")
 .target("myapp")        // omit: every image, test binaries included
 .submit();
```

See [07 — build.mcpp](07-build-mcpp.md). Naming such a file in
`[build].ldflags` also "works", but ldflags is a flat string in the link
command: nothing tracks it, and editing the file gives you `ninja: no work to
do`.

## Appendix A. Schema Ownership Principle (admission criteria for new fields)

> **Closed syntax, open vocabulary**: whoever owns the parsing semantics defines the keys; whoever owns the domain knowledge defines the values.

- mcpp only defines **mechanisms** (feature union/closure, capability
  require/provide/override, profile→compiler flags, platform→triple); the keys and
  shapes are fixed. Domain vocabulary such as feature names, capability names, and
  backend names **appears only in values**, never in mcpp's code.
- **Package-custom toml keys are not supported**: key legitimacy must not depend on
  "first parsing the target package," otherwise the manifest loses static
  parseability (a prerequisite for lockfiles/LSP/auditing). A package's extension
  point = open value domains within fixed mechanisms.
- Package-level knobs all converge into features; for sugar keys (such as `backend=`)
  to enter the core syntax, they must satisfy: ① domain-neutral (a cross-ecosystem
  general pattern) ② 1:1 desugaring with zero new parsing semantics.
- See `.agents/docs/2026-06-04-manifest-schema-ownership.md` for the full field-ownership
  table and the finalized decisions.

## 3. Worked Examples

### 3.1 Simple Hello World

```toml
[package]
name    = "hello"
version = "0.1.0"
```

```cpp
// src/main.cpp
import std;
int main() { std::println("Hello, mcpp!"); }
```

```bash
mcpp build && mcpp run
```

### 3.2 Module-based Library + Tests

```toml
[package]
name    = "mymath"
version = "1.0.0"

[targets.mymath]
kind = "lib"

[dev-dependencies]
gtest = "1.15.2"
```

```cpp
// src/mymath.cppm
export module mymath;
export int add(int a, int b) { return a + b; }
```

```cpp
// tests/test_add.cpp
#include <gtest/gtest.h>
import mymath;
TEST(Math, Add) { EXPECT_EQ(add(1, 2), 3); }
```

```bash
mcpp build   # Compile the library
mcpp test    # Compile + run tests
```

### 3.3 An Application Depending on Other Packages

```toml
[package]
name    = "myapp"
version = "0.1.0"

[dependencies]
ftxui = "6.1.9"

[dependencies.mcpplibs]
cmdline = "0.0.2"
llmapi  = "0.2.5"
```

mcpp automatically:
1. Downloads source tarballs from mcpp-index
2. Propagates header search paths per `[build].include_dirs`
3. Pulls transitive dependencies into the graph (llmapi → tinyhttps → mbedtls, fully automatic)

### 3.4 Pure C Library

```toml
[package]
name    = "myc"
version = "0.1.0"

[build]
c_standard   = "c99"
include_dirs = ["include"]
sources      = ["src/**/*.c"]

[targets.myc]
kind = "lib"
```

### 3.5 Mixed C / C++23 Module Project

```toml
[package]
name    = "hybrid"
version = "0.1.0"

[build]
include_dirs = ["include"]
c_standard   = "c11"

[dependencies]
lua = "5.4.7"     # Pure C library; mcpp compiles .c files with the C compiler automatically

[targets.hybrid]
kind = "bin"
```

### 3.6 Cross-Compiled Static Release

```toml
[package]
name    = "mytool"
version = "1.0.0"

[toolchain]
default = "gcc@16.1.0"

[target.x86_64-linux-musl]
toolchain = "gcc@16.1.0"
linkage   = "static"
```

```bash
mcpp build --target x86_64-linux-musl
# → Produces a fully statically linked binary that can be scp'd directly to any Linux x86_64 machine and run
```

## 4. Conventions and Defaults Cheat Sheet

| Item | Default | Notes |
|---|---|---|
| Source files | `src/**/*.{cppm,cpp,cc,c,S,s,asm}` | Scanned recursively and automatically |
| Entry point | `src/main.cpp` | If this file exists, a `bin` target is inferred |
| Library root | `src/<pkg-tail>.cppm` | Override with `[lib].path` |
| C++ standard | `c++23` | Configure with `[package].standard`; supports `c++20` / `c++26` / `c++2a` / `c++2c` / `gnu++NN` / `c++latest` / `c++fly` (experimental playground) |
| C standard | `c11` | `.c` files go through the C compiler automatically |
| Static stdlib | `true` | Portable binary |
| Headers | `include/` (if present) | Added to `-I` automatically |
| Tests | `tests/**/*.cpp` | Discovered automatically by `mcpp test` |
| Dependency namespace | `mcpp` (default) | The flat form uses the default ns |

### 4.1 Legacy `[language]` Compatibility Layer

The old configuration is still readable:

```toml
[language]
standard = "c++26"
```

New projects should use `[package].standard`. If both locations are present, `[package].standard` is authoritative.
