-
Notifications
You must be signed in to change notification settings - Fork 14
224 lines (212 loc) · 10.5 KB
/
Copy pathopenkal-cross.yml
File metadata and controls
224 lines (212 loc) · 10.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
name: openkal cross-build (3 hosts × 3 targets)
# ⭐⭐ WHAT THIS WORKFLOW ASSERTS, AND WHY IT IS A MATRIX RATHER THAN A ROW.
#
# `cross-build-test.yml` verifies the crosses served by a PAYLOAD: a toolchain
# whose driver has exactly one target. There the host and the target are joined
# — `x86_64-w64-mingw32-g++` is the Windows cross and nothing else — so one row
# per supported combination is the honest shape.
#
# openkal changes the shape of the question. The target side — the C library,
# the C++ runtime, the platform's own implementation — is a set of PACKAGES in
# the dependency graph, and the compiler is an ordinary retargetable clang. The
# claim that follows is that N hosts × N targets collapses to N implementations
# plus one tool: **the machine doing the building stops being a variable.**
#
# ⚠️ THAT IS A CLAIM, AND CLAIMS OF THIS SHAPE HAVE BEEN WRONG IN THIS
# REPOSITORY. Reaching PE from a Linux host needed four separate repairs, and
# adding the other two hosts found seven more — every one of them a decision
# that had been keyed on which machine was building rather than on which machine
# the output was for:
#
# the link line's three host-shaped branches, only one of which carried
# `--target=`; the `std` module command's Windows branch, which dropped the
# package's own include paths; `cd X && …` not changing the drive in cmd.exe;
# the artefact-format test matching LLVM's `apple` rather than mcpp's `macos`;
# the C++ runtime contract naming a library to link when one was already in
# the objects; `-nostdinc` missing so a host SDK header could be found; and
# `-lgcc` naming GCC's runtime on a link whose compiler is clang.
#
# None of those was visible from one host. So the matrix is the test.
#
# ── The shape ──────────────────────────────────────────────────────────────
#
# THREE build jobs, one per host, each producing THREE artefacts — nine builds.
# THREE run jobs, one per system, each executing the artefact FOR that system
# produced by ALL THREE hosts.
#
# build on Linux build on macOS build on Windows
# run Linux ✓ ✓ ✓
# run macOS ✓ ✓ ✓
# run Windows ✓ ✓ ✓
#
# ⭐ The diagonal is an ordinary native build. The six off-diagonal cells are
# the claim, and they are what a single-host workflow cannot reach.
#
# ⚠️ THE RUN JOBS INSTALL NOTHING — not mcpp, not a compiler, not a C runtime.
# A program above openkal carries its C library, its C++ runtime and its
# unwinder; what remains is the operating system it was built for. If a
# toolchain step is ever added to one of them because "the program needs it",
# that is the finding rather than the fix.
#
# ⚠️ AND THE ASSERTION IS ON THE OUTPUT, NOT THE EXIT STATUS. The program prints
# four lines, and `unwound: true` is the one a link cannot fake: it says a
# destructor ran while an exception was being carried out of a frame, which
# means the unwinder found this image's own frame descriptions.
on:
pull_request:
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# ⚠️ No mcpp or xlings version here. `bootstrap-mcpp` owns both, and a second
# statement of them is a second thing to keep in step — the pin check
# (.github/tools/check_version_pins.sh) enforces the ones that exist and would
# not know about a copy in this file.
XLINGS_NON_INTERACTIVE: '1'
# The branch of the openkal packages this change is verified against. They
# move together with it; when they are on `main` this becomes `main`.
OPENKAL_BRANCH: feat/openkal-closure
jobs:
build:
name: build 3 targets on ${{ matrix.host }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- { host: linux, runner: ubuntu-24.04 }
- { host: macos, runner: macos-14 }
- { host: windows, runner: windows-2022 }
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
# ⚠️ THE REPOSITORY'S OWN BOOTSTRAP, NOT A SECOND ONE.
#
# This job first wrote its own: fetch xlings, then
# `xlings install mcpp@<version>`. It failed on the very first run:
#
# xlings: version '2026.8.17.1' not found for 'mcpp'
# available: 2026.8.19.4
#
# `.xlings.json` at this repository's root pins the mcpp that BUILDS mcpp,
# and that pin does not move when mcpp is released — so it names a version
# the index no longer carries, and a bare install inside the checkout
# obeys the pin rather than the argument. `bootstrap-mcpp` already knows
# this (it runs `install_pinned_mcpp.sh`), works on all three systems, and
# shares the cache lineage every other job lands on.
#
# ⇒ Two bootstraps would be two things to keep correct, and the second one
# was wrong within a day of being written.
- uses: ./.github/actions/bootstrap-mcpp
# ⭐ THE mcpp UNDER TEST. Everything after this uses the binary this step
# produces; the bootstrapped one above is only what compiles it.
- name: Build the mcpp in this pull request
run: |
set -euo pipefail
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$XLINGS_BIN" config --mirror GLOBAL 2>/dev/null || true
"$MCPP" self config --mirror GLOBAL 2>/dev/null || true
# ⚠️ `--dev` and not `--release`. What is under test is a set of
# decisions about compile and link flags; an optimisation level
# changes none of them and a release self-build is most of the budget
# of a job on a two-core runner.
"$MCPP" build --dev
BUILT=$(find target -type f \( -name 'mcpp' -o -name 'mcpp.exe' \) | head -1)
[ -n "$BUILT" ] || { echo "::error::mcpp did not build"; exit 1; }
BUILT=$(cd "$(dirname "$BUILT")" && pwd)/$(basename "$BUILT")
echo "MCPP_UNDER_TEST=$BUILT" >> "$GITHUB_ENV"
"$BUILT" --version
- name: Select the toolchain the openkal packages ask for
run: |
set -euo pipefail
# ⚠️ Install, then select. `toolchain default` names one and does not
# fetch it.
"$MCPP_UNDER_TEST" self config --mirror GLOBAL 2>/dev/null || true
"$MCPP_UNDER_TEST" toolchain install llvm 22.1.8
"$MCPP_UNDER_TEST" toolchain default 'llvm@22.1.8'
- name: The program — one source, three targets
run: |
set -euo pipefail
git clone --quiet --depth 1 -b "$OPENKAL_BRANCH" \
https://github.com/mcpplibs/openkal-llvm-runtime "$RUNNER_TEMP/okl"
cd "$RUNNER_TEMP/okl/examples/same-source"
mkdir -p "$RUNNER_TEMP/out"
# ⚠️ The three HOSTED targets. Bare metal is verified by
# `openkal-llvm-runtime`'s own CI under qemu; it has no runner here to
# execute on, and a build-only cell in a workflow whose point is
# running would be the weaker claim.
for t in x86_64-linux-gnu aarch64-macos x86_64-windows-gnu; do
rm -rf target
"$MCPP_UNDER_TEST" build --target "$t"
a=$(find target -type f \( -name 'openkal-same-source' -o -name 'openkal-same-source.exe' \) | head -1)
[ -n "$a" ] || { echo "::error::$t produced no artefact on ${{ matrix.host }}"; exit 1; }
case "$t" in
x86_64-windows-gnu) cp "$a" "$RUNNER_TEMP/out/windows.exe" ;;
aarch64-macos) cp "$a" "$RUNNER_TEMP/out/macos" ;;
*) cp "$a" "$RUNNER_TEMP/out/linux" ;;
esac
echo "${{ matrix.host }} → $t : $(ls -l "$a" | awk '{print $5}') bytes"
done
- uses: actions/upload-artifact@v4
with:
name: openkal-built-on-${{ matrix.host }}
path: ${{ runner.temp }}/out/
if-no-files-found: error
run:
name: run 3 builds on ${{ matrix.system }}
needs: build
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- { system: linux, runner: ubuntu-24.04, file: linux }
- { system: macos, runner: macos-14, file: macos }
- { system: windows, runner: windows-2022, file: windows.exe }
defaults:
run:
shell: bash
steps:
# ⚠️ NO checkout AND NO toolchain. This job is the claim: a program built
# above openkal needs the operating system it was built for and nothing
# else. Anything installed here would weaken what a pass means.
- uses: actions/download-artifact@v4
with: { pattern: openkal-built-on-*, path: art }
- name: The same program, from all three build hosts
run: |
set -euo pipefail
fail=0
for host in linux macos windows; do
bin="art/openkal-built-on-$host/${{ matrix.file }}"
echo "──────── built on $host, running on ${{ matrix.system }} ────────"
if [ ! -f "$bin" ]; then
echo "::error::$bin is missing"; fail=1; continue
fi
# ⚠️ The executable bit does not survive an artefact upload.
chmod +x "$bin" || true
# ⚠️ arm64 macOS refuses an unsigned image, so the signature is
# asserted before the run: a failure here is "the linker did not
# ad-hoc sign it", which is a different repair from "it crashed".
if [ "${{ matrix.system }}" = "macos" ]; then
codesign -dv "$bin" 2>&1 | grep -q 'adhoc\|Signature' \
|| { echo "::error::built on $host: no code signature"; fail=1; continue; }
fi
if ! "./$bin" > out.log 2>&1; then
echo "::error::built on $host: it did not run"; cat out.log; fail=1; continue
fi
cat out.log
ok=1
grep -q 'sorted: 2 4 7' out.log || ok=0
grep -q 'caught: 42' out.log || ok=0
# ⭐ The line a link cannot fake.
grep -q 'unwound: true' out.log || ok=0
grep -q 'import std over openkal: ok' out.log || ok=0
[ "$ok" = 1 ] || { echo "::error::built on $host: wrong output"; fail=1; }
done
[ "$fail" = 0 ] || exit 1
echo "three builds, one system, same four lines"