Draft. Fixes the Swift-side failures of the framework-search-path response file under Xcode 26. See the “Scope and limitation” section: this is necessary but not sufficient when the .resp is missing at build time.
What changed
LinkGenerator.setupFrameworkSearchPath no longer injects the @<Target>.resp reference into OTHER_SWIFT_FLAGS. Instead it passes the precompiled framework search paths to the Swift compiler as inline native -F flags. OTHER_CFLAGS (clang) and OTHER_LDFLAGS (ld) are unchanged — they keep the @resp.
Why
The consolidation introduced in #10228 (shipped 4.195.7) writes precompiled framework search paths to Derived/FrameworkSearchPaths/<Target>.resp when a target has ≥20 of them, and references that file via @file in OTHER_CFLAGS, OTHER_SWIFT_FLAGS, and OTHER_LDFLAGS. Both forms we have tried for OTHER_SWIFT_FLAGS break under Xcode 26:
-
-Xcc @file (original, ≤ 4.195.12): -Xcc forwards the token verbatim to Swift’s ClangImporter, whose createInvocation does not expand @file under Xcode 26. clang then sees the .resp path as a second Objective-C input, emits two -cc1 jobs, and fails with unable to handle compilation, expected exactly one compiler job / clang importer creation failed.
-
bare @file (current main, 4.195.13, from #11023): Xcode’s integrated builtin-SwiftDriver does not expand the token when the .resp is absent at build time. Because the literal argument is @/abs/path (starts with @, not /), the Swift input-path resolver treats it as relative and joins it to -working-directory (= SRCROOT), producing the doubled path <srcroot>/@<srcroot>/Derived/FrameworkSearchPaths/<Target>.resp and failing with Unexpected input file.
Per #10228’s own analysis, only C/ObjC compilation hits ARG_MAX; “Swift compilation handles this fine (uses .resp response files)”. So Swift never needed the @file indirection at all. Passing the paths as inline native -F flags:
- routes them through swift-driver as ordinary search-path flags (the same thing
FRAMEWORK_SEARCH_PATHS produces below the consolidation threshold), so there is no @file token for either the ClangImporter or the integrated driver to mishandle, and
- does not depend on the
.resp existing at Swift-compile time.
clang and ld both expand @file correctly and genuinely need it to stay under ARG_MAX, so they are left on the @resp.
Validation (Xcode 26.4.1, integrated builtin-SwiftDriver)
Reproduction project: a macOS app with 22 precompiled .framework dependencies plus a SwiftPM external dependency (crosses the consolidation threshold and produces the same OTHER_SWIFT_FLAGS shape as the affected projects).
| Scenario |
main (bare @resp) |
This PR (inline -F) |
.resp present |
Swift OK |
Swift OK, link OK |
.resp absent |
Swift fails: Unexpected input file: <srcroot>/@<srcroot>/…/App.resp |
Swift OK (compiles via inline -F); link fails on the absent OTHER_LDFLAGS @resp |
- The generated
OTHER_SWIFT_FLAGS now contains -F <path> pairs and no @…resp token.
- Unit test
test_setupFrameworkSearchPath_consolidatesIntoResponseFile_whenManyPrecompiledPaths updated to assert inline -F (no .resp, no -Xcc) in OTHER_SWIFT_FLAGS; passes.
xcodebuild build -scheme tuist succeeds; lint clean.
Scope and limitation
This fixes the Swift side: the Xcode-26 “expected exactly one compiler job” regression, and makes the Swift compile independent of the .resp file.
It does not by itself fix builds where the .resp is missing at build time. In that case clang/ld still reference @resp, so the failure moves from the Swift compile to the link step (clang: error: no such file or directory: '@…/<Target>.resp'). The .resp lives in the gitignored Derived/ directory; the reported failures are on CI where the project is built without Derived/ being regenerated alongside it. Ensuring the project is generated (so Derived/FrameworkSearchPaths/*.resp exists) at build time is required for those setups, and a follow-up may revisit whether the hand-rolled response file is still needed on Xcode 26 at all (Xcode 26 wraps clang compiles in its own …-common-args.resp response files).