Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/PackageGraph/PackageModel+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ extension PackageContainerConstraint {
internal func nodes() -> [DependencyResolutionNode] {
switch products {
case .everything:
return [.root(package: self.package)]
return [.root(package: self.package, enabledTraits: self.enabledTraits)]
case .specific:
switch products {
case .everything:
Expand All @@ -70,7 +70,7 @@ extension PackageContainerConstraint {
if set.isEmpty { // Pointing at the package without a particular product.
return [.empty(package: self.package)]
} else {
return set.sorted().map { .product($0, package: self.package) }
return set.sorted().map { .product($0, package: self.package, enabledTraits: self.enabledTraits) }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ public struct PubGrubDependencyResolver {
}

for dependency in try await container.underlying
.getUnversionedDependencies(productFilter: node.productFilter, constraint.enabledTraits)
.getUnversionedDependencies(productFilter: node.productFilter, node.enabledTraits)
{
if let versionedBasedConstraints = VersionBasedConstraint.constraints(dependency) {
for constraint in versionedBasedConstraints {
Expand Down Expand Up @@ -431,7 +431,7 @@ public struct PubGrubDependencyResolver {
var unprocessedDependencies = try await container.underlying.getDependencies(
at: revisionForDependencies,
productFilter: constraint.products,
constraint.enabledTraits
node.enabledTraits
)
if let sharedRevision = node.revisionLock(revision: revision) {
unprocessedDependencies.append(sharedRevision)
Expand Down
3 changes: 2 additions & 1 deletion Sources/Workspace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ add_library(Workspace
Workspace+ResolvedPackages.swift
Workspace+Signing.swift
Workspace+SourceControl.swift
Workspace+State.swift)
Workspace+State.swift
Workspace+Traits.swift)
target_link_libraries(Workspace PUBLIC
TSCBasic
TSCUtility
Expand Down
11 changes: 10 additions & 1 deletion Sources/Workspace/Workspace+Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,15 @@ extension Workspace {
let rootManifestsMinimumToolsVersion = rootManifests.values.map(\.toolsVersion).min() ?? ToolsVersion.current
let resolvedFileOriginHash = try self.computeResolvedFileOriginHash(root: root)

// Precompute enabled traits, beginning with
// root manifests, if we haven't already done so.
if self.enabledTraitsMap.dictionaryLiteral.isEmpty {
let rootManifestMap = rootManifests.values.reduce(into: [PackageIdentity: Manifest]()) { manifestMap, manifest in
manifestMap[manifest.packageIdentity] = manifest
}
self.enabledTraitsMap = .init(try precomputeTraits(rootManifests.values.map({ $0 }), rootManifestMap))
}

// Load the current manifests.
let graphRoot = try PackageGraphRoot(
input: root,
Expand All @@ -525,7 +534,6 @@ extension Workspace {
enabledTraitsMap: self.enabledTraitsMap
)

// Of the enabled dependencies of targets, only consider these for dependency resolution
let currentManifests = try await self.loadDependencyManifests(
root: graphRoot,
observabilityScope: observabilityScope
Expand Down Expand Up @@ -598,6 +606,7 @@ extension Workspace {
computedConstraints += currentManifests.editedPackagesConstraints
computedConstraints += try graphRoot.constraints(self.enabledTraitsMap) + constraints


// Perform dependency resolution.
let resolver = try self.createResolver(resolvedPackages: resolvedPackagesStore.resolvedPackages, observabilityScope: observabilityScope)
self.activeResolver = resolver
Expand Down
49 changes: 1 addition & 48 deletions Sources/Workspace/Workspace+Manifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -738,54 +738,6 @@ extension Workspace {
)
}

public func precomputeTraits(
_ topLevelManifests: [Manifest],
_ manifestMap: [PackageIdentity: Manifest]
) throws -> [PackageIdentity: Set<String>] {
var visited: Set<PackageIdentity> = []

func dependencies(of parent: Manifest, _ productFilter: ProductFilter = .everything) throws {
let parentTraits = self.enabledTraitsMap[parent.packageIdentity]
let requiredDependencies = try parent.dependenciesRequired(for: productFilter, parentTraits)
let guardedDependencies = parent.dependenciesTraitGuarded(withEnabledTraits: parentTraits)

_ = try (requiredDependencies + guardedDependencies).compactMap({ dependency in
return try manifestMap[dependency.identity].flatMap({ manifest in

let explicitlyEnabledTraits = dependency.traits?.filter {
guard let condition = $0.condition else { return true }
return condition.isSatisfied(by: parentTraits)
}.map(\.name)

if let enabledTraitsSet = explicitlyEnabledTraits.flatMap({ Set($0) }) {
let calculatedTraits = try manifest.enabledTraits(
using: enabledTraitsSet,
.init(parent)
)
self.enabledTraitsMap[dependency.identity] = calculatedTraits
}

let result = visited.insert(dependency.identity)
if result.inserted {
try dependencies(of: manifest, dependency.productFilter)
}

return manifest
})
})
}

for manifest in topLevelManifests {
// Track already-visited manifests to avoid cycles
let result = visited.insert(manifest.packageIdentity)
if result.inserted {
try dependencies(of: manifest)
}
}

return self.enabledTraitsMap.dictionaryLiteral
}

/// Loads the given manifests, if it is present in the managed dependencies.
///

Expand Down Expand Up @@ -962,6 +914,7 @@ extension Workspace {
diagnostics: manifestLoadingDiagnostics,
duration: duration
)

return manifest
}

Expand Down
66 changes: 66 additions & 0 deletions Sources/Workspace/Workspace+Traits.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import class PackageModel.Manifest
import struct PackageModel.PackageIdentity
import enum PackageModel.ProductFilter

extension Workspace {
public func precomputeTraits(
_ topLevelManifests: [Manifest],
_ manifestMap: [PackageIdentity: Manifest]
) throws -> [PackageIdentity: Set<String>] {
var visited: Set<PackageIdentity> = []

func dependencies(of parent: Manifest, _ productFilter: ProductFilter = .everything) throws {
let parentTraits = self.enabledTraitsMap[parent.packageIdentity]
let requiredDependencies = try parent.dependenciesRequired(for: productFilter, parentTraits)
let guardedDependencies = parent.dependenciesTraitGuarded(withEnabledTraits: parentTraits)

_ = try (requiredDependencies + guardedDependencies).compactMap({ dependency in
return try manifestMap[dependency.identity].flatMap({ manifest in

let explicitlyEnabledTraits = dependency.traits?.filter {
guard let condition = $0.condition else { return true }
return condition.isSatisfied(by: parentTraits)
}.map(\.name)

if let enabledTraitsSet = explicitlyEnabledTraits.flatMap({ Set($0) }) {
let calculatedTraits = try manifest.enabledTraits(
using: enabledTraitsSet,
.init(parent)
)
self.enabledTraitsMap[dependency.identity] = calculatedTraits
}

let result = visited.insert(dependency.identity)
if result.inserted {
try dependencies(of: manifest, dependency.productFilter)
}

return manifest
})
})
}

for manifest in topLevelManifests {
// Track already-visited manifests to avoid cycles
let result = visited.insert(manifest.packageIdentity)
if result.inserted {
try dependencies(of: manifest)
}
}

return self.enabledTraitsMap.dictionaryLiteral
}

}