SPM Deployment Target Resolution#
Overview#
Tuist maps Swift Package Manager (SPM) package metadata to Xcode project deployment targets via PackageInfoMapper.swift. The process has two layers: computing a per-target DeploymentTargets from Swift version constraints and package-declared platforms, then propagating package-wide baseSettings (including MACOSX_DEPLOYMENT_TARGET) into every generated target's build settings dictionary.
Step 1: Computing DeploymentTargets per Target#
For each mapped SPM target, the mapper :
-
Queries the active Swift toolchain version via
SwiftVersionProvider.current.swiftVersion(). -
Derives the minimum floor using
DeploymentTargets.oldestVersions(for:), which returns Swift-version-specific minimums:Swift version iOS macOS watchOS tvOS visionOS < 5.7 9.0 10.10 2.0 9.0 1.0 < 5.9 11.0 10.13 4.0 11.0 1.0 < 6.0 12.0 10.13 4.0 12.0 1.0 < 6.2 15.0 10.13 7.0 15.0 1.0 < 6.4 15.0 11.0 8.0 15.0 1.0 β₯ 6.4 15.0 12.0 9.0 15.0 1.0 -
Resolves the final value with
DeploymentTargets.from(minDeploymentTargets:package:destinations:packageName:), taking themax()of the floor and the version declared in the package's platform list for each active destination . A package that declaresmacOS: "10.12"with Swift 6.4 toolchain will be clamped to macOS 12.0.
Step 2: Destination Assignment#
Before deployment targets are computed, the mapper determines which destinations a target builds for :
- Macro and executable targets β
Set([.mac])only . This forces macOS-only compilation since Swift macros are host-side tools run at build time. - Test targets β intersection of destinations supported by their dependencies.
- Other targets β product destinations (local packages) or all destinations (external packages).
The destinations set is then passed into DeploymentTargets.from(...), so only relevant platforms get a deployment target string.
Step 3: Propagating baseSettings Into Target Build Settings#
The Settings.from(...) method constructs each target's SettingsDictionary in priority order:
- Mapped SPM flags (from
SettingsMapper) form the initial dictionary. PackageSettings.baseSettings.baseis merged in next β this is where package-level settings likeMACOSX_DEPLOYMENT_TARGEToriginate .PRODUCT_BUNDLE_IDENTIFIERis explicitly excluded to avoid clobbering per-target sanitized identifiers .- Per-target overrides from
PackageSettings.targetSettingsare merged last with arrays unioned and scalars overriding .
Bug History: Macro Targets Missing baseSettings (Xcode 27)#
Xcode 27 beta introduced stricter deployment target validation, surfacing a long-standing gap: macro targets were not receiving PackageSettings.baseSettings.base . This caused build failures when MACOSX_DEPLOYMENT_TARGET was set at the package level.
- Issue #11447 reported that macro dependencies do not inherit deployment targets after the original fix for #11163.
- PR #11451 (community contribution) provided an initial fix merging
baseSettings.baseintobaseSettingsDictionaryfor macro targets, usinguniquingKeysWith: { _, new in new }. - PR #11574 (merged 2026-06-30) is the canonical fix:
Settings.from(...)now merges package-level base settings before target-specific settings for all mapped targets, including macros. The fix was verified with a regression test (map_macroTarget_appliesBaseSettingsBaseToMacroTarget) that assertsMACOSX_DEPLOYMENT_TARGETfrompackageSettings.baseappears in the generated macro target's settings.
Key Files#
| File | Purpose |
|---|---|
PackageInfoMapper.swift | Central mapper: computes DeploymentTargets, assigns destinations, calls Settings.from(...) |
SettingsMapper.swift | Translates SPM build flags to Xcode build settings |
PackageInfoMapperTests.swift | Regression tests including map_macroTarget_appliesBaseSettingsBaseToMacroTarget |