Skip to content

fix(sdk): patch Clang resource headers for Xcode 26+ compatibility on Linux#215

Open
reklis wants to merge 1 commit into
xtool-org:mainfrom
reklis:fix/host-clang-resource-headers
Open

fix(sdk): patch Clang resource headers for Xcode 26+ compatibility on Linux#215
reklis wants to merge 1 commit into
xtool-org:mainfrom
reklis:fix/host-clang-resource-headers

Conversation

@reklis
Copy link
Copy Markdown

@reklis reklis commented May 1, 2026

Summary

  • Replace Apple's bundled Clang resource headers (arm_neon.h, x86 intrinsics, etc.) with the host Clang's matching headers at the end of xtool sdk install.
  • Fixes builds on Linux against Xcode 26.x SDKs, which currently fail with error: incompatible constant for this __builtin_neon function the moment any source pulls in <simd/base.h> (transitively via simd, CoreMedia, etc.).

Background

Apple's Clang resource headers are auto-generated to match Apple's Clang fork. On Linux, the host Clang (e.g. from swiftly) is the one that actually compiles C/Objective-C sources during cross-build. Apple's intrinsic headers expand to __builtin_neon_* calls whose constant-argument signatures don't line up with upstream Clang's, so every NEON intrinsic in the header fails the constant-validity check.

This is a structural mismatch — even pinning to a stable Xcode (e.g. Xcode 26.3) doesn't help, because Apple's Clang is patched ahead of upstream LLVM. With Xcode 26.2 SDK + swiftly Swift 6.3.0 (Clang 21), every Swift source in the build produces ~2 MB of identical arm_neon.h errors before SwiftPM bails with too many errors emitted.

Related: llvm/llvm-project#158312 documents the same class of mismatch (NDK Clang vs upstream Clang). May also be relevant to xtool-org/xtool#214 (different symptom — Apple-only LLVM flag -aarch64-use-tbi — but same family of "Apple toolchain assets ↔ upstream toolchain").

Approach

After installDeveloper finishes, locate the host Clang's resource directory via clang -print-resource-dir and replace:

  • Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/clang/include/
  • Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/<version>/include/ (every versioned subdir)

with the host Clang's include/ contents.

Why this is safe

  • These dirs are only Clang-generated intrinsics + builtin headers. No Apple framework code lives there (system headers are elsewhere in the SDK and are untouched).
  • Apple frameworks (UIKit, Foundation, Accelerate, Metal, etc.) are precompiled binaries — .tbd/.dylib — that link via stable ABI. They were already compiled by Apple's Clang and never go through these headers again at app build time.
  • NEON / x86 intrinsic names lower to the same hardware ISA regardless of which Clang emits them. There is no runtime divergence.
  • Host Clang 21's arm_neon.h is a superset of Apple's (~74k lines vs ~70k), so substituting up rarely loses intrinsics.

If a future Xcode SDK uses an Apple-only intrinsic that upstream Clang doesn't have, this surfaces at build time as a normal "unknown intrinsic" error, not as a silent runtime failure.

Verified

Manual repro on Linux (Ubuntu 24.04, swiftly Swift 6.3.0 → Clang 21) with Xcode 26.3 → iPhoneOS 26.2 SDK:

  1. Without patch: swift build --swift-sdk arm64-apple-ios fails immediately with NEON incompatible constant errors flooding from every source file.
  2. After patching the installed artifactbundle by hand using the same logic this PR puts into SDKBuilder: build progresses through the full Swift source compilation pass.

Test plan

  • xtool sdk install <Xcode26.x.xip> on Linux completes and prints [Patching Clang resource headers]
  • A SwiftPM project that imports a NEON-touching framework (e.g. import simd, import CoreMedia) builds for arm64-apple-ios without __builtin_neon errors
  • On macOS this is effectively a no-op: replacing Apple Clang's resource headers with the host's (same Apple Clang) shouldn't change behavior — confirm xtool sdk install still works there
  • The resulting app installs and launches on a real iOS device — NEON-using code paths execute correctly (intrinsics lower to the same hardware ISA)

Alternatives considered

  • Bundle Apple's Clang in darwin-tools-linux-llvm: would be most exact, but Apple Clang is closed-source patches on top of LLVM and shipping a Linux build would mean independently tracking Apple's NEON/intrinsic additions. Not practical for this fix.
  • -Xcc -resource-dir <host-path> in toolset.json: would work but bakes the host path into the SDK at install time, breaking if the user later changes Clang versions. Substituting at install time keeps the bundle self-contained.

🤖 Generated with Claude Code

Apple's bundled Clang resource headers (arm_neon.h, x86 intrinsics,
etc.) in Xcode's toolchain are auto-generated to match the version of
Clang that built them. On Linux the host Clang (e.g. from swiftly) does
the actual C compilation against this SDK, so Apple's intrinsics use
__builtin_neon_* signatures upstream Clang doesn't recognise — failing
with "incompatible constant for this __builtin_neon function" the moment
anything imports `simd/base.h` (CoreMedia, simd, etc.).

After installDeveloper, replace the SDK's clang resource includes —
both `swift/clang/include` and `clang/<ver>/include` — with the host
Clang's matching headers so they line up with the compiler.

Safe because Apple frameworks are precompiled binaries that don't go
through these headers, and intrinsic names lower to fixed ARM64/x86
hardware ISA regardless of which Clang emits them.
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