What Changed
- De-duplicate identical
.xcframework dependencies while expanding .external SwiftPM products into graph target dependencies.
- Restore the duplicate-dependency linter’s normal behavior so authored duplicate
.xcframework entries still warn.
- Add mapper coverage for both sides of the boundary:
- duplicate xcframework artifacts reached through multiple external products collapse to one graph dependency
- duplicate hand-authored xcframework dependencies are preserved for linting
- Add linter coverage proving duplicate target and xcframework graph dependencies still warn when they reach
TargetLinter.
Fixes #11495.
Why
tuist generate could warn that a target had a duplicate xcframework dependency when the same binary SwiftPM target was reached through multiple external SwiftPM products. The generated project already de-duplicated that artifact, so the warning described an intermediate expansion artifact rather than something users could fix in their consuming manifest.
The initial narrow fix was to skip duplicate .xcframework warnings in the linter. This PR now takes the longer-term route instead: normalize the duplicated external SwiftPM expansion before linting and generation consume the graph.
Root Cause
ProjectDescription.TargetDependency.external expands to one or more XcodeGraph.TargetDependency values in Target+ManifestMapper. When two external SwiftPM products converged on the same binary target, that expansion appended two byte-identical .xcframework dependencies to the target.
Generation later collapsed those identical precompiled references when writing project file elements, but linting ran against the pre-normalized dependency list and therefore warned before generation’s de-duplication took effect.
Approach
The mapper now tracks .xcframework dependencies produced by .external expansion and only appends the first identical one. This keeps the graph aligned with the generated .pbxproj for SwiftPM convergence while preserving authored manifest duplicates:
- two external products resolving to the same
.xcframework -> one graph dependency, no false-positive warning
- two explicit
.xcframework entries in a manifest -> two graph dependencies, duplicate warning preserved
This avoids adding origin-blind exceptions to TargetLinter.
Validation
tuist generate tuist TuistGenerator TuistGeneratorTests TuistLoader TuistLoaderTests ProjectDescription --no-open
xcsiftbuild test -workspace Tuist.xcworkspace -scheme Tuist-Workspace -only-testing TuistLoaderTests/TargetManifestMapperTests CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" COMPILATION_CACHE_ENABLE_CACHING=NO
xcsiftbuild test -workspace Tuist.xcworkspace -scheme Tuist-Workspace -only-testing TuistGeneratorTests/TargetLinterTests CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" COMPILATION_CACHE_ENABLE_CACHING=NO
Results:
TargetManifestMapperTests: 5 passed, 0 failed
TargetLinterTests: 28 passed, 0 failed