Summary
Fixes a Redefinition of module 'X' regression introduced in 4.201.0-rc.1 when a target depends on a precompiled xcframework that ships a module.modulemap (reported by a user building against 4.201.0-rc.1 with no changes on their side).
Important
This is a regression in the current RC (4.201.0-rc.1), so it needs to be backported to the current 4.201.0 RC rather than only shipping on the next minor.
Root cause
Xcode’s ProcessXCFramework already extracts the SDK-matching slice’s headers — including any module.modulemap — into $(BUILT_PRODUCTS_DIR)/include, which is on the header search path.
Since #11290, GraphTraverser.librariesPublicHeadersFolders additionally adds every xcframework slice’s Headers to HEADER_SEARCH_PATHS. For an xcframework that ships a module map, this puts a second copy of the same module on the search path, so the compiler fails with:
.../My.xcframework/<slice>/Headers/module.modulemap:1:8: error: redefinition of module 'X'
.../Build/Products/<config>/include/module.modulemap:1:8: note: previously defined here
Because the headers are added for every slice, a single-platform build (e.g. macOS) also ends up with the iOS device/simulator slices’ module maps on the search path.
Fix
Skip adding an xcframework’s headers when it ships a module map (xcframework.moduleMaps.isEmpty) — the ProcessXCFramework include/ copy already exposes them. Module-map-less xcframeworks (e.g. the cached Swift/library products from #11290) keep their headers, so that feature is unaffected.
Verification
- Reproduced with
4.201.0-canary.13 (= the rc.1 regression): a target depending on a 2-slice Greeter.xcframework (each slice shipping Headers/module.modulemap) → error: redefinition of module 'Greeter'.
- Unit tests (
GraphTraverserTests): new test asserts headers are excluded for module-map xcframeworks; the existing #11290 test still asserts module-map-less xcframeworks keep their headers. ✅ pass.
- End-to-end: rebuilt the CLI from this branch, regenerated the reproduction with it (0 slice headers in
HEADER_SEARCH_PATHS, down from 2), and the project now builds successfully.
🤖 Generated with Claude Code