-
Notifications
You must be signed in to change notification settings - Fork 13
310 lines (281 loc) · 14.5 KB
/
Copy pathci-linux.yml
File metadata and controls
310 lines (281 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
name: ci-linux
# Self-host CI on Linux: mcpp builds mcpp. The bootstrap mcpp comes from
# `xlings install mcpp` (xim:mcpp in the xlings package index), so this
# workflow no longer depends on a previous-release tarball — the
# chicken-and-egg now lives upstream in the xlings index.
#
# SHAPE: four INDEPENDENT jobs, no `needs:` between them. Each restores the
# same cache lineage (see .github/actions/bootstrap-mcpp) and pays one warm
# `mcpp build` (~2.5 min) to get the PR's own binary, then does its own leg.
# That warm rebuild is far cheaper than serialising the legs behind a shared
# artifact would be:
#
# before: build → unit → gcc → musl → llvm → xlings ≈ 18 min (one job)
# after: max(build+unit, gcc, musl+llvm, xlings) ≈ 7-8 min
#
# The ~18 min e2e suite is a SEPARATE workflow (ci-linux-e2e.yml, sharded)
# that runs in parallel on the same caches.
#
# Paired workflows: ci-linux-e2e.yml, ci-macos.yml, ci-windows.yml.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# MCPP_HOME pinned so the cache keys below restore into the same path
# mcpp resolves at runtime.
MCPP_HOME: /home/runner/.mcpp
# Verbose every mcpp invocation for richer CI diagnostics (src/cli.cppm).
# Safe here: this workflow no longer runs the e2e suite, which is what
# asserts mcpp's default quiet output (tests 48/53).
MCPP_VERBOSE: "1"
jobs:
build-test:
name: build + unit tests (linux x86_64, self-host)
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
# Before the bootstrap, not after: this needs no toolchain and no mcpp,
# takes under a second, and the drift it catches (a stale xlings pin)
# would otherwise surface minutes later as an unrelated-looking
# dependency-resolution failure. Pure text extraction on purpose — it
# has to work when the build is broken.
- name: Check version / xlings pin consistency
run: bash .github/tools/check_version_pins.sh
# Same placement and the same reason as the pin check above: adding a
# package under `modules/` means editing four files, and three of those
# omissions fail later and elsewhere — the worst of them only on a macOS
# machine that has no mcpp to rebuild with.
- name: Check modules/ wiring
run: bash .github/tools/check_modules_wiring.sh
# Same placement, same reason: pure text, no toolchain, under a second.
#
# This one is a HARD gate (unlike lint-ci-assertions.sh below) because it
# has no false positives left — its scope was cut to the two directories
# that walk trees mcpp does not control, and the one legitimate site
# carries a NARROW-OK rationale. See the script's header for why the
# scope is that narrow, and mcpp#516 for what it costs when it is missed.
#
# It runs on LINUX on purpose even though the bug it guards is
# Windows-only: it is text analysis, and putting it where the fast leg is
# means a violation is reported in seconds rather than after a Windows
# bootstrap.
- name: Check no walk-derived path is narrowed directly
run: bash .github/tools/check_narrow_conversions.sh
# Same placement, same reason: pure text, no toolchain.
#
# IT PRINTS AND DOES NOT FAIL, DELIBERATELY. The three rules it carries
# each have real false positives — a control step legitimately pins one
# target, a `grep -q .` legitimately expects output — and the whole value
# of the thing is that somebody READS it. A hard gate would be routed
# around with a suppression within a month, and the suppression would then
# be the only record that a rule ever existed.
- name: Where the CI assertions live
run: bash tools/lint-ci-assertions.sh
# Text-only, like the two steps around it, and it belongs here rather
# than in the target-matrix workflow: that workflow runs the matrix, and
# this asserts a property of the TABLE, which is readable without a
# single toolchain.
- name: The target matrix names every refusal
run: bash .github/tools/check_matrix_reasons.sh
# Same placement and the same reason: pure text, no toolchain, and the
# drift it catches — a 简体中文 page that has silently fallen behind its
# English original — is invisible to every other job.
- name: Check documentation style and bilingual parity
run: bash .github/tools/check_docs_style.sh
- uses: ./.github/actions/bootstrap-mcpp
- name: Configure mirror + Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
# Set GLOBAL mirror via xlings directly (bootstrap mcpp may lack --mirror flag)
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build
- name: Unit + integration tests via `mcpp test`
run: |
# Use freshly-built mcpp for test (it has --mirror support)
MCPP_FRESH=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
echo "MCPP_FRESH=$MCPP_FRESH" >> "$GITHUB_ENV"
"$MCPP_FRESH" self config --mirror GLOBAL
"$MCPP_FRESH" test
# Each package under `modules/` carries its own tests, and they are built
# as that package ALONE -- a configuration the root suite never produces,
# since there every module is linked together. A subsystem that has
# quietly come to depend on something it does not declare compiles in the
# root build and fails here, which is the whole reason to run both.
#
# Every member is run, and the loop is derived from the manifest rather
# than written out: a list maintained by hand is a list that stops
# matching, and `check_modules_wiring.sh` cannot see this file.
- name: Per-subsystem tests (`mcpp test -p <member>`)
run: |
set -euo pipefail
# `$MCPP_FRESH` comes from the step above, which takes the NEWEST
# mcpp anywhere under target/. That is correct in this job because
# nothing between the two steps writes another one — but `mcpp pack`
# leaves a copy under `target/dist/`, and a pack step inserted above
# would silently redirect both this loop and the suite before it to
# the packaged binary. Observed locally; kept as a note because the
# expression is shared with three other jobs and this is not the
# place to change it.
case "$MCPP_FRESH" in
*/target/dist/*) echo "MCPP_FRESH points into target/dist — a pack step now runs before the tests"; exit 1 ;;
esac
members=$(sed -n '/^\[workspace\]/,/^\[/p' mcpp.toml \
| grep -oE '"modules/[a-z0-9-]+"' | tr -d '"' | sed 's|modules/||')
[ -n "$members" ] || { echo "no workspace members found in mcpp.toml"; exit 1; }
echo "members: $(echo $members | tr '\n' ' ')"
for m in $members; do
echo "::group::mcpp test -p $m"
"$MCPP_FRESH" test -p "$m"
echo "::endgroup::"
done
# A cold, from-scratch self-host build with the manifest-pinned GCC: the
# property `build-test` cannot cover, because it builds incrementally on a
# restored target/. `mcpp test` is deliberately NOT repeated here — it would
# be the same suite, same toolchain, same driver binary as `build-test`,
# differing only in incremental state.
toolchain-gcc:
name: "toolchain: gcc (cold self-host)"
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/bootstrap-mcpp
- name: Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build
MCPP_FRESH=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
cp "$MCPP_FRESH" /tmp/mcpp-fresh
echo "MCPP=/tmp/mcpp-fresh" >> "$GITHUB_ENV"
- name: "Toolchain: GCC — cold rebuild with the PR binary"
run: |
"$MCPP" clean
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved gcc@16.1.0" build.log
# The two cheap legs share one runner: each is ~1 min after the warm build,
# so a runner apiece would cost more in setup than it saves in wall-clock.
toolchain-cross:
name: "toolchain: musl + llvm"
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/bootstrap-mcpp
- name: Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build
MCPP_FRESH=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
cp "$MCPP_FRESH" /tmp/mcpp-fresh
echo "MCPP=/tmp/mcpp-fresh" >> "$GITHUB_ENV"
# Auto-installs gcc@16.1.0-musl on demand (cached across runs).
- name: "Toolchain: musl-gcc — build mcpp (--target)"
run: |
"$MCPP" clean
"$MCPP" build --target x86_64-linux-musl 2>&1 | tee build.log; grep -q "Resolved gcc@16.1.0 → x86_64-linux-musl" build.log
- name: "Toolchain: LLVM — build mcpp"
run: |
"$MCPP" toolchain install llvm 20.1.7
# Override project toolchain to use LLVM for this build
sed -i 's/^default = "gcc@16.1.0"/default = "llvm@20.1.7"/' mcpp.toml
"$MCPP" clean
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved llvm@20.1.7" build.log
# Restore
sed -i 's/^default = "llvm@20.1.7"/default = "gcc@16.1.0"/' mcpp.toml
# Integration: the mcpp built from THIS PR's source builds & runs a real
# external C++ project — xlings (openxlings/xlings ships its own mcpp.toml).
# MCPP_VENDORED_XLINGS only supplies the xlings package backend that mcpp
# resolves deps through.
integration-xlings:
name: "integration: mcpp builds & runs xlings"
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
XLINGS_NON_INTERACTIVE: '1'
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/bootstrap-mcpp
- name: Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build
MCPP_FRESH=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
cp "$MCPP_FRESH" /tmp/mcpp-fresh
echo "MCPP=/tmp/mcpp-fresh" >> "$GITHUB_ENV"
- name: "Integration: mcpp builds & runs xlings (openxlings/xlings)"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$GITHUB_WORKSPACE/.github/tools/git_clone_retry.sh" \
--depth 1 --recurse-submodules \
https://github.com/openxlings/xlings /tmp/xlings-src
cd /tmp/xlings-src
"$MCPP" self config --mirror GLOBAL
"$MCPP" build
"$MCPP" run
# THE CURRICULUM WAS NEVER BUILT BY ANY JOB. e2e 616 checks that the examples
# and the table that documents them agree, and says so explicitly: it builds
# nothing. Every example in this repository could therefore stop compiling and
# the only signal would be a user reporting it.
#
# The list is DERIVED FROM THE TREE, not written here. An example that is in
# neither the build list nor the skip table fails this job, so adding one
# forces a decision about whether CI can build it rather than silently
# leaving it uncovered.
examples:
name: "examples: the curriculum builds"
runs-on: ubuntu-24.04
# Generous rather than tight: six cold example builds plus the Vulkan one,
# which provisions the shader compiler and a software driver as payloads.
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/bootstrap-mcpp
- name: Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build
MCPP_FRESH=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
cp "$MCPP_FRESH" /tmp/mcpp-fresh
echo "MCPP=/tmp/mcpp-fresh" >> "$GITHUB_ENV"
- name: Build every example the runner can build
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
bash .github/tools/build_examples.sh
- name: "Vulkan example: build the device half and run it on lavapipe"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
cd examples/09-heterogeneous/vulkan/app
"$MCPP" build
# BOTH STORES, because which one holds the payload is decided by how
# this mcpp resolved its home -- and a `find` over only one of them
# is the defect this repository has already paid for twice (e2e 614,
# llama.cpp-m's Vulkan job). Ask for the file, not for a layout.
icd=$(find "${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs/xim-x-mesa-lavapipe" \
"$HOME/.xlings/data/xpkgs/xim-x-mesa-lavapipe" \
-name 'lvp_icd.x86_64.json' -print -quit 2>/dev/null || true)
[ -n "$icd" ] || { echo "no lavapipe ICD in either store"; exit 1; }
echo "using ICD $icd"
out=$(VK_DRIVER_FILES="$icd" "$MCPP" run 2>&1) || { echo "$out"; exit 1; }
echo "$out"
# The device leg is the point: a run that silently fell back to the
# CPU variant would print the same numbers, so the assertion is that
# the program says which device answered AND that it is the payload's.
echo "$out" | grep -q 'llvmpipe' \
|| { echo "the run did not reach the lavapipe device"; exit 1; }
echo "$out" | grep -q '12 24 36 48' \
|| { echo "the device produced the wrong result"; exit 1; }