| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…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 进行中,至此无新增失败。
⚠️ 上一版的拒绝是语义上干净的答案,而它在 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 改为「必须报出、禁止据此失败」,并记下被否掉的那一版。
…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 下均运行。
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 同步:该目录里的程序在内核路线上跑不起来,
因为它向平台接口提问而那里没有平台接口 —— 说明白比含糊带过有用。
我写了「两种写法下产物相同」,然后去核验这句可证伪的话:
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` 这一行可直接粘贴,
因为「说清楚哪里不对」和「说清楚该怎么写」不是同一件事。
…ad 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。
| Back | FazBrowse Home | New Git URL |
Summary
两个症状,一个病根: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。
⚠️ 请求必须在规范化之前捕获 —— 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 库若来自依赖图,该载荷根本不被使用。
⚠️ 早决定被双向实测否掉:无条件应用会替换用户设下的工具链;不应用会让零依赖的交叉构建从可用变为不可用。两者都错,因为都在猜一个尚不存在的事实。
判据换成它本来就该是的:图供给 kernel-abi 或 c-abi 时,约定不适用。
⚠️ 代码不搬,只搬执行时机 —— 原地包成 lambda,在图已知处调用。先前记录的「39 处读写挡着」是没测就写下的:实测依赖解析段读 tc 仅 1 处,且那一处要的是三元组不是编译器。逃逸出 lambda 的局部只有 tcSpecIsMsvc 一个。
实测
Test plan
⚠️ v2026.8.24.2 的 GitHub Release 资产已发布但索引未采纳(bump PR 未开),所以用户仍在 24.1。本 PR 发 2026.8.24.3,由它进索引。