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

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

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