Skip to content

Commit 59a10d4

Browse files
committed
feat(toolchain): the three iOS tiers move, each on its own reading
MEASURED ON macos-15 (Xcode 16.4, iPhoneOS and iPhoneSimulator 18.5, `ios_deployment_target = "18.0"`), through mcpp built from this branch: aarch64-ios-sim verified arm64 platform 7 (IOSSIMULATOR) minos 18.0 mcpp run --target aarch64-ios-sim -> 1-2-3 aarch64-ios preview arm64 platform 2 (IOS) minos 18.0 x86_64-ios-sim preview x86_64 platform 7 (IOSSIMULATOR) minos 18.0 The simulator row ran through the SUPPORTED PATH -- the `runner` a manifest declares and the program `xim:apple-simulator-tools` provides -- and not only under a hand-written `simctl spawn`. This repository has a standing note that publishing and verifying an asset is not the same as being able to use it through a supported path, and the difference here was a defect: the program was at the payload root and mcpp searched only `<payload>/bin`. NEITHER `preview` IS WORK NOT YET DONE. `aarch64-ios` cannot be run off a device without a signature the developer owns, which is not something a build tool or a package can supply; `x86_64-ios-sim` cannot be run on an Apple-silicon host, because a simulator runs the HOST's architecture. Both are tiers bounded by a fact rather than by a gap, and the rows say so. `platform 2` AGAINST `platform 7` IS THE PAIR WORTH HAVING A READING FOR. A successful build cannot tell them apart, and an artefact reporting IOSSIMULATOR from the device row is one no later step refuses. A NEW REFUSAL CODE, BECAUSE THE MATRIX COMPARES REASONS AND NOT ONLY OUTCOMES. `apple-sdk-absent` is distinct from `host-cannot-serve`, which says no payload here produces the target and waits for the graph in case a package supplies the system -- an Apple SDK is not redistributable, so no package can and nothing a later step learns changes the answer. It is distinct from `tier-planned`, which says the row is not wired: these rows are. Without a code the refusal would be recorded as `other`, which `check_matrix_reasons.sh` refuses on the ground that it freezes an unnamed branch into the expected table. THE TIER MOVE REACHES SIX PLACES AND A SEVENTH THAT WOULD HAVE STAYED GREEN. The engine's table, both READMEs (where one combined row became three, because the three tiers now differ), both copies of docs/21 -- including the per-host columns, which said `planned` everywhere and now say `SDK` on macos-arm64 and `—` elsewhere -- the test's one-line pairs, and `examples/13`'s matrix. The seventh is e2e 641, whose simulator case asserted `tier-planned`: it would have gone green on an expectation the table had left behind, which is the shape `check_target_tiers.py` was written for one axis over.
1 parent 0f45d81 commit 59a10d4

11 files changed

Lines changed: 168 additions & 75 deletions

File tree

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,9 @@ list` reports for this machine):
422422
| `wasm32-emscripten` | `emsdk@6.0.9` — Emscripten ships its own sysroot and its own libc++ module surface; `mcpp run` executes the module with `node` | verified |
423423
| `x86_64-linux-android` | `android-ndk@30.0.16248370` — bionic from the NDK, one payload for both ABIs; ran on an API 24 x86_64 emulator image | verified |
424424
| `aarch64-linux-android` | the same payload and the same build; ran under qemu-user over the system image's own bionic, which the platform emulator cannot do from an x86_64 host | verified |
425-
| `aarch64-ios` · `aarch64-ios-sim` · `x86_64-ios-sim` | the iPhoneOS and iPhoneSimulator SDKs ship inside Xcode and are not redistributable, so the blocker is a licence rather than a payload | planned |
425+
| `aarch64-ios-sim` | llvm 22 plus the machine's iPhoneSimulator SDK, which mcpp locates rather than installs; ran on a simulator through `simctl-run` ³ | verified |
426+
| `aarch64-ios` | the same split for the device; the artefact names the iOS platform, and running it off a device needs a signature the developer owns ³ | preview |
427+
| `x86_64-ios-sim` | the same build; nothing ran it, because a simulator runs the host's architecture and the machine measured was Apple silicon ³ | preview |
426428

427429
`verified` an image has been built **and run** for the row, qemu and wine
428430
included · `preview` it builds and links, and no emulator run has been recorded
@@ -449,6 +451,14 @@ such a target is refused rather than attempted.
449451
> payload produces these targets. The C library, startup code, memory layout
450452
> and emulator travel with a board-support package rather than with mcpp — see
451453
> [40 — Bare-Metal and Freestanding Targets](docs/40-baremetal.md).
454+
>
455+
> ³ The three iOS rows need a macOS host, and the compiler is still the
456+
> ecosystem's: `xim:llvm` emits arm64 Mach-O for an iOS deployment target. What
457+
> the machine supplies is the SDK, which ships inside Xcode and is not
458+
> redistributable, so mcpp locates it through `xcrun` exactly as it has always
459+
> located the macOS SDK — and refuses, naming the SDK, when it cannot. The
460+
> simulator session belongs to `xim:apple-simulator-tools`. See
461+
> [20 — Toolchain Management](docs/20-toolchains.md).
452462
453463
## Documentation
454464

README.zh-CN.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,9 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
409409
| `wasm32-emscripten` | `emsdk@6.0.9` —— Emscripten 自带 sysroot 和它自己的 libc++ 模块面;`mcpp run``node` 把模块跑起来 | verified |
410410
| `x86_64-linux-android` | `android-ndk@30.0.16248370` —— bionic 来自 NDK,一个载荷服务两个 ABI;在 API 24 的 x86_64 模拟器镜像上跑过 | verified |
411411
| `aarch64-linux-android` | 同一个载荷、同样的构建;在 qemu-user 上、配系统镜像自带的 bionic 跑过 —— 这是平台模拟器从 x86_64 宿主做不到的 | verified |
412-
| `aarch64-ios` · `aarch64-ios-sim` · `x86_64-ios-sim` | iPhoneOS 与 iPhoneSimulator 的 SDK 在 Xcode 里且不可再分发,所以阻塞项是许可而不是载荷 | planned |
412+
| `aarch64-ios-sim` | llvm 22 加上机器自己的 iPhoneSimulator SDK,mcpp 定位而不安装它;经 `simctl-run` 在模拟器上跑过 ³ | verified |
413+
| `aarch64-ios` | 真机取同一种切分;产物命名 iOS 平台,而把它跑在一台设备上需要开发者自己拥有的签名 ³ | preview |
414+
| `x86_64-ios-sim` | 同一次构建;没有东西跑过它,因为模拟器跑宿主的架构,而被测的那台是 Apple 芯片 ³ | preview |
413415

414416
`verified` 该行的镜像已被构建**并运行**过,qemu 与 wine 都算 · `preview` 可构建
415417
可链接,未记录过模拟器运行 · `planned` 已登记在词表中,尚未接线 —— 面向这类目标
@@ -431,6 +433,12 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
431433
> LLVM 载荷的宿主都能产出这些目标。C 库、启动代码、内存布局与模拟器随板级支持包
432434
> 走,而不随 mcpp 走 —— 见
433435
> [40 — 裸机与 freestanding 目标](docs/zh/40-baremetal.md)
436+
>
437+
> ³ 三条 iOS 行需要一台 macOS 宿主,而编译器仍然是生态的:`xim:llvm` 能为一个 iOS
438+
> 部署目标产出 arm64 Mach-O。机器供给的是 SDK —— 它在 Xcode 里且不可再分发,所以
439+
> mcpp 通过 `xcrun` 定位它,与它一直以来定位 macOS SDK 的方式完全相同 —— 并在找不到
440+
> 时点名那个 SDK 拒绝。模拟器那次会话属于 `xim:apple-simulator-tools`。见
441+
> [20 —— 工具链管理](docs/zh/20-toolchains.md)
434442
435443
## 文档
436444

docs/21-the-target-triple.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,9 @@ other's rows.
487487
| `armv7a-none-eabihf` | verified | `llvm@22.1.8` | payload | payload | payload | payload |
488488
| `aarch64-linux-android` | verified | `android-ndk@30.0.16248370` | payload | payload | payload ||
489489
| `x86_64-linux-android` | verified | `android-ndk@30.0.16248370` | payload | payload | payload ||
490-
| `aarch64-ios` | planned | | planned | planned | planned | planned |
491-
| `aarch64-ios-sim` | planned | | planned | planned | planned | planned |
492-
| `x86_64-ios-sim` | planned | | planned | planned | planned | planned |
490+
| `aarch64-ios` | preview | `llvm@22.1.8` | | | SDK | |
491+
| `aarch64-ios-sim` | verified | `llvm@22.1.8` | | | SDK | |
492+
| `x86_64-ios-sim` | preview | `llvm@22.1.8` | | | SDK | |
493493
| `wasm32-emscripten` | verified | `emsdk@6.0.9` | payload | payload | payload | payload |
494494

495495
`payload` a toolchain payload here produces it · `graph` no payload, but a

docs/zh/21-the-target-triple.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,9 @@ CRT;图供给时是 `musl`。一个目标字符串,两个不同的 C 库 ——
437437
| `armv7a-none-eabihf` | verified | `llvm@22.1.8` | 载荷 | 载荷 | 载荷 | 载荷 |
438438
| `aarch64-linux-android` | verified | `android-ndk@30.0.16248370` | payload | payload | payload ||
439439
| `x86_64-linux-android` | verified | `android-ndk@30.0.16248370` | payload | payload | payload ||
440-
| `aarch64-ios` | planned | | planned | planned | planned | planned |
441-
| `aarch64-ios-sim` | planned | | planned | planned | planned | planned |
442-
| `x86_64-ios-sim` | planned | | planned | planned | planned | planned |
440+
| `aarch64-ios` | preview | `llvm@22.1.8` | | | SDK | |
441+
| `aarch64-ios-sim` | verified | `llvm@22.1.8` | | | SDK | |
442+
| `x86_64-ios-sim` | preview | `llvm@22.1.8` | | | SDK | |
443443
| `wasm32-emscripten` | verified | `emsdk@6.0.9` | payload | payload | payload | payload |
444444

445445
`载荷` 这里有工具链载荷产出它 · `` 没有载荷,但依赖可以供给系统 ·

examples/13-platform-targets/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,6 @@ error: target aarch64-ios needs the iphoneos SDK, which this machine does not pr
178178
| `wasm32-emscripten` | verified | `emsdk@6.0.9` | 是,`node` |
179179
| `x86_64-linux-android` | verified | `android-ndk@30.0.16248370` | 是,平台模拟器 |
180180
| `aarch64-linux-android` | verified | `android-ndk@30.0.16248370` | 是,`qemu-aarch64-static` + 从镜像取出的 bionic |
181-
| `aarch64-ios` | planned | `llvm@22.1.8` | 否 —— 真机需要开发者自己的签名 |
182-
| `aarch64-ios-sim` | planned | `llvm@22.1.8` | 是,`simctl-run`(macos-15) |
183-
| `x86_64-ios-sim` | planned | `llvm@22.1.8` | 否 —— 模拟器跑宿主架构,而那台宿主是 arm64 |
181+
| `aarch64-ios` | preview | `llvm@22.1.8` | 否 —— 真机需要开发者自己的签名 |
182+
| `aarch64-ios-sim` | verified | `llvm@22.1.8` | 是,`simctl-run`(macos-15) |
183+
| `x86_64-ios-sim` | preview | `llvm@22.1.8` | 否 —— 模拟器跑宿主架构,而那台宿主是 arm64 |

modules/toolchain-model/src/triple.cppm

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,24 @@ inline constexpr TargetInfo kKnownTargets[] = {
746746
// The simulator is deliberately not a row. It has its own SDK and produces
747747
// its own object, so folding it in would make two targets share an
748748
// identity -- the mistake `x86_64-windows-musl` was added to undo.
749-
{ "aarch64-ios", "planned", "", "llvm@22.1.8","", false },
749+
//
750+
// `preview`: BUILT, AND NOT RUN, AND NOTHING HERE CAN RUN IT. Measured
751+
// 2026-09-11 on macos-15 (Xcode 16.4, iPhoneOS 18.5,
752+
// `ios_deployment_target = "18.0"`):
753+
//
754+
// Mach-O 64-bit executable arm64
755+
// LC_BUILD_VERSION platform 2 (IOS) minos 18.0 sdk 18.5
756+
//
757+
// `platform 2` against the simulator rows' `platform 7` is the reading
758+
// worth having: a successful build cannot tell them apart, and an artefact
759+
// reporting IOSSIMULATOR from this row is one no later step refuses.
760+
//
761+
// The row keeps `runner` unset and will stay `preview`. An artefact cannot
762+
// be run off an iOS device without a signature the developer owns, which
763+
// is not something a build tool or a package can supply -- so this is a
764+
// tier bounded by a fact about the platform rather than by work not yet
765+
// done.
766+
{ "aarch64-ios", "preview", "", "llvm@22.1.8","", false },
750767
// THE SIMULATOR'S TWO ROWS. Not a convenience and not a runner: a
751768
// simulator build has its own SDK (`iPhoneSimulator.sdk`), produces its own
752769
// object, and takes `-mios-simulator-version-min` rather than
@@ -759,13 +776,26 @@ inline constexpr TargetInfo kKnownTargets[] = {
759776
// an Intel one needs `x86_64`. A single row would describe a simulator half
760777
// the machines cannot run.
761778
//
762-
// `planned`, and the blocker is the same licence question as the device
763-
// row -- the simulator SDK ships inside Xcode and is no more
764-
// redistributable than the iPhoneOS one. What these rows buy today is that
765-
// `mcpp build --target aarch64-ios-sim` answers `tier-planned` naming the
766-
// row, instead of `unknown target`, which was false.
767-
{ "aarch64-ios-sim", "planned", "", "llvm@22.1.8","", false },
768-
{ "x86_64-ios-sim", "planned", "", "llvm@22.1.8","", false },
779+
// ONE `verified` AND ONE `preview`, AND THE DIFFERENCE IS THE HOST'S
780+
// ARCHITECTURE RATHER THAN ANYTHING ABOUT THE ROWS.
781+
//
782+
// `aarch64-ios-sim` was built AND RUN. Measured 2026-09-11 on macos-15
783+
// (Xcode 16.4, iPhoneSimulator 18.5, `ios_deployment_target = "18.0"`):
784+
//
785+
// artefact Mach-O 64-bit executable arm64
786+
// LC_BUILD_VERSION platform 7 (IOSSIMULATOR) minos 18.0
787+
// run xcrun simctl spawn <udid> <artefact> -> 1-2-3
788+
//
789+
// with no bundle, no signature and no Info.plist -- the measurement that
790+
// made `simctl-run` a boot-and-spawn wrapper rather than a bundle builder.
791+
//
792+
// `x86_64-ios-sim` builds and its artefact is correct -- Mach-O x86_64,
793+
// the same `platform 7`, the same floor -- and nothing ran it, because a
794+
// SIMULATOR RUNS THE HOST'S ARCHITECTURE and the runner is Apple silicon.
795+
// That is a property of the machine the measurement was taken on, so the
796+
// row stays `preview` until an Intel host takes it.
797+
{ "aarch64-ios-sim", "verified", "", "llvm@22.1.8","", false },
798+
{ "x86_64-ios-sim", "preview", "", "llvm@22.1.8","", false },
769799

770800
// WEB IS THE OUTLIER, AND IT IS THE ONLY ONE OF THE THREE THAT CHANGES THE
771801
// MODEL RATHER THAN EXTENDING A TABLE. A new arch (`wasm32`), a new os

src/build/prepare.cppm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,11 @@ prepare_build(bool print_fingerprint,
24932493
: mcpp::platform::macos::sdk_iphoneos;
24942494
appleSdkLocated = mcpp::platform::macos::sdk_path(which);
24952495
if (!appleSdkLocated) {
2496+
// A CODE, BECAUSE THE MATRIX COMPARES REASONS AND NOT ONLY
2497+
// OUTCOMES. A refusal with no code is recorded as `other`,
2498+
// which `check_matrix_reasons.sh` refuses on the ground that
2499+
// it freezes an unnamed branch into the expected table.
2500+
refusal::record(refusal::Code::AppleSdkAbsent);
24962501
return std::unexpected(std::format(
24972502
"target {} needs the {} SDK, which this machine does not "
24982503
"provide.\n"

src/build/refusal.cppm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ enum class Code {
9393
// hold. Distinct from VersionFloorUnmet, which is about the machine: this
9494
// one is about two manifests disagreeing over a tool.
9595
ToolVersionConflict,
96+
// An Apple SDK this target needs is not on this machine. Distinct from
97+
// HostCannotServe, which says no PAYLOAD here produces the target and
98+
// waits for the graph in case a package supplies the system: an Apple SDK
99+
// is not redistributable, so no package can supply it and nothing a later
100+
// step learns changes the answer. Distinct from TierPlanned, which says
101+
// the row is not wired at all -- these rows are.
102+
AppleSdkAbsent,
96103
Other, // a refusal that has not been given a code yet
97104
};
98105

@@ -104,6 +111,7 @@ constexpr std::string_view name(Code c) {
104111
case Code::CompilerRequirementConflict:
105112
return "compiler-requirement-conflict";
106113
case Code::TierPlanned: return "tier-planned";
114+
case Code::AppleSdkAbsent: return "apple-sdk-absent";
107115
case Code::HostCannotServe: return "host-cannot-serve";
108116
case Code::CapabilityPin: return "capability-pin";
109117
case Code::ConventionUnreplaced: return "convention-unreplaced";

tests/e2e/641_the_android_rows_are_wired_and_the_simulator_is_a_row.sh

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,48 @@ for target in aarch64-linux-android x86_64-linux-android; do
6060
fi
6161
done
6262

63-
# 3. THE SIMULATOR IS A ROW, so its spelling resolves. Before it existed,
64-
# `--target aarch64-ios-sim` answered `unknown target`, which was false: the
65-
# vocabulary has the device row and the simulator is a different target, not
66-
# an unspellable one.
63+
# 3. THE SIMULATOR IS A ROW, so its spelling resolves -- and the reason it is
64+
# refused on this host moved when the tier did.
65+
#
66+
# Before the rows existed, `--target aarch64-ios-sim` answered `unknown
67+
# target`, which was false. Then it answered `tier-planned`, which was true
68+
# while nothing had built one. It now answers the SDK gate, because the rows
69+
# are `verified` and `preview` and the thing this machine lacks is the SDK.
70+
#
71+
# ALL THREE ANSWERS ARE ASSERTED AGAINST, not just the first: `unknown
72+
# target` is a lie about the vocabulary, and `tier-planned` after the tier
73+
# moved would mean the table and the gate disagree.
6774
for target in aarch64-ios-sim x86_64-ios-sim; do
6875
d="$t/sim-$target"; pkg "$d"
6976
out=$( cd "$d" && MCPP_NO_AUTO_INSTALL=1 "$MCPP" build --target "$target" 2>&1 ) || true
70-
if grep -q "unknown target" <<<"$out"; then
71-
echo "FAIL: $target reported as unknown; it is a registered row"
72-
fail=1
73-
elif grep -q "not yet supported (planned)" <<<"$out"; then
74-
echo " ok: $target says planned, naming the row"
75-
else
76-
echo "FAIL: $target refused for neither reason"
77-
grep -m2 -E "^error" <<<"$out" | sed 's/^/ /'
78-
fail=1
79-
fi
77+
case "$(uname -s)" in
78+
Darwin)
79+
# On a machine that has the SDK the row is simply built, and nothing
80+
# here asserts the artefact -- ci-macos-ios.yml does that.
81+
if grep -qE "unknown target|not yet supported \(planned\)" <<<"$out"; then
82+
echo "FAIL: $target refused on a machine that can build it"
83+
grep -m2 -E "^error" <<<"$out" | sed 's/^/ /'
84+
fail=1
85+
else
86+
echo " ok: $target is not refused on macOS"
87+
fi
88+
;;
89+
*)
90+
if grep -q "unknown target" <<<"$out"; then
91+
echo "FAIL: $target reported as unknown; it is a registered row"
92+
fail=1
93+
elif grep -q "not yet supported (planned)" <<<"$out"; then
94+
echo "FAIL: $target still says planned; the table says otherwise"
95+
fail=1
96+
elif grep -q "needs the iphonesimulator SDK" <<<"$out"; then
97+
echo " ok: $target is refused for the SDK, naming the simulator one"
98+
else
99+
echo "FAIL: $target refused for none of the three known reasons"
100+
grep -m2 -E "^error" <<<"$out" | sed 's/^/ /'
101+
fail=1
102+
fi
103+
;;
104+
esac
80105
done
81106

82107
# 4. AN EFFECTIVE TRIPLE mcpp PRINTS ITSELF MUST PARSE BACK.
@@ -164,8 +189,14 @@ done
164189
# No payload is needed: the SDK is located before the toolchain is resolved,
165190
# which is itself a property worth asserting -- a machine without Xcode used
166191
# to download a 700 MB compiler before being told the compiler was not what
167-
# was missing. Hence MCPP_NO_AUTO_INSTALL=1 and the explicit override, which
168-
# opens the tier gate so that this gate is the one that answers.
192+
# was missing. Hence MCPP_NO_AUTO_INSTALL=1.
193+
#
194+
# THE OVERRIDE IS KEPT AND NO LONGER LOAD-BEARING. It opened the tier gate
195+
# while the rows were `planned`; they are not any more, so this case would
196+
# reach the SDK gate without it. It stays because the claim it makes is
197+
# stronger with it: declaring a toolchain says which compiler and says
198+
# nothing about where the headers are, so the SDK gate must refuse even a
199+
# project that has named its own.
169200
for target in aarch64-ios aarch64-ios-sim; do
170201
d="$t/sdk-$target"
171202
pkg "$d" "" "[target.$target]" 'toolchain = "llvm@22.1.8"'

0 commit comments

Comments
 (0)