Skip to content

feat: migrate to current upstream idb — Device Hub works via DTUHID transport auto-selection#68

Open
onevcat wants to merge 8 commits into
mainfrom
migrate/idb-bump
Open

feat: migrate to current upstream idb — Device Hub works via DTUHID transport auto-selection#68
onevcat wants to merge 8 commits into
mainfrom
migrate/idb-bump

Conversation

@onevcat

@onevcat onevcat commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Executes step 2 of the Xcode 27 plan (docs/ai/xxxx-xcode27-support/README.md): the pinned idb ref moves from 76639e4d (2025-05-29) to upstream main @ 1f6943f8 (2026-07-23). This is a migration, not a version bump — upstream Swiftified the HID layer, moved to XcodeGen-generated static frameworks, and rewrote the video layer — and it buys the headline feature:

Simulators booted while Device Hub is open now just work. The HID transport is selected automatically per boot (DTUHID through dtuhidd's CoreDevice service when the legacy HID is suppressed, classic Indigo otherwise), so the state that v0.11.0's guard could only refuse now delivers tap/type/swipe normally.

What changed

  • Build (scripts/build.sh): pins 1f6943f8, generates the Xcode project with XcodeGen (new build dep: brew install xcodegen), packages static XCFrameworks with -allow-internal-distribution, stages the idb PrivateHeaders modules for consumers, and drops FBDeviceControl (never imported). No library evolution → build_products/ is toolchain-locked; re-run build.sh dev after switching Xcode.
  • All four patches/idb/ patches retired — superseded upstream or targeting deleted code.
  • Package.swift: private-module maps via -Xcc -fmodule-map-file; per-archive -force_load for the three ObjC-bearing frameworks + weak-linked CoreSimulator/APT tbds. The entire SwiftBuild-rpath machinery (slice rpaths, stage-fb-frameworks.sh, release rpath stripping) is deleted — static linking dissolves the dlopen problem it existed for. Deliberately NOT -ObjC: blanket force-loading makes Xcode 26.5-built binaries trip a Swift task-allocator abort (bisected to CompanionUtilities), while no force-load at all drops category-only members (+[FBControlCoreLoggerFactory osLoggerWithLevel:] unrecognized selector — both reproduced, see the linking addendum in the work record).
  • API migration: FBFuture bridging deleted; HID events are Swift enums sent via async hid.send(event:logger:); connections construct FBSimulatorHID(for:transport:) directly (upstream's connectToHID() cache has no boot-identity gate and would resurrect iOS daemon holds a stale HID connection across simulator reboot — tap reports success but is not delivered #55); accessibility consumes the typed FBAccessibilityElement API through a legacy-shape bridge (serializer shapes unchanged → outline/recovery/calibration pipelines untouched); video uses the async createStream(configuration:to:) lifecycle; multi-touch rides upstream's native .twoFingerTouch, assembled as one composite so the DTUHID transport drains once per gesture.
  • Guard retirement: DeviceHubHIDSuppression + SIM_USE_SKIP_DTUHIDD_CHECK removed; SIM_USE_HID_TRANSPORT=indigo|dtuhid added as a debug override, and connection creation logs the dtuhidd-presence signal the auto-selection keys on. The Indigo HID transport constructs the HID client before SimulatorKit is loaded — clientClassUnavailable for library consumers facebook/idb#941 mitigation (explicit xcodeFrameworks preload) is kept.
  • Release path: payload shrinks to the binary + two resource bundles (no Frameworks/, no framework codesigning); formula/staging/dev-install updated accordingly.
  • CI: caches PrivateHeaders alongside the XCFrameworks, keys on the Xcode build id (betas share a marketing version but ship incompatible toolchain-locked swiftmodules), installs XcodeGen on cache miss.

Verification

Both toolchains, full regression: Xcode 26.5 and 27 Beta 4 each run 1116 unit tests + 21/21 E2E suites, zero warnings, zero failures.

Live 4-case matrix (deterministic judges: Playground tap counter, Settings navigation, Safari URL field):

Case Transport picked tap type
Xcode 27 B4 + Device Hub (poisoned boot) auto → DTUHID
Xcode 27 B4 + classic Simulator.app auto → Indigo
Xcode 26.5 + classic Simulator.app auto → Indigo
Xcode 26.5 + Device Hub (poisoned boot) auto → DTUHID

Plus: forced-transport A/B in the poisoned state (forced dtuhid delivers; forced indigo keyboard fail-louds with upstream's diagnostic), manual pinch-out recognized on the gesture playground (multi-touch has no scripted iOS E2E suite), and the standalone-harness re-validation recorded in the work-record addenda.

Follow-ups

Work record with full investigation trail: docs/ai/xxxx-xcode27-support/README.md.

🤖 Generated with Claude Code

onevcat added 8 commits July 24, 2026 18:05
Migrate the idb pin from 76639e4d (2025-05-29) to main @ 1f6943f8
(2026-07-23). Upstream moved its build to XcodeGen-generated projects
producing STATIC frameworks, so the packaging pipeline changes shape:

- scripts/build.sh generates FBSimulatorControl.xcodeproj with XcodeGen
  (new build-time dependency: `brew install xcodegen`), builds
  CompanionUtilities first (FBSimulator.h imports
  CompanionUtilities-Swift.h and a bare xcodebuild does not order the
  generated header), then FBControlCore / XCTestBootstrap /
  FBSimulatorControl. FBDeviceControl is dropped — nothing in sim-use
  ever imported it. XCFrameworks are created with
  -allow-internal-distribution: no library evolution (upstream's
  Swift 6 code rejects non-frozen-enum exhaustiveness), so
  build_products/ is locked to the producing toolchain — re-run
  `build.sh dev` after switching Xcode versions.
- The idb PrivateHeaders modules (Clang module maps + headers + .tbd
  stubs) are staged under build_products/PrivateHeaders: the FB* Swift
  modules import them, and the static archives defer their
  CoreSimulator / AccessibilityPlatformTranslation class references to
  the final link.
- Package.swift wires those module maps into every FB-consuming target
  (-Xcc -fmodule-map-file, absolute via Context.packageDirectory) and
  links the executable/test bundle with -ObjC plus the weak .tbd stubs.
  The binary target set swaps FBDeviceControl for CompanionUtilities.
- Static linking dissolves the SwiftBuild-backend dlopen problem the
  rpath machinery existed for: the Package.swift slice rpaths,
  scripts/stage-fb-frameworks.sh, and the release-time
  build_products-rpath stripping are all retired. Framework
  strip/sign steps are gone too (static archives have no nested
  frameworks; their signatures are irrelevant at runtime).
- CI caches build_products/PrivateHeaders alongside the XCFrameworks,
  keys purely on scripts/build.sh + the Xcode version (the patches/
  glob is gone), and installs XcodeGen on cache miss.
- All four patches/idb/ patches are retired:
  xcode27-simulatorkit-sharedframeworks and multi-touch-spike are
  superseded upstream; headerpad-shims targeted the removed checked-in
  project's shim build phase; fbprocess-runtime-rename renamed a class
  that no longer exists upstream and was never referenced by sim-use.

Note: this commit does not build on its own — the Swift-side API
migration lands in the following commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
…ust work

Adapt sim-use to the Swiftified FBSimulatorControl surface at 1f6943f8
and retire the stopgaps it supersedes:

- HID events are Swift enums: touchDownAt/keyDown/buttonDown-style
  factories become .touch/.keyboard/.button(direction:...), composites
  become .composite([...]), and events are sent with the native async
  `hid.send(event:logger:)` (FBFuture bridging deleted: FutureBridge,
  BridgeQueues).
- HID connections construct `FBSimulatorHID(for:transport:)` directly —
  deliberately NOT `simulator.connectToHID()`, whose upstream-side
  cache has no boot-identity gate and would resurrect exactly the stale
  handles issue #55 guards against. Discarded cache entries are now
  disconnect()ed explicitly.
- Transport selection is upstream's automatic per-boot rule: DTUHID on
  dtuhidd-suppressed simulators (booted while Device Hub was open),
  legacy Indigo otherwise. The DeviceHubHIDSuppression guard and its
  SIM_USE_SKIP_DTUHIDD_CHECK escape hatch are removed — the state the
  guard refused is now simply functional; verified live on iOS 27.0 /
  Beta 4, where a poisoned-boot simulator delivers tap and type via
  DTU. SIM_USE_HID_TRANSPORT=indigo|dtuhid is the new debug override
  (forcing indigo on a suppressed simulator fail-louds with upstream's
  keyboardSuppressedByActiveDTUHIDD).
- The explicit xcodeFrameworks preload stays in GlobalSetup /
  HIDInteractor: facebook/idb#941 (Indigo transport constructs its
  client before SimulatorKit loads) is still open, and the preload is
  also what surfaces a clean error when SimulatorKit is absent.
- Accessibility consumption goes through the typed
  FBAccessibilityElement + serialize API via a legacy-shape bridge
  (LegacyAccessibilityBridge): upstream's serializer output shapes are
  unchanged, so the downstream pipeline (outline, collapsed-children
  recovery, orientation calibration) is untouched.
- Multi-touch rides upstream's native `.twoFingerTouch` primitive; the
  whole trajectory is assembled into one composite event so the
  transport is drained exactly once per gesture, as DTUHID requires.
  The local spike primitive (sendMultiTouch) is gone with the patch.
- Video streaming uses the async
  `createStream(configuration:to:)` / awaitCompletion / stopStreaming
  lifecycle; screenshots use `takeScreenshot(format:)`;
  FBEventReporter/EmptyEventReporter now come from CompanionUtilities.

Verified: 1116 unit tests green; full make e2e-ios 21/21 suites green
(Beta 4); manual pinch-out recognized on the gesture playground; live
Xcode 27 matrix (clean boot via Indigo, poisoned boot via DTU) passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
With the FB* code statically linked into the binary, the release
payload shrinks to the executable plus the two SwiftPM resource
bundles:

- release-payload.sh / release-artifacts.sh stop copying and verifying
  a Frameworks/ directory (the executable arch checks cover the linked
  code); the homebrew formula installs and post_install-resigns the
  binary only.
- local-release.sh / dev-install.sh check for
  build_products/XCFrameworks + PrivateHeaders (the swift build inputs)
  instead of build_products/Frameworks, drop the framework signing
  steps, and lose the framework loops from their smoke tests. The
  --build-frameworks path maps to `build.sh frameworks install
  xcframeworks` (strip/sign-frameworks no longer exist).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
- CHANGELOG: Unreleased entry for the idb bump (DTU transport
  auto-selection, static linking, retired patches/guard, new
  SIM_USE_HID_TRANSPORT override, XcodeGen build dependency).
- README / AGENTS.md: xcodegen prerequisite, toolchain-locked
  build_products note, and a rewritten Xcode 27 section — Device Hub
  workflows are now supported, the reboot-dance guidance is gone.
- Skill: drop the Device-Hub-poisoned-simulator pitfall from SKILL.md
  and references/pitfalls.md; the shipped binary no longer has that
  failure mode.
- docs/ai/xxxx-xcode27-support: phase-1 re-validation addendum
  (harness zero-drift at 1f6943f8, facebook/idb#941 still open,
  keyboard-only suppression observed headlessly — the full touch kill
  likely needs Device Hub's device view actively attached) and the
  migration-executed addendum recording the shape of the change and
  the live verification matrix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
The FB* swiftmodules are toolchain-locked (no library evolution), and
two betas of the same marketing version ship incompatible Swift
compilers — keying the cache on "27.0" alone would serve one beta's
build to another. Spotted in AXe's equivalent migration (PR #61 keys
on the build id).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
Upstream does not expose which transport its auto-selection resolved
to, so log the inputs instead: the SIM_USE_HID_TRANSPORT override when
forced, otherwise the dtuhidd-presence fact the selection keys on
(reusing the sysctl process-table probe the boot-identity token
already pays for). Presence is reported as a fact rather than a
claimed selection, so the line cannot drift out of sync with
upstream's predicate.

Note: SimUseLogger currently writes to no sink by default, so the
line surfaces only once a debug logging path is wired up — tracked
with the transport-race follow-up issue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
The Xcode 26.5 leg of the toolchain matrix caught both failure modes of
the blanket flag:

- With -ObjC (the spike-recipe default), 26.5-built binaries abort with
  "freed pointer was not the last allocation" — a Swift task-allocator
  LIFO trap — in unrelated async code (deterministic from the first
  ViewerAPIHandlers.run call). Per-archive bisection attributes it to
  force-loading CompanionUtilities; Beta 4 builds of identical source
  never trip it, and neither +load side effects nor cross-archive member
  duplication explain it (both checked).
- Without any force-load, category-only members are dropped by the
  archive link and the first runtime use dies with
  "+[FBControlCoreLoggerFactory osLoggerWithLevel:]: unrecognized
  selector" (FBControlCoreLogger+OSLog.o).

Thread the needle: -force_load FBControlCore, FBSimulatorControl, and
XCTestBootstrap (their ObjC categories must survive), ordinary archive
link for CompanionUtilities (pure Swift — everything it provides is
pulled in by symbol references). 1116 unit tests + 21/21 E2E suites
green on both Xcode 26.5 and 27 Beta 4 with this shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
The Xcode 26.5 compiler flags two non-Sendable captures in the
send-deadline @sendable closure that Beta 4 stays quiet about (both are
errors in Swift 6 language mode):

- FBSimulatorHIDEvent gets a retroactive @unchecked Sendable — it is a
  pure value tree (coordinates, key codes, delays, nested composites)
  that upstream simply never declared conformant, public enums getting
  no implicit conformance.
- The closure captures the @unchecked-Sendable FBSimulatorHID handle
  instead of the whole Session, which carries the non-Sendable
  FBSimulator.

(@preconcurrency import does not silence closure-capture diagnostics —
verified before reaching for the conformance.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Signed-off-by: onevcat <onevcat@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant