Skip to content

Commit 1747d15

Browse files
ci(cross): dedicated cross-build-test.yml (host≠target matrix) (#150)
* ci(cross): dedicated mcpp cross-build matrix (ci-cross.yml); supersede ci-aarch64 Single source of truth for the cross-build target combinations mcpp supports, verified e2e by cross-building mcpp ITSELF and xlings from source for each target, arch-checking the static ELF, and smoke-running --version (native when target arch == host, qemu-user otherwise). Matrix: - x86_64-linux-musl (gcc@15.1.0-musl native musl) → run native - aarch64-linux-musl (aarch64-linux-musl-gcc cross) → run under qemu Header documents the planned-but-not-yet-wired rows (llvm/clang cross — mcpp does not yet inject clang -target + cross sysroot; more triples once their toolchain assets ship). Removes ci-aarch64.yml: its aarch64-musl cross e2e is now the aarch64 matrix row here, which additionally builds xlings (more coverage). Note: ci-linux.yml still has a same-arch x86_64-linux-musl --target step inside its toolchain matrix; left in place (follow-up: fold into this file if desired). * ci(cross): rename ci-cross.yml -> cross-build-test.yml; keep cross-arch only Cross-build is defined as host arch != target arch. Drop the x86_64-linux-musl row (host==target x86_64, a native musl/static build, not cross) — it's already covered by ci-linux.yml's musl-gcc --target step and release.yml. The matrix now holds only true cross-arch combos (aarch64-linux-musl today; riscv64 etc. later). Rename the file to cross-build-test.yml. * ci(cross): xlings --version smoke-run best-effort under bare qemu The musl-static cross toolchain validates end-to-end: both mcpp and xlings cross-build to static aarch64 ELFs and mcpp --version runs under qemu. xlings expects a real runtime env and may exit non-zero on a bare --version under qemu, so don't gate on it — the build step already asserts its arch + static linkage. --------- Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
1 parent 868a9b0 commit 1747d15

2 files changed

Lines changed: 155 additions & 130 deletions

File tree

.github/workflows/ci-aarch64.yml

Lines changed: 0 additions & 130 deletions
This file was deleted.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
name: cross-build-test
2+
3+
# mcpp cross-build test — the single source of truth for "which CROSS-build
4+
# target combinations mcpp supports", verified end-to-end.
5+
#
6+
# Cross = host arch ≠ target arch. Verification targets are mcpp ITSELF and
7+
# xlings (real, self-hosting C++23 module projects), cross-built from source for
8+
# each target triple, arch-checked, and smoke-run under qemu-user.
9+
#
10+
# ── Supported cross matrix (built + verified below) ────────────────────────
11+
# target | toolchain | host→target | run
12+
# ----------------------|---------------------------------|---------------|-----
13+
# aarch64-linux-musl | aarch64-linux-musl-gcc@15.1.0 | x86_64→arm64 | qemu
14+
#
15+
# mcpp resolves a cross `--target <triple>-musl` build to the triple-named cross
16+
# gcc musl toolchain from the xlings ecosystem (xim:<triple>-gcc, see
17+
# src/build/prepare.cppm). Output is a fully static musl ELF (no PT_INTERP),
18+
# which also makes the aarch64 artefact runnable natively in Termux/Android —
19+
# qemu-aarch64 is the CI proxy for "does this cross artefact actually execute".
20+
#
21+
# ── NOT here ───────────────────────────────────────────────────────────────
22+
# * Same-arch builds (host arch == target arch) are NOT cross. The native musl
23+
# static build `--target x86_64-linux-musl` (x86_64 host) is exercised by
24+
# ci-linux.yml's "Toolchain: musl-gcc" step, and release.yml for the static
25+
# release artefact. Keep them there; this file is cross-arch only.
26+
#
27+
# ── Planned cross rows (documented; NOT yet wired in mcpp — keep as comments) ─
28+
# * llvm/clang cross : clang is inherently a cross-compiler, but mcpp does not
29+
# yet inject `-target <triple>` + a cross sysroot for a
30+
# clang toolchain; cross `--target` resolves to gcc musl
31+
# only. Wire the clang cross path first, then add a row.
32+
# * riscv64-linux-musl: add once xim:riscv64-linux-musl-gcc ships to
33+
# xlings-res + xim-pkgindex.
34+
35+
on:
36+
push:
37+
branches: [ main ]
38+
pull_request:
39+
branches: [ main ]
40+
workflow_dispatch:
41+
42+
concurrency:
43+
group: ci-${{ github.workflow }}-${{ github.ref }}
44+
cancel-in-progress: true
45+
46+
jobs:
47+
cross-build:
48+
name: cross-build ${{ matrix.target }} (mcpp + xlings)
49+
runs-on: ubuntu-24.04
50+
timeout-minutes: 60
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
include:
55+
- target: aarch64-linux-musl
56+
file_arch: "ARM aarch64"
57+
qemu_bin: qemu-aarch64-static
58+
env:
59+
MCPP_HOME: /home/runner/.mcpp
60+
steps:
61+
- uses: actions/checkout@v4
62+
63+
- name: Cache mcpp sandbox
64+
uses: actions/cache@v4
65+
with:
66+
path: ~/.mcpp
67+
key: mcpp-sandbox-${{ runner.os }}-cross-${{ matrix.target }}-${{ hashFiles('mcpp.toml', '.xlings.json') }}
68+
restore-keys: |
69+
mcpp-sandbox-${{ runner.os }}-cross-${{ matrix.target }}-
70+
71+
- name: Cache xlings
72+
uses: actions/cache@v4
73+
with:
74+
path: ~/.xlings
75+
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
76+
restore-keys: |
77+
xlings-${{ runner.os }}-v2-
78+
79+
- name: Install qemu-user-static
80+
run: |
81+
sudo apt-get update -qq
82+
sudo apt-get install -y qemu-user-static
83+
${{ matrix.qemu_bin }} --version | head -1
84+
85+
- name: Bootstrap mcpp via xlings
86+
env:
87+
XLINGS_NON_INTERACTIVE: '1'
88+
XLINGS_VERSION: '0.4.30'
89+
run: |
90+
tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz"
91+
curl -fsSL -o "/tmp/${tarball}" \
92+
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}"
93+
tar -xzf "/tmp/${tarball}" -C /tmp
94+
"/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install
95+
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
96+
xlings --version
97+
# Refresh the index so a cached ~/.xlings still sees newly published
98+
# cross toolchains (xim:aarch64-linux-musl-gcc, static ninja, ...).
99+
xlings config --mirror GLOBAL 2>/dev/null || true
100+
xlings update -y 2>/dev/null || xlings update 2>/dev/null || true
101+
xlings install mcpp -y
102+
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
103+
echo "MCPP_BOOT=$HOME/.xlings/subos/default/bin/mcpp" >> "$GITHUB_ENV"
104+
105+
- name: Self-host build (bootstrap mcpp -> fresh host mcpp)
106+
run: |
107+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
108+
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
109+
"$MCPP_BOOT" self config --mirror GLOBAL 2>/dev/null || true
110+
"$MCPP_BOOT" build
111+
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
112+
test -x "$MCPP"
113+
"$MCPP" self config --mirror GLOBAL
114+
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
115+
116+
- name: "Cross-build mcpp -> ${{ matrix.target }}"
117+
run: |
118+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
119+
"$MCPP" build --target ${{ matrix.target }}
120+
bin=$(find target/${{ matrix.target }} -type f -name mcpp | head -1)
121+
[ -n "$bin" ] || { echo "no mcpp artefact for ${{ matrix.target }}"; exit 1; }
122+
echo "== file =="; file "$bin"
123+
file "$bin" | grep -q "${{ matrix.file_arch }}" || { echo "expected ${{ matrix.file_arch }}"; exit 1; }
124+
file "$bin" | grep -q "statically linked" || { echo "expected static"; exit 1; }
125+
echo "MCPP_XBIN=$bin" >> "$GITHUB_ENV"
126+
127+
- name: "Cross-build xlings -> ${{ matrix.target }}"
128+
run: |
129+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
130+
git clone --depth 1 https://github.com/openxlings/xlings /tmp/xlings-src
131+
cd /tmp/xlings-src
132+
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
133+
"$MCPP" build --target ${{ matrix.target }}
134+
xbin=$(find target/${{ matrix.target }} -type f -name xlings | head -1)
135+
[ -n "$xbin" ] || { echo "no xlings artefact for ${{ matrix.target }}"; exit 1; }
136+
echo "== file =="; file "$xbin"
137+
file "$xbin" | grep -q "${{ matrix.file_arch }}" || { echo "expected ${{ matrix.file_arch }}"; exit 1; }
138+
file "$xbin" | grep -q "statically linked" || { echo "expected static"; exit 1; }
139+
echo "XLINGS_XBIN=$xbin" >> "$GITHUB_ENV"
140+
141+
- name: "Smoke-run cross artefacts under qemu"
142+
run: |
143+
RUN="${{ matrix.qemu_bin }}"
144+
# mcpp is self-contained, so --version runs cleanly under bare qemu —
145+
# this is the hard execution proof for the cross artefact.
146+
echo "== mcpp --version =="
147+
mver=$($RUN "$MCPP_XBIN" --version)
148+
echo "$mver"; echo "$mver" | grep -q "mcpp" || { echo "mcpp --version failed"; exit 1; }
149+
# xlings expects a real runtime environment (sandbox/config) and may
150+
# exit non-zero on a bare `--version` under qemu; its ELF arch + static
151+
# linkage were already asserted in the build step, so treat execution
152+
# here as best-effort rather than gating.
153+
echo "== xlings --version (best-effort under qemu) =="
154+
xver=$($RUN "$XLINGS_XBIN" --version 2>&1 || true)
155+
echo "$xver"

0 commit comments

Comments
 (0)