Skip to content

[WIP] Fix for trait-guarded deps being included in resolution #8852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion Sources/PackageDescription/PackageDependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ extension Package {
public let kind: Kind

/// The dependencies traits configuration.
@available(_PackageDescription, introduced: 999.0)
@available(_PackageDescription, introduced: 6.1)
public let traits: Set<Trait>

/// The name of the dependency.
Expand Down
4 changes: 2 additions & 2 deletions Sources/PackageGraph/GraphLoadingNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public struct GraphLoadingNode: Equatable, Hashable {
public let productFilter: ProductFilter

/// The enabled traits for this package.
package var enabledTraits: Set<String>
package var enabledTraits: Set<String>?

public init(
identity: PackageIdentity,
manifest: Manifest,
productFilter: ProductFilter,
enabledTraits: Set<String>
enabledTraits: Set<String>?
) throws {
self.identity = identity
self.manifest = manifest
Expand Down
43 changes: 16 additions & 27 deletions Sources/PackageGraph/ModulesGraph+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,15 @@ extension ModulesGraph {
})

let rootManifestNodes = try root.packages.map { identity, package in
// TODO bp
// If we have enabled traits passed then we start with those. If there are no enabled
// traits passed then the default traits will be used.
let enabledTraits = root.enabledTraits[identity]
return try GraphLoadingNode(
identity: identity,
manifest: package.manifest,
productFilter: .everything,
enabledTraits: calculateEnabledTraits(
parentPackage: nil,
identity: identity,
manifest: package.manifest,
explictlyEnabledTraits: enabledTraits
)
enabledTraits: enabledTraits
)
}
let rootDependencyNodes = try root.dependencies.lazy.filter { requiredDependencies.contains($0.packageRef) }
Expand All @@ -78,7 +74,7 @@ extension ModulesGraph {
identity: dependency.identity,
manifest: $0.manifest,
productFilter: dependency.productFilter,
enabledTraits: []
enabledTraits: root.enabledTraits[dependency.identity]
)
}
}
Expand All @@ -100,26 +96,16 @@ extension ModulesGraph {
// We are going to check the conditionally enabled traits here and enable them if
// required. This checks the current node and then enables the conditional
// dependencies of the dependency node.
let explictlyEnabledTraits = dependency.traits?.filter {
guard let conditionTraits = $0.condition?.traits else {
return true
}
return !conditionTraits.intersection(node.item.enabledTraits).isEmpty
}.map(\.name)

let calculatedTraits = try calculateEnabledTraits(
parentPackage: node.item.identity,
identity: dependency.identity,
manifest: manifest,
explictlyEnabledTraits: explictlyEnabledTraits.flatMap { Set($0) }
)

// TODO bp: shouldn't need to do any traits computation here,
// if we've successfully computed them in the PackageGraphRoot.

return try KeyedPair(
GraphLoadingNode(
identity: dependency.identity,
manifest: manifest,
productFilter: dependency.productFilter,
enabledTraits: calculatedTraits
enabledTraits: root.enabledTraits[manifest.packageIdentity]//calculatedTraits
),
key: dependency.identity
)
Expand Down Expand Up @@ -153,7 +139,7 @@ extension ModulesGraph {
allNodes[$0.key] = $0.item
} onDuplicate: { first, second in
// We are unifying the enabled traits on duplicate
allNodes[first.key]?.enabledTraits.formUnion(second.item.enabledTraits)
// TODO bp: to remove this, precompute traits elsewhere
}

// Create the packages.
Expand Down Expand Up @@ -184,7 +170,7 @@ extension ModulesGraph {
createREPLProduct: manifest.packageKind.isRoot ? createREPLProduct : false,
fileSystem: fileSystem,
observabilityScope: nodeObservabilityScope,
enabledTraits: node.enabledTraits
enabledTraits: node.enabledTraits ?? []//["default"] TODO bp
)
let package = try builder.construct()
manifestToPackage[manifest] = package
Expand Down Expand Up @@ -304,9 +290,12 @@ private func checkAllDependenciesAreUsed(
// that can be configured by enabling traits e.g. the depdency has a trait for its logging
// behaviour. This allows the root package to configure traits of transitive dependencies
// without emitting an unused dependency warning.
if !dependency.enabledTraits.isEmpty {
if dependency.manifest.supportsTraits {
continue
}
// if !dependency.enabledTraits.isEmpty {
// continue
// }

// Make sure that any diagnostics we emit below are associated with the package.
let packageDiagnosticsScope = observabilityScope.makeChildScope(
Expand Down Expand Up @@ -410,7 +399,7 @@ private func createResolvedPackages(
return ResolvedPackageBuilder(
package,
productFilter: node.productFilter,
enabledTraits: node.enabledTraits,
enabledTraits: node.enabledTraits /*?? []*/,
isAllowedToVendUnsafeProducts: isAllowedToVendUnsafeProducts,
allowedToOverride: allowedToOverride,
platformVersionProvider: platformVersionProvider
Expand Down Expand Up @@ -1449,7 +1438,7 @@ private final class ResolvedPackageBuilder: ResolvedBuilder<ResolvedPackage> {
var products: [ResolvedProductBuilder] = []

/// The enabled traits of this package.
var enabledTraits: Set<String> = []
var enabledTraits: Set<String>? //= ["default"] TODO bp

/// The dependencies of this package.
var dependencies: [ResolvedPackageBuilder] = []
Expand All @@ -1473,7 +1462,7 @@ private final class ResolvedPackageBuilder: ResolvedBuilder<ResolvedPackage> {
init(
_ package: Package,
productFilter: ProductFilter,
enabledTraits: Set<String>,
enabledTraits: Set<String>?,
isAllowedToVendUnsafeProducts: Bool,
allowedToOverride: Bool,
platformVersionProvider: PlatformVersionProvider
Expand Down
72 changes: 71 additions & 1 deletion Sources/PackageGraph/ModulesGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,62 @@ func topologicalSortIdentifiable<T: Identifiable>(
return result.reversed()
}

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

var visited: Set<PackageIdentity> = []

func dependencies(of parent: Manifest, _ productFilter: ProductFilter = .everything) throws /*-> [Manifest]*/ {
let parentTraits = enabledTraits[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)

var enabledTraitsSet = explicitlyEnabledTraits.flatMap { Set($0) }

// Check for existing traits; TODO bp see if these are the same?
if let depTraits = enabledTraits[dependency.identity] {
enabledTraitsSet?.formUnion(depTraits)
}

let calculatedTraits = try manifest.enabledTraits(
using: enabledTraitsSet,
.init(parent)
)

enabledTraits[dependency.identity] = calculatedTraits

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

return manifest
})
})
}

for manifest in topLevelManifests {
let result = visited.insert(manifest.packageIdentity)
if result.inserted {
try dependencies(of: manifest)
}
}

return enabledTraits
}

@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly)
public func loadModulesGraph(
identityResolver: IdentityResolver = DefaultIdentityResolver(),
Expand All @@ -471,13 +527,27 @@ public func loadModulesGraph(

let packages = Array(rootManifests.keys)
let input = PackageGraphRootInput(packages: packages, traitConfiguration: traitConfiguration)
let graphRoot = try PackageGraphRoot(
var graphRoot = try PackageGraphRoot(
input: input,
manifests: rootManifests,
explicitProduct: explicitProduct,
observabilityScope: observabilityScope
)

let manifestMap = manifests.reduce(into: [PackageIdentity: Manifest]()) { manifestMap, manifest in
manifestMap[manifest.packageIdentity] = manifest
}

let updatedTraitsMap = try precomputeTraits(root: graphRoot, manifests, manifestMap)
// TODO bp: Post-process the trait computation here; a little hacky since we actually do this during dependency resolution...
// for manifest in manifests {
// var enabledTraits = graphRoot.enabledTraits[manifest.packageIdentity]
// enabledTraits = try manifest.enabledTraits(using: enabledTraits, nil)
//
// graphRoot.enabledTraits[manifest.packageIdentity] = enabledTraits
// }
graphRoot.enabledTraits = updatedTraitsMap

return try ModulesGraph.load(
root: graphRoot,
identityResolver: identityResolver,
Expand Down
21 changes: 17 additions & 4 deletions Sources/PackageGraph/PackageGraphRoot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,24 @@ public struct PackageGraphRoot {

// Calculate the enabled traits for each dependency of this root:
manifest.dependencies.forEach { dependency in
if let traits = dependency.traits {
let traitNames = traits.map(\.name)
traitsMap[dependency.identity, default: []].formUnion(Set(traitNames))
// TODO bp: check for condition on the dependency traits here
let explicitlyEnabledTraits = dependency.traits?.filter({
guard let condition = $0.condition else { return true }
return condition.isSatisfied(by: enabledTraits)
}).map(\.name) //?? []
var enabledTraitsSet = explicitlyEnabledTraits.flatMap { Set($0) }

if let depTraits = traitsMap[dependency.identity] {
enabledTraitsSet?.formUnion(depTraits)
}

// to fix with precompute fix here
traitsMap[dependency.identity] = enabledTraitsSet
//
// if let traits = dependency.traits {
// let traitNames = traits.map(\.name)
// traitsMap[dependency.identity, default: []].formUnion(Set(traitNames))
// }
}
}

Expand All @@ -138,7 +152,6 @@ public struct PackageGraphRoot {
// If not, then we can omit this dependency if pruning unused dependencies
// is enabled.
return manifests.values.reduce(false) { result, manifest in
guard manifest.pruneDependencies else { return true }
let enabledTraits: Set<String>? = enableTraitsMap[manifest.packageIdentity]
if let isUsed = try? manifest.isPackageDependencyUsed(dep, enabledTraits: enabledTraits) {
return result || isUsed
Expand Down
4 changes: 2 additions & 2 deletions Sources/PackageGraph/Resolution/ResolvedPackage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public struct ResolvedPackage {
public let products: [ResolvedProduct]

/// The enabled traits of this package.
public let enabledTraits: Set<String>
public let enabledTraits: Set<String>?

/// The dependencies of the package.
public let dependencies: [PackageIdentity]
Expand All @@ -62,7 +62,7 @@ public struct ResolvedPackage {
defaultLocalization: String?,
supportedPlatforms: [SupportedPlatform],
dependencies: [PackageIdentity],
enabledTraits: Set<String>,
enabledTraits: Set<String>?,
modules: IdentifiableSet<ResolvedModule>,
products: [ResolvedProduct],
registryMetadata: RegistryReleaseMetadata?,
Expand Down
Loading