@@ -197,3 +197,100 @@ jobs:
197197 # The four lines are the same four lines.
198198 diff <(grep -E '^(sorted|caught|unwound|import std over openkal):' out.log) \
199199 <(grep -E '^(sorted|caught|unwound|import std over openkal):' host.log)
200+
201+ # ⭐⭐ AND THE SAME SOURCE FOR TWO MACHINES THIS ONE IS NOT.
202+ #
203+ # The two steps above prove the source does not know which machine it is
204+ # for. These prove the BUILD does not need to be on it: one Linux host
205+ # produces a PE and a Mach-O, and the jobs below run them on the real
206+ # thing with nothing installed.
207+ #
208+ # ⚠️ THE ARTEFACT IS THE ARGUMENT, WHICH IS WHY THOSE JOBS INSTALL NOTHING.
209+ # Not mcpp, not a compiler, not a C runtime — the program carries its C
210+ # library, its C++ runtime and its unwinder, and what remains is the
211+ # operating system it was built for. A run that needed a redistributable
212+ # installed first would be demonstrating something weaker.
213+ - name : The same source, built here for Windows and for macOS
214+ if : matrix.toolchain == 'llvm@22.1.8'
215+ run : |
216+ set -euo pipefail
217+ cd examples/same-source
218+ mkdir -p "$RUNNER_TEMP/cross"
219+ for t in x86_64-windows-gnu aarch64-macos; do
220+ rm -rf target
221+ mcpp build --target "$t"
222+ a=$(find target -type f \( -name 'openkal-same-source' -o -name '*.exe' \) | head -1)
223+ [ -n "$a" ] || { echo "::error::$t produced no artefact"; exit 1; }
224+ echo "$t → $(file -b "$a")"
225+ cp "$a" "$RUNNER_TEMP/cross/"
226+ done
227+ # ⚠️ The format is asserted here rather than left to the run jobs. A
228+ # run that fails tells you the program did not work; this tells you
229+ # what was produced, and the two failures need different fixes.
230+ file "$RUNNER_TEMP/cross/openkal-same-source.exe" | grep -q 'PE32+ executable'
231+ file "$RUNNER_TEMP/cross/openkal-same-source" | grep -q 'Mach-O 64-bit arm64'
232+
233+ - uses : actions/upload-artifact@v4
234+ if : matrix.toolchain == 'llvm@22.1.8'
235+ with :
236+ name : cross-artifacts
237+ path : ${{ runner.temp }}/cross/
238+ if-no-files-found : error
239+
240+ # ---------------------------------------------------------------------------
241+ # ⭐⭐ THE ACCEPTANCE CRITERION FOR PORTABILITY OF THE ARTEFACT.
242+ #
243+ # A cross build that produces a well-formed file proves the compiler was told
244+ # the right target. It does not prove the program runs, and every difference
245+ # this ecosystem has had to find on these two platforms — the loader-bootstrapped
246+ # thread-local, the unwinder's search for its own tables, the personality
247+ # routine — links successfully and fails at run time.
248+ #
249+ # ⚠️ These jobs deliberately have NO toolchain steps. If one is ever added
250+ # because "the program needs it", that is the finding, not the fix.
251+ run-on-windows :
252+ name : the artefact built on Linux runs on Windows
253+ needs : runtime
254+ runs-on : windows-2022
255+ timeout-minutes : 10
256+ defaults :
257+ run :
258+ shell : bash
259+ steps :
260+ - uses : actions/download-artifact@v4
261+ with : { name: cross-artifacts, path: art }
262+ - name : It runs, and it unwinds
263+ run : |
264+ set -euo pipefail
265+ ./art/openkal-same-source.exe 2>&1 | tee out.log
266+ grep -q 'sorted: 2 4 7' out.log
267+ grep -q 'caught: 42' out.log
268+ # ⭐ The line a link cannot fake: a destructor ran during the unwind,
269+ # so libunwind found `.eh_frame` by reading the image rather than by
270+ # asking the operating system to enumerate modules.
271+ grep -q 'unwound: true' out.log
272+ grep -q 'import std over openkal: ok' out.log
273+
274+ run-on-macos :
275+ name : the artefact built on Linux runs on macOS
276+ needs : runtime
277+ runs-on : macos-14
278+ timeout-minutes : 10
279+ steps :
280+ - uses : actions/download-artifact@v4
281+ with : { name: cross-artifacts, path: art }
282+ - name : It runs, and it unwinds
283+ run : |
284+ set -euo pipefail
285+ # ⚠️ The executable bit does not survive an artefact upload.
286+ chmod +x art/openkal-same-source
287+ # ⚠️ AND THE SIGNATURE DOES. arm64 macOS refuses an unsigned image, so
288+ # this is asserted before the run: a failure here is "the linker did
289+ # not ad-hoc sign it", which is a different repair from "the program
290+ # crashed".
291+ codesign -dv art/openkal-same-source 2>&1 | grep -q 'adhoc\|Signature'
292+ ./art/openkal-same-source 2>&1 | tee out.log
293+ grep -q 'sorted: 2 4 7' out.log
294+ grep -q 'caught: 42' out.log
295+ grep -q 'unwound: true' out.log
296+ grep -q 'import std over openkal: ok' out.log
0 commit comments