Skip to content

Commit 521d02e

Browse files
committed
ci: linux 加一条 llvm 腿,并给它一个装了系统 ffmpeg 的宿主机
#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:一条非阻塞的腿很容易被永久无视,和一条长期红的腿是同一种病。
1 parent 4376903 commit 521d02e

1 file changed

Lines changed: 94 additions & 19 deletions

File tree

.github/workflows/validate.yml

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,10 @@ jobs:
410410
shell: bash
411411
run: |
412412
full=0; [ "$MEMBERS" = "__ALL__" ] && full=1
413-
emit() { # platform os suffix ext mcpp xlings shards
414-
for i in $(seq 0 $(( $7 - 1 ))); do
415-
printf '{"platform":"%s","os":"%s","suffix":"%s","ext":"%s","mcpp":"%s","xlings":"%s","shard":%d,"shards":%d},' \
416-
"$1" "$2" "$3" "$4" "$5" "$6" "$i" "$7"
413+
emit() { # platform os suffix ext mcpp xlings toolchain shards
414+
for i in $(seq 0 $(( $8 - 1 ))); do
415+
printf '{"platform":"%s","os":"%s","suffix":"%s","ext":"%s","mcpp":"%s","xlings":"%s","toolchain":"%s","shard":%d,"shards":%d},' \
416+
"$1" "$2" "$3" "$4" "$5" "$6" "$7" "$i" "$8"
417417
done
418418
}
419419
# Shard count follows the WORK, per platform, measured.
@@ -477,9 +477,27 @@ jobs:
477477
wn=$(shards_for windows 2)
478478
{
479479
printf '{"include":['
480-
emit linux ubuntu-latest linux-x86_64 tar.gz bin/mcpp registry/bin/xlings "$ln"
481-
emit macos macos-15 macosx-arm64 tar.gz bin/mcpp registry/bin/xlings "$mn"
482-
emit windows windows-latest windows-x86_64 zip bin/mcpp.exe registry/bin/xlings.exe "$wn"
480+
# linux runs TWICE: once on mcpp's default toolchain and once on
481+
# llvm. Not redundancy — the two differ in a way that decides
482+
# whether a descriptor is correct. mcpp's linux default (gcc)
483+
# reaches the compiler through --sysroot into a clean xlings
484+
# subos, so the HOST /usr/include is not on the search path;
485+
# llvm has no sysroot and the host's headers are. compat.ffmpeg
486+
# put its vendored source root on -idirafter (below the system
487+
# dirs) and was green here for months while being broken on any
488+
# developer box with libavutil-dev installed — the gcc leg
489+
# structurally cannot see that class of bug.
490+
#
491+
# Cost, measured: this doubles linux to 6 jobs (2 x 3 shards) at
492+
# ~75min each, against a measured linux runner concurrency of 3 —
493+
# so the second leg QUEUES behind the first rather than running
494+
# beside it, and a full run's linux wall-clock roughly doubles.
495+
# That is the price of the coverage; the levers if it hurts are
496+
# in the shards_for comment above.
497+
emit linux ubuntu-latest linux-x86_64 tar.gz bin/mcpp registry/bin/xlings default "$ln"
498+
emit linux ubuntu-latest linux-x86_64 tar.gz bin/mcpp registry/bin/xlings llvm "$ln"
499+
emit macos macos-15 macosx-arm64 tar.gz bin/mcpp registry/bin/xlings default "$mn"
500+
emit windows windows-latest windows-x86_64 zip bin/mcpp.exe registry/bin/xlings.exe default "$wn"
483501
printf ']}'
484502
} | sed 's/,]}/]}/' > /tmp/matrix.json
485503
echo "matrix=$(cat /tmp/matrix.json)" >> "$GITHUB_OUTPUT"
@@ -563,8 +581,10 @@ jobs:
563581
bash tests/check_graphics_install_side_effects.sh
564582
565583
workspace:
566-
# The shard suffix appears only when the platform is actually split.
567-
name: workspace (${{ matrix.platform }}${{ matrix.shards == 1 && '' || format(' {0}/{1}', matrix.shard, matrix.shards) }})
584+
# The shard suffix appears only when the platform is actually split, and
585+
# the toolchain only when it is not mcpp's default — so the existing job
586+
# names are unchanged and only the new llvm leg is labelled.
587+
name: workspace (${{ matrix.platform }}${{ matrix.toolchain == 'default' && '' || format(' {0}', matrix.toolchain) }}${{ matrix.shards == 1 && '' || format(' {0}/{1}', matrix.shard, matrix.shards) }})
568588
needs: select
569589
if: needs.select.outputs.members != ''
570590
runs-on: ${{ matrix.os }}
@@ -606,7 +626,11 @@ jobs:
606626
# only a Git-Bash convenience.
607627
h=$(git ls-files -s -- 'pkgs/**/*.lua' 'tests/**' '.github/workflows/validate.yml' \
608628
| git hash-object --stdin)
609-
echo "REGISTRY_CACHE_KEY=mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-$h" >> "$GITHUB_ENV"
629+
# matrix.toolchain is in the key: both linux legs are runner.os
630+
# Linux, and the cache holds TOOLCHAINS and built compat packages.
631+
# Sharing one entry would let the gcc leg's objects answer for the
632+
# llvm leg — exactly the confusion this second leg exists to catch.
633+
echo "REGISTRY_CACHE_KEY=mcpp-registry-${{ runner.os }}-${{ matrix.toolchain }}-${{ env.MCPP_VERSION }}-$h" >> "$GITHUB_ENV"
610634
- name: Restore mcpp registry cache
611635
uses: actions/cache@v4
612636
with:
@@ -615,7 +639,7 @@ jobs:
615639
path: ~/.mcpp/registry
616640
key: ${{ env.REGISTRY_CACHE_KEY }}
617641
restore-keys: |
618-
mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-
642+
mcpp-registry-${{ runner.os }}-${{ matrix.toolchain }}-${{ env.MCPP_VERSION }}-
619643
# Host tools built from source (protoc, grpc_cpp_plugin, …).
620644
#
621645
# Measured on the run that added grpc-codegen: 636s to build protoc and
@@ -639,9 +663,9 @@ jobs:
639663
uses: actions/cache@v4
640664
with:
641665
path: ~/.mcpp/build-cache/v1/tool
642-
key: mcpp-toolstore-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ github.run_id }}-${{ matrix.platform }}-${{ matrix.shard }}
666+
key: mcpp-toolstore-${{ runner.os }}-${{ matrix.toolchain }}-${{ env.MCPP_VERSION }}-${{ github.run_id }}-${{ matrix.platform }}-${{ matrix.shard }}
643667
restore-keys: |
644-
mcpp-toolstore-${{ runner.os }}-${{ env.MCPP_VERSION }}-
668+
mcpp-toolstore-${{ runner.os }}-${{ matrix.toolchain }}-${{ env.MCPP_VERSION }}-
645669
- name: Download mcpp
646670
shell: bash
647671
env:
@@ -688,6 +712,43 @@ jobs:
688712
# plans, mcpp#232). The sandbox copy lands in ~/.mcpp/registry, so
689713
# the cache carries it across runs.
690714

715+
# ── Make the llvm leg look like a developer's box ─────────────────
716+
# A bare GitHub runner has no libav* under /usr/include, so a
717+
# descriptor that loses its vendored headers to the host's would still
718+
# be green here. That is precisely how compat.ffmpeg's -idirafter hole
719+
# survived: nothing in CI had a system ffmpeg to lose to. Installing
720+
# the dev headers is what turns this leg into a real test of include
721+
# ORDER rather than of include EXISTENCE.
722+
#
723+
# Deliberately only on the llvm leg. The default leg reaches its
724+
# compiler through --sysroot, so it is immune by construction and
725+
# installing there would change an existing job's environment for no
726+
# coverage. Keeping the blast radius to the new leg means a red here
727+
# is always about the thing this leg was added to watch.
728+
- name: Install host ffmpeg dev headers (llvm leg only)
729+
if: matrix.platform == 'linux' && matrix.toolchain == 'llvm'
730+
shell: bash
731+
run: |
732+
sudo apt-get update -qq
733+
sudo apt-get install -y -qq \
734+
libavutil-dev libavcodec-dev libavformat-dev \
735+
libavfilter-dev libavdevice-dev libswscale-dev libswresample-dev
736+
dpkg -l | grep -E 'libav(util|codec)-dev|libswscale-dev'
737+
738+
# ── Select the toolchain for this leg ─────────────────────────────
739+
# No version pin: `llvm` resolves to whatever the toolchain family's
740+
# current release is for the PINNED MCPP_VERSION, which is the version
741+
# that mcpp itself vouches for. Pinning a concrete llvm here would fail
742+
# hard the day that mcpp release stops shipping it, and the point of
743+
# this leg is the ABSENCE of a sysroot, not a particular llvm.
744+
- name: Select toolchain
745+
if: matrix.toolchain != 'default'
746+
shell: bash
747+
run: |
748+
"$MCPP" toolchain install ${{ matrix.toolchain }}
749+
"$MCPP" toolchain default ${{ matrix.toolchain }}
750+
"$MCPP" toolchain list
751+
691752
# ── This shard's slice of the plan ────────────────────────────────
692753
# `select` decided WHAT runs; this decides which part of it runs HERE.
693754
# Round-robin by position, which is what spreads the expensive members:
@@ -780,7 +841,10 @@ jobs:
780841
if: always() && hashFiles('timings.tsv') != ''
781842
uses: actions/upload-artifact@v4
782843
with:
783-
name: timings-${{ matrix.platform }}-${{ matrix.shard }}
844+
# The toolchain is part of the name because upload-artifact@v4
845+
# refuses duplicates: without it both linux legs would race to
846+
# upload `timings-linux-0` and the second one would fail the job.
847+
name: timings-${{ matrix.platform }}-${{ matrix.toolchain }}-${{ matrix.shard }}
784848
path: timings.tsv
785849
retention-days: 14
786850

@@ -831,18 +895,21 @@ jobs:
831895
exit 0
832896
fi
833897
834-
# Artifact name carries the platform: timings-<platform>-<shard>.
835-
for plat in linux macos windows; do
898+
# Artifact name carries platform AND toolchain leg:
899+
# timings-<platform>-<toolchain>-<shard>. Ranking per LEG, not per
900+
# platform: the two linux legs are different builds and averaging
901+
# them would describe neither.
902+
for leg in linux-default linux-llvm macos-default windows-default; do
836903
rows=$(mktemp)
837-
for f in timings/timings-$plat-*/timings.tsv; do
904+
for f in timings/timings-$leg-*/timings.tsv; do
838905
[ -f "$f" ] && cat "$f" >> "$rows"
839906
done
840907
[ -s "$rows" ] || { rm -f "$rows"; continue; }
841908
842909
total=$(awk -F'\t' '{s += $1} END {print s+0}' "$rows")
843910
count=$(wc -l < "$rows")
844911
{
845-
echo "### $plat — ${count} member(s), ${total}s of member wall-clock"
912+
echo "### $leg — ${count} member(s), ${total}s of member wall-clock"
846913
echo
847914
echo "| rank | seconds | share | member | result |"
848915
echo "|---:|---:|---:|---|---|"
@@ -867,7 +934,15 @@ jobs:
867934
echo "# <platform>\t<member>\t<seconds> — from run ${{ github.run_id }}"
868935
echo "# refresh: download the member-timings artifact and replace this file"
869936
for plat in linux macos windows; do
870-
for f in timings/timings-$plat-*/timings.tsv; do
937+
# ONLY the default-toolchain leg feeds this table. The llvm leg
938+
# runs the SAME members again, so globbing every leg would put
939+
# two rows per (platform, member) into the file — `sort -u`
940+
# keeps both, since the seconds differ — and shards_for sums
941+
# every matching row. Linux work would read as roughly double
942+
# and its shard count would be permanently pinned at the cap.
943+
# Both legs are planned from these numbers, so the default leg
944+
# is the right single baseline.
945+
for f in timings/timings-$plat-default-*/timings.tsv; do
871946
[ -f "$f" ] || continue
872947
awk -F'\t' -v p="$plat" '{ printf "%s\t%s\t%s\n", p, $2, $1 }' "$f"
873948
done

0 commit comments

Comments
 (0)