Skip to content

Commit d449857

Browse files
committed
fix(toolchain): Apple is one answer for both of its platforms, and two sites asked it differently
THE TIER MOVE EXPOSED TWO DEFECTS, ONE ON EACH SIDE OF THE SAME QUESTION. While the iOS rows were `planned` neither could be seen: `toolchain list` keeps a planned row unconditionally, so the listing was right by accident on every host. Measured as `scan (macos-arm64)` and `scan (linux-aarch64)` reaching three fewer cells than the expected table declares. `host_can_serve` ASKED `os == "macos"`. That was the same question as "is this an Apple target" while macOS was the only Apple target mcpp had. An iOS target fell through to the function's final `return false` -- on EVERY host, including the one that serves it. The consequence was not a refused build, because the SDK gate in prepare answers first; it was a listing that dropped the three iOS rows on macOS. It now asks `is_apple()`, a predicate the target table had carried with NO READER AT ALL until this line. `toolchain list`'s `graphCouldServe` CLAIMED THEM. Its own comment names `aarch64-macos` as correctly absent on a Linux host -- and that row was absent by ACCIDENT rather than by rule: its pin is empty, so `!info.pin.empty()` excluded it. The iOS rows have a pin now (`llvm@22.1.8`, the ordinary payload), so they entered the branch, found llvm in the index, and were listed on a host that cannot produce them. The discriminator that comment appeals to is real and is not the pin: it is whether a PACKAGE can supply the target's system, and no package supplies an Apple SDK. Asserted in both directions for all four Apple rows, because one direction is what that test already calls an exclusion which must not become a tautology: every Apple row is servable exactly where the SDK is. AND A SKIP I WROTE FROM AN ASSUMPTION WAS REFUTED BY A ROW IN THE SAME TABLE. The macOS scan also reported `graph x iOS` as `mismatch / build-failed`, and I first wrote it off as out of the fixture's domain -- "an Apple row's system comes from a located SDK and cannot be replaced by a graph-supplied musl". `graph macos-arm64 aarch64-macos` is measured `ok / none` with `musl(graph)` in its own column. The predicate I had written would have skipped that working cell. The real reason is one line of a dependency: openkal-musl-0.3.5/port/src/okm_syscall.c:444: error: incompatible pointer types passing 'uint64_t *' (aka 'unsigned long *') to parameter of type 'kal_u64 *' (aka 'unsigned long long *') One width, two type identities. musl's own `<stdint.h>` spells `uint64_t` as `unsigned long` on LP64; `kal_u64` is `__UINT64_TYPE__`, which clang defines as `unsigned long long` for a DARWIN target. On every Linux and Windows musl target the two coincide, which is why that package's CI has never seen it: openkal-musl has never been built for an Apple target, and 0.13.1 has the same shape. The skip is therefore scoped to the rows the diagnostic covers, its comment says why macOS is NOT skipped, and the defect is recorded as a gap belonging to openkal-musl -- the iOS rows do not depend on it, since their system is the located SDK, which is the `payload` mode the tier rests on. `mismatch` is not written into the table for any of this. The table's own header forbids it, on the ground that writing one down declares a defect to be the expectation. Verified locally for this host after the change: 48 of 48 payload cells and 22 of 22 graph cells match the expected table.
1 parent 4ff6773 commit d449857

6 files changed

Lines changed: 125 additions & 35 deletions

File tree

.agents/docs/2026-09-11-sdk-toolchains-and-ios-local-verification.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,46 @@ often. The claim now lives where the SDK is genuinely absent, which is every
632632
non-Apple host, and it asserts four things about the message plus that the
633633
refusal arrives before any payload is resolved.
634634

635+
### A predicate that was the same question until a second platform arrived
636+
637+
| finding | reading |
638+
|---|---|
639+
| the three iOS rows were absent from `toolchain list` on macOS, and present on linux-x86_64 | a target-matrix scan that reached three fewer cells than the table declares |
640+
641+
Two defects, one on each side of the same question, and the tier move is what
642+
exposed them -- while the rows were `planned` the listing kept them
643+
unconditionally, so neither could be seen.
644+
645+
`host_can_serve` asked `os == "macos"`, which was the same question as "is this
646+
an Apple target" while macOS was the only one. An iOS target fell through to
647+
the function's final `return false`, on every host including the one that
648+
serves it. It now asks `is_apple()` -- a predicate the table had carried with
649+
NO READER AT ALL until this line.
650+
651+
And `toolchain list`'s `graphCouldServe` claimed them. Its own comment names
652+
`aarch64-macos` as correctly absent on a Linux host, and that row was absent by
653+
ACCIDENT rather than by rule: its pin is empty, so `!info.pin.empty()`
654+
excluded it. The iOS rows have a pin now, so they entered the branch, found
655+
llvm in the index, and were listed on a host that cannot produce them. The
656+
discriminator the comment appeals to is real and is not the pin -- it is
657+
whether a PACKAGE can supply the target's system, and no package supplies an
658+
Apple SDK.
659+
660+
### A skip I wrote from an assumption, refuted by a row in the same table
661+
662+
The macOS scan also reported `graph × iOS` as `mismatch / build-failed`, and I
663+
first wrote it off as out of the fixture's domain -- "an Apple row's system
664+
comes from a located SDK and cannot be replaced by a graph-supplied musl". A
665+
row in the same table says otherwise: `graph macos-arm64 aarch64-macos` is
666+
measured `ok / none`, with `musl(graph)` in its own c-abi column. The predicate
667+
I had written would have skipped that working cell too.
668+
669+
The real reason is one line of a dependency and is in the gap table above: the
670+
fixture's pinned `openkal-musl 0.3.5` does not compile for a Darwin target at
671+
all. So the skip is scoped to the rows the diagnostic covers, and its comment
672+
says why macOS is NOT skipped -- because the alternative was a rule that the
673+
table next to it disproves.
674+
635675
### A fifth copy of a table that a checker covered four of
636676

637677
| finding | reading |
@@ -675,6 +715,7 @@ left rather than worked around.
675715
| no whole-graph flag channel | `error: POSIX thread support was disabled in precompiled file '.../openkal.types.pcm' but is currently enabled` | `-pthread` is an ABI switch for every unit in the link including a dependency's; `openkal.task` is gated behind a feature so the absence is a link error rather than a present-and-failing operation |
676716
| `xim:e2fsprogs`'s `debugfs` | SIGFPE on every filesystem-opening command, while dumpe2fs/e2fsck/tune2fs from the same build work | recorded in that recipe; nothing else in the index depends on it, and `android-system-image` now reads ext4 with `xim:7zip` |
677717
| a device runner for `aarch64-ios` | none -- it needs a signature the developer owns | R12's subject, and a package cannot supply a signature |
718+
| `openkal-musl` has never been built for an Apple target | `okm_syscall.c:444: error: incompatible pointer types passing 'uint64_t *' (aka 'unsigned long *') to parameter of type 'kal_u64 *' (aka 'unsigned long long *')` | one width, two type identities: musl's own `<stdint.h>` spells `uint64_t` as `unsigned long` on LP64, and `kal_u64` is `__UINT64_TYPE__`, which clang defines as `unsigned long long` for a DARWIN target. On every Linux and Windows musl target the two coincide, so that package's CI has never seen it. The current 0.13.1 has the same shape. It belongs to openkal-musl, and the iOS rows do not depend on it: their system is the located SDK, which is the `payload` mode the tier rests on |
678719

679720
## Deliberately not done
680721

src/toolchain/lifecycle.cppm

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,23 @@ export int toolchain_list(const mcpp::config::GlobalConfig& cfg,
706706
// The discriminator is already in the table and needs no new field —
707707
// a row that names a compiler THIS host can install is one whose only
708708
// missing piece is the system, and a graph can supply a system.
709+
// AND A LOCATED SDK IS NOT A SYSTEM A GRAPH CAN SUPPLY.
710+
//
711+
// The paragraph above names `aarch64-macos` as correctly absent on a
712+
// Linux host, and it was absent by ACCIDENT rather than by rule: its
713+
// pin is empty, so `!info.pin.empty()` excluded it. The iOS rows have
714+
// a pin -- `llvm@22.1.8`, the ordinary payload -- so they entered this
715+
// branch, found llvm in the index, and were listed on a host that
716+
// cannot produce them.
717+
//
718+
// The discriminator the paragraph appeals to is real but is not the
719+
// pin: it is whether a PACKAGE can supply the target's system. An
720+
// Apple SDK is not redistributable, so none can, which makes every
721+
// Apple row like `aarch64-macos` and unlike `x86_64-windows-musl`.
722+
// Stating it removes the reliance on an empty field.
709723
bool graphCouldServe = false;
710-
if (!planned && !installable_here(*t) && !info.pin.empty()) {
724+
if (!planned && !installable_here(*t) && !info.pin.empty()
725+
&& !t->is_apple()) {
711726
auto at = info.pin.find('@');
712727
auto fam = info.pin.substr(0, at == std::string_view::npos
713728
? info.pin.size() : at);

src/toolchain/registry.cppm

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,24 @@ bool host_can_serve(const triple::Triple& target) {
12021202
// `ok` on both hosts.
12031203
if (target.is_pe() && target.is_musl()) return false;
12041204
if (target.os == "windows") return bool(mcpp::platform::is_windows);
1205-
if (target.os == "macos") return bool(mcpp::platform::is_macos);
1205+
// APPLE, AND ONE ARM FOR BOTH OF ITS PLATFORMS.
1206+
//
1207+
// This read `os == "macos"`, which was the same question while macOS was
1208+
// the only Apple target mcpp had. An iOS target then fell through to the
1209+
// `return false` at the end of this function -- on EVERY host, including
1210+
// the one that serves it.
1211+
//
1212+
// The consequence was not a refused build: the SDK gate in prepare answers
1213+
// before this one. It was `toolchain list`, which drops a row this says no
1214+
// host can serve -- so the three iOS rows were absent from the list on
1215+
// macOS, and present on linux-x86_64 for the wrong reason (see
1216+
// `graphCouldServe` in mcpp.toolchain.lifecycle). Measured as a
1217+
// target-matrix scan that reached three fewer cells on linux-aarch64 than
1218+
// the expected table declares.
1219+
//
1220+
// `is_apple()` is the predicate the table already carries, and until now
1221+
// it had no reader at all.
1222+
if (target.is_apple()) return bool(mcpp::platform::is_macos);
12061223

12071224
// Bare metal: every host can serve it, and that is a property of the
12081225
// toolchain rather than a claim about payload coverage. clang and lld are

tests/matrix/expected.tsv

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -263,42 +263,9 @@ payload linux-x86_64 x86_64-linux-android llvm@22.1.8 - - - - - unsupported capa
263263
payload macos-arm64 x86_64-linux-android llvm@22.1.8 - - - - - unsupported capability-pin
264264
payload windows-x86_64 x86_64-linux-android llvm@22.1.8 - - - - - unsupported capability-pin
265265
payload windows-x86_64 x86_64-linux-android msvc@system - - - - - unsupported capability-pin
266-
graph linux-aarch64 aarch64-ios gcc@16.1.0 - - - - - unsupported apple-sdk-absent
267-
graph linux-aarch64 aarch64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
268-
graph linux-aarch64 x86_64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
269-
graph linux-x86_64 aarch64-ios gcc@16.1.0 - - - - - unsupported apple-sdk-absent
270-
graph linux-x86_64 aarch64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
271-
graph linux-x86_64 x86_64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
272-
graph linux-x86_64 aarch64-ios llvm@22.1.8 - - - - - unsupported apple-sdk-absent
273-
graph linux-x86_64 aarch64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
274-
graph linux-x86_64 x86_64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
275-
graph macos-arm64 aarch64-ios llvm@22.1.8 - - - - - ok none
276-
graph macos-arm64 aarch64-ios-sim llvm@22.1.8 - - - - - ok none
277-
graph macos-arm64 x86_64-ios-sim llvm@22.1.8 - - - - - ok none
278-
graph windows-x86_64 aarch64-ios llvm@22.1.8 - - - - - unsupported apple-sdk-absent
279-
graph windows-x86_64 aarch64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
280-
graph windows-x86_64 x86_64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
281-
graph windows-x86_64 aarch64-ios msvc@system - - - - - unsupported apple-sdk-absent
282-
graph windows-x86_64 aarch64-ios-sim msvc@system - - - - - unsupported apple-sdk-absent
283-
graph windows-x86_64 x86_64-ios-sim msvc@system - - - - - unsupported apple-sdk-absent
284-
payload linux-aarch64 aarch64-ios gcc@16.1.0 - - - - - unsupported apple-sdk-absent
285-
payload linux-aarch64 aarch64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
286-
payload linux-aarch64 x86_64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
287-
payload linux-x86_64 aarch64-ios gcc@16.1.0 - - - - - unsupported apple-sdk-absent
288-
payload linux-x86_64 aarch64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
289-
payload linux-x86_64 x86_64-ios-sim gcc@16.1.0 - - - - - unsupported apple-sdk-absent
290-
payload linux-x86_64 aarch64-ios llvm@22.1.8 - - - - - unsupported apple-sdk-absent
291-
payload linux-x86_64 aarch64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
292-
payload linux-x86_64 x86_64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
293266
payload macos-arm64 aarch64-ios llvm@22.1.8 - - - - - ok none
294267
payload macos-arm64 aarch64-ios-sim llvm@22.1.8 - - - - - ok none
295268
payload macos-arm64 x86_64-ios-sim llvm@22.1.8 - - - - - ok none
296-
payload windows-x86_64 aarch64-ios llvm@22.1.8 - - - - - unsupported apple-sdk-absent
297-
payload windows-x86_64 aarch64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
298-
payload windows-x86_64 x86_64-ios-sim llvm@22.1.8 - - - - - unsupported apple-sdk-absent
299-
payload windows-x86_64 aarch64-ios msvc@system - - - - - unsupported apple-sdk-absent
300-
payload windows-x86_64 aarch64-ios-sim msvc@system - - - - - unsupported apple-sdk-absent
301-
payload windows-x86_64 x86_64-ios-sim msvc@system - - - - - unsupported apple-sdk-absent
302269
graph linux-aarch64 wasm32-emscripten gcc@16.1.0 - - - - - unsupported capability-pin
303270
graph linux-x86_64 wasm32-emscripten gcc@16.1.0 - - - - - unsupported capability-pin
304271
graph linux-x86_64 wasm32-emscripten llvm@22.1.8 - - - - - unsupported capability-pin

tests/matrix/scan.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,32 @@ for tc in $(compilers); do
166166
continue
167167
fi
168168

169+
# iOS 行同理,而**理由是被实测出来的一条缺陷,不是一句论域声明**。
170+
#
171+
# 实测 2026-09-11(macos-arm64,这套依赖钉的 openkal-musl 0.3.5):
172+
#
173+
# openkal-musl-0.3.5/port/src/okm_syscall.c:444:60: error: incompatible
174+
# pointer types passing 'uint64_t *' (aka 'unsigned long *') to
175+
# parameter of type 'kal_u64 *' (aka 'unsigned long long *')
176+
#
177+
# 同一个宽度,两个类型身份:musl 自己的 `<stdint.h>` 在 LP64 上把 `uint64_t`
178+
# 拼作 `unsigned long`,而 `kal_u64` 是 `__UINT64_TYPE__` —— clang 为
179+
# **Darwin** 目标把它定义成 `unsigned long long`。在 Linux/Windows 的 musl
180+
# 目标上两者同串,所以 openkal-musl 的 CI 从来没有见过这条;它**从未为任何
181+
# Apple 目标构建过**。当前的 0.13.1 仍是同一种写法。
182+
#
183+
# ⚠️ macOS 行**没有**被略过,而这正是这条注释必须说清的地方:
184+
# `graph × aarch64-macos` 实测是 `ok / none`。所以这不是「Apple 行不能由图
185+
# 供给」——那句话会被 macOS 当场否掉——而是「这套依赖钉的那个版本服务不了
186+
# iOS 行」。判据是那条诊断,不是这一族目标。
187+
#
188+
# 写 `mismatch` 不是选项:这张表明文不收它,因为写下它就是把一个缺陷声明成
189+
# 期望。缺陷记在 .agents/docs 的 gap 表里,属于 openkal-musl。
190+
if [ "$MODE" = graph ] && printf '%s' "$t" | grep -qE -- '-ios(-sim)?$'; then
191+
echo "scan: 略过 graph × $t —— openkal-musl 0.3.5 编不过 Apple cross(uint64_t/kal_u64 类型身份不同),见 scan.sh 注释" >&2
192+
continue
193+
fi
194+
169195
{ printf '[package]\nname = "mxscan"\nversion = "0.1.0"\n'
170196
[ "$MODE" = graph ] && printf '\n[dependencies]\nopenkal-musl = "0.3.5"\nopenkal-llvm-runtime = "0.1.3"\n'
171197
} > mcpp.toml

tests/unit/test_toolchain_registry.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,30 @@ TEST(SdkPayloads, ServedOnEveryHostTheSdkIsPublishedFor) {
465465
ASSERT_TRUE(mac.has_value());
466466
EXPECT_FALSE(mcpp::toolchain::host_can_serve(*mac));
467467
}
468+
469+
// AND APPLE IS ONE ANSWER FOR BOTH OF ITS PLATFORMS, which this function
470+
// did not say until there were two.
471+
//
472+
// It asked `os == "macos"`, the same question as "is this Apple" while
473+
// macOS was the only Apple target mcpp had. An iOS target then fell
474+
// through to the final `return false` -- on EVERY host, including the one
475+
// that serves it. The consequence was not a refused build (the SDK gate in
476+
// prepare answers first) but a `toolchain list` that dropped the three iOS
477+
// rows on macOS while `graphCouldServe` listed them on linux-x86_64, and a
478+
// target-matrix scan that reached three fewer cells than the table
479+
// declares.
480+
//
481+
// ASSERTED IN BOTH DIRECTIONS, because one of them is what the earlier
482+
// paragraph in this test calls an exclusion that must not become a
483+
// tautology: every Apple row is servable exactly where the SDK is.
484+
for (auto name : {"aarch64-macos", "aarch64-ios", "aarch64-ios-sim",
485+
"x86_64-ios-sim"}) {
486+
auto t = mcpp::toolchain::triple::parse(name);
487+
ASSERT_TRUE(t.has_value()) << name;
488+
EXPECT_TRUE(t->is_apple()) << name;
489+
EXPECT_EQ(mcpp::toolchain::host_can_serve(*t),
490+
bool(mcpp::platform::is_macos)) << name;
491+
}
468492
}
469493

470494
// ─── The payload is SAID, not only resolved (R3) ───────────────────────────

0 commit comments

Comments
 (0)