When a framework.static target declares an SPM package dependency without explicit link: false, XcodeGen adds the package to both packageProductDependencies and the Frameworks build phase. This causes duplicate symbol errors at link time when multiple static framework targets depend on the same package, because Xcode bakes the package's object code into each static framework independently.
The existing logic in PBXProjGenerator.swift correctly handles library.static (staticLibrary) targets — it excludes them from the Frameworks build phase by default. But framework.static (staticFramework) targets are not covered by the same check, so they get the default linking behavior intended for dynamic frameworks and applications.
To reproduce
- Create two or more targets with
type: framework.static
- Add the same SPM package dependency to each (without
link: false)
- Create an app target that depends on both static frameworks, with
transitivelyLinkDependencies: true
- Generate and build — the linker reports duplicate symbols from the shared package
Workaround
Setting link: false on each package dependency in static framework targets avoids the issue, but this must be maintained manually for every package on every static framework target.
Expected behavior
framework.static targets should be treated like library.static targets for package linking: packages should not be added to the Frameworks build phase, and should not be added to packageProductDependencies (since Xcode embeds package object code into the static framework from that list). Instead, packages should be added as build-order-only PBXTargetDependency entries, which is sufficient for module resolution within the project.
When a
framework.statictarget declares an SPM package dependency without explicitlink: false, XcodeGen adds the package to bothpackageProductDependenciesand the Frameworks build phase. This causes duplicate symbol errors at link time when multiple static framework targets depend on the same package, because Xcode bakes the package's object code into each static framework independently.The existing logic in
PBXProjGenerator.swiftcorrectly handleslibrary.static(staticLibrary) targets — it excludes them from the Frameworks build phase by default. Butframework.static(staticFramework) targets are not covered by the same check, so they get the default linking behavior intended for dynamic frameworks and applications.To reproduce
type: framework.staticlink: false)transitivelyLinkDependencies: trueWorkaround
Setting
link: falseon each package dependency in static framework targets avoids the issue, but this must be maintained manually for every package on every static framework target.Expected behavior
framework.statictargets should be treated likelibrary.statictargets for package linking: packages should not be added to the Frameworks build phase, and should not be added topackageProductDependencies(since Xcode embeds package object code into the static framework from that list). Instead, packages should be added as build-order-onlyPBXTargetDependencyentries, which is sufficient for module resolution within the project.