-
Notifications
You must be signed in to change notification settings - Fork 6
247 lines (227 loc) · 11.6 KB
/
Copy pathcross-build-test.yml
File metadata and controls
247 lines (227 loc) · 11.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
name: cross-build-test
# mcpp cross-build test — the single source of truth for "which CROSS-build
# target combinations mcpp supports", verified end-to-end.
#
# Cross = host arch ≠ target arch. Verification targets are mcpp ITSELF and
# xlings (real, self-hosting C++23 module projects), cross-built from source for
# each target triple, arch-checked, and smoke-run under qemu-user.
#
# ── Supported cross matrix (built + verified below) ────────────────────────
# target | toolchain | host→target | run
# ----------------------|---------------------------------|---------------|-----
# aarch64-linux-musl | aarch64-linux-musl-gcc@16.1.0 | x86_64→arm64 | qemu
# x86_64-w64-mingw32 | mingw-cross-gcc@16.1.0 (MSVCRT) | linux→windows | wine
#
# The mingw row is OS-cross (same arch, different OS/ABI: ELF→PE), so it lives
# in its own job below with wine verification instead of the qemu arch matrix.
# See .agents/docs/2026-07-15-mingw-linux-cross-windows-design.md.
#
# mcpp resolves a cross `--target <triple>-musl` build to the triple-named cross
# gcc musl toolchain from the xlings ecosystem (xim:<triple>-gcc, see
# src/build/prepare.cppm). Output is a fully static musl ELF (no PT_INTERP),
# which also makes the aarch64 artefact runnable natively in Termux/Android —
# qemu-aarch64 is the CI proxy for "does this cross artefact actually execute".
#
# ── NOT here ───────────────────────────────────────────────────────────────
# * Same-arch builds (host arch == target arch) are NOT cross. The native musl
# static build `--target x86_64-linux-musl` (x86_64 host) is exercised by
# ci-linux.yml's "Toolchain: musl-gcc" step, and release.yml for the static
# release artefact. Keep them there; this file is cross-arch only.
#
# ── Planned cross rows (documented; NOT yet wired in mcpp — keep as comments) ─
# * llvm/clang cross : clang is inherently a cross-compiler, but mcpp does not
# yet inject `-target <triple>` + a cross sysroot for a
# clang toolchain; cross `--target` resolves to gcc musl
# only. Wire the clang cross path first, then add a row.
# * riscv64-linux-musl: add once xim:riscv64-linux-musl-gcc ships to
# xlings-res + xim-pkgindex.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
cross-build:
name: cross-build ${{ matrix.target }} (mcpp + xlings)
runs-on: ubuntu-24.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-linux-musl
file_arch: "ARM aarch64"
qemu_bin: qemu-aarch64-static
env:
MCPP_HOME: /home/runner/.mcpp
# Verbose every mcpp invocation for richer CI diagnostics (src/cli.cppm).
MCPP_VERBOSE: "1"
steps:
- uses: actions/checkout@v4
- name: Cache mcpp sandbox
uses: actions/cache@v4
with:
path: ~/.mcpp
key: mcpp-sandbox-${{ runner.os }}-cross-${{ matrix.target }}-${{ hashFiles('mcpp.toml', '.xlings.json') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-cross-${{ matrix.target }}-
- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-v2-
- name: Install qemu-user-static
run: |
sudo apt-get update -qq
sudo apt-get install -y qemu-user-static
${{ matrix.qemu_bin }} --version | head -1
- name: Bootstrap mcpp via xlings
env:
XLINGS_NON_INTERACTIVE: '1'
# 0.4.62 (current) — kept in lock-step with the xlings the release
# is built/bundled against (release.yml). A past 0.4.61 "download 404
# for mcpp@<pin>" was NOT a version bug — the xlings-res/mcpp GitHub
# release assets were uploaded in a broken state (records present,
# blobs missing → 404 on GET); re-uploaded clean. The stale-INDEX
# half is handled by the marker-clear below.
XLINGS_VERSION: '0.4.62'
run: |
tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz"
curl -fsSL -o "/tmp/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "/tmp/${tarball}" -C /tmp
"/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
xlings --version
# Force a real index re-sync even on a warm cache: drop the TTL refresh
# markers so `xlings update` actually pulls the latest index (sees the
# current bootstrap pin) while the toolchain payloads stay cached.
find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true
xlings config --mirror GLOBAL 2>/dev/null || true
xlings update -y 2>/dev/null || xlings update 2>/dev/null || true
xlings install mcpp -y
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
echo "MCPP_BOOT=$HOME/.xlings/subos/default/bin/mcpp" >> "$GITHUB_ENV"
- name: Self-host build (bootstrap mcpp -> fresh host mcpp)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP_BOOT" self config --mirror GLOBAL 2>/dev/null || true
"$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" self config --mirror GLOBAL
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
- name: "Cross-build mcpp -> ${{ matrix.target }}"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$MCPP" build --target ${{ matrix.target }}
bin=$(find target/${{ matrix.target }} -type f -name mcpp | head -1)
[ -n "$bin" ] || { echo "no mcpp artefact for ${{ matrix.target }}"; exit 1; }
echo "== file =="; file "$bin"
file "$bin" | grep -q "${{ matrix.file_arch }}" || { echo "expected ${{ matrix.file_arch }}"; exit 1; }
file "$bin" | grep -q "statically linked" || { echo "expected static"; exit 1; }
echo "MCPP_XBIN=$bin" >> "$GITHUB_ENV"
- name: "Cross-build xlings -> ${{ matrix.target }}"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
git clone --depth 1 https://github.com/openxlings/xlings /tmp/xlings-src
cd /tmp/xlings-src
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP" build --target ${{ matrix.target }}
xbin=$(find target/${{ matrix.target }} -type f -name xlings | head -1)
[ -n "$xbin" ] || { echo "no xlings artefact for ${{ matrix.target }}"; exit 1; }
echo "== file =="; file "$xbin"
file "$xbin" | grep -q "${{ matrix.file_arch }}" || { echo "expected ${{ matrix.file_arch }}"; exit 1; }
file "$xbin" | grep -q "statically linked" || { echo "expected static"; exit 1; }
echo "XLINGS_XBIN=$xbin" >> "$GITHUB_ENV"
- name: "Smoke-run cross artefacts under qemu"
run: |
RUN="${{ matrix.qemu_bin }}"
# mcpp is self-contained, so --version runs cleanly under bare qemu —
# this is the hard execution proof for the cross artefact.
echo "== mcpp --version =="
mver=$($RUN "$MCPP_XBIN" --version)
echo "$mver"; echo "$mver" | grep -q "mcpp" || { echo "mcpp --version failed"; exit 1; }
# xlings expects a real runtime environment (sandbox/config) and may
# exit non-zero on a bare `--version` under qemu; its ELF arch + static
# linkage were already asserted in the build step, so treat execution
# here as best-effort rather than gating.
echo "== xlings --version (best-effort under qemu) =="
xver=$($RUN "$XLINGS_XBIN" --version 2>&1 || true)
echo "$xver"
# ── Linux → Windows MinGW cross (OS-cross, same arch: ELF→PE) ─────────────
# Builds a demo project for x86_64-w64-mingw32 with the from-source GCC-16
# MSVCRT cross toolchain, asserts the artefact is a fully-static PE, and runs
# it under wine. Delegated to the e2e harness (tests/e2e/102_mingw_cross_wine.sh,
# `# requires: mingw-cross wine`) so the run_all cap-gating stays the single
# source of truth. See 2026-07-15-mingw-linux-cross-windows-design.md Part C.
mingw-cross-wine:
name: mingw-cross linux→windows (build + wine run)
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
MCPP_HOME: /home/runner/.mcpp
MCPP_VERBOSE: "1"
steps:
- uses: actions/checkout@v4
- name: Cache mcpp sandbox
uses: actions/cache@v4
with:
path: ~/.mcpp
key: mcpp-sandbox-${{ runner.os }}-mingw-cross-${{ hashFiles('mcpp.toml', '.xlings.json') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-mingw-cross-
- name: Cache xlings
uses: actions/cache@v4
with:
path: ~/.xlings
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-v2-
- name: Install wine
run: |
sudo dpkg --add-architecture i386 || true
sudo apt-get update -qq
sudo apt-get install -y wine64 wine || sudo apt-get install -y wine
wine --version
- name: Bootstrap mcpp via xlings
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '0.4.62'
run: |
tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz"
curl -fsSL -o "/tmp/${tarball}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
tar -xzf "/tmp/${tarball}" -C /tmp
"/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true
xlings config --mirror GLOBAL 2>/dev/null || true
xlings update -y 2>/dev/null || xlings update 2>/dev/null || true
xlings install mcpp -y
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
echo "MCPP_BOOT=$HOME/.xlings/subos/default/bin/mcpp" >> "$GITHUB_ENV"
- name: Self-host build (fresh host mcpp)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$MCPP_BOOT" self config --mirror GLOBAL 2>/dev/null || true
"$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" self config --mirror GLOBAL
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
- name: Install mingw-cross toolchain
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$MCPP" toolchain install mingw-cross 16.1.0
- name: "e2e: cross-build + wine run"
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
bash tests/e2e/102_mingw_cross_wine.sh