Skip to content

Commit 4a36f55

Browse files
authored
[Traits] Change TraitConfiguration to be an enum instead of struct (#8370)
Fixes #8355. Since the way a `TraitConfiguration` is set up can affect how traits are calculated and whether default traits are overridden or not, it's more ergonomic to change this to an `enum` that lists the various possible cases of configuration: - No trait configuration, which defaults to using default traits if applicable - A configuration with an empty set of traits that overrides the default traits - A configuration that enables all traits - A configuration that specifies an exact set of traits that are enabled This way we can avoid doing multiple checks on the properties of the `struct` and instead rely on the exact case that describes how to continue calculating enabled traits. The refactoring done as a part of this change also fixes #8356.
1 parent d57bd36 commit 4a36f55

File tree

51 files changed

+1117
-612
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1117
-612
lines changed

Sources/Build/LLBuildDescription.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import SPMBuildCore
1717
import PackageGraph
1818
import PackageModel
1919

20+
import enum PackageModel.TraitConfiguration
21+
2022
import struct TSCBasic.ByteString
2123

2224
/// Contains the description of the build that is needed during the execution.

Sources/Commands/PackageCommands/EditCommands.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extension SwiftPackageCommand {
3737
var packageIdentity: String
3838

3939
func run(_ swiftCommandState: SwiftCommandState) async throws {
40-
try await swiftCommandState.resolve(nil)
40+
try await swiftCommandState.resolve()
4141
let workspace = try swiftCommandState.getActiveWorkspace()
4242

4343
// Put the dependency in edit mode.
@@ -66,7 +66,7 @@ extension SwiftPackageCommand {
6666
var packageIdentity: String
6767

6868
func run(_ swiftCommandState: SwiftCommandState) async throws {
69-
try await swiftCommandState.resolve(nil)
69+
try await swiftCommandState.resolve()
7070
let workspace = try swiftCommandState.getActiveWorkspace()
7171

7272
try await workspace.unedit(

Sources/Commands/PackageCommands/Resolve.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import CoreCommands
1616
import TSCUtility
1717
import Workspace
1818

19-
import struct PackageGraph.TraitConfiguration
19+
import enum PackageModel.TraitConfiguration
2020

2121
extension SwiftPackageCommand {
2222
struct ResolveOptions: ParsableArguments {

Sources/CoreCommands/BuildSystemSupport.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import class Basics.ObservabilityScope
2222
import struct PackageGraph.ModulesGraph
2323
import struct PackageLoading.FileRuleDescription
2424
import protocol TSCBasic.OutputByteStream
25+
import enum PackageModel.TraitConfiguration
2526

2627
private struct NativeBuildSystemFactory: BuildSystemFactory {
2728
let swiftCommandState: SwiftCommandState

Sources/CoreCommands/Options.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import struct PackageModel.PackageIdentity
2727
import enum PackageModel.Sanitizer
2828
@_spi(SwiftPMInternal) import struct PackageModel.SwiftSDK
2929

30-
import struct PackageGraph.TraitConfiguration
30+
import enum PackageModel.TraitConfiguration
3131

3232
import struct SPMBuildCore.BuildParameters
3333
import struct SPMBuildCore.BuildSystemProvider

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,14 @@ public final class SwiftCommandState {
214214
}
215215

216216
/// Get the current workspace root object.
217-
public func getWorkspaceRoot(traitConfiguration: TraitConfiguration? = nil) throws -> PackageGraphRootInput {
217+
public func getWorkspaceRoot(traitConfiguration: TraitConfiguration = .default) throws -> PackageGraphRootInput {
218218
let packages: [AbsolutePath]
219219

220220
if let workspace = options.locations.multirootPackageDataFile {
221221
packages = try self.workspaceLoaderProvider(self.fileSystem, self.observabilityScope)
222222
.load(workspace: workspace)
223223
} else {
224-
packages = [try getPackageRoot()]
224+
packages = try [self.getPackageRoot()]
225225
}
226226

227227
return PackageGraphRootInput(packages: packages, traitConfiguration: traitConfiguration)
@@ -457,10 +457,7 @@ public final class SwiftCommandState {
457457
}
458458

459459
/// Returns the currently active workspace.
460-
public func getActiveWorkspace(
461-
emitDeprecatedConfigurationWarning: Bool = false,
462-
traitConfiguration: TraitConfiguration? = nil
463-
) throws -> Workspace {
460+
public func getActiveWorkspace(emitDeprecatedConfigurationWarning: Bool = false, traitConfiguration: TraitConfiguration = .default) throws -> Workspace {
464461
if let workspace = _workspace {
465462
return workspace
466463
}
@@ -525,9 +522,7 @@ public final class SwiftCommandState {
525522
return workspace
526523
}
527524

528-
public func getRootPackageInformation(traitConfiguration: TraitConfiguration? = nil) async throws
529-
-> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]])
530-
{
525+
public func getRootPackageInformation(traitConfiguration: TraitConfiguration = .default) async throws -> (dependencies: [PackageIdentity: [PackageIdentity]], targets: [PackageIdentity: [String]]) {
531526
let workspace = try self.getActiveWorkspace(traitConfiguration: traitConfiguration)
532527
let root = try self.getWorkspaceRoot(traitConfiguration: traitConfiguration)
533528
let rootManifests = try await workspace.loadRootManifests(
@@ -652,7 +647,7 @@ public final class SwiftCommandState {
652647
}
653648

654649
/// Resolve the dependencies.
655-
public func resolve(_ traitConfiguration: TraitConfiguration?) async throws {
650+
public func resolve(_ traitConfiguration: TraitConfiguration = .default) async throws {
656651
let workspace = try getActiveWorkspace(traitConfiguration: traitConfiguration)
657652
let root = try getWorkspaceRoot(traitConfiguration: traitConfiguration)
658653

@@ -682,7 +677,7 @@ public final class SwiftCommandState {
682677
) async throws -> ModulesGraph {
683678
try await self.loadPackageGraph(
684679
explicitProduct: explicitProduct,
685-
traitConfiguration: nil,
680+
traitConfiguration: .default,
686681
testEntryPointPath: testEntryPointPath
687682
)
688683
}
@@ -695,7 +690,7 @@ public final class SwiftCommandState {
695690
@discardableResult
696691
package func loadPackageGraph(
697692
explicitProduct: String? = nil,
698-
traitConfiguration: TraitConfiguration? = nil,
693+
traitConfiguration: TraitConfiguration = .default,
699694
testEntryPointPath: AbsolutePath? = nil
700695
) async throws -> ModulesGraph {
701696
do {
@@ -750,7 +745,7 @@ public final class SwiftCommandState {
750745
try self._manifestLoader.get()
751746
}
752747

753-
public func canUseCachedBuildManifest(_ traitConfiguration: TraitConfiguration? = nil) async throws -> Bool {
748+
public func canUseCachedBuildManifest(_ traitConfiguration: TraitConfiguration = .default) async throws -> Bool {
754749
if !self.options.caching.cacheBuildManifest {
755750
return false
756751
}

Sources/PackageGraph/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ add_library(PackageGraph
1919
PackageModel+Extensions.swift
2020
PackageRequirement.swift
2121
ResolvedPackagesStore.swift
22-
TraitConfiguration.swift
2322
Resolution/PubGrub/Assignment.swift
2423
Resolution/PubGrub/ContainerProvider.swift
2524
Resolution/PubGrub/DiagnosticReportBuilder.swift

Sources/PackageGraph/ModulesGraph+Loading.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ private func calculateEnabledTraits(
10351035
}
10361036
}
10371037

1038-
if let parentPackage, !(explictlyEnabledTraits == nil || areDefaultsEnabled) && manifest.traits.isEmpty {
1038+
if let parentPackage, !(explictlyEnabledTraits == nil || areDefaultsEnabled) && !manifest.supportsTraits {
10391039
// We throw an error when default traits are disabled for a package without any traits
10401040
// This allows packages to initially move new API behind traits once.
10411041
throw ModuleError.disablingDefaultTraitsOnEmptyTraits(

Sources/PackageGraph/ModulesGraph.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ public func loadModulesGraph(
458458
useXCBuildFileRules: Bool = false,
459459
customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none,
460460
observabilityScope: ObservabilityScope,
461-
traitConfiguration: TraitConfiguration? = nil
461+
traitConfiguration: TraitConfiguration = .default
462462
) throws -> ModulesGraph {
463463
let rootManifests = manifests.filter(\.packageKind.isRoot).spm_createDictionary { ($0.path, $0) }
464464
let externalManifests = try manifests.filter { !$0.packageKind.isRoot }
@@ -471,7 +471,7 @@ public func loadModulesGraph(
471471

472472
let packages = Array(rootManifests.keys)
473473
let input = PackageGraphRootInput(packages: packages, traitConfiguration: traitConfiguration)
474-
let graphRoot = PackageGraphRoot(
474+
let graphRoot = try PackageGraphRoot(
475475
input: input,
476476
manifests: rootManifests,
477477
explicitProduct: explicitProduct,

Sources/PackageGraph/PackageContainer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public protocol PackageContainer {
102102
/// Fetch the enabled traits of a package container.
103103
///
104104
/// NOTE: This method should only be called on root packages.
105-
func getEnabledTraits(traitConfiguration: TraitConfiguration?, version: Version?) async throws -> Set<String>
105+
func getEnabledTraits(traitConfiguration: TraitConfiguration, version: Version?) async throws -> Set<String>
106106
}
107107

108108
extension PackageContainer {
@@ -118,7 +118,7 @@ extension PackageContainer {
118118
return true
119119
}
120120

121-
public func getEnabledTraits(traitConfiguration: TraitConfiguration?, version: Version? = nil) async throws -> Set<String> {
121+
public func getEnabledTraits(traitConfiguration: TraitConfiguration, version: Version? = nil) async throws -> Set<String> {
122122
return []
123123
}
124124
}

Sources/PackageGraph/PackageGraphRoot.swift

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ public struct PackageGraphRootInput {
2424
public let dependencies: [PackageDependency]
2525

2626
/// The trait configuration for the root packages.
27-
public let traitConfiguration: TraitConfiguration?
27+
public let traitConfiguration: TraitConfiguration
2828

2929
/// Create a package graph root.
3030
public init(
3131
packages: [AbsolutePath],
3232
dependencies: [PackageDependency] = [],
33-
traitConfiguration: TraitConfiguration? = nil
33+
traitConfiguration: TraitConfiguration = .default
3434
) {
3535
self.packages = packages
3636
self.dependencies = dependencies
@@ -49,6 +49,7 @@ public struct PackageGraphRoot {
4949
return self.packages.compactMapValues { $0.manifest }
5050
}
5151

52+
/// The root manifest(s)'s enabled traits (and their transitively enabled traits).
5253
public var enabledTraits: [PackageIdentity: Set<String>]
5354

5455
/// The root package references.
@@ -94,7 +95,7 @@ public struct PackageGraphRoot {
9495
explicitProduct: String? = nil,
9596
dependencyMapper: DependencyMapper? = nil,
9697
observabilityScope: ObservabilityScope
97-
) {
98+
) throws {
9899
self.packages = input.packages.reduce(into: .init(), { partial, inputPath in
99100
if let manifest = manifests[inputPath] {
100101
let packagePath = manifest.path.parentDirectory
@@ -103,20 +104,27 @@ public struct PackageGraphRoot {
103104
}
104105
})
105106

106-
do {
107-
// Calculate the enabled traits for root.
108-
self.enabledTraits = try packages.reduce(into: [PackageIdentity: Set<String>]()) { traitsMap, package in
109-
let manifest = package.value.manifest
110-
let traitConfiguration = input.traitConfiguration
111-
112-
let enabledTraits = try manifest.enabledTraits(using: traitConfiguration?.enabledTraits, enableAllTraits: traitConfiguration?.enableAllTraits ?? false)
113-
114-
traitsMap[package.key] = enabledTraits
107+
// Calculate the enabled traits for root.
108+
var enableTraitsMap: [PackageIdentity: Set<String>] = [:]
109+
enableTraitsMap = try packages.reduce(into: [PackageIdentity: Set<String>]()) { traitsMap, package in
110+
let manifest = package.value.manifest
111+
let traitConfiguration = input.traitConfiguration
112+
113+
// Should only ever have to use trait configuration here for roots.
114+
let enabledTraits = try manifest.enabledTraits(using: traitConfiguration)
115+
traitsMap[package.key] = enabledTraits
116+
117+
// Calculate the enabled traits for each dependency of this root:
118+
manifest.dependencies.forEach { dependency in
119+
if let traits = dependency.traits {
120+
let traitNames = traits.map(\.name)
121+
traitsMap[dependency.identity, default: []].formUnion(Set(traitNames))
122+
}
115123
}
116-
} catch {
117-
self.enabledTraits = [:]
118124
}
119125

126+
self.enabledTraits = enableTraitsMap
127+
120128
// FIXME: Deprecate special casing once the manifest supports declaring used executable products.
121129
// Special casing explicit products like this is necessary to pass the test suite and satisfy backwards compatibility.
122130
// However, changing the dependencies based on the command line arguments may force `Package.resolved` to temporarily change,
@@ -129,18 +137,26 @@ public struct PackageGraphRoot {
129137
// Check that the dependency is used in at least one of the manifests.
130138
// If not, then we can omit this dependency if pruning unused dependencies
131139
// is enabled.
132-
return manifests.values.reduce(false) {
133-
guard $1.pruneDependencies else { return $0 || true }
134-
if let isUsed = try? $1.isPackageDependencyUsed(dep, enabledTraits: input.traitConfiguration?.enabledTraits, enableAllTraits: input.traitConfiguration?.enableAllTraits ?? false) {
135-
return $0 || isUsed
140+
return manifests.values.reduce(false) { result, manifest in
141+
guard manifest.pruneDependencies else { return true }
142+
let enabledTraits: Set<String>? = enableTraitsMap[manifest.packageIdentity]
143+
if let isUsed = try? manifest.isPackageDependencyUsed(dep, enabledTraits: enabledTraits) {
144+
return result || isUsed
136145
}
146+
137147
return true
138148
}
139149
})
140150

141151
if let explicitProduct {
142152
// FIXME: `dependenciesRequired` modifies manifests and prevents conversion of `Manifest` to a value type
143-
let deps = try? manifests.values.lazy.map({ try $0.dependenciesRequired(for: .everything, input.traitConfiguration?.enabledTraits, enableAllTraits: input.traitConfiguration?.enableAllTraits ?? false) }).flatMap({ $0 })
153+
let deps = try? manifests.values.lazy
154+
.map({ manifest -> [PackageDependency] in
155+
let enabledTraits: Set<String>? = enableTraitsMap[manifest.packageIdentity]
156+
return try manifest.dependenciesRequired(for: .everything, enabledTraits)
157+
})
158+
.flatMap({ $0 })
159+
144160
for dependency in deps ?? [] {
145161
adjustedDependencies.append(dependency.filtered(by: .specific([explicitProduct])))
146162
}
@@ -224,3 +240,4 @@ extension PackageDependency.Registry.Requirement {
224240
}
225241
}
226242
}
243+

Sources/PackageGraph/Resolution/DependencyResolutionNode.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public enum DependencyResolutionNode {
5858
/// It is a warning condition, and builds do not actually need these dependencies.
5959
/// However, forcing the graph to resolve and fetch them anyway allows the diagnostics passes access
6060
/// to the information needed in order to provide actionable suggestions to help the user stitch up the dependency declarations properly.
61-
case root(package: PackageReference, traitConfiguration: TraitConfiguration? = nil)
61+
case root(package: PackageReference, traitConfiguration: TraitConfiguration = .default)
6262

6363
/// The package.
6464
public var package: PackageReference {
@@ -94,14 +94,25 @@ public enum DependencyResolutionNode {
9494
public var traits: Set<String>? {
9595
switch self {
9696
case .root(_, let config):
97-
return config?.enabledTraits
97+
return config.enabledTraits
9898
case .product(_, _, let enabledTraits):
9999
return enabledTraits
100100
default:
101101
return nil
102102
}
103103
}
104104

105+
public var traitConfiguration: TraitConfiguration {
106+
switch self {
107+
case .root(_, let config):
108+
return config
109+
case .product(_, _, let enabledTraits):
110+
return .init(enabledTraits: enabledTraits)
111+
case .empty:
112+
return .default
113+
}
114+
}
115+
105116
/// Returns the dependency that a product has on its own package, if relevant.
106117
///
107118
/// This is the constraint that requires all products from a package resolve to the same version.

Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public struct PubGrubDependencyResolver {
165165
}
166166

167167
/// Execute the resolution algorithm to find a valid assignment of versions.
168-
public func solve(constraints: [Constraint], traitConfiguration: TraitConfiguration? = nil) async -> Result<[DependencyResolverBinding], Error> {
168+
public func solve(constraints: [Constraint], traitConfiguration: TraitConfiguration = .default) async -> Result<[DependencyResolverBinding], Error> {
169169
// the graph resolution root
170170
let root: DependencyResolutionNode
171171
if constraints.count == 1, let constraint = constraints.first, constraint.package.kind.isRoot {

Sources/PackageGraph/Resolution/PubGrub/PubGrubPackageContainer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,7 @@ final class PubGrubPackageContainer {
154154
at version: Version,
155155
node: DependencyResolutionNode,
156156
overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)],
157-
root: DependencyResolutionNode,
158-
traitConfiguration: TraitConfiguration? = nil
157+
root: DependencyResolutionNode
159158
) async throws -> [Incompatibility] {
160159
// FIXME: It would be nice to compute bounds for this as well.
161160
if await !self.underlying.isToolsVersionCompatible(at: version) {
@@ -168,10 +167,11 @@ final class PubGrubPackageContainer {
168167
)]
169168
}
170169

170+
let enabledTraits = node.package.kind.isRoot ? try await self.underlying.getEnabledTraits(traitConfiguration: node.traitConfiguration) : node.traits
171171
var unprocessedDependencies = try await self.underlying.getDependencies(
172172
at: version,
173173
productFilter: node.productFilter,
174-
node.traits
174+
enabledTraits
175175
)
176176
if let sharedVersion = node.versionLock(version: version) {
177177
unprocessedDependencies.append(sharedVersion)

Sources/PackageGraph/TraitConfiguration.swift

Lines changed: 0 additions & 28 deletions
This file was deleted.

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
440440

441441
let manifest = Manifest(
442442
displayName: parsedManifest.name,
443+
packageIdentity: packageIdentity,
443444
path: manifestPath,
444445
packageKind: packageKind,
445446
packageLocation: packageLocation,

0 commit comments

Comments
 (0)