-
Notifications
You must be signed in to change notification settings - Fork 14
487 lines (447 loc) · 24.6 KB
/
Copy pathci-linux.yml
File metadata and controls
487 lines (447 loc) · 24.6 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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
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
# The architecture half: which tree a document lives in, whether every
# path it names resolves, and whether each specification is reachable
# from all three indexes. Same placement and the same reason -- it needs
# no toolchain, and what it catches is invisible to every other job.
- name: Check documentation structure
run: bash .github/tools/check_docs_structure.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
# TWO CRITERIA A BUILD CANNOT SHOW, from the examples whose READMEs state
# them. Both are the shape this repository has paid for before: a green
# build over a result nobody compared.
- name: "examples: the feature criterion and the device language"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
set -e
# 11-features. NOT "the default build works" -- that passes while the
# optional package is resolved and merely unused. The criterion is
# that the default build's RESOLUTION does not name it.
cd examples/11-features/greeter
"$MCPP" build 2>&1 | tee /tmp/f-default.log
"$MCPP" build --features metrics 2>&1 | tee /tmp/f-metrics.log
grep -qi counters /tmp/f-default.log && {
echo "FAIL: the default build resolved 'counters'"; exit 1; }
grep -qi counters /tmp/f-metrics.log || {
echo "FAIL: --features metrics did not resolve 'counters'"; exit 1; }
echo "ok: the optional package is absent without its feature"
cd - >/dev/null
# 12-a-new-device-language. Three criteria, and a probe run exits
# non-zero on purpose -- the program returns 1 when the answer is not
# 42 -- so those runs are guarded with `|| true` and judged by their
# output rather than their status.
cd examples/12-a-new-device-language/app
"$MCPP" run | tee /tmp/toy.log
grep -q 'answer() = 42' /tmp/toy.log || {
echo "FAIL: the .toy did not reach the link"; exit 1; }
# THE LANGUAGE IS EXECUTED, NOT PATTERN-MATCHED: 21 is what the
# emitted `while` loop computes, and no constant in the tree holds it.
grep -q 'gcd(1071, 462) = 21' /tmp/toy.log || {
echo "FAIL: the emitted loop did not run"; exit 1; }
# Editing the .toy reaches the artifact.
sed -i 's/scale(gcd(1071, 462), 2)/scale(gcd(1071, 462), 3)/' src/kernels/answer.toy
"$MCPP" run > /tmp/toy2.log 2>&1 || true
sed -i 's/scale(gcd(1071, 462), 3)/scale(gcd(1071, 462), 2)/' src/kernels/answer.toy
grep -q 'answer() = 63' /tmp/toy2.log || {
cat /tmp/toy2.log; echo "FAIL: editing the .toy did not reach the artifact"; exit 1; }
# THE COMPILER IS A DECLARED INPUT OF THE ACTION -- the half that
# fails silently, and the one criterion here that has to be built to
# isolate it. Bumping the tool's version is NOT that criterion: the
# tool's path is on the action's command line, so a new path re-runs
# the edge whether or not it is also declared as an input. Measured:
# with `a.input(compiler)` removed, a version bump still reached the
# artifact, and this step passed.
#
# The isolating change is different BYTES AT THE SAME PATH. Build a
# compiler that behaves differently, overwrite the cached binary in
# place, and the command line is byte-identical.
#
# BOTH DIRECTIONS ARE THE CRITERION, not either one. Falsified by
# removing `a.input(compiler)`: the artifact followed the first
# overwrite anyway and stopped following the restore, so a check that
# asserted only the first direction would have passed on a rule that
# tracks nothing.
#
# `n.value * 2` keeps `0` at `0`: a probe that changed every literal
# would turn `while (b != 0)` into a loop that divides by zero, and
# this step would report a crash rather than an answer.
probe_on() { sed -i 's/return std::format("{}", n.value);/return std::format("{}", n.value * 2);/' ../toyc/src/compile.cppm; }
probe_off() { sed -i 's/return std::format("{}", n.value \* 2);/return std::format("{}", n.value);/' ../toyc/src/compile.cppm; }
install_toyc() {
( cd ../toyc && "$MCPP" build >/dev/null )
cp "$(find ../toyc/target -name toyc -type f -perm -u+x | head -1)" "$1"
}
# `mcpp cache dir` prints a legacy-directory note on a second line.
store="$("$MCPP" cache dir | head -1)/tool"
cached="$(find "$store" -path '*toyc@0.1.0*/bin/toyc' | head -1)"
[ -n "$cached" ] || {
echo "FAIL: no toyc in the tool store under $store"; exit 1; }
probe_on; install_toyc "$cached"; probe_off
"$MCPP" run > /tmp/toy3.log 2>&1 || true
grep -q 'answer() = 168' /tmp/toy3.log || {
cat /tmp/toy3.log
echo "FAIL: a changed compiler binary did not reach the artifact."
echo " rules-toy must declare the compiler among the action's inputs."
exit 1; }
install_toyc "$cached"
"$MCPP" run > /tmp/toy4.log 2>&1 || true
grep -q 'answer() = 42' /tmp/toy4.log || {
cat /tmp/toy4.log
echo "FAIL: restoring the compiler binary did not reach the artifact --"
echo " the action is not tracking the compiler's bytes."
echo " rules-toy must declare the compiler among the action's inputs."
exit 1; }
# THE STORE HOLDS NO SOURCE CONTENT, which the example's README and
# docs/30 both state. Editing the compiler's sources at the same
# version changes nothing, because nothing rebuilds the tool.
probe_on
"$MCPP" run > /tmp/toy5.log 2>&1 || true
grep -q 'answer() = 42' /tmp/toy5.log || {
cat /tmp/toy5.log
echo "FAIL: the tool store now sees source content."
echo " The example's README and docs/30 state that it does not; update them."
exit 1; }
# ... and bumping the version is the way out both of them offer.
sed -i 's/^version = "0.1.0"/version = "0.1.1"/' ../toyc/mcpp.toml
"$MCPP" run > /tmp/toy6.log 2>&1 || true
sed -i 's/^version = "0.1.1"/version = "0.1.0"/' ../toyc/mcpp.toml
probe_off
grep -q 'answer() = 168' /tmp/toy6.log || {
cat /tmp/toy6.log; echo "FAIL: bumping the tool version did not rebuild it"; exit 1; }
echo "ok: the compiler is a declared input, and the store is keyed on the version"
- name: "Graphics example: render offscreen on lavapipe and assert the pixels"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
cd examples/10-graphics/offscreen
"$MCPP" build
# THE SHADERS ARE THE FIRST ASSERTION, and both of them: a rule that
# compiled only the first source would leave the second header absent
# and everything after this would still pass.
for f in triangle_vert triangle_frag; do
d="target/.build-mcpp/out/spirv"
test -f "$d/$f.h" || { echo "missing $d/$f.h"; exit 1; }
# Either file: which of the two carries the words is a property of
# the shader compiler the rule chose, not of the shader. See the
# cross-platform jobs, where that choice differs.
# ONE FILE AT A TIME, because `grep -qs a b` exits 2 when `b`
# does not exist -- even on a match in `a`, and even with `-s`,
# which suppresses the message and not the status. Written as one
# grep over both names, this criterion fails whenever the route
# that produces only a header is taken, which is a failure about
# the criterion and not about the shader.
found=""
for g in "$d/$f.h" "$d/$f.inc"; do
[ -f "$g" ] && grep -q '0x07230203' "$g" && found=1
done
[ -n "$found" ] \
|| { echo "$f carries no SPIR-V magic in either $f.h or $f.inc"; exit 1; }
done
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; }
out=$(VK_DRIVER_FILES="$icd" "$MCPP" run 2>&1) || { echo "$out"; exit 1; }
echo "$out"
# The program asserts the corners and the centre itself and exits
# non-zero on either. What CI adds is that the run reached the DEVICE:
# the software rasteriser produces the same pixels by construction, so
# the image cannot distinguish them and the device name is what does.
echo "$out" | grep -q 'llvmpipe' \
|| { echo "the run did not reach the lavapipe device"; exit 1; }
echo "$out" | grep -qE 'centre pixel: \([0-9]+, [0-9]+, [0-9]+, 255\)' \
|| { echo "no centre pixel was reported"; exit 1; }
# THE REVERSE LEG, AND IT COMPARES THE PIXELS RATHER THAN JUST
# RUNNING. The claim this example makes is that the image is a
# contract two independent rasterisers satisfy, not a property of one
# device -- so the criterion is that the two legs report the SAME
# centre pixel while reporting different devices. Measured: both give
# (124, 70, 62, 255), byte for byte.
gpu_px=$(echo "$out" | grep -m1 '^centre pixel:')
"$MCPP" build --no-accel
"$MCPP" run --no-accel | tee cpu.log
grep -q 'cpu rasteriser' cpu.log || { echo "the CPU leg did not run"; exit 1; }
cpu_px=$(grep -m1 '^centre pixel:' cpu.log)
[ -n "$gpu_px" ] && [ "$gpu_px" = "$cpu_px" ] || {
echo "the two legs disagree about the image:"
echo " device: $gpu_px"
echo " cpu: $cpu_px"
exit 1; }
echo "ok: both legs agree on $cpu_px"
- 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; }