Skip to content

fix(run): --target X wrote X's build into the host's cache slot - #498

Merged
Sunrisepeak merged 2 commits into
mainfrom
fix/run-target-cache-slot
Aug 24, 2026
Merged

fix(run): --target X wrote X's build into the host's cache slot#498
Sunrisepeak merged 2 commits into
mainfrom
fix/run-target-cache-slot

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

mcpp run --target X 正确地为 X 交叉构建,然后把结果记在了宿主的槽里。损害不在这条命令,而在下一条:

$ mcpp run --target riscv64-none-elf        # 正确,经 qemu 跑起来
$ mcpp run
     Running `target/riscv64-none-elf/…/bin/openkal-same-source`
exit=1

缓存文件里逐字可见:一次 riscv 交叉构建写下的是 [target=]。空键的含义是「为这台机器构建」,而这正是 try_fast_run 允许自己直接 exec 缓存产物的唯一依据。裸的 mcpp run 命中它,跳过整个 prepare_build(既没构建宿主目标,也没解析 runner),在本机执行了另一个目标的二进制。

真因是一个丢掉的实参:

-  run_build_plan(*ctx, false, no_cache);
+  run_build_plan(*ctx, false, no_cache, target_triple);

最后那个形参就是缓存条目的 [target=] 键。cmd_build 两个调用点都传了,只有 run 这条没传。

⚠️ try_fast_run 的注释里记着同一个缺陷的另一条到达路径(目标写在清单的 [build] target 里),并且只守了那一扇门。这是另一扇门:flag。

回归测试的判据是路径,不是退出码

⭐ 异架构产物 exec 会失败,所以只看退出码的测试在那里因错误的理由通过,而在同架构上完全测不到——后者更危险:用户不会看到崩溃,只会拿到一个 musl 产物冒充宿主产物。测试因此用 x86_64-linux-musl(同架构、本机能跑),断言 Running 那行的路径

修复前实测为红:

FAIL: a bare `mcpp run` exec'd the x86_64-linux-musl artifact
       target/x86_64-linux-musl/50af2973556be3d7/bin/runslot

修复后转绿;单元测试 93 通过 / 0 失败。

发现路径:openkal-llvm-runtime 的 CI 有一步名为「on bare metal」却跑裸的 mcpp run(即宿主)。改成真的 --target riscv64-none-elf 之后,下一步炸了——缺陷一直都在,只是没有任何东西走到那条顺序上。

`mcpp run --target X` 正确地为 X 交叉构建,然后把结果**记在了宿主的槽里**。
损害不在这条命令,而在下一条:

    $ mcpp run --target riscv64-none-elf        # 正确,经 qemu 跑起来
    $ mcpp run
         Running `target/riscv64-none-elf/…/bin/openkal-same-source`
    exit=1

缓存文件里逐字可见 —— 一次 riscv 交叉构建写下的是:

    [target=]

空键的含义是「为这台机器构建」,而这正是 `try_fast_run` 允许自己
**直接 exec 缓存产物**的唯一依据。于是裸的 `mcpp run` 命中它,
跳过整个 prepare_build(所以既没有构建宿主目标,也没有解析 runner),
把另一个目标的二进制在本机执行了。

真因是一个丢掉的实参。`build_run_target` 收到了 `target_triple`、
用它建好 `ov` 并正确交叉构建,唯独在这里没有传下去:

    -  run_build_plan(*ctx, false, no_cache);
    +  run_build_plan(*ctx, false, no_cache, target_triple);

而最后那个形参就是缓存条目的 `[target=]` 键。`cmd_build` 的两个调用点
都传了,只有 run 这条路径没传。

⚠️ **`try_fast_run` 的注释里记着同一个缺陷的另一条到达路径**(目标写在
清单的 `[build] target` 里),并且只守了那一扇门:

    if (!want->defaultTarget.empty()) return std::nullopt;

这是另一扇门:flag。同一个缺陷有两个入口,修好一个不会暴露另一个 ——
只有走到那条路径上才会。

── 回归测试的判据是路径,不是退出码 ────────────────────────

⭐ 异架构产物 exec 会失败,所以只看退出码的测试**在那里因错误的理由通过,
而在同架构上完全测不到** —— 后者恰恰更危险:用户不会看到崩溃,
只会拿到一个 musl 产物冒充宿主产物,静默地跑下去。

`283_run_target_flag_owns_its_cache_slot.sh` 因此用
`x86_64-linux-musl`(同架构、本机能跑),断言 `Running \`…\`` 那行里的
**路径**不含交叉目标的目录。在修复前的二进制上实测为红:

    FAIL: a bare `mcpp run` exec'd the x86_64-linux-musl artifact
           target/x86_64-linux-musl/50af2973556be3d7/bin/runslot

修复后转绿。单元测试 93 通过 / 0 失败。

发现路径:openkal-llvm-runtime 的 CI 有一步名为「on bare metal」却跑裸的
`mcpp run`(即宿主)。把它改成真的 `--target riscv64-none-elf` 之后,
**下一步**炸了 —— 缺陷一直都在,只是没有任何东西走到那条顺序上。
版本号两处同步(mcpp.toml + src/version.cppm),CHANGELOG 记下缺陷、
两条到达路径,以及回归测试为何断言路径而非退出码。

bootstrap pin 不动:它是自举起点,指向一个已发布且能构建当前树的版本。
@Sunrisepeak
Sunrisepeak merged commit 6ad9a57 into main Aug 24, 2026
26 checks passed
Sunrisepeak added a commit to mcpplibs/openkal-llvm-runtime that referenced this pull request Aug 24, 2026
…l step

把 pin 移到 24.3 让裸机那一步第一次真正到达 OpenSBI,而**它之后的那一步**
随即失败:`mcpp run --target X` 一直把 X 的构建记在宿主的缓存槽里,
于是下一条裸的 `mcpp run` 直接 exec 了交叉产物。

那个缺陷早于本包,只因为一个自称测裸机的步骤开始真的测裸机才现形。
mcpp-community/mcpp#498 修复,随 2026.8.24.4 发布。
Sunrisepeak added a commit to mcpplibs/openkal-llvm-runtime that referenced this pull request Aug 24, 2026
…s package (#4)

* declare the compiler runtime, which was always the larger half of this package

本包 729 个对象里,**498 个是 compiler-rt 的 builtins、21 个是 libunwind 的**。
那些是一个 **C 程序**需要的东西 —— `__udivti3` 和它的亲戚与 C++ 无关 ——
而清单此前只声明 `mcpp:c++-abi=libc++`,于是它说自己供给了不供给的东西,
同时隐瞒了真正供给的那一半。

    provides = [
        "hosted-standard-library",
        "mcpp:c++-abi=libc++",
        "mcpp:compiler-runtime=compiler-rt",   ← 新增
    ]

实测(本机,mcpp 2026.8.24.3):

    compiler          llvm          (22.1.8, payload)
    compiler-runtime  compiler-rt   (openkal-llvm-runtime@0.1.2, graph)   ← 原为 payload
    kernel-abi        openkal       (openkal-linux@0.5.3, graph)
    c-abi             musl          (openkal-musl@0.3.3, graph)
    c++-abi           libc++        (openkal-llvm-runtime@0.1.2, graph)

五层里四层来自图,只剩编译器本身来自载荷。

── ⚠️ 同一改动里必须上移 MCPP_VERSION,原因不是兼容性 ────────

一个键有三种结局,而其中两种都是 exit 0:

    provides = ["mcpp:compiler-runtime=…"]   2026.8.19.4  exit 0  **没有被校验**
                                             2026.8.24.1  exit 2  拒绝
                                             2026.8.24.3  exit 0  解析成功

⭐ 19.4 那个零和 24.3 那个零**不是同一种成功**。前者早于层词表,
整个数组被当作不认识的键忽略 —— 它既不拒绝这条声明,也不读它。
CI 停在那里会在一份**中心主张从未被检查过**的清单上报绿。
因此 `MCPP_VERSION` 2026.8.19.4 → 2026.8.24.3 与本行同时改。

拒绝窗口只有 2026.8.24.1 一个版本(开始校验 `mcpp:` 前缀之后、
学会这一层之前)。索引已于今日到达 2026.8.24.3,且这些包尚无存量用户。

* ci: drop the branch-built tool, and make the bare-metal step reach bare metal

删掉的那个步骤自己写着退出条件:「⇒ When #486 ships, delete this step and
raise MCPP_VERSION」。#486 已于 2026-08-23 合入并随 2026.8.24.1 起发布,
而这个步骤还在从 `feat/import-std-capability` 分支构建 mcpp 并盖掉刚装好的那个。

后果不是「多跑一次构建」。日志里两行并排:

    Install mcpp                              mcpp 2026.8.24.3
    The build tool, from the branch…          mcpp 2026.8.21.3   ← 实际跑的是它

于是 `MCPP_VERSION` 这个变量对**被测的行为**不起作用,本 PR 把它移到 24.3
之后 CI 仍然用 21.3 跑,报出三层版的错误信息。两个 job 各一处,一并删除。

── ⚠️ 并且那一步在测宿主,而它的名字说的是裸机 ──────────────

`The same source on bare metal, with exceptions` 跑的是裸的 `mcpp run`。
这个清单**刻意没有 `[build] target`**(注释就写在第 5 行),所以裸的
`mcpp run` 构建的是**宿主**。四行断言两种跑法都会打印 —— 那是清单自己的
主张,不是巧合 —— 于是这一步在宿主构建上报绿,**一次都没有到过 OpenSBI**。

实测 2026-08-24:产物是 `target/x86_64-linux-gnu/…`。

改法两处:
  · `mcpp run --target riscv64-none-elf`
  · 加一条 `grep -q 'Boot HART'`,即固件自己的横幅 —— 这样一次没离开宿主
    的运行**无法**靠打印那四行应用输出来满足本步骤

本机实测改后:OpenSBI 横幅出现,四行输出与宿主一致。

* ci: pin 2026.8.24.4, which is the release that survives the bare-metal step

把 pin 移到 24.3 让裸机那一步第一次真正到达 OpenSBI,而**它之后的那一步**
随即失败:`mcpp run --target X` 一直把 X 的构建记在宿主的缓存槽里,
于是下一条裸的 `mcpp run` 直接 exec 了交叉产物。

那个缺陷早于本包,只因为一个自称测裸机的步骤开始真的测裸机才现形。
mcpp-community/mcpp#498 修复,随 2026.8.24.4 发布。

* ci: the emulated serial console ends lines with CRLF

裸机那一步的四行与宿主的四行**内容完全一致**,而 `diff` 报四行全不同:

    1,4c1,4
    < sorted: 2 4 7
    ---
    > sorted: 2 4 7

不可见字符。裸机运行经由模拟的 16550 UART 到达控制台,而串口控制台
以 CRLF 结束一行;宿主运行不是。

⭐ 这种失败读起来像是**被测的主张不成立**,而不像承载它的传输层不同 ——
两侧各加一个 `tr -d '\r'`。断言的是程序打印了什么,不是字节怎么到达的。

同样是把那一步从裸的 `mcpp run`(宿主)改成真的
`--target riscv64-none-elf` 之后才暴露的。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants