Xcode 27 support: SimulatorKit SharedFrameworks path + SwiftBuild-backend toolchain fixes#56
Merged
Merged
Conversation
Xcode 27 moved SimulatorKit.framework from Contents/Developer/Library/ PrivateFrameworks to Contents/SharedFrameworks (early betas shipped without it entirely; Beta 4+ include it again). Backport upstream idb commit 98110129 onto our pinned idb ref so the XCFramework build prefers the new location and falls back to the legacy one for Xcode <= 26, and teach the XcodeCompatibility pre-flight to accept either path. Also refresh the dtuhidd guard messaging with what Beta 4 / CoreSimulator 1169.1 testing established: the legacy-HID disconnection is decided at simulator boot, a simulator booted while Device Hub is open now loses tap as well as type, and a Device Hub attached after a clean boot leaves typing functional (the guard can false-positive there, hence the documented env override). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
The Xcode 26.6+/27 toolchains switch SwiftPM to the SwiftBuild backend (products under .build/out/Products/<config>), which broke the dev loop in two independent ways: 1. The backend emits no LC_RPATH for binary-target XCFrameworks, so the sim-use binary and the SimUseTests bundle fail to dlopen the FB* frameworks at load time. scripts/stage-fb-frameworks.sh stages symlinks into the PackageFrameworks directory dyld still searches (no-op on the classic layout) and is wired into make build/test and both E2E runners. 2. The test suites located the sim-use binary by shelling out to `swift build --show-bin-path` from inside the running `swift test`, which now deadlocks on the package lock the test run holds (observed as idle swift-build children under swiftpm-testing-helper, including during plain unit runs, where E2E-gated suites still resolve the path at collection). getSimUsePath now prefers SIM_USE_TEST_BINARY (exported by the runners), then derives the products directory from the test bundle location, and only shells out as a last resort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
…elog README documents the supported Xcode 27 workflow (classic Simulator.app or headless simctl with Device Hub closed; full Device Hub support arrives with the planned idb migration). docs/ai/xxxx-xcode27-support is the full work record: Beta 1 -> Beta 4 deltas, the dtuhidd suppression matrix on CoreSimulator 1169.1, upstream idb status (DTUHID transport verified live), the bugs filed upstream and locally (facebook/idb#941, #55), the SwiftBuild-backend breakages, and the step-2 migration checklist with a reusable verification harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
…works Review follow-up: the PackageFrameworks staging only ran inside make targets and the E2E runners, so a bare `swift test --filter ...` — the single-suite path CLAUDE.md documents — still failed to dlopen the FB* frameworks on SwiftBuild-backend toolchains when run from a fresh scratch directory (reproduced on Xcode 27 Beta 4). Package.swift now emits explicit rpath entries per XCFramework slice: @loader_path-relative for the classic and SwiftBuild product layouts (executable and test-bundle depths differ), plus a repo-root-relative fallback that covers custom --scratch-path runs started from the repo root. The staging script stays as a belt-and-suspenders layer. Verified: fresh-scratch bare `swift test --filter` and a no-staging non-repo-CWD run both pass on Beta 4; `make test` 1094 green on 26.5 and Beta 4; full 20-suite E2E run green on Beta 4 + iOS 26.5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
- stage-fb-frameworks.sh exits early when scripts/build.sh has not produced build_products/XCFrameworks yet, so a fresh checkout gets a clean no-op instead of dangling symlinks. Document why the staged directory is still pre-created unconditionally otherwise (the layout cannot be known without building) and the script's Debug-dev-loop scope (release-config local builds are covered by the Package.swift rpath entries). - XcodeCompatibility: build both SimulatorKit candidate paths through the same NSString API. - Work record: note that agent-facing SKILL.md guidance for the suppressed-state tap no-op is deliberately deferred to the idb-migration work, which rewrites the Device Hub story. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
In the suppressed-at-boot state (simulator booted while Device Hub is open) taps report success without landing and there is no runtime guard on the touch path, so the bundled skill is the only place an agent can learn the symptom and the recovery (quit Device Hub + reboot; view via classic Simulator.app). Added as a pitfall table row plus a detailed recipe, phrased as a temporary limitation to be rewritten when the idb migration lands native Device Hub support. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
Review follow-ups: - The rpath entries Package.swift emits for the SwiftBuild-backend dev loop (build_products/... XCFramework slices, including a CWD-relative fallback) rode into release binaries, where dyld searches them ahead of the appended @executable_path/Frameworks - a shipped binary run from a checkout would silently load dev-built frameworks (brew ad-hoc resigns the binary, so no library validation blocks the swap). Release staging now scrubs them: remove_xcode_rpaths is generalized into remove_rpaths_matching, and configure_executable_rpath calls the new remove_build_products_rpaths. - The test bundle re-emitted the CWD-relative entries it already inherits from the SimUse dependency, tripping ld's duplicate-rpath warning on every link; fbRPathFlags gains includeCWDFallback and the test target passes false. - stage-fb-frameworks.sh cds to the repository root instead of assuming the caller's CWD. - README/CHANGELOG note that the SimulatorKit relocation fix lives in the patched FB* XCFrameworks - pre-existing build_products need a ./scripts/build.sh dev rerun or the pre-flight passes while the framework load still fails. - Refresh the stale @executable_path comment in Package.swift and the work-record wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: onevcat <onevcat@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes sim-use fully usable under Xcode 27 (Beta 4+) in the classic Simulator.app / headless
simctlworkflow, and unbreaks the dev loop (make build/make test/ E2E runners) on the SwiftBuild-backend toolchains (Xcode 26.6+ / 27).1. SimulatorKit relocation (Xcode 27)
Xcode 27 moved
SimulatorKit.frameworktoContents/SharedFrameworks(Beta 1 shipped without it; Beta 4+ include it again). This PR backports upstream idb commit98110129onto our pinned idb ref (patches/idb/xcode27-simulatorkit-sharedframeworks.patch) so the framework loads from the new location with a fallback to the Xcode ≤ 26 path, and theXcodeCompatibilitypre-flight accepts either.The dtuhidd guard messaging is refreshed with what Beta 4 / CoreSimulator 1169.1 testing established: suppression is decided at simulator boot; a simulator booted while Device Hub is open loses
tapas well astype; Device Hub attached after a clean boot leaves typing functional.Scope note: the supported Xcode 27 workflow for now is classic Simulator.app / headless with Device Hub closed (documented in README). Full Device Hub support arrives with the planned migration to current upstream idb — see
docs/ai/xxxx-xcode27-support/README.mdfor the verified plan (upstream's DTUHID transport was validated live, including in the suppressed-at-boot state).2. SwiftBuild-backend dev-loop fixes (pre-existing on main, reproducible with Xcode 26.6)
LC_RPATHfor binary-target XCFrameworks →scripts/stage-fb-frameworks.shstages framework symlinks into thePackageFrameworksdirectory dyld still searches (wired intomake build/make testand both E2E runners; no-op on the classic layout).swift build --show-bin-pathfrom inside the runningswift test, which deadlocks on the package lock the test run now holds →getSimUsePath()prefersSIM_USE_TEST_BINARY(exported by the runners), then derives the path from the test bundle location, and only shells out as a last resort.Related upstream report from the same investigation: facebook/idb#941. Local follow-up filed as #55 (daemon holds a stale HID connection across simulator reboot; pre-existing, out of scope here).
Verification
Full E2E matrix (
scripts/test-runner.sh, 20 suites each), all on this branch's final tree, Device Hub closed:Unit tests (
make test, 1094 tests): ✅ on Xcode 26.5, 26.6, and 27 Beta 4 (26.6/Beta 4 previously failed at test-bundle dlopen on main).Manual spot-check under Xcode 27 Beta 4: drove a real-world app (LINE dev build on an iOS 26.2 simulator) — launch,
describe-ui, tab-switch taps, opening a chat, nativetypeinto the message field (verified via screenshot),screenshot.🤖 Generated with Claude Code