-
Notifications
You must be signed in to change notification settings - Fork 14
309 lines (290 loc) · 13.2 KB
/
Copy pathci-macos-ios.yml
File metadata and controls
309 lines (290 loc) · 13.2 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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
name: ci-macos-ios
# The iOS rows, measured on the only machine that can answer for them.
#
# iOS needs an ecosystem compiler and a LOCATED SDK: `xim:llvm` emits arm64
# Mach-O for an iOS deployment target, and only the machine's Xcode can supply
# the iPhoneOS / iPhoneSimulator headers and stub libraries, which are not
# redistributable. The simulator runtime is the same category. So every claim
# about these three rows is a claim about a macOS runner, and this job is where
# they are made.
#
# Kept out of ci-macos.yml deliberately: that job asserts mcpp's default quiet
# output shape and tacking a differently-shaped leg onto it has broken that
# assertion before.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ci-macos-ios-${{ github.ref }}
cancel-in-progress: true
jobs:
# THE PREMISES, AS A PROBE THAT RUNS ON REQUEST.
#
# This job measured what the iOS rows were scheduled on: that a GitHub macOS
# runner ships both SDKs and a bootable simulator, that `simctl spawn` takes a
# bare Mach-O, that the payload's `clang++.cfg` names the macOS SDK, and
# which C++ runtime an iOS link can use. Every answer is now encoded -- in
# `simctl-run`, in `--no-default-config` on the Apple cross path, and in the
# MachO contract cell -- and asserted by `ios-engine` below.
#
# Every step continues on error, because a probe's value is the complete set
# of answers. That is also why it does not run on every change: a job that
# cannot fail shown as a green check beside the gate reads as a second gate.
# It stays available for the day a runner image changes one of the premises.
ios-host-surface:
name: iOS - what this runner actually provides
if: github.event_name == 'workflow_dispatch'
runs-on: macos-15
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-macos-llvm
- name: "Host surface: the two located SDKs and the simulator runtime"
continue-on-error: true
run: |
set -x
xcode-select -p
xcrun --sdk iphoneos --show-sdk-path
xcrun --sdk iphoneos --show-sdk-version
xcrun --sdk iphonesimulator --show-sdk-path
xcrun --sdk iphonesimulator --show-sdk-version
set +x
echo "--- runtimes ---"
xcrun simctl list runtimes
echo "--- devices available ---"
xcrun simctl list devices available
# THE PAYLOAD'S CLANG READS A CONFIG FILE, AND THAT CONFIG NAMES A
# DIFFERENT SDK. Measured with `-isysroot <iPhoneSimulator18.5.sdk>` on
# the command line and the cfg not suppressed:
#
# ld64.lld: error: /Library/Developer/CommandLineTools/SDKs/
# MacOSX.sdk/usr/lib/libc++.tbd(/usr/lib/libc++.1.dylib) is
# incompatible with arm64 (iOS Simulator18.0.0)
#
# which is why mcpp's Apple cross path carries `--no-default-config`.
- name: "The payload's default config, which is why --no-default-config"
continue-on-error: true
run: |
set -x
ls -la "$LLVM_ROOT/bin/"*.cfg || true
for f in "$LLVM_ROOT/bin/"*.cfg; do echo "=== $f"; cat "$f"; done || true
# WHETHER A BARE MACH-O CAN BE RUN AT ALL, which decided how much the
# runner program has to do: `simctl launch` needs an installed .app,
# `simctl spawn` takes an executable.
- name: "Device: is one bootable, and does spawn take a bare executable"
continue-on-error: true
run: |
set -uo pipefail
UDID=$(xcrun simctl list devices available \
| grep -A50 -- '-- iOS' \
| grep -m1 -oE '[0-9A-F]{8}-[0-9A-F-]{27}' || true)
echo "udid=[$UDID]"
if [ -z "$UDID" ]; then
echo "NO-IOS-SIMULATOR-DEVICE"
exit 0
fi
xcrun simctl boot "$UDID" || true
xcrun simctl bootstatus "$UDID" -b 2>&1 | tail -3 || true
cat > /tmp/hello.cpp << 'CPP'
#include <cstdio>
int main() { std::puts("1-2-3"); return 0; }
CPP
SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)
if ! "$LLVM_ROOT/bin/clang++" -std=c++23 --no-default-config \
-target arm64-apple-ios18.0-simulator \
-isysroot "$SDK" -o /tmp/hello /tmp/hello.cpp; then
echo "SIM-COMPILE-FAILED"
exit 0
fi
file /tmp/hello
otool -l /tmp/hello | grep -A5 LC_BUILD_VERSION || true
echo "--- simctl spawn on a bare Mach-O ---"
if xcrun simctl spawn "$UDID" /tmp/hello; then
echo "SPAWN-OK"
else
echo "SPAWN-FAILED exit=$?"
fi
# THE C++ RUNTIME QUESTION. macOS links the PAYLOAD's static libc++ so
# that mcpp's deployment floor is real; that archive is built for macOS,
# and ld64 refuses an object built for one platform in a link for
# another. Both routes are tried because "which one works" is the fact
# the contract table needed.
- name: "C++ runtime: SDK libc++ versus the payload static archive"
continue-on-error: true
run: |
set -x
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
cat > /tmp/cxx.cpp << 'CPP'
#include <string>
#include <cstdio>
int main() { std::string s = "1-2-3"; std::puts(s.c_str()); return 0; }
CPP
echo "--- (a) SDK libc++, dynamic ---"
if "$LLVM_ROOT/bin/clang++" -std=c++23 --no-default-config \
-target arm64-apple-ios18.0 \
-isysroot "$SDK" -o /tmp/cxx-sdk /tmp/cxx.cpp; then
otool -L /tmp/cxx-sdk
echo "SDK-LIBCXX-OK"
else
echo "SDK-LIBCXX-FAILED"
fi
echo "--- (b) payload static libc++ ---"
if "$LLVM_ROOT/bin/clang++" -std=c++23 --no-default-config \
-target arm64-apple-ios18.0 \
-isysroot "$SDK" -nostdlib++ \
"$LLVM_ROOT/lib/libc++.a" "$LLVM_ROOT/lib/libc++abi.a" \
-o /tmp/cxx-static /tmp/cxx.cpp; then
echo "PAYLOAD-STATIC-OK"
else
echo "PAYLOAD-STATIC-FAILED"
fi
set +x
# THE GATE. Every step here fails the job when its claim does not hold.
#
# The first version of this job was a probe too -- every step continued on
# error -- and it stayed that way after the rows it measured moved to
# `verified` and `preview`. That left a verified tier with no check that
# could turn red: a regression in the Apple cross path would have printed
# `RUN-THROUGH-RUNNER-FAILED` inside a green job.
ios-engine:
name: iOS - mcpp builds and the simulator runs it
runs-on: macos-15
timeout-minutes: 40
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: ./.github/actions/setup-macos-llvm
- name: Build mcpp from source (self-host)
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$MCPP" build
echo "MCPP_DEV=$(ls -td "$PWD"/target/*/*/bin/mcpp | head -1)" >> "$GITHUB_ENV"
# NO TOOLCHAIN IS DECLARED, AND THAT IS WHAT MAKES THIS THE USER'S PATH.
#
# While the rows were `planned` the fixture had to write
# `[target.<row>] toolchain = "llvm@22.1.8"` to get past the tier gate,
# which meant it measured an override and never the row's own pin. The
# rows are now `verified` and `preview` and resolve `llvm@22.1.8` by
# themselves, so the fixture says nothing and the default is what is
# measured.
- name: "Fixture: a project that imports std and prints 1-2-3"
run: |
set -euo pipefail
mkdir -p /tmp/iostest/src
cat > /tmp/iostest/mcpp.toml << 'TOML'
[package]
name = "iostest"
version = "0.1.0"
[build]
ios_deployment_target = "18.0"
# THE RUNNER IS AN ARGV PREFIX AND THE SESSION BELONGS TO A
# PACKAGE. `simctl-run` comes from `xim:apple-simulator-tools`; it
# chooses a device, boots it if it is not booted, waits, spawns, and
# returns the program's own exit status. The device row keeps
# `runner` unset: an artefact cannot be run off an iOS device without
# a signature the developer owns.
[target.aarch64-ios-sim]
runner = ["simctl-run"]
# Declared at the top level here and not in examples/13, because a
# tool declaration is not conditional on a target and this package
# exists for macOS alone. This fixture is macOS-only, so it can say
# it; a portable manifest cannot.
[xlings.workspace]
"xim:apple-simulator-tools" = ""
TOML
cat > /tmp/iostest/src/main.cpp << 'CPP'
import std;
int main() {
std::vector<int> v{3, 1, 2};
std::ranges::sort(v);
std::print("{}-{}-{}\n", v[0], v[1], v[2]);
}
CPP
cat /tmp/iostest/mcpp.toml
# ONE ASSERTION FOR THE THREE ARTEFACTS. A build that succeeds cannot
# tell an iOS binary from a macOS one; LC_BUILD_VERSION can. Each
# value is read from the load command and compared whole, so an empty
# reading is a failure and not a pass.
cat > /tmp/assert-artefact.sh << 'SH'
#!/usr/bin/env bash
set -uo pipefail
target=$1 arch=$2 platform=$3 minos=$4
arts=(/tmp/iostest/target/"$target"/*/bin/iostest)
art=${arts[0]}
if [ ! -f "$art" ]; then
echo "FAIL: $target produced no artefact under /tmp/iostest/target/$target"
exit 1
fi
desc=$(file "$art")
lc=$(otool -l "$art")
echo "$desc"
grep -A5 LC_BUILD_VERSION <<<"$lc" || true
got_platform=$(awk '/cmd LC_BUILD_VERSION/{f=1} f && $1=="platform"{print $2; exit}' <<<"$lc")
got_minos=$(awk '/cmd LC_BUILD_VERSION/{f=1} f && $1=="minos"{print $2; exit}' <<<"$lc")
fail=0
if ! grep -qE "Mach-O 64-bit executable ${arch}\$" <<<"$desc"; then
echo "FAIL: $target is not a Mach-O $arch executable"; fail=1
fi
if [ "$got_platform" != "$platform" ]; then
echo "FAIL: $target LC_BUILD_VERSION platform is '$got_platform', expected $platform"; fail=1
fi
if [ "$got_minos" != "$minos" ]; then
echo "FAIL: $target minos is '$got_minos', expected $minos from ios_deployment_target"; fail=1
fi
[ "$fail" = 0 ] && echo "ok: $target is Mach-O $arch, platform $platform, minos $minos"
exit "$fail"
SH
chmod +x /tmp/assert-artefact.sh
# THE DEVICE ROW. Nothing runs it -- that needs a signature the developer
# owns -- so the claim is the artefact: `platform 2` is IOS. The refusal
# for a machine WITHOUT the SDK is asserted by tests/e2e/641 on every
# non-Apple host, which is the only place the SDK is genuinely absent:
# pointing `DEVELOPER_DIR` at nothing here measured nothing, because
# `xcrun` falls back to the recorded developer directory.
- name: "aarch64-ios: the artefact names the iOS platform"
run: |
set -euo pipefail
cd /tmp/iostest
"$MCPP_DEV" build --target aarch64-ios
/tmp/assert-artefact.sh aarch64-ios arm64 2 18.0
# `platform 7` is IOSSIMULATOR. The number is what separates this row from
# the device row; the architecture does not.
- name: "aarch64-ios-sim: the artefact names the simulator platform"
run: |
set -euo pipefail
cd /tmp/iostest
"$MCPP_DEV" build --target aarch64-ios-sim
/tmp/assert-artefact.sh aarch64-ios-sim arm64 7 18.0
# THE SUPPORTED PATH, which is what the `verified` tier claims: a runner
# the manifest declares and a program a package provides. The program's
# own line is compared whole. An iOS-simulator Mach-O does not execute on
# the macOS host directly, so the line appearing at all means the
# simulator ran it.
- name: "aarch64-ios-sim: mcpp run prints 1-2-3 through the runner"
run: |
set -uo pipefail
cd /tmp/iostest
out=$("$MCPP_DEV" run --target aarch64-ios-sim 2>&1) && rc=0 || rc=$?
printf '%s\n' "$out" | tail -20
if [ "$rc" -ne 0 ]; then
echo "FAIL: mcpp run --target aarch64-ios-sim exited $rc"
exit 1
fi
if ! grep -qx '1-2-3' <<<"$out"; then
echo "FAIL: the program's output line '1-2-3' is absent"
exit 1
fi
echo "ok: mcpp run --target aarch64-ios-sim printed 1-2-3 and exited 0"
# THE THIRD ROW, ON A HOST THAT CANNOT RUN IT. A simulator runs the host's
# architecture and this runner is Apple silicon, so the claim is the
# artefact alone -- which is exactly the `preview` tier the row carries.
- name: "x86_64-ios-sim: the artefact, on a host that cannot run it"
run: |
set -euo pipefail
cd /tmp/iostest
"$MCPP_DEV" build --target x86_64-ios-sim
/tmp/assert-artefact.sh x86_64-ios-sim x86_64 7 18.0