Skip to content

Commit cbb1a07

Browse files
speak-agentclaude
andcommitted
the c-abi probe measures the target it verifies, and a capability is answered where the answer exists (2026.9.20.1)
Four changes, one subject: information about a capability becomes available at three times, each being the earliest at which it exists (openkal SPEC 0.14 §6.2), and this engine had been answering two of those questions at the wrong one. ## The probe measured the build host The c-abi verification probe selected no target on every freestanding build. `Toolchain::crossTargetFlag` is set for hosted targets only --- its own comment states the reason --- and a freestanding target's `--target` travels with the ISA flags that must accompany it, in `mcpp.freestanding.linkline`, which the probe never asked. `cenv::realise` adds none either. The command line was therefore `-D__unix__ -fno-short-wchar -ffreestanding -x c++ -E -dM -`, and clang answered for the machine it was running on. On a Linux host that machine satisfies `__unix__` defined, `_WIN32` undefined, 32-bit `wchar_t`, so the check passed for the wrong reason. On a Windows host it reported `_WIN32` defined and a 16-bit `wchar_t`, and 2026.9.18.3 read those two as a `--target=` substitution failing to strip host predefines. Clang's predefines follow the target: `--target= riscv64-none-elf` on a Linux host reports `__riscv`, no `__linux__`, and `__SIZEOF_WCHAR_T__` 4. Had that flag been on the command line it would have answered 4 rather than 2; 2 is the evidence that it was absent. The probe now receives the freestanding compile prefix. `hostStripMacros` is removed, and its removal is the point rather than a tidy-up: it deleted the one piece of evidence that said the probe was measuring the wrong machine. `cenv_probe::assemble_argv` holds the invariant --- every piece is legitimately empty in some configuration, so none of them could carry it at the call site --- and refuses a freestanding argv that selects no target while accepting a native hosted one, where the host IS the target and the absence is the decision rather than its omission. The three `hostStripMacros` tests are replaced. They pinned whether `-U` reached the command line, which is the mechanism; what matters is whether the probe measures the right machine, which is the property. The five new `CenvProbeArgv` tests reach it with no cross toolchain at all. ## builtins: the conclusion stands, the mechanism beside it did not `cenv.cppm` said clang's bundled `intrin.h` / `mm_malloc.h` were already closed by `-nostdlibinc`. That flag removes the standard system include directories and leaves clang's own resource directory in place, which is what `-nobuiltininc` removes; with it present `intrin.h:12:15` still reproduces, character for character the diagnostic mcpp-index recorded for fmtlib.fmt. What closes both is the Cygwin-flavoured realisation: `mm_malloc.h:42` selects `__mingw_aligned_malloc` on `__MINGW32__` and falls to `posix_memalign` without it. No Windows loop-idiom builtin to disable, as before. ## A capability is enumerated by the consumer, at resolution openkal SPEC 0.14 §3.3 withdrew `hosted`, the one name it had given to a set of interfaces, because a name describing a class of environment is falsified by an environment nobody had in mind --- and that one was falsified inside its own ecosystem within a release. Enumeration by the consumer replaced it. mcpp now carries that enumeration for the `kernel-abi` layer: `provides-interfaces` on the package that supplies it, `requires-interfaces` on anyone. The engine learns no member of either set. The only operation is a set difference, so a specification may add an interface without a release of mcpp, and a misspelling produces a refusal naming the string rather than a silently disabled check. A graph whose provider states nothing keeps building: a package that states nothing is not a package that provides nothing. ## [c-abi.absent]: enumerate the exception, not the rule POSIX has about twelve hundred names and enumerating them is the mistake §3.3 records withdrawing. The exceptions are enumerable --- openkal-musl's README lists six, in prose that nothing executes and that was contradicted once already. `form` is required and closed: `link` is the shape openkal's own model requires of an implementation (§6.1 calls a run-time report of unsupportedness a defect), and the other two are departures from it, named so that a departure is something that can be counted. mcpp reads the list back when a link names a `link`-shaped entry. ## presents is frozen docs/22 now states that `presents` answers which environment-identity macros source sees and answers nothing about capability, and that its value set does not grow --- for the reason openkal gives for closing its core set. Design: .agents/docs/2026-09-20-openkal-c-environment-ecosystem-design.md Review: .agents/docs/2026-09-20-issue-674-design-review.md Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 361874d commit cbb1a07

23 files changed

Lines changed: 3334 additions & 251 deletions

.agents/docs/2026-09-19-issue-674-cenv-posix-preinclude-design.md

Lines changed: 359 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
subject: plan
3+
status: active
4+
---
5+
6+
# C 环境生态方案:执行计划
7+
8+
- 依据:`.agents/docs/2026-09-20-openkal-c-environment-ecosystem-design.md`
9+
- 日期:2026-09-20
10+
- 原则:每个仓库一个 PR;测量先行,数据不通过就停在测量。
11+
12+
## 0. 起点状态(2026-09-20 实测)
13+
14+
| 仓库 | 版本 | 状态 |
15+
| --- | --- | --- |
16+
| mcpp | 2026.9.18.3 | main 干净 |
17+
| openkal | 0.14.0 | 已发布并登记 |
18+
| openkal-musl | 0.16.0 | 已发布并登记 |
19+
| openkal-llvm-runtime | 0.12.0 | 已发布并登记 |
20+
| mcpp-index `pins.toml` | `runtime = "0.10.0"` | **未抬**,测量图里没有任何包声明 `[c-abi]` |
21+
22+
窗口仍开着:包侧 `_WIN32` 适配已撤(#439),引擎侧实现未在测量中生效。
23+
24+
## 1. 轨道与依赖
25+
26+
```
27+
E1 抬 pins + 重测 ──────────────────────┐ (独立,最高价值,先跑)
28+
29+
A mcpp 引擎(P0/P1/P5-L2/P7-L3/absent)─┼─→ E2 refused + 新引擎 pin
30+
31+
B openkal tools + 文档(SURFACE→接口集)─┼─→ D 各实现填 provides-interfaces
32+
33+
C openkal-musl(absent + requires)─────┘
34+
35+
└─→ F 发布 + 沙箱验证
36+
```
37+
38+
|| 仓库 | 内容 | 依赖 |
39+
| --- | --- | --- | --- |
40+
| **E1** | mcpp-index | `pins.toml` runtime 0.10.0 → 0.12.0,重测 30 成员 ||
41+
| **A** | mcpp | P0.1 探针 `--target`、P0.2 注释、P1 冻结语义、P5-L2 解析期集合包含、P7-L3 链接期集合差、`[c-abi.absent]` 解析与诊断 ||
42+
| **B** | openkal | `tools/interfaces-from-surface.sh`、README 记述四级阶梯 ||
43+
| **C** | openkal-musl | `[c-abi.absent]` 声明 + CI 断言、`requires-interfaces` | A(字段语义)、B |
44+
| **D** | openkal-linux / -windows / -macos | `provides-interfaces` 由产物生成 + CI 断言 | B |
45+
| **E2** | mcpp-index | `refused` status、新引擎 pin、重测 | A、E1 |
46+
| **F** | 全部 | 发布、gtc 镜像、沙箱验证 | 全部 |
47+
48+
## 2. 判据
49+
50+
|| 判据 |
51+
| --- | --- |
52+
| E1 | 重测后 Windows 腿:第一类(`_WIN32` 选错分支)应自愈;逐条记录未自愈的与原因 |
53+
| A | 单测覆盖新字段的解析与拒绝;`riscv64-none-elf` 探针 dump 含 `__riscv` 不含 `__linux__`;未声明的包命令行逐字节不变 |
54+
| B | 脚本对 `SURFACE.txt` 产出 16 个接口名;删掉一个组,产出少一个 |
55+
| C |`fork``form``link` 改成 `enosys`,CI 必须红 |
56+
| D | 删掉某实现的一个接口定义,CI 必须红并指名该接口 |
57+
| E2 | `refused``fails` 分开计;`cfg(c-abi = ...)` 的出现次数记录在案 |
58+
| F | 沙箱中只写版本号即可解析并构建 |
59+
60+
## 3. 不做
61+
62+
- P3(撤 `__CYGWIN__`):需要 E1 的重测数据才能判断代价,本轮只记录证据,不落地。
63+
- P4(合成节点层):openkal-musl 的移植工作量独立,另轮。
64+
- P6(openkal-win-ucrt):需要 c++-abi 侧配套,另轮。

.agents/docs/2026-09-20-issue-674-design-review.md

Lines changed: 649 additions & 0 deletions
Large diffs are not rendered by default.

.agents/docs/2026-09-20-openkal-c-environment-ecosystem-design.md

Lines changed: 842 additions & 0 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,114 @@
55

66
## [Unreleased]
77

8+
## [2026.9.20.1] - 2026-09-20
9+
10+
### 校验探针量的是构建宿主,而不是它要核对的那个目标
11+
12+
2026.9.18.3 的 c-abi 校验探针在**每一次 freestanding 构建上都没有选中目标**。
13+
`Toolchain::crossTargetFlag` 只为 hosted 目标设置(它自己的注释写明了原因:freestanding
14+
目标的 `--target` 必须与随行的 ISA 标志一起给出,放两处就是同一个决定写两遍),而那另一处
15+
是 `mcpp.freestanding.linkline` 的编译前缀,探针从不问它;`cenv::realise` 对 freestanding
16+
也不产 `--target`。于是探针的命令行是 `-D__unix__ -fno-short-wchar -ffreestanding -x c++
17+
-E -dM -`——没有任何目标选择,clang 回答的是它自己所在的那台机器。
18+
19+
在 Linux 宿主上这台机器恰好满足 `__unix__` 已定义、`_WIN32` 未定义、`wchar_t` 32 位,
20+
于是检查**以错误的理由通过**;在 Windows 宿主上它报 `_WIN32` 已定义、`wchar_t` 16 位,
21+
两条不匹配同时出现。2026.9.18.3 把这两条读成"Windows 宿主的 clang 即使带上 `--target=`
22+
仍注入宿主预定义",并据此加了 `hostStripMacros`。该归因不成立:clang 的预定义跟随目标
23+
而不是宿主。本机实测——`clang --target=riscv64-none-elf -dM` 在 Linux 上 `__linux__` 计数
24+
为 0、`__SIZEOF_WCHAR_T__` 为 4,而 `--target=x86_64-w64-windows-gnu` 在同一台 Linux 上
25+
定义 `_WIN32`——如果那个 `--target` 真在命令行上,clang 会答 4 而不是 2。观察到 2,说明
26+
它不在。
27+
28+
这一版从根因修:
29+
30+
1. **探针拿到目标。** freestanding 目标的 `--target` 与 ISA 标志由
31+
`mcpp.freestanding.linkline` 的编译前缀提供,现在进入探针 argv。
32+
2. **`hostStripMacros` 删除,而删除本身是要点。** `-U_WIN32 -U_WIN64 -U__MINGW32__
33+
-U__MINGW64__` 抹掉的正是"探针量错了机器"这件事的唯一证据。将来若真有宿主泄漏,它必须
34+
到达不匹配报告,而不是在能被看见之前就被 undefine 掉。
35+
3. **装配处拒绝这次遗漏,而不是调用点记得不要犯。** 新增
36+
`cenv_probe::assemble_argv`:各个部件在某些配置下都合法地为空,所以没有任何单独一个
37+
能承载这条不变量。freestanding 目标的 argv 没有选中目标即拒绝并点名目标;宿主本地
38+
构建接受没有目标选择的 argv——那里宿主就是目标,缺席是那个决定本身。
39+
40+
单测 `test_cenv_probe.cpp` 的三个 strip 测试被替换:它们测的是"`-U` 有没有到达命令行"
41+
这个机制,而不是"探针量的是不是正确的机器"这条性质。新的五个 `CenvProbeArgv` 测试**不需要
42+
交叉工具链**,直接对装配函数断言;另加两个带 clang 的端到端探针测试,在没有 clang 的宿主
43+
上跳过而不谎报。
44+
45+
### `builtins` 的 Windows 行:结论不变,写在旁边的机制是错的
46+
47+
`cenv.cppm` 称 clang 自带 `intrin.h` / `mm_malloc.h` 的问题"已由既有的 `-nostdlibinc`
48+
隔离关闭"。实测不成立:`-nostdlibinc` 移除的是标准**系统**头目录,clang 自己的 resource
49+
目录仍在(那是 `-nobuiltininc` 移除的),带着该标志仍复现 `intrin.h:12:15`——与 mcpp-index
50+
为 fmtlib.fmt 记录的诊断逐字符相同。真正关掉这两者的是 Cygwin 式实现:`mm_malloc.h:42`
51+
在 `__MINGW32__` 上选 `__mingw_aligned_malloc`,没有它则落到 `posix_memalign`;而去取
52+
`<intrin.h>` 的源码是在 `_WIN32` 之后才这么做的。调查结论不变(Windows 上没有可关的循环
53+
惯用法内建),改的是写在它旁边的那句机制。
54+
55+
### 一个包可以陈述它需要该层的哪些接口,解析期回答
56+
57+
能力的"在不在"过去无处可问,于是全被挤到预处理期,而那比答案存在得更早——mcpp#674 的
58+
全部压力来自这一格空着。openkal SPEC 0.14 §6.2 列出三个时刻并规定每个都是该信息**最早
59+
能存在**的时刻;§3.3 撤回了它给接口集合起过的唯一一个名字(`hosted`),理由是"一个描述
60+
环境类别的名字会被没有人想到过的那个环境证伪",替代做法是由消费者逐条列举。
61+
62+
新增 `[kernel-abi]` 表:
63+
64+
```toml
65+
# 实现方(只有提供该层的包可以写 provides-interfaces)
66+
[kernel-abi]
67+
provides-interfaces = ["openkal.abort", "openkal.stream", "openkal.memory"]
68+
69+
# 消费方(任何包都可以写 requires-interfaces)
70+
[kernel-abi]
71+
requires-interfaces = ["openkal.fs", "openkal.net"]
72+
```
73+
74+
**引擎不认识这两个集合的任何一个成员**:对它们做的唯一操作是集合差
75+
(`targetside::interfaces_not_provided`),因此某个规范新增一个接口不需要 mcpp 发版。
76+
不满足即在**编译任何东西之前**拒绝,并同时点名缺的接口、要它的包、以及没提供它的实现——
77+
只报"缺"会让读者自己去猜该改哪一边。
78+
79+
**一个什么都没陈述的提供者,不是一个什么都不提供的提供者**:实现方没有写
80+
`provides-interfaces` 的图照常构建,链接仍以它一贯的词汇报告缺席。什么都不写的清单,
81+
产出的命令行与这项能力存在之前逐字节相同。
82+
83+
### `[c-abi.absent]`:枚举例外,不枚举规则
84+
85+
一个 C 库供给的名字集合在清单里不可枚举(POSIX 约一千二百个),枚举它正是 §3.3 记录下
86+
撤回的那个错误。例外是可枚举的——openkal-musl 的 README 列了六项,而那段散文没有任何
87+
东西在执行它,并且已经被推翻过一次(0.16.0 之前 `SIG_IGN` 对每个信号都被接受却一个都没
88+
安装)。
89+
90+
```toml
91+
[c-abi.absent]
92+
fork = { form = "link" }
93+
mprotect = { form = "enosys", note = "openkal 没有作用于映射保护属性的操作" }
94+
tcsetattr = { form = "accepted-no-effect", note = "openkal 不命名的那些字段不被施加" }
95+
```
96+
97+
`form` 必填且封闭。`link` 是 openkal 自己的能力模型对实现所要求的形状(§6.1 把运行期
98+
报告不支持称为缺陷);另外两个是对它的偏离,给它们命名是为了让一次偏离成为可以被数出来
99+
的东西。链接点到 `link` 形状里的某一项时,mcpp 把清单读回来:`undefined reference to
100+
'fork'` 因此带着那句说明它是缺陷还是环境限制的话一起到达。
101+
102+
### `presents` 的取值集冻结
103+
104+
docs/22 写明:`presents` 回答的是源码看到哪些环境身份宏,**不回答任何能力是否存在**。
105+
一个包不得由它推断某个接口、某个头或某个路径是否可用。取值集不增长,理由与 openkal 为
106+
自己的核心集封闭所给的相同——一个描述环境**类别**的名字会被没有人想到过的那个环境证伪,
107+
而 openkal 把自己发过的唯一一个这样的名字在一个发布周期之内撤回了。
108+
109+
(`src/toolchain/cenv.cppm`、`src/toolchain/cenv_probe.cppm`、`src/build/prepare.cppm`、
110+
`src/build/ninja_backend.cppm`、`src/build/refusal.cppm`、
111+
`modules/manifest/src/{targetside_model,toml,types}.cppm`,
112+
单测 `test_cenv_probe.cpp`、`test_manifest.cpp`、`test_targetside.cpp`、
113+
`test_build_flags.cpp`,e2e `tests/e2e/743_kernel_abi_interfaces_are_resolved_not_preprocessed.sh`,
114+
docs/22 及其 zh 镜像,`modules/versioning/src/version.cppm`、`mcpp.toml`)
115+
8116
## [2026.9.18.3] - 2026-09-18
9117

10118
### Windows 主机 × freestanding 目标的 c-abi 校验探针两处真实缺陷被关掉

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
<img src="https://github.com/user-attachments/assets/6c85896e-9a37-4f62-acfb-d37a4eae2363" alt="mcpp demo" width="720">
1919
</p>
2020

21-
> **Note (2026.9.18.3):** this release adds `cenv_probe::verify` host-macro
22-
> stripping for the Windows host and forces `-fno-short-wchar` on freestanding
23-
> wchar realisation. `[c-abi]` packages (`openkal-musl` 0.15.0 and any future
24-
> C-library package) require this engine. Older engines silently misbuild them:
25-
> the c-abi probe sees host contamination (`_WIN32`, `__MINGW32__`,
26-
> `__MINGW64__`, `_WIN64`) on Windows and reads the wrong `__SIZEOF_WCHAR_T__`
27-
> on a freestanding target. Upgrade: `xlings install mcpp --force`.
21+
> **Note (2026.9.20.1):** the `[c-abi]` verification probe now selects the
22+
> target it is verifying. On a freestanding target it selected none and
23+
> answered for the build host, which on a Linux host passed for the wrong
24+
> reason and on a Windows host failed for one. The `hostStripMacros`
25+
> compensation 2026.9.18.3 added is removed with it. This release also adds
26+
> `[kernel-abi] provides-interfaces` / `requires-interfaces`, answered at
27+
> dependency resolution, and `[c-abi.absent]`, which states what a C library
28+
> does not supply and in what shape. See CHANGELOG and docs/22.
2829
2930
## Highlights
3031

0 commit comments

Comments
 (0)