You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The C library layer declares the C environment it presents, the engine realises and checks it
Until now the compiler payload's target triple implied the environment a C
program compiles against. The moment a package supplies the C library
instead (mcpp:c-abi=<impl>), that stops being true: openkal-musl on
x86_64-windows-gnu generates PE/Win64 code while presenting a POSIX
environment, and every #ifdef _WIN32 above it is asking the wrong layer.
- [c-abi] manifest block: presents (posix/windows/none), data-model
(arch-default/lp64/llp64/ilp32), wchar (16/32), builtins (iso/platform).
Only a package that also provides mcpp:c-abi=<impl> may declare it.
presents/data-model/wchar carry no default; an unknown key or value is
always a parse error naming the key. Absent block changes nothing.
(modules/manifest/src/{targetside_model,toml,types}.cppm)
- Realisation (src/toolchain/cenv.cppm): a pure, table-driven mapping from
request to compiler tokens with no package names. The flagship case --
presents=posix on Windows/x86_64 -- realises as a Cygwin-flavoured
compile-only identity switch (--target=x86_64-pc-cygwin,
-U__CYGWIN__ -U__CYGWIN32__), leaving the link line on the graph's
resolved triple, because the two triples measure identical machine code.
Reaches C, C++, the dependency scan and the std module precompile.
A request this engine cannot realise is refused naming the target,
request and what is missing. [package] c-environment = "platform" opts a
package out of the realisation entirely.
- Verification, not trust (src/toolchain/cenv_probe.cppm): one -E -dM
predefined-macro dump, no codegen and no execution, checked against the
declaration and cached per configuration. Caught a real mapping bug
during development (data-model = "llp64" alongside presents = "posix" on
Windows silently read as already-satisfied); the mapping now refuses that
combination directly instead of relying on the probe to catch it late.
- __openkal__ defined for every target-side unit when the resolved
kernel-abi layer's interface is openkal, read from the layer's value.
- Closure visibility: provides = ["platform-sdk"] is a package's own
statement; the Target report gains a platform-deps line, and
[build] platform-dependencies = "refuse" fails the build when one is
present. (src/build/prepare.cppm)
- The realised environment and __openkal__ fold into the build fingerprint,
so LP64 and LLP64 builds of one source/manifest never share a directory.
The equivalent store-key gap for install-hook artifacts is designed but
not built here -- documented in docs/22 as a known gap.
Two pre-existing defects found while verifying this are filed separately,
not fixed here: #666 (a Clang-built mcpp binary SIGSEGVs
in its own ELF runtime inspector; GCC-built does not) and #667 (a GCC
self-host ICE importing mcpp.targetside from a new consumer under a
parallel build; -j1 avoids it).
Docs: docs/22 ([c-abi], verification, fingerprint), docs/21 (a declared
environment moves the compiled triple, not the linked one), docs/24 (the
three macro families, the __openkal__ rule, platform units), docs/06
(platform-sdk), and their zh mirrors. Tests: test_manifest.cpp,
test_cenv.cpp (14 cases covering the whole mapping table), e2e 741.
Design: mcpplibs/openkal .agents/docs/2026-09-18-openkal-c-environment-and-personalities-design.md
builtins = "iso"# iso | platform (default platform)
312
+
```
313
+
314
+
**Who may declare it.** A package that writes `[c-abi]` without also listing
315
+
`mcpp:c-abi=<impl>` in `provides` is stating a fact about a layer it does not
316
+
supply, and that is always wrong rather than merely unusual — it is refused
317
+
at manifest parse time, naming the missing `provides` entry.
318
+
319
+
**The four keys, and their closed value sets.**
320
+
321
+
| Key | Values | Answers |
322
+
|---|---|---|
323
+
|`presents`|`posix` / `windows` / `none`| which environment-identity macros source sees (`__unix__` vs `_WIN32` vs neither) |
324
+
|`data-model`|`arch-default` / `lp64` / `llp64` / `ilp32`| how wide `long` is |
325
+
|`wchar`|`16` / `32`| how wide `wchar_t` is |
326
+
|`builtins`|`iso` / `platform` (default) | whether the compiler may assume the platform C library's own extensions |
327
+
328
+
`presents`, `data-model` and `wchar` carry no default: a block that omits one
329
+
of them is refused naming the missing key, because "absent" is not the same
330
+
statement as any of the three closed values could make. `builtins` alone
331
+
defaults to `platform`, today's behaviour. An unrecognised key or an
332
+
unrecognised value is always a parse error naming the key — never silently
333
+
ignored. **A package that declares no `[c-abi]` block changes nothing**: the
334
+
resolved target side, every compile command and every cache key are
335
+
byte-identical to a build before this feature existed.
336
+
337
+
Three facts, kept separate, because none of them implies another: `presents`
338
+
picks the source branch, `data-model`/`wchar` pick the ABI. POSIX does not
339
+
imply LP64 (it is ILP32 on a 32-bit architecture), and LP64 does not imply
340
+
POSIX.
341
+
342
+
**Realisation.** Once the `c-abi` layer resolves to a package that declares
343
+
this block, mcpp turns the request into compiler configuration for every
344
+
target-side unit — the C library itself, the C++ runtime, the compiler
345
+
runtime's builtins, and every ordinary package in the graph — covering C,
346
+
C++ and assembly compiles, the dependency scan, and the `std` module
347
+
precompile alike. mcpp holds one mapping table from request to triple and
348
+
flags, generic knowledge that names no C library:
349
+
350
+
| Target | Request | Realisation |
351
+
|---|---|---|
352
+
| Linux |`posix` / `arch-default`| the default triple already satisfies it |
353
+
| macOS |`posix` / `arch-default`| the default triple already satisfies it |
354
+
| Windows |`posix` / `arch-default`| Cygwin-flavoured: `--target=x86_64-pc-cygwin` on the compile line only, `-U__CYGWIN__ -U__CYGWIN32__` (those interfaces are not in this graph); `data-model` becomes LP64 as a consequence of the triple, not a separate flag |
355
+
| any |`builtins = "iso"`| turns off code-generation idioms that assume a platform C library — `-fno-builtin-memset_pattern16` on Apple targets is the one this survey measured; see `src/toolchain/cenv.cppm` for what else was checked and found not to apply |
356
+
| anything else || refused, naming the target, the request and what is missing — never a silent downgrade |
357
+
358
+
The Windows row is the flagship case: `x86_64-w64-windows-gnu` and
359
+
`x86_64-pc-cygwin` produce IDENTICAL machine code — same PE format, same
360
+
Win64 calling convention, same SEH — and differ only in what the
361
+
preprocessor sees and how wide `long` is. Realisation therefore touches only
362
+
the **compile** line; the **link** line keeps the triple the graph resolved,
363
+
because nothing about the object format changed.
364
+
365
+
**A package's own units can opt out.** A package that provides
366
+
`mcpp:kernel-abi=openkal` (openkal-windows, say) has to see the platform's
367
+
own environment — it includes platform declarations and `_WIN32` must be
368
+
true for it. Such a package, or a platform shim, states:
369
+
370
+
```toml
371
+
[package]
372
+
provides = ["mcpp:kernel-abi=openkal"]
373
+
c-environment = "platform"# this package's own units compile in the
374
+
# triple's own default environment, whatever
375
+
# the graph's c-abi declares
376
+
```
377
+
378
+
This is a boundary rule, documented rather than enforced by the engine
379
+
beyond the flag itself: the interface such a package exposes to the rest of
380
+
the graph must still cross in fixed-width types only (SPEC §5.4).
381
+
382
+
**Verification, not trust.** A declaration is checked, never trusted — the
383
+
same rule openkal applies to its own conformance claims. Once the tokens
384
+
above are known, mcpp compiles one syntax-only probe (`-E -dM`, a predefined-
385
+
macro dump — cheap, and it needs no execution, which matters because the
386
+
realised environment is routinely a cross target) with them and reads back
387
+
`__SIZEOF_LONG__`, `__SIZEOF_WCHAR_T__` and which environment-identity
388
+
macros are defined, comparing them against the declaration. A mismatch fails
389
+
the build and prints both the declared and the measured values. The result
390
+
is cached per configuration (compiler binary identity + exact flags), so a
391
+
build that resolves the same configuration twice pays for the probe once.
392
+
393
+
**Fingerprint.** The realised environment participates in the build's
394
+
fingerprint (`compileFlags`, §92's field 7): two builds whose C library
395
+
declares `lp64` and `llp64` compile the same source into objects whose
396
+
`long` disagrees in width, so they never share an output directory, and
397
+
neither can reuse a cached object the other produced.
398
+
399
+
**Store key — not yet closed.** A package whose *install hook* compiles a
400
+
static library from source into the shared store is keyed by package and
401
+
version, not by which environment it was built against — the same gap
402
+
[requires](#requires) already documents for a C++ runtime selection. Closing
403
+
it the same way (a `requires`-shaped statement of the environment, checked
404
+
at resolution and refused on mismatch) is designed but not yet implemented;
405
+
until it is, such a package's install hook must not build more than one
406
+
environment's variant into one store directory, exactly as the C++-runtime
407
+
case already requires.
408
+
248
409
### Standard Library Module Sources
249
410
250
411
A package that is a standard library states where its `std` module source is
Copy file name to clipboardExpand all lines: docs/24-openkal-cross.md
+39Lines changed: 39 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,45 @@ reaches every architecture the compiler supports.
34
34
The claim is verified by a matrix of three hosts and three targets, each cell
35
35
building one source and running the result.
36
36
37
+
## Three Layers, Three Macro Families (mcpp 2026.9.18+)
38
+
39
+
A build over openkal answers three different questions, and until this
40
+
release one macro (`_WIN32`) answered two of them at once — the root cause
41
+
of every openkal-Windows failure whose diagnosis named a missing platform
42
+
header: the code was asking "is this openkal" through a macro that actually
43
+
meant "is the Windows CRT present."
44
+
45
+
| Family | States | Defined by | Example |
46
+
|---|---|---|---|
47
+
| kernel ABI |`kal_*` is callable, and behaves the same on every platform | the layer providing `mcpp:kernel-abi=openkal`|`__openkal__`|
48
+
| C environment | the shape of the C environment source sees | the layer providing `mcpp:c-abi=<impl>`, via [`[c-abi]`](22-target-side.md#the-c-environment-a-c-abi-package-presents-mcpp-2026918)|`__unix__`, `_WIN32`, `__MINGW32__`|
49
+
| system & architecture | the underlying OS and processor | the target triple |`__linux__`, `__APPLE__`, `__x86_64__`|
50
+
51
+
**`__openkal__` — the rule.** The engine defines it, for every target-side
52
+
unit, whenever the resolved `kernel-abi` layer's interface name is
53
+
`openkal` — read from the LAYER's value, never from a package name, so a
54
+
second implementation (`openkal-macos`, `openkal-opensbi`, …) needs no
55
+
engine change.
56
+
57
+
*Allowed:* gating whether a call site invokes `kal_*` at all. Its meaning is
58
+
identical on every target, so using it this way never smuggles platform
59
+
information into source that is supposed to be implementation-agnostic.
60
+
61
+
*Forbidden:* selecting a header, inferring whether `_WIN32` is real,
62
+
working around a missing SDK, or telling `linux`/`windows`/`macos` apart.
63
+
Those are the C-environment layer's or the platform layer's questions —
64
+
write `cfg(c-abi = "…")` or `cfg(kernel-abi = "…")` in the manifest instead
65
+
(and see [22 — Adaptation To The Resolved Target Side](22-target-side.md#adaptation-to-the-resolved-target-side)
66
+
for the predicate grammar).
67
+
68
+
**Platform units.** A package that itself needs the platform's own
69
+
environment — openkal-windows, or a platform shim under [06's private
0 commit comments