Hive Hive
Sign in

fix(cli): exclude module-map xcframework headers from search paths

GitHub issue · Closed

Metadata
Source
tuist/tuist #11472
Updated
Jun 24, 2026
Domains
Generated projects
Details

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

Comments
T
tuist[bot] Jun 24, 2026

🛠️ Tuist Run Report 🛠️

Tests 🧪
Scheme Status Cache hit rate Tests Skipped Ran Commit
TuistAcceptanceTests 81 % 193 0 193 a0fbbd5f0
TuistUnitTests 82 % 3483 5 3478 a0fbbd5f0
Flaky Tests ⚠️
  • TuistUnitTests: 3 flaky tests (View all)
Test case Module Suite
parseTestStatuses_returnsCorrectStatuses() TuistXCResultServiceTests XCResultServiceTests
parseTestStatuses_extractsModuleAndSuiteNames() TuistXCResultServiceTests XCResultServiceTests
parseTestWithCustomLabelXCResult() TuistXCResultServiceTests XCResultServiceTests
Builds 🔨
Scheme Status Duration Commit
TuistAcceptanceTests 1m 26s a0fbbd5f0
TuistUnitTests 2m 5s a0fbbd5f0