Skip to content
Merged
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
7 changes: 6 additions & 1 deletion .agents/docs/2026-09-20-wave-self-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,10 @@ status: active
最大的偏差。
2. **P3 撤 `__CYGWIN__`。** 判据已由 mimalloc 与 sqlite3 给出,代价需一次重测。
3. **`__cxa_thread_atexit`。** 本轮测量新发现的 C++ 运行时缺口。
4. **openkal-macos 的 `provides-interfaces`。** 无法在 Linux 宿主派生,需 macOS runner。
4. **openkal-macos 的 `provides-interfaces`。** 已在 0.12.0 关闭。
**「无法在 Linux 宿主派生,需 macOS runner」这句判断是错的**——openkal 的前提就是通用
交叉构建,Linux 宿主经 openkal 栈可以构建 `aarch64-macos`(实测:`kernel-abi`
openkal-macos、`c-abi` musl、`c++-abi` libc++ 全部解析,产出 Mach-O arm64)。需要
Apple SDK 的是**平台路径**,不是 openkal 路径。这条错误认知一度把 C4 也挡在"本机不可测"
之外。
5. **P4 / P6。** 独立工作量。
521 changes: 521 additions & 0 deletions .agents/docs/2026-09-21-openkal-ecosystem-completion-and-acceptance.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
---
```

302 records.
303 records.

## By subject

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

### plan

- [openkal 生态:完整性收尾与验收方案](2026-09-21-openkal-ecosystem-completion-and-acceptance.md) — active
- [C 环境生态方案:执行计划](2026-09-20-ecosystem-execution-plan.md) — active

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

### 2026-09

- [openkal 生态:完整性收尾与验收方案](2026-09-21-openkal-ecosystem-completion-and-acceptance.md) — active
- [本轮生态级自审](2026-09-20-wave-self-review.md) — active
- [openkal 生态:能力的时刻模型,以及 C 环境方案空间的划分](2026-09-20-openkal-c-environment-ecosystem-design.md) — active
- [#674 设计方案评审:`-include unistd.h` 在 Windows + `presents = "posix"` 上的可行性](2026-09-20-issue-674-design-review.md) — active
Expand Down
49 changes: 48 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,53 @@

## [Unreleased]

## [2026.9.21.1] - 2026-09-21

### 引擎定义的宏成为一份规范,而规范就是那个模块

`src/toolchain/predefines.cppm` 同时是契约与实现:契约是模块里的数据(`kContract`),
发出是它旁边的函数(`define_tokens`),`tests/unit/test_predefines.cpp` **双向**断言两者
一致——发出去却不在表里、或在表里却没人发,都让构建变红。规范与实现分处两地就会漂移,
这一点本轮已经在理由令牌表上付过一次学费。

**`__mcpp_target_<os>__`,覆盖所有平台而不只是 Windows。** 拼法取自三元组自己的 `os`
字段,**引擎不认识任何操作系统名**——三元组解析器新增一个目标,它的宏随之存在。实测:
`x86_64-linux-gnu` → `__mcpp_target_linux__`;`x86_64-windows-gnu` →
`__mcpp_target_windows__`;`riscv64-none-elf` → `__mcpp_target_none__`。

**总是定义,不只在有东西被压掉时。** 条件式发出会让缺席含义不唯一:「不是 Windows」与
「是 Windows 但没有东西压掉它的宏」会读成同一件事。

**命名:小写,`__mcpp_` 前缀。** 业界并存两套约定——厂商与产品名大写(`__APPLE__`、
`_WIN32`),系统种类名小写(`__linux__`、`__unix__`)——这些命名的是目标种类,在真实守卫里
与第二族并排。`__mcpp_` 前缀承重:**一个 mcpp 拥有的名字,语义由 mcpp 自己定。**
`__openkal__` 收进同一份契约;`__unix__` 列在表里但标注「供给而非拥有」,保持标准拼法。

### mcpp 为「这个目标是 Windows」给出自己的名字

`presents = "posix"` 在 Windows 上实现成 Cygwin 形状的目标,有意压掉 `_WIN32`——那正是
「呈现 POSIX」的含义。但 **ABI 并没有跟着环境一起变**:调用约定仍是 Win64,寄存器保存区
仍按它的大小。生态里有两个**已安装的**头按这个事实定尺寸——openkal-musl 的
`bits/setjmp.h` 定 `jmp_buf`,openkal-llvm-runtime 的 `__libunwind_config.h` 定
`unw_context_t`。已安装的头会被**应用程序自己的编译**读到,所以两者都用不了包私有的 define。

本版发 `-D__mcpp_target_windows__`。它是 mcpp 自己的名字,语义由 mcpp 自己定;只在这次
替换下发出,普通 Windows 构建仍有 `_WIN64`。

**`__CYGWIN__` 仍然定义着,这是次序不是结论。** 30 成员测量已判定这个借来的名字代价是
四个成员(`archive`、`sqlite3`、`mimalloc`、`c-ares` 各自停在 `#include <windows.h>`,
经由 `#if defined(_WIN32) || defined(__CYGWIN__)`);上游用它表达「Win32 可用」,
mimalloc 把这句话写在守卫自己的注释里。**一个借来的名字,语义由借出方的历史决定。**

**撤掉它试过了,被跨仓库交叉验证挡下。** 上面那两个头正是因为没有别的 target-wide 名字
才读它;撤掉后 libunwind 的 `static_assert` 响亮地红了,而 `setjmp.h` 那一处**不会**响——
它自己的注释写着「a mismatch nothing reports until the record overruns」。那次测量数的是
**第三方**读者,没数我们自己的。

于是撤销分三步,每个中间状态都能构建:本版**增加**新名字;两个包改读新名字并保留
`|| defined(__CYGWIN__)`;之后的版本再停止定义借来的那个。先做第三步会让**已发布**的那
两个头全部落进 `#else`——错误的记录尺寸,没有任何东西报告。

## [2026.9.20.1] - 2026-09-20

### 校验探针量的是构建宿主,而不是它要核对的那个目标
Expand Down Expand Up @@ -100,7 +147,7 @@ requires-interfaces = ["openkal.fs", "openkal.net"]
`refusal.cppm` 能发出的令牌与表里的行,并要求简体中文镜像携带同一个集合。
两个方向各去掉一条都会红。

⚠️ 这张表的**列头**是 `| \`reason\` | |`——第一格里一个反引号名字,形状与下面每一行
这张表的**列头**是 `| \`reason\` | |`——第一格里一个反引号名字,形状与下面每一行
完全相同。按「行首反引号名字」匹配会把 `reason` 当成一个令牌。行与列头的区别在**第二格
非空**,所以判据按性质挑对象,不按语法挑。

Expand Down
75 changes: 75 additions & 0 deletions docs/21-the-target-triple.md
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,81 @@ produced rows* and *every row the expected table names was reached*, rather than
a total: a cell that vanishes because a payload was not restored is otherwise
indistinguishable from a cell that passed.

## The macros mcpp defines

`src/toolchain/predefines.cppm` is the specification and the implementation of
this table at once: the contract is data in that module, the emission is a
function beside it, and `tests/unit/test_predefines.cpp` asserts the two agree
in both directions. A macro emitted and unlisted, or listed and never emitted,
fails a build rather than drifting into a release.

| macro | when | owned |
|---|---|---|
| `__mcpp_target_<os>__` | one per build, spelt from the triple's `os` field | yes |
| `__openkal__` | the resolved `kernel-abi` layer's interface name is `openkal` | yes |
| `__unix__` | the `[c-abi]` realisation supplies it where the toolchain would not | no |

**An engine should not define macros, and each row has to justify itself.** A
package states what it needs in its manifest and the engine answers by
RESOLUTION — `cfg(os = "windows")`, `cfg(c-abi = "musl")`, a capability, a
feature. That path is testable, reportable, and visible to a reader of the
manifest; a macro is none of those. Two justifications have survived:

1. **The source is not ours to edit** and asks in the preprocessor. Upstream C
selects platform behaviour with `#if`, and no manifest key reaches inside a
third-party `.c`.
2. **The reader is an installed header.** A package's own build defines can be
spelled in its manifest, but a header it INSTALLS is read by an
application's own compile, which those defines never reach.

**`__mcpp_target_<os>__` — the rule.** Defined for every target-side
translation unit, always, one per build. The spelling is the triple's own `os`
field, so `x86_64-linux-gnu` gives `__mcpp_target_linux__`, `x86_64-windows-gnu`
gives `__mcpp_target_windows__`, and `riscv64-none-elf` gives
`__mcpp_target_none__`. The engine learns no operating-system name: a target
added to the triple parser gets its macro with no engine change.

*Allowed:* learning the target's operating system where the C environment
presented above it has suppressed the platform's own macros, and sizing a
record by the target's ABI. A Windows target presenting POSIX has no `_WIN32`
on purpose — while the calling convention is still Win64, which is what
`openkal-musl`'s `bits/setjmp.h` and `openkal-llvm-runtime`'s
`__libunwind_config.h` each size a record by.

*Forbidden:* selecting a header the manifest could select, or standing in for
`cfg(os = …)` in a package this project controls.

**It is defined always, not only where something is suppressed.** Conditional
emission would make its absence ambiguous: "not Windows" and "Windows, but
nothing hid its macros" would read the same. A macro whose absence means one
thing is worth one `-D`.

**Naming.** `__mcpp_`-prefixed, `__`-suffixed, lowercase, words separated by
`_`. Two conventions exist in the wild — vendor and product names are upper
(`__APPLE__`, `_WIN32`, `__MINGW32__`), kind-of-system names are lower
(`__linux__`, `__unix__`) — and these name kinds of target, sitting beside
that second family in real guards: `#if defined(__linux__) ||
defined(__mcpp_target_windows__)`.

The `__mcpp_` prefix is load-bearing. A name mcpp owns means what mcpp says it
means. The alternative was tried: `__CYGWIN__` was left defined so that code
needing "PE object format, POSIX C environment" would have a name, and a
30-member measurement found four members reading it as *Win32 is available*
and reaching `#include <windows.h>` — which is what upstream means by it. **A
borrowed name means what the lender's history made it mean**, not what the
borrower intended.

`__unix__` is the exception that proves the rule: mcpp SUPPLIES it rather than
owning it, so it keeps the standard spelling and mcpp may not change its
meaning. Read it exactly as on any other POSIX system.

**Stability.** An entry here is a published interface. Removing one, or
narrowing when it is defined, is a breaking change for source this project
does not control, and the failure is usually SILENT — a `#if` selects the
other branch and compiles. Withdrawal is therefore a sequence, never an edit:
add the replacement, let consumers move onto it while still accepting the old
name, and only then stop defining it.

## Custom Targets


Expand Down
58 changes: 43 additions & 15 deletions docs/22-target-side.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ that names no C library:
| Linux | `posix` / `arch-default` | the default triple already satisfies it |
| macOS | `posix` / `arch-default` | one token, `-D__unix__` — Apple's clang predefines `__APPLE__`/`__MACH__` on its default triple, never `__unix__` |
| freestanding | `posix` / `arch-default` | the same one token, `-D__unix__`, for the same reason: nothing here defines it either |
| Windows | `posix` / `arch-default` | Cygwin-flavoured: `--target=x86_64-pc-cygwin` on the compile line only; `__CYGWIN__`/`__CYGWIN32__` are left defined (see the note below); `data-model` becomes LP64 as a consequence of the triple, not a separate flag |
| Windows | `posix` / `arch-default` | Cygwin-flavoured: `--target=x86_64-pc-cygwin` on the compile line only; plus `-D__mcpp_target_windows__` (see the note below); `data-model` becomes LP64 as a consequence of the triple, not a separate flag |
| 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 |
| anything else | | refused, naming the target, the request and what is missing — never a silent downgrade |

Expand Down Expand Up @@ -418,20 +418,48 @@ preprocessor sees and how wide `long` is. Realisation therefore touches only
the **compile** line; the **link** line keeps the triple the graph resolved,
because nothing about the object format changed.

**`__CYGWIN__`/`__CYGWIN32__` are left defined — a revision from the
openkal-musl spike, not the design's original claim.** Undefining them was
tried first, on the reasoning that a real Cygwin userland is not in the
graph. Portable third-party code that needs to know the **object format** —
not the C environment, not the platform API — has no name for "PE format
with a POSIX-presenting C environment" other than `__CYGWIN__`, and such code
cannot be patched the way this ecosystem's own packages can. `presents =
"posix"` answers one question, which environment-identity macros source
sees; it does not get to answer a different one, what object format this is,
by deleting the only macro that names it. This is a **trade-off for the
30-member measurement to settle, not a settled fact**: a library reaching for
`__CYGWIN__` may also reach for a real Cygwin interface (`sys/cygwin.h`,
`cygwin_conv_path`) that does not exist here, and if defining it produces
more new failures than it fixes, the answer flips.
**`__mcpp_target_windows__` is defined here, and `__CYGWIN__` still is too
(2026.9.21.1).**

The substitution suppresses `_WIN32` on purpose — that is what presenting
POSIX means. But the **ABI did not change with the environment**: the calling
convention is still Win64 and the register save areas are still its. Two
INSTALLED headers in this ecosystem size records by that fact:

| package | header | sizes |
|---|---|---|
| openkal-musl | `port/include/bits/setjmp.h` | `jmp_buf` |
| openkal-llvm-runtime | `__libunwind_config.h` | `unw_context_t`, `unw_cursor_t` |

An installed header is read by an **application's own compile**, so neither
can use a package-private define. `__mcpp_target_windows__` is mcpp's own
name for the question they ask — is this target Windows, whatever C
environment is presented above it — and being mcpp's own, its meaning is not
decided by anyone else's history. It is emitted only under this substitution;
an ordinary Windows build still has `_WIN64`.

**`__CYGWIN__` is still defined, and that is a sequence rather than a
decision to keep it.** The 30-member measurement settled that the borrowed
name costs four members: `archive`, `sqlite3`, `mimalloc` and `c-ares` each
stop at `#include <windows.h>`, reached through
`#if defined(_WIN32) || defined(__CYGWIN__)`. Upstream means *Win32 is
available* by it — mimalloc states so in the guard itself, sqlite3 lists it
under `SQLITE_OS_WIN`. **A borrowed name means what the lender's history made
it mean**, not what the borrower intended.

Withdrawing it was tried and broke the two headers above, which read it for
want of any other target-wide name. libunwind's `static_assert` failed
loudly; `setjmp.h`'s equivalent would not have — its own comment says *a
mismatch nothing reports until the record overruns*. So the withdrawal is
three steps, and each intermediate state builds:

1. this release defines `__mcpp_target_windows__` as well — purely additive;
2. those packages read the new name, keeping `|| defined(__CYGWIN__)` so they
build on both engines;
3. a later release stops defining the borrowed one.

Taking step three first would leave every **published** copy of those headers
falling to its `#else`: the wrong record size, reported by nothing.

**A `kernel-abi` provider's own units are INFERRED onto the platform boundary
— it never has to say so (mcpp 2026.9.18+, a mid-PR revision from the
Expand Down
13 changes: 9 additions & 4 deletions docs/24-openkal-cross.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,15 @@ combination, "PE format with a POSIX-presenting C environment," is
`__CYGWIN__`/`__CYGWIN32__`, which the Cygwin-flavoured realisation
therefore leaves DEFINED rather than folding into the three rows above —
see [22's own note](22-target-side.md#the-c-environment-a-c-abi-package-presents-mcpp-2026918)
for the full trade-off (it is not a settled fact, and may flip). Reading it
as a fourth C-environment macro, rather than as what it actually is — an
object-format fact `__CYGWIN__` happens to be the only name for — is
exactly the shape of confusion this whole section exists to head off.
for the full trade-off. Since 2026.9.21.1 `__CYGWIN__` is no longer the only
name for that fact: mcpp defines `__mcpp_target_<os>__` for every target
(`docs/21`, "The macros mcpp defines"), so source needing to know the target
under a presented environment has a name mcpp owns. `__CYGWIN__` remains
defined while the ecosystem's installed headers move onto the new name, and
is then withdrawn. Reading either as a fourth C-environment macro, rather
than as what they are — a fact about the TARGET, not about the environment
presented above it — is exactly the shape of confusion this whole section
exists to head off.

**`__openkal__` — the rule.** The engine defines it, for every target-side
unit, whenever the resolved `kernel-abi` layer's interface name is
Expand Down
Loading
Loading