-
Notifications
You must be signed in to change notification settings - Fork 10
271 lines (253 loc) · 13.3 KB
/
Copy pathci-linux-e2e.yml
File metadata and controls
271 lines (253 loc) · 13.3 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
name: ci-linux-e2e
# The e2e suite (tests/e2e/run_all.sh) split out of ci-linux.yml so it runs in
# PARALLEL with the build/unit/toolchain jobs instead of tacked on after them,
# and SHARDED across two runners on top of that. Both workflows share the same
# cache lineage (mcpp sandbox + xlings + target/), so each shard restores a warm
# build and the only added wall-clock vs. the inline version is one extra warm
# `mcpp build` per runner.
#
# Paired workflows: ci-linux.yml (build + unit + toolchain legs + integration),
# ci-macos.yml / ci-macos-e2e.yml, ci-windows.yml / ci-windows-e2e.yml.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e:
name: e2e ${{ matrix.shard }}/2 (linux x86_64, self-host)
runs-on: ubuntu-24.04
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
shard: [1, 2]
env:
# Round-robin slice of tests/e2e (see run_all.sh). Two shards halve the
# suite's wall-clock; the split is computed on the full file list, so a
# test's shard does not move when host capabilities differ.
E2E_SHARD: ${{ matrix.shard }}/2
MCPP_HOME: /home/runner/.mcpp
# NOTE: do NOT force MCPP_VERBOSE here. The e2e suite includes tests that
# assert mcpp's DEFAULT (quiet) output — e.g. 48_build_error_output and
# 53_namespaced_cache_label — which forced verbose would break. Verbose is
# set only in the fresh-install workflows (cold bootstrap, no such asserts).
# A specific test that needs verbose passes `--verbose` itself.
steps:
- uses: actions/checkout@v4
# Same cache lineage as ci-linux.yml so this job lands on a warm
# toolchain/sandbox instead of re-installing it.
- uses: ./.github/actions/bootstrap-mcpp
- name: Configure mirror + 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
- name: E2E suite
# Per-test 600s timeout lives in tests/e2e/run_all.sh and identifies
# WHICH test hung; this caps the whole suite so a hang fails fast.
timeout-minutes: 25
run: |
# Point the e2e runner at the freshly-built binary, not the
# bootstrap one. Tests cd into mktemp -d, so $MCPP must be
# absolute or the relative path breaks under the temp cwd.
# The RELEASED mcpp this job bootstrapped from, kept for e2e 252: the
# claim that an older client can still build against a package the PR
# produces is only worth making if something checks it against a real
# old binary. Captured before $MCPP is repointed at the fresh build.
export MCPP_BOOT="$MCPP"
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
test -x "$MCPP"
export MCPP
# Tests that set MCPP_HOME to a fresh tmpdir need an xlings to
# bootstrap from; surface the xlings binary installed above.
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
test -x "$MCPP_VENDORED_XLINGS"
# GitHub-hosted runners are outside CN; keep CI toolchain downloads on
# the global mirror while mcpp's default remains CN for fresh local
# sandboxes. E2E tests with their own MCPP_HOME read this variable.
export MCPP_E2E_TOOLCHAIN_MIRROR=GLOBAL
"$MCPP" self config --mirror "$MCPP_E2E_TOOLCHAIN_MIRROR"
"$MCPP" self config
# Pin the global default so test 28 (default-toolchain path) gets a
# deterministic GNU answer instead of an auto-install pick.
#
# Installed explicitly, not assumed. `toolchain default` requires the
# toolchain to be present, and nothing in this job puts it there:
# building mcpp above uses whatever first-run picks (llvm). It worked
# only because the sandbox cache happened to carry a gcc some earlier
# run had installed -- so the moment the cache key changed (an xlings
# pin bump moves the whole prefix), every e2e shard failed with
# `gcc@16.1.0 is not installed` before running a single test.
# Warm runs re-install nothing; this costs a lookup.
"$MCPP" toolchain install gcc 16.1.0
"$MCPP" toolchain default gcc@16.1.0
# Warm musl once so fresh-home e2e tests inherit the payload.
"$MCPP" toolchain install gcc 16.1.0-musl
bash tests/e2e/run_all.sh
# ──────────────────────────────────────────────────────────────────
# Bare metal: the one chain the sharded suite above cannot be trusted
# to exercise.
#
# tests/e2e/130_freestanding_riscv_build_and_run.sh declares
# `# requires: qemu-riscv`, which is legitimately absent on the macOS and
# Windows runners — so it must be a SOFT token, and a soft token means the
# test skips in silence on a Linux runner that lost qemu too. That is the
# exact shape this repository has been burned by twice (65_* never ran at
# all; ten pack e2e skipped on two platforms), and no token can tell the two
# cases apart.
#
# So the guard lives here, where it can be exact: install qemu, run the one
# test, and assert its PASS line appeared. A skip fails this job.
# ──────────────────────────────────────────────────────────────────
baremetal:
name: bare-metal e2e (riscv64-none-elf, qemu)
runs-on: ubuntu-24.04
timeout-minutes: 40
env:
MCPP_HOME: /home/runner/.mcpp
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
- name: Install the emulator (xim:qemu-riscv)
run: |
# ⚠️ BOTH homes. The shim on PATH dispatches against whichever home
# owns it, and `mcpp run` runs the runner through that shim — so an
# emulator installed only in the ambient xlings home answers
# "xlings: 'qemu-system-riscv64' is not installed" when mcpp asks.
# Measured: the job installed it once, the shim resolved, and the
# run still failed.
"$XLINGS_BIN" install xim:qemu-riscv -y
XLINGS_HOME="${MCPP_HOME:-$HOME/.mcpp}/registry" \
"$XLINGS_BIN" install xim:qemu-riscv -y
# Assert it is reachable AND runnable BEFORE the tests. Without this
# the capability probe simply would not add `qemu-riscv` and the
# tests would skip — which is what this job exists to prevent.
command -v qemu-system-riscv64
qemu-system-riscv64 --version | head -1
# The target sysroot, into the home MCPP uses. Test 131's BSP
# declares it as an `[xlings] deps` entry and finds it through
# `xpkg_dir`; installed into the ambient xlings home instead, the
# test would SKIP and the seam would go unexercised.
XLINGS_HOME="${MCPP_HOME:-$HOME/.mcpp}/registry" \
"$XLINGS_BIN" install xim:picolibc-riscv -y
test -d "${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs/xim-x-picolibc-riscv"
- name: Bare-metal e2e
timeout-minutes: 25
run: |
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
test -x "$MCPP"
export MCPP
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
export MCPP_E2E_TOOLCHAIN_MIRROR=GLOBAL
"$MCPP" self config --mirror "$MCPP_E2E_TOOLCHAIN_MIRROR"
# llvm is the toolchain a freestanding target pins; install it
# explicitly rather than relying on whatever the sandbox cache holds.
"$MCPP" toolchain install llvm 22.1.8
# Run the two scripts DIRECTLY rather than through run_all.sh.
# They are standalone (they take $MCPP and nothing else), run_all.sh
# accepts no filter — it would run the whole 250-test suite here for
# two tests — and, more to the point, run_all.sh exits 0 on a skip.
# Invoked directly, a skip is visible: the script either prints its
# PASS line or it does not.
for t in tests/e2e/130_freestanding_riscv_build_and_run.sh \
tests/e2e/131_freestanding_bsp_supplies_everything.sh \
tests/e2e/132_freestanding_test_and_artifacts.sh \
tests/e2e/133_freestanding_std_subset.sh; do
echo "=== $t ==="
bash "$t" 2>&1 | tee "$(basename "$t").log"
rc=${PIPESTATUS[0]}
[ "$rc" = "0" ] || { echo "$t failed (exit $rc)"; exit 1; }
done
# The assertion this job exists for: both tests RAN. Each has an
# early `exit 0` for a missing capability, so a zero exit code alone
# does not distinguish "passed" from "skipped".
grep -q 'PASS: freestanding riscv64 build + run' \
130_freestanding_riscv_build_and_run.sh.log || {
echo "130 (engine chain) skipped on the runner that must run it"; exit 1; }
grep -q 'PASS: BSP supplies the sysroot' \
131_freestanding_bsp_supplies_everything.sh.log || {
echo "131 (ecosystem chain) skipped on the runner that must run it"; exit 1; }
grep -q 'PASS: bare-metal mcpp test names its failure' \
132_freestanding_test_and_artifacts.sh.log || {
echo "132 (test + artifacts) skipped on the runner that must run it"; exit 1; }
grep -q 'PASS: the freestanding std subset' \
133_freestanding_std_subset.sh.log || {
echo "133 (std subset) skipped on the runner that must run it"; exit 1; }
# ──────────────────────────────────────────────────────────────────
# Hermetic (no host toolchain): the ONLY environment class that
# faithfully reproduces issue #195. Standard runners ship gcc +
# libc6-dev, so a sandbox toolchain that leaks to the host's CRT
# still links "green" there; this container has no compiler and no
# host Scrt1.o, so any leak fails loudly. Builds PR code with the
# bootstrap mcpp, then runs the llvm flow end-to-end.
# ──────────────────────────────────────────────────────────────────
hermetic:
name: hermetic e2e (no host toolchain, container)
runs-on: ubuntu-24.04
container: debian:stable-slim
timeout-minutes: 60
env:
XLINGS_NON_INTERACTIVE: '1'
steps:
- name: Install base utilities (NO compiler)
run: |
apt-get update -qq
apt-get install -y -qq curl ca-certificates git xz-utils unzip
# The whole point of this job: no host toolchain, no host CRT.
! command -v gcc
! command -v cc
test ! -e /usr/lib/x86_64-linux-gnu/Scrt1.o
test ! -e /usr/lib/gcc
- uses: actions/checkout@v4
# Payload cache (downloads only — the container still has no host
# toolchain, which is the property under test).
- name: Cache mcpp sandbox payloads
uses: actions/cache@v4
with:
path: ~/.mcpp
key: mcpp-hermetic-${{ hashFiles('mcpp.toml') }}
restore-keys: |
mcpp-hermetic-
- name: Bootstrap xlings + released mcpp
run: |
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.8.17.2
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
xlings update
xlings install mcpp -y -g
MCPP_BOOT="$HOME/.xlings/subos/current/bin/mcpp"
"$MCPP_BOOT" --version
"$MCPP_BOOT" self config --mirror GLOBAL
echo "MCPP_BOOT=$MCPP_BOOT" >> "$GITHUB_ENV"
- name: Build PR mcpp from source (sandbox gcc only)
run: |
"$MCPP_BOOT" build
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
test -x "$MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
- name: "issue #195 reproduction: manifest llvm toolchain, fresh"
run: |
cd "$(mktemp -d)"
"$MCPP" new hello195
cd hello195
printf '\n[toolchain]\nlinux = "llvm@22.1.8"\n' >> mcpp.toml
printf 'import std;\nint main() { std::println("hello {}", 195); return 0; }\n' > src/main.cpp
"$MCPP" run
- name: Hermetic llvm e2e subset
run: |
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
export MCPP
bash tests/e2e/86_llvm_hermetic_link.sh
bash tests/e2e/37_llvm_import_std.sh