@@ -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