Skip to content

ci: linux 加一条 llvm 腿,并给它一个装了系统 ffmpeg 的宿主机 - #184

Merged
Sunrisepeak merged 1 commit into
mainfrom
ci/llvm-leg-on-linux
Aug 8, 2026
Merged

ci: linux 加一条 llvm 腿,并给它一个装了系统 ffmpeg 的宿主机#184
Sunrisepeak merged 1 commit into
mainfrom
ci/llvm-leg-on-linux

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

#183 修的两个包在这份 CI 里一直是绿的,不是因为它们对,而是因为这份 CI 结构上看不见那类问题:

  • mcpp 在 linux 的默认工具链是 gcc,它通过 --sysroot 进 xlings subos 编译 —— 那里的 /usr/include 干干净净,宿主机的头根本不在搜索路径上。
  • 就算换了工具链,GitHub runner 的 /usr/include 里也没有 libav*,-idirafter 没有东西可输。

所以这条腿必须同时改两件事才有意义:换 llvm(无 sysroot),并真的把 ffmpeg 的 dev 头装上。只做前者,#183 那个 bug 照样照不出来。

矩阵

emit() 多一个 toolchain 维度,linux 发两次(default + llvm),macos / windows 保持 default。job 名只在非 default 时才带工具链后缀,所以既有 job 名不变。

成本是实打实的:linux 从 3 个 shard 变 6 个,而实测 linux runner 并发是 3 —— 第二条腿排在第一条后面跑,full run 的 linux 墙钟大致翻倍。要调的话杠杆在 shards_for 上面那段注释里。

三处必须跟着改的地方

改动 不改会怎样
registry / toolstore 缓存键加 matrix.toolchain 两条腿的 runner.os 都是 Linux,缓存装的是工具链和编译好的 compat 包 —— 共用一个条目会让 gcc 的产物替 llvm 回答,正好抹掉这条腿存在的理由
timings artifact 名加 toolchain upload-artifact@v4 拒绝重名,两条腿会抢 timings-linux-0,第二个直接把 job 弄失败
member-timings.tsv 只吃 default 腿 llvm 腿跑同一批成员,全 glob 进去会让每个 (platform, member) 出现两行(sort -u 留不住,秒数不同),而 shards_for 把匹配行全加起来 —— linux 工作量读成约两倍,shard 数被永久顶到上限

step summary 里则按分别列(linux-default / linux-llvm / …),两条腿是不同的构建,平均它们谁也不描述。

验证

本地在一台装了 ffmpeg 6.1.1 dev 头和 Catch2 v3 的机器上,llvm@22.1.8 取样跑了 19/60 个成员(C compat / header-only / 编译型 C++ / module 包 / 测试框架 / install() 钩子包各若干):

矩阵生成逻辑单独跑过:10 个 job(linux×2 腿×3 shard + macos×2 + windows×2),JSON 合法,step 顺序为 Download mcpp → 装头 → 选工具链 → 取成员 → 刷索引 → test。

已知会红 / 未覆盖

catch2-v2-main 在装了系统 Catch2 v3 的机器上失败:catch2_main.cpp__has_include(<catch2/catch_all.hpp>) 判 v2/v3,该探测同样落到系统目录。GitHub runner 不装 catch2,所以这条腿上它是绿的 —— 实测 #183 的 CI,linux / macos / windows 三平台 catch2-v2-main 全 ok。真要修得等 per-version build blocks(mcpp#290)。

其余 41 个成员(grpc / protobuf / godot / llamacpp / openssl 等重型)受本机磁盘所限没在 llvm 下跑过 —— 这条腿的第一轮就是它们第一次在 linux 上被 llvm 编译,可能还有别的既有问题被照出来。这是新增覆盖必然的第一轮代价,不是本 PR 引入的回归;但也意味着第一次跑很可能不是全绿。

刻意不加 continue-on-error:一条非阻塞的腿很容易被永久无视,和一条长期红的腿是同一种病。如果第一轮红得太散,更好的处理是逐个修或临时缩小成员集,而不是把腿静音。

#183 修的两个包(compat.ffmpeg 的 -idirafter 排在系统目录之后、compat.catch2
漏 #include <new>)在这份 CI 里一直是绿的,不是因为它们对,而是因为这份 CI
结构上看不见它们:

  * mcpp 在 linux 的默认工具链是 gcc,而它通过 --sysroot 进 xlings subos 编
    译 —— 那里的 /usr/include 干干净净,宿主机的头根本不在搜索路径上。
  * 就算换了工具链,GitHub runner 的 /usr/include 里也没有 libav*,-idirafter
    没有东西可输。

所以这条腿要同时改两件事才有意义:换 llvm(无 sysroot),并真的把 ffmpeg 的
dev 头装上。只做前者,本次这个 bug 照样照不出来。

## 矩阵

emit() 多一个 toolchain 维度,linux 发两次(default + llvm),macos / windows
保持 default。job 名只在非 default 时才带工具链后缀,所以既有 job 名不变。

成本是实打实的:linux 从 3 个 shard 变 6 个,而实测 linux runner 并发是 3,
所以第二条腿是排在第一条后面跑,full run 的 linux 墙钟大致翻倍。要调的话
杠杆在 shards_for 上面那段注释里。

## 三处必须跟着改的地方

  * registry / toolstore 缓存键加 matrix.toolchain。两条腿的 runner.os 都是
    Linux,而缓存装的是工具链和编译好的 compat 包 —— 共用一个条目会让 gcc
    的产物替 llvm 回答,正好抹掉这条腿存在的理由。
  * timings artifact 名加 toolchain。upload-artifact@v4 拒绝重名,不加的话两
    条腿会抢 `timings-linux-0`,第二个直接把 job 弄失败。
  * member-timings.tsv 只吃 default 腿。llvm 腿跑的是同一批成员,把每条腿都
    glob 进去会让每个 (platform, member) 出现两行(sort -u 留不住,秒数不
    同),而 shards_for 是把匹配行全加起来的 —— linux 的工作量会读成约两倍,
    shard 数被永久顶到上限。step summary 里则按腿分别列,两条腿是不同的构建,
    平均它们谁也不描述。

## 已知会红

catch2-v2-main 在装了系统 Catch2 v3 的机器上会失败:catch2_main.cpp 用
__has_include(<catch2/catch_all.hpp>) 判 v2/v3,这个探测同样会落到系统目录。
GitHub runner 不装 catch2,所以这条腿上它是绿的(实测 #183 的 CI:linux /
macos / windows 三平台 catch2-v2-main 全 ok)。真要修得等 per-version build
blocks(mcpp#290)。

本地只在 llvm 下取样跑了 19/60 个成员,其余 41 个(grpc / protobuf / godot /
llamacpp / openssl 等重型)受本机磁盘所限没跑 —— 这条腿的第一轮就是它们第一
次在 linux 上被 llvm 编译,可能还有别的既有问题被照出来。刻意不加
continue-on-error:一条非阻塞的腿很容易被永久无视,和一条长期红的腿是同一种病。
@Sunrisepeak
Sunrisepeak merged commit 13b1fe3 into main Aug 8, 2026
15 checks passed
Sunrisepeak added a commit that referenced this pull request Aug 8, 2026
`Plan the shards` 用平台的矩阵条目数当分片数:

    linux:$(jq -r '[.include[]|select(.platform=="linux")]|length' ...)

在 #184 之前这两个数恰好相等,所以一直是对的。#184 给 linux 加了第二条工具链
腿之后不再相等:平台发 2 x 3 = 6 个条目,而切分仍然是 3 路,每个条目带的
shard 是 0..2。于是 plan 按 6 路切,job 只消费 0/1/2 —— 分到 3/4/5 的成员
一个都没跑。

65 个成员实际只跑了 29 个,而且是**静默**的:一个从未被分配的成员,和一个跑
过并通过的成员,在 CI 界面上长得一模一样。丢掉的里面有 ffmpeg、opencv-module
及其两个 feature 成员、catch2-v2、catch2-main、openssl —— 正是那条新腿被加进
来要测的东西。#184 全绿,但它想验证的路径一次都没执行。

改成读 `.shards`,也就是 emit() 已经写进每个条目、job 自己也在用
(matrix.shards)的那个值。plan 和消费方从此读同一个数,而不是两个碰巧相等
的数。

macos / windows 不受影响也不需要改:它们仍是单腿,条目数正好等于分片数 ——
这正是这个 bug 只咬 linux 的原因,也是它能在 review 里活下来的原因。

验证:拿 #184 那次运行的真实 matrix.json 跑 jq,linux 6 → 3,macos / windows
维持 2;plan_shards 三片合计从 29 回到 65/65,上面点名的成员全部归位。
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.

1 participant