Skip to content

Commit c6ed83c

Browse files
fix(target): the triple is a request, and the row's convention waits for the graph (#495)
* fix(target): the triple is a request, and the row's convention waits for the graph 两个症状,一个病根:**`compiler` 被声明为第五层,而它的解析留在了包含它的结构 之外。** 一个在包含它的结构之前就被决定的层,不是结构的成员,是伪装成成员的输入。 ── 症状 1:`--target x86_64-linux` 报告 `-gnu` ──────────── Target x86_64-linux-gnu → x86_64-unknown-linux-gnu c-abi musl (openkal-musl@0.3.3, graph) 名字与它下面那一行矛盾,而构建成功了。 三元组同时充当**身份**(输出目录、缓存键、`cfg()` 的主语)和**请求**。 身份必须是全的,请求必须能说「没指定」。`parse` 用自动填充让身份变全, 代价是请求消失 —— 两态在下游不可区分。 ⚠️ 修法是窄的,而窄是量出来的:读 `env` 的共 **22 处,其中 10 处在 triple.cppm 自己里**。保留填充,另记 `Triple::envExplicit`。 ⚠️ `operator==` 不能再 `= default`:`envExplicit` 记录的是来源不是身份, 默认比较会让用户写的 `x86_64-linux-gnu` 与 `host_triple()` 推出的不相等, 第一个坏掉的是 `mcpp toolchain list` 的 `host` 标记。 ⚠️ 请求**必须在规范化之前捕获**。`str()` 渲染的是填好的身份, `overrides.target_triple = parsed->str()` 之后再 parse 就分不出来了 —— 第一版正是在这里丢的,`--target x86_64-linux` 被当成显式 gnu 而遭拒。 ── 症状 2:约定在图之前应用,而它的问题在图之后才有答案 ── `x86_64-linux-musl → gcc@16.1.0` 说的不是「偏好 gcc」,是 「musl-gcc 载荷供给这个目标的 C 库」。工程的 C 库若来自依赖图, 该载荷根本不被使用 —— 而这只有解析完图才知道。 ⚠️ 早决定被**双向实测**否掉:无条件应用会替换用户用 `mcpp toolchain default` 设下的工具链(上一版的做法);不应用会让一个零依赖的交叉构建从可用变为不可用 (更早那一版被回退的做法)。两者都错,因为都在猜一个尚不存在的事实。 判据换成它本来就该是的那个:**图供给 `kernel-abi` 或 `c-abi` 时,约定不适用。** ⚠️ 代码不搬,只搬执行时机:原地包成 `resolve_target_toolchain` lambda, 在 `packages` 填好之后调用。先前记录的「39 处读写挡着」是**没测就写下的** —— 实测依赖解析段读 `tc` 仅 **1 处**,而那一处要的是**三元组不是编译器** (`tc->targetTriple` 在检测后几行就被改写成请求的三元组,两者同值)。 逃逸出 lambda 的局部只有 `tcSpecIsMsvc` 一个,提到外层即可。 ── 实测 ──────────────────────────────────────────────── | 场景 | 结果 | |---|---| | openkal 工程 + 全局 llvm 默认 + `--target x86_64-linux-musl` | `Resolved llvm@22.1.8` ✅ | | 无依赖工程 + 同一目标 | `Resolved gcc@16.1.0`(约定生效)✅ | | 无依赖工程 + `x86_64-windows-gnu`(先前的回归点) | `Resolved gcc@16.1.0` ✅ | | `--target x86_64-linux` + 图供 musl | 构建成功,报告写 `Target x86_64-linux` ✅ | | `--target x86_64-linux-gnu` + 图供 musl | 拒绝,指出请求与事实 ✅ | test: test_targetside 31 passed(新增请求语义三格);unit 93 passed; e2e 新增 282;全量 e2e 进行中,至此无新增失败。 * fix(target): report the name/fact mismatch instead of refusing it ⚠️ 上一版的拒绝是语义上干净的答案,而它在 mcpp 自己的 openkal 矩阵上第一个开火: $ mcpp build --target x86_64-linux-gnu # openkal 工程 error: this build requests the `gnu` C ABI, and its dependency graph supplies `musl`. `x86_64-linux-gnu` 正是 `mcpp toolchain list` 打印的宿主目标拼写, 因此也是人们会写的那一个。拒绝它等于打破每一个已经这么写的工程与 CI 配置。 判据是该请求**不改变任何东西**:图两种写法下都供给同一个 C 库, 所以那一段是**被忽略而非被违反**。一个去掉它也完全相同的构建,不是该被拒绝的 构建 —— 它是一个名字与自身不符的构建,而说出来就是全部的补救。 warning: the target name asks for the `gnu` C ABI and the dependency graph supplies `musl`. The graph decides, so the build below uses `musl` — the name is what is inaccurate, not the artifact. Drop the segment to say what is actually meant: --target x86_64-linux ⚠️ 补救必须可执行,而这正是 `x86_64-linux` 必须先能用的原因: 告诉一个人他的目标名字写错了,只有在存在一个对的名字可以给他时才有用。 test: unit 93 passed;e2e 268/280/281/282 全绿。 spec: SPEC-002 §3.4 改为「必须报出、禁止据此失败」,并记下被否掉的那一版。 * feat(examples): one source over four machines, and a chapter for the model ── examples/06-openkal-cross ────────────────────────────── 一个「询问它落在哪台机器上」的程序,从任何宿主构建到四个目标而不修改一个字符。 `src/main.cpp` 里**没有一条预处理指令,也没有一处按目标名分支**。 实测,同一份源码、同一台 Linux 宿主、两个目标: | 事实 | x86_64-linux | x86_64-windows-gnu | |---|---|---| | 预开目录数 | 2 | 5 | | 路径区分大小写 | yes | **no** | | 单调时钟粒度 (ns) | 1 | **100** | 代码路径处处相同,答案不同 —— 这正是「询问而不假定」与「在若干套 `#if` 下 编译」的区别。选的几项查询各自压到目标侧的不同层,而其中展开那一项最严: 展开是 C++ 运行时里**无论是否可用都能链接成功**的那一部分, 一个没有展开器的程序照样构建,只在抛出时失败;析构函数在展开中运行, 才把两者分开。 ⚠️ 而这个程序在移植过程中挖出了一条规范缺陷 —— 见 openkal-opensbi#2: 一个**一次文件系统调用都没有**的程序,仅仅因为「询问是否存在文件系统」 就在裸机后端上链接失败,因为查询是属性对象上的 inline 函数。 读规范两轮没发现,写一个程序跑一次就挖出来了。 ── docs/15(中英)──────────────────────────────────────── 模型、工程书写什么、生态供给什么、以及三条已实测的界限。 ⚠️ 顺带修掉一处我自己刚引入的语义错误:请求检查把 env 段一律当作 C 库请求, 而**在 Windows 上那一段命名的是对象 ABI**(`gnu` = PE/GNU,`msvc` = PE/MSVC), 两者都与不止一种 C 库相容。把 Windows 构建报成「请求了 gnu C ABI」描述的是 一条该名字从未涉及的轴,而它建议的更正指向一个不存在的目标。 检查因此限定在该段确实命名 C 库的平台上 —— 这一点我自己的 §4 分析写过, 实现时没照做。 test: unit 93 passed;e2e 268/280/281/282 全绿; 示例四个宿主目标(host / x86_64-linux / aarch64-macos / x86_64-windows-gnu) 全部构建通过,Linux 与 wine 下均运行。 * docs: the two routes to a bare x86_64 machine are different things UEFI 是其中一条,而不是全部。补上另一条:x86 内核开发。 | 路线 | 目标 | 平台层 | 入口 | |---|---|---|---| | UEFI 应用 | `x86_64-windows-gnu` | `openkal-uefi` | 固件,Boot Services 可用 | | 内核 / 裸机 | `x86_64-none-elf` | 无,或 `openarch` | 复位向量,其下一无所有 | 区别在于**谁加载这个程序**。UEFI 应用有固件服务可调,因此它是 PE/COFF、 经微软 x64 调用约定进入,与 Windows 程序共用三元组;内核没有, 因此它是零 libc 档的 ELF,在自己的 `_start` 被进入并直接触达硬件。 ⚠️ `openarch` **不应答 `mcpp:kernel-abi`**,写清楚这一点是必要的: 它不是平台接口,而是架构机制 —— 执行上下文、陷阱、每 CPU 状态、地址空间 —— 跨若干指令集的一个接口,每个指令集一个后端包。一个内核依赖它, 并供给自己的平台层,或者不供给。 ── 并记下 x86_64 裸机为何需要引擎侧的工作 ──────────────── `riscv64-none-elf` 与 `aarch64-none-elf` 只是表中的行:clang 对两者都有 BareMetal 工具链。它对 x86_64 没有,于是那个三元组落到通用 GCC 工具链, 而后者的链接器是**宿主的 g++**: g++: error: unrecognized command-line option '-fuse-ld=…/ld.lld' 对裸 x86_64 三元组的每一种拼写都实测过且无法由任何 flag 纠正, 因此该行携带链接器 emulation,由 mcpp 自己调用 `ld.lld`。 示例 README 同步:该目录里的程序在内核路线上跑不起来, 因为它向平台接口提问而那里没有平台接口 —— 说明白比含糊带过有用。 * docs: "identical either way" was measured, and it was not quite true 我写了「两种写法下产物相同」,然后去核验这句可证伪的话: x86_64-linux b9bcf550… x86_64-linux-musl 481b27d9… ← 不同 strip 之后 5a3ed2e3… 两侧一致 .text 单独取出 ba856ed6… 两侧一致 ⭐ 代码是同一份代码,差异整个落在**调试信息**里 —— 它记录了输出目录, 而目录以三元组命名。原话按字面是假的,而它想说的那件事是真的, 于是把测出来的东西写进去,而不是把话说得更满。 ── 顺带记下验收结果 ────────────────────────────── 用报告里那两条命令实测(2026.8.24.3,本机): --target x86_64-linux Target x86_64-linux → …-gnu c-abi musl (graph) 标题不再自相矛盾 --target x86_64-linux-musl exit 0 原先是「未知目标」 --target x86_64-linux-gnu warning + 可粘贴的改法 原先静默 第三条的提示给出 `--target x86_64-linux` 这一行可直接粘贴, 因为「说清楚哪里不对」和「说清楚该怎么写」不是同一件事。 * docs(examples): the link error was the specification working, and I had it backwards 示例声称「一份源码,四台机器」,其中第四台是 riscv64 裸机。它跑不起来, 而我上一轮把原因定成了后端的缺陷,给 `openkal-opensbi` 加上了 const kal_uintptr kal_fs_props = 0; const kal_uintptr kal_task_props = 0; 发布为 0.1.3 并合进了索引。⚠️ 这是规范明令禁止的那一种补法。 openkal SPEC 6.1:「实现不提供的接口,作为链接期定义是缺席的, 使用它的消费者链接失败。」6.2 的表把三个时机分开: 依赖解析 │ 包声明它提供什么 │ 这个程序可否针对这个实现构建 链接 │ 未定义符号 │ 是否用了它不提供的接口 运行 │ 能力字 │ 在**它提供的接口内**它如何表现 ⭐ 能力字回答的问题比它初看上去窄得多。给一个没有任何操作的接口 定义能力字,是在回答第三个问题而跳过第二个 —— 程序随后越过了 链接器存在的意义,接着撞上未定义的 `kal_fs_open`,或者认为自己 有一个零能力的文件系统。opensbi 只实现 abort/stream/memory/env/time, 一个 `kal_fs_*` 操作都没有。 于是本提交把方向反过来:**程序才是那个不该这么写的东西**。 示例 mcpp.toml 删掉 `[target.riscv64-none-elf]`,并写明为何没有 示例 main.cpp 「可以问一个文件系统怎样比较名字,不可以问一台 机器有没有文件系统」;两处正文注释原先说反了 示例 README 新增「本程序不问什么」,把那条链接错误作为机制展示 docs/15 中英 新增「源码是同一份,程序不是」 「同一份源码」是关于工具链与标准库的断言,它成立;它从来不是 「任何程序都能为任何目标构建」的断言 —— 后者由依赖图回答。 实测(本机 Linux 宿主,2026.8.24.3): x86_64-linux ELF 64-bit LSB executable aarch64-macos Mach-O 64-bit arm64 executable x86_64-windows-gnu PE32+ executable (console) x86-64 上游:mcpplibs/openkal-opensbi#4 撤回 0.1.3,发 0.1.4。 --------- Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 61c7446 commit c6ed83c

19 files changed

Lines changed: 1609 additions & 338 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ bench-child.log
4949
# to be expanded (`> $binDir`) and was not. Listed so the same slip is caught
5050
# next time rather than reviewed again.
5151
binDir
52+
examples/*/target/

CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,50 @@
33
> 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。
44
> 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)
55
6+
## [2026.8.24.3] — 2026-08-24
7+
8+
### 修复
9+
10+
- **⚠️ 三元组是请求,而解析把「未指定」抹掉了。**
11+
12+
```
13+
$ mcpp build --target x86_64-linux # 我写的是「不指定 C 库」
14+
Target x86_64-linux-gnu → x86_64-unknown-linux-gnu ← 被改写
15+
c-abi musl (openkal-musl@0.3.3, graph) ← 名字自相矛盾
16+
```
17+
18+
三元组同时充当**身份**(输出目录、缓存键、`cfg()` 的主语)与**请求**
19+
身份必须是全的,请求必须能说「没指定」;`parse` 用自动填充让身份变全,
20+
代价是请求消失 —— 两态在下游不可区分。
21+
22+
⚠️ 修法是窄的:保留填充,另记 `Triple::envExplicit`。而请求**必须在规范化
23+
之前捕获** —— `str()` 渲染的是填好的身份,之后再 parse 就分不出来了。
24+
25+
未指定 ⇒ 报告显示工程写的那个拼写;写了且与图矛盾 ⇒ **拒绝**
26+
27+
- **⚠️ 目标行的约定在图之前就被应用,而它要回答的问题在图之后才有答案。**
28+
29+
`x86_64-linux-musl → gcc@16.1.0` 说的不是「偏好 gcc」,是「musl-gcc 载荷
30+
供给这个目标的 C 库」。工程的 C 库若来自依赖图,该载荷根本不被使用。
31+
32+
⚠️ 早决定被**双向实测**否掉:无条件应用会替换用户用 `mcpp toolchain default`
33+
设下的工具链;不应用会让一个零依赖的交叉构建从可用变为不可用。
34+
35+
⭐ 判据换成它本来就该是的那个:**图供给 `kernel-abi``c-abi` 时,约定不适用。**
36+
工具链解析因此移到依赖解析之后。
37+
38+
⚠️ 代码不搬,只搬执行时机 —— 原地包成 lambda,在图已知处调用。
39+
先前记录的「39 处读写挡着」是**没测就写下的**:实测依赖解析段读 `tc` 仅 1 处,
40+
而那一处要的是三元组不是编译器。
41+
42+
实测三格:
43+
44+
| 场景 | 结果 |
45+
|---|---|
46+
| openkal 工程 + 全局 llvm 默认 + `--target x86_64-linux-musl` | `Resolved llvm@22.1.8`|
47+
| 无依赖工程 + 同一目标 | `Resolved gcc@16.1.0`(约定生效)✅ |
48+
| 无依赖工程 + `x86_64-windows-gnu` | `Resolved gcc@16.1.0`|
49+
650
## [2026.8.24.2] — 2026-08-24
751

852
### 新增

docs/14-target-side.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ interface. The environment field states a request for a C library; it is a
120120
request rather than the answer, and the resolved value is reported by the
121121
build.
122122

123+
Omitting the field declines to state one: `x86_64-linux` asks for whatever
124+
supplies that layer, and `x86_64-linux-musl` asks for musl. When the dependency
125+
graph supplies a different one the graph decides, and the build reports that the
126+
name is inaccurate together with the spelling to use instead. The request is
127+
ignored rather than violated, so the artifact is the same either way.
128+
123129
### The Toolchain
124130

125131
`mcpp toolchain default <family>@<version>`, `[toolchain]` in the manifest, or
@@ -128,8 +134,9 @@ layer, which is the one layer no package may supply.
128134

129135
A target row may carry a convention — a toolchain whose payload supplies that
130136
target's C library. The convention applies when the manifest states nothing for
131-
that target. When it replaces a default set with `mcpp toolchain default`, the
132-
status line reports the substitution and names the one-line override.
137+
that target AND nothing in the dependency graph supplies the target's system.
138+
The second condition is knowable only after resolution, so the toolchain is
139+
resolved there rather than before it.
133140

134141
### Dependencies
135142

docs/15-openkal-cross.md

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Cross-Compilation Over openkal
2+
3+
Conventional cross-compilation is served by a payload. A toolchain is built for
4+
one target, its driver has exactly one answer, and reaching a second target
5+
means obtaining a second toolchain. The number of payloads a distribution must
6+
publish is therefore the number of host-target pairs it supports.
7+
8+
openkal changes what is being crossed. The target side — the platform interface,
9+
the C library, the compiler runtime and the C++ runtime — becomes a set of
10+
packages resolved from the dependency graph and compiled from source by whichever
11+
compiler is running. What remains for the compiler is code generation, and one
12+
Clang binary emits every object format it was built with.
13+
14+
This document states the model, what a project writes, what the ecosystem
15+
supplies, and the limits that have been measured.
16+
17+
## The Claim
18+
19+
An ecosystem of N platforms and M architectures requires N implementations of
20+
one interface rather than N×M toolchains. The count follows from where the
21+
target side lives: a package built from source is built for whatever target the
22+
compiler is asked to emit, so a platform implementation is written once and
23+
reaches every architecture the compiler supports.
24+
25+
The claim is verified by a matrix of three hosts and three targets, each cell
26+
building one source and running the result.
27+
28+
## What A Project Writes
29+
30+
```toml
31+
[dependencies]
32+
openkal-llvm-runtime = "0.1.1"
33+
34+
[toolchain]
35+
default = "llvm@22.1.8"
36+
```
37+
38+
Two lines. The first selects three layers of the target side; the second names
39+
a compiler and says nothing about where anything else comes from.
40+
41+
Targets are given on the command line:
42+
43+
```bash
44+
mcpp build --target x86_64-linux
45+
mcpp build --target aarch64-macos
46+
mcpp build --target x86_64-windows-gnu
47+
```
48+
49+
No `[target.<triple>]` section is required for a hosted target, and no
50+
preprocessor directive is required in the source. A worked example is
51+
[examples/06-openkal-cross](../examples/06-openkal-cross).
52+
53+
## What The Ecosystem Supplies
54+
55+
| Package | Layer | Content |
56+
|---|---|---|
57+
| `openkal` || the specification, and the C++ modules that declare it |
58+
| `openkal-linux` | `kernel-abi` | the reference implementation, on Linux system calls |
59+
| `openkal-macos` | `kernel-abi` | on the macOS system-call surface |
60+
| `openkal-windows` | `kernel-abi` | on Win32 and the object manager, using no C runtime symbol |
61+
| `openkal-opensbi` | `kernel-abi` | on the RISC-V Supervisor Binary Interface, no operating system |
62+
| `openkal-uefi` | `kernel-abi` | on UEFI Boot Services, before an operating system exists |
63+
| `openkal-musl` | `c-abi` | musl redirected onto openkal, ported once |
64+
| `openkal-llvm-runtime` | `compiler-runtime`, `c++-abi` | compiler-rt builtins, libunwind, libc++abi and libc++, configured for openkal-musl |
65+
66+
A project names the last of these. The others follow from its dependencies.
67+
68+
## Why The Compiler Must Be LLVM
69+
70+
`openkal-llvm-runtime` declares the requirement rather than leaving it to be
71+
discovered:
72+
73+
```toml
74+
requires = ["mcpp:compiler=llvm"]
75+
```
76+
77+
Its sources are libc++'s, and its `std` module source in particular is compiled
78+
by Clang. Handing that source to GCC fails inside libc++'s own headers, in a
79+
message naming a file the reader has never opened:
80+
81+
```
82+
fatal error: __config: No such file or directory
83+
```
84+
85+
With the requirement declared, the build refuses the combination before it
86+
compiles anything, and names the command that selects a compiler which satisfies
87+
it.
88+
89+
## How The Target Is Chosen
90+
91+
The target row of mcpp's own vocabulary may carry a toolchain convention. That
92+
convention names the payload which supplies **that target's C library**, and it
93+
applies only when two conditions hold: the manifest states nothing for the
94+
target, and nothing in the dependency graph supplies the target's system.
95+
96+
The second condition is knowable only after the graph is resolved. A project
97+
whose C library comes from `openkal-musl` therefore keeps the compiler it asked
98+
for, while a project with no dependencies still receives the payload the row
99+
names. Both behaviours were measured; deciding either way in advance was wrong
100+
for the other.
101+
102+
## The Environment Segment
103+
104+
On Linux the third segment of a target triple names the C library. Under openkal
105+
the C library comes from the graph, so a triple that names one states a request
106+
the graph may not honour:
107+
108+
```
109+
mcpp build --target x86_64-linux-gnu # asks for glibc
110+
c-abi musl (openkal-musl@0.3.3, graph)
111+
```
112+
113+
The graph decides. Omitting the segment states no request and produces the same
114+
artifact:
115+
116+
```
117+
mcpp build --target x86_64-linux
118+
```
119+
120+
The build reports the mismatch when the segment is present and disagrees. It is
121+
a report rather than a refusal, because the segment is ignored rather than
122+
violated. Measured on one host, `x86_64-linux` against `x86_64-linux-musl`: the
123+
two executables differ, and after stripping they are byte-identical. What
124+
differs is the debug information, which records the output directory, and the
125+
directory is named after the triple. The code is the same code.
126+
127+
On Windows the same segment names the object ABI instead — `gnu` for PE with the
128+
GNU ABI, `msvc` for PE with Microsoft's — and both are compatible with more than
129+
one C library. The mismatch report is therefore scoped to platforms where the
130+
segment names a C library.
131+
132+
## Bare Metal
133+
134+
A target with no operating system is the same model with the platform layer
135+
supplied by firmware rather than by a kernel. `riscv64-none-elf` over OpenSBI
136+
runs the same source as a hosted target, including `import std`, because the
137+
standard library it uses is the one the graph supplied rather than the
138+
compiler's own.
139+
140+
Two things must be declared, both properties of the board rather than defaults:
141+
142+
```toml
143+
[target.riscv64-none-elf]
144+
sysroot = ""
145+
runner = ["qemu-system-riscv64", "-machine", "virt", "-nographic",
146+
"-no-reboot", "-bios", "default", "-kernel"]
147+
```
148+
149+
`sysroot = ""` selects the zero-libc tier. Which machine model and which
150+
firmware mode to use are board facts, and an engine that guesses one is an
151+
engine a different board has to fight.
152+
153+
### The Source Is The Same, The Program Is Not
154+
155+
"The same source" is a claim about the toolchain and the standard library, and
156+
it holds: `import std` works, the C++ runtime is the one the graph supplied, and
157+
no `#if` distinguishes the targets. It is not a claim that any given program
158+
builds for any given target, and the specification is explicit about why.
159+
160+
A bare-metal backend provides some interfaces and not others. `openkal-opensbi`
161+
provides `abort`, `stream`, `memory`, `env` and `time`; it provides no
162+
filesystem and no tasks, because the machine has none. Clause 6.1 makes that
163+
absence a link-time fact:
164+
165+
> An interface that an implementation does not provide is absent as a link-time
166+
> definition, and a consumer that uses it fails to link.
167+
168+
A capability word therefore answers a narrower question than it first appears
169+
to. It says how an implementation behaves *within an interface it provides*
170+
whether names are compared case-sensitively, what the granularity of a clock is.
171+
Whether the interface exists at all is answered before that, by the dependency
172+
graph, and failing that by the linker.
173+
174+
The distinction is easy to lose, because the query is an inline function over a
175+
data object, so a program that merely asks whether a filesystem exists takes the
176+
address of `kal_fs_props` and fails to link with no filesystem call anywhere in
177+
it. Defining that word as zero in the backend removes the error and is the one
178+
remedy the clause forbids: the program then proceeds past the point the linker
179+
existed to stop it at. It was tried, published as `openkal-opensbi@0.1.3`, and
180+
retracted.
181+
182+
### Two Routes To A Bare x86_64 Machine
183+
184+
An x86_64 machine with no operating system is reached in two different ways, and
185+
the difference is what loads the program.
186+
187+
| Route | Target | Platform layer | Entry |
188+
|---|---|---|---|
189+
| UEFI application | `x86_64-windows-gnu` | `openkal-uefi` | firmware, with Boot Services available |
190+
| Kernel, or raw bare metal | `x86_64-none-elf` | none, or `openarch` | the reset vector, with nothing beneath |
191+
192+
A UEFI application is PE/COFF entered through the Microsoft x64 calling
193+
convention. Both are properties the LLVM toolchain already has, so its target is
194+
the same triple as a Windows program and firmware function pointers are called
195+
directly. What distinguishes it from a Windows build is which implementation of
196+
the platform interface the graph resolved, together with three link flags that
197+
select `IMAGE_SUBSYSTEM_EFI_APPLICATION`.
198+
199+
A kernel has no firmware services to call. Its target is `x86_64-none-elf`, the
200+
zero-libc tier: no C library on the compile line, no library directory on the
201+
link, and `#include <stdio.h>` does not resolve. The program is entered at its
202+
own `_start` and reaches hardware directly.
203+
204+
`openarch` is the layer such a program builds on. It is not a platform interface
205+
and does not answer to `mcpp:kernel-abi`; it is the architecture mechanism —
206+
execution contexts, traps, per-CPU state and address spaces — presented as one
207+
interface over several instruction sets, with a backend package per instruction
208+
set. A kernel depends on it and supplies its own platform layer, or none.
209+
210+
### Why x86_64 Bare Metal Required Engine Work
211+
212+
`riscv64-none-elf` and `aarch64-none-elf` are rows in a table and nothing more:
213+
Clang has a BareMetal toolchain for both, drives their links itself and reaches
214+
`ld.lld`. It has none for x86_64, so that triple falls through to the generic
215+
GCC toolchain, whose linker is the host's `g++`:
216+
217+
```
218+
g++: error: unrecognized command-line option '-fuse-ld=…/ld.lld'
219+
```
220+
221+
Measured for every spelling of a bare x86_64 triple, and not correctable by any
222+
flag. The row therefore carries a linker emulation and mcpp invokes `ld.lld`
223+
itself, which is also why the host toolchain must be shown not to participate in
224+
such a link.
225+
226+
## Measured Limits
227+
228+
Three, recorded because each was found by building rather than by reading.
229+
230+
**A backend must define every capability word.** The specification's queries are
231+
inline functions over property objects, so a program that merely asks whether a
232+
filesystem exists takes the address of `kal_fs_props`. A backend that omits the
233+
words for layers it lacks makes the question fail to link on exactly the class of
234+
machine the question exists for.
235+
236+
**Two suppliers of one layer is an error rather than a choice.** A C library, a
237+
platform interface and a C++ runtime are mutually exclusive. Selecting the wrong
238+
one does not fail the link; it produces a program that runs and intermittently
239+
does not.
240+
241+
**A payload's C++ runtime cannot sit above a foreign C library.** Its
242+
`__config_site` records the configuration it was built with. The resolver's
243+
structure prevents the combination on the default path, and a diagnostic covers
244+
the paths where a project overrides the contract explicitly.
245+
246+
## Reference
247+
248+
[docs/14 — The Target Side](14-target-side.md) for the five layers, the four
249+
origins and the rules. [SPEC-002](spec/target-side.md) for the normative
250+
statement of the capability grammar.

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- [12 - Distributing a Prebuilt Library](12-binary-distribution.md)
1818
- [13 - Bare-Metal and Freestanding Targets](13-baremetal.md)
1919
- [14 - The Target Side](14-target-side.md)
20+
- [15 - Cross-Compilation Over openkal](15-openkal-cross.md)
2021

2122
## Specifications
2223

docs/spec/target-side.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,32 @@ mcpp:<层名>[=<实现名>]
132132
⚠️ 当前实现覆盖「两层均来自图」与「两层均来自载荷」。
133133
「一层预制、一层来自图」的接线尚不完整。
134134

135+
### 3.4 规则四:三元组是请求 ✅ 已实现
136+
137+
三元组的 env 段**必须**被当作对 `c-abi` 的请求,而非其答案。
138+
139+
- 段缺席 ⇒ 未作陈述,任何供给者都不与之矛盾;
140+
- 段存在且与解析出的 `c-abi` 不同,且后者来自图 ⇒ 引擎**必须**报出该不一致,
141+
**必须**给出不含该段的目标拼写;**禁止**据此使构建失败。
142+
143+
⚠️ 拒绝曾被实现并被实测否掉:它打破了每一个把宿主目标拼作 `x86_64-linux-gnu`
144+
的工程与 CI 配置 —— 而那正是 `mcpp toolchain list` 打印的拼写。
145+
判据是该请求**不改变任何东西**:图两种写法下都供给同一个 C 库,
146+
因此该段是被忽略而非被违反。
147+
148+
⚠️ 规范化会把 `x86_64-linux` 写成 `x86_64-linux-gnu`,因为身份必须是全的。
149+
请求**必须**在规范化之前捕获;报告**应当**显示工程书写的拼写。
150+
151+
### 3.5 目标表的约定何时生效 ✅ 已实现
152+
153+
目标行的 `pin` 命名的是**供给该目标 C 库的载荷**,不是偏好的编译器。
154+
**必须**仅在两个条件同时成立时生效:清单对该目标未作陈述,
155+
且依赖图中无人供给 `kernel-abi``c-abi`
156+
157+
⚠️ 第二个条件在依赖解析之后才可知,因此工具链**必须**在其之后解析。
158+
过早决定被双向实测否掉:无条件应用会替换用户用 `mcpp toolchain default`
159+
设下的工具链;不应用会让一个零依赖的交叉构建从可用变为不可用。
160+
135161
---
136162

137163
## 4. 报告

docs/zh/14-target-side.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,21 @@ C 库、平台接口与 C++ 运行时是互斥的选择,而非可叠加的贡献
100100
`--target <三元组>`,或 `[build] target`。OS 段选择平台接口。
101101
env 段陈述一条对 C 库的请求;它是请求而非答案,解析出的值由构建报告。
102102

103+
省略该段即为不陈述:`x86_64-linux` 请求「供给该层的任何实现」,
104+
`x86_64-linux-musl` 请求 musl。依赖图供给了另一个时以图为准,
105+
构建会报出该名字不准确并给出应当使用的拼写。该请求是被忽略而非被违反,
106+
因此产物两种写法下相同。
107+
103108
### 工具链
104109

105110
`mcpp toolchain default <族>@<版本>`、清单中的 `[toolchain]`,
106111
或针对单一目标的 `[target.<三元组>].toolchain`。它选择 `compiler` 层 ——
107112
唯一一个任何包都不能供给的层。
108113

109114
目标表的行可以携带一条约定,即其载荷供给该目标 C 库的工具链。
110-
该约定在清单对该目标未作陈述时生效。当它替换了由 `mcpp toolchain default`
111-
设定的默认时,状态行报出该替换并给出一行覆盖写法。
115+
该约定在两个条件同时成立时生效:清单对该目标未作陈述,****依赖图中没有任何
116+
东西供给该目标的系统。第二个条件只有在解析之后才可知,
117+
因此工具链在那之后解析,而不在那之前。
112118

113119
### 依赖
114120

0 commit comments

Comments
 (0)