Skip to content

[NFC] Remove redundant DependencyResolver protocol #7127

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

Merged
merged 1 commit into from
Nov 30, 2023
Merged
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
28 changes: 15 additions & 13 deletions Sources/PackageGraph/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
add_library(PackageGraph
BoundVersion.swift
DependencyMirrors.swift
DependencyResolutionNode.swift
DependencyResolver.swift
Diagnostics.swift
GraphLoadingNode.swift
ModuleAliasTracker.swift
Expand All @@ -21,17 +19,21 @@ add_library(PackageGraph
PackageModel+Extensions.swift
PackageRequirement.swift
PinsStore.swift
PubGrub/Assignment.swift
PubGrub/ContainerProvider.swift
PubGrub/DiagnosticReportBuilder.swift
PubGrub/Incompatibility.swift
PubGrub/PartialSolution.swift
PubGrub/PubGrubDependencyResolver.swift
PubGrub/PubGrubPackageContainer.swift
PubGrub/Term.swift
ResolvedPackage.swift
ResolvedProduct.swift
ResolvedTarget.swift
Resolution/PubGrub/Assignment.swift
Resolution/PubGrub/ContainerProvider.swift
Resolution/PubGrub/DiagnosticReportBuilder.swift
Resolution/PubGrub/Incompatibility.swift
Resolution/PubGrub/PartialSolution.swift
Resolution/PubGrub/PubGrubDependencyResolver.swift
Resolution/PubGrub/PubGrubPackageContainer.swift
Resolution/PubGrub/Term.swift
Resolution/DependencyResolutionNode.swift
Resolution/DependencyResolverBinding.swift
Resolution/DependencyResolverDelegate.swift
Resolution/DependencyResolverError.swift
Resolution/ResolvedPackage.swift
Resolution/ResolvedProduct.swift
Resolution/ResolvedTarget.swift
Version+Extensions.swift
VersionSetSpecifier.swift)
target_link_libraries(PackageGraph PUBLIC
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageGraph/GraphLoadingNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import PackageModel
///
/// This node uses the product filter that was already finalized during resolution.
///
/// - SeeAlso: DependencyResolutionNode
/// - SeeAlso: ``DependencyResolutionNode``
public struct GraphLoadingNode: Equatable, Hashable {

/// The package identity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import struct TSCUtility.Version
///
/// See the documentation of each case for more detailed descriptions of each kind and how they interact.
///
/// - SeeAlso: `GraphLoadingNode`
/// - SeeAlso: ``GraphLoadingNode``
public enum DependencyResolutionNode {

/// An empty package node.
Expand Down
20 changes: 20 additions & 0 deletions Sources/PackageGraph/Resolution/DependencyResolverBinding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import enum PackageModel.ProductFilter
import struct PackageModel.PackageReference

public struct DependencyResolverBinding {
public let package: PackageReference
public let boundVersion: BoundVersion
public let products: ProductFilter
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
Expand All @@ -16,25 +16,6 @@ import PackageModel

import struct TSCUtility.Version

public protocol DependencyResolver {
typealias Binding = (package: PackageReference, binding: BoundVersion, products: ProductFilter)
typealias Delegate = DependencyResolverDelegate
}

public enum DependencyResolverError: Error, Equatable {
/// A revision-based dependency contains a local package dependency.
case revisionDependencyContainsLocalPackage(dependency: String, localPackage: String)
}

extension DependencyResolverError: CustomStringConvertible {
public var description: String {
switch self {
case .revisionDependencyContainsLocalPackage(let dependency, let localPackage):
return "package '\(dependency)' is required using a revision-based requirement and it depends on local package '\(localPackage)', which is not supported"
}
}
}

public protocol DependencyResolverDelegate {
func willResolve(term: Term)
func didResolve(term: Term, version: Version, duration: DispatchTimeInterval)
Expand All @@ -44,7 +25,7 @@ public protocol DependencyResolverDelegate {
func satisfied(term: Term, by assignment: Assignment, incompatibility: Incompatibility)
func partiallySatisfied(term: Term, by assignment: Assignment, incompatibility: Incompatibility, difference: Term)
func failedToResolve(incompatibility: Incompatibility)
func solved(result: [DependencyResolver.Binding])
func solved(result: [DependencyResolverBinding])
}

public struct ObservabilityDependencyResolverDelegate: DependencyResolverDelegate {
Expand Down Expand Up @@ -82,9 +63,9 @@ public struct ObservabilityDependencyResolverDelegate: DependencyResolverDelegat
self.debug("\(term) is partially satisfied by '\(assignment)', which is caused by '\(assignment.cause?.description ?? "unknown cause")'. new incompatibility \(incompatibility)")
}

public func solved(result: [DependencyResolver.Binding]) {
for (package, binding, _) in result {
self.debug("solved '\(package.identity)' (\(package.locationString)) at '\(binding)'")
public func solved(result: [DependencyResolverBinding]) {
for binding in result {
self.debug("solved '\(binding.package.identity)' (\(binding.package.locationString)) at '\(binding.boundVersion)'")
}
self.debug("dependency resolution complete!")
}
Expand Down Expand Up @@ -129,7 +110,7 @@ public struct MultiplexResolverDelegate: DependencyResolverDelegate {
underlying.forEach { $0.failedToResolve(incompatibility: incompatibility) }
}

public func solved(result: [(package: PackageReference, binding: BoundVersion, products: ProductFilter)]) {
public func solved(result: [DependencyResolverBinding]) {
underlying.forEach { $0.solved(result: result) }
}

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

import Foundation

public enum DependencyResolverError: Error, Equatable {
/// A revision-based dependency contains a local package dependency.
case revisionDependencyContainsLocalPackage(dependency: String, localPackage: String)
}

extension DependencyResolverError: CustomStringConvertible {
public var description: String {
switch self {
case .revisionDependencyContainsLocalPackage(let dependency, let localPackage):
return "package '\(dependency)' is required using a revision-based requirement and it depends on local package '\(localPackage)', which is not supported"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public struct PubGrubDependencyResolver {
}

/// Execute the resolution algorithm to find a valid assignment of versions.
public func solve(constraints: [Constraint]) -> Result<[DependencyResolver.Binding], Error> {
public func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> {
// the graph resolution root
let root: DependencyResolutionNode
if constraints.count == 1, let constraint = constraints.first, constraint.package.kind.isRoot {
Expand Down Expand Up @@ -180,7 +180,7 @@ public struct PubGrubDependencyResolver {
/// Find a set of dependencies that fit the given constraints. If dependency
/// resolution is unable to provide a result, an error is thrown.
/// - Warning: It is expected that the root package reference has been set before this is called.
internal func solve(root: DependencyResolutionNode, constraints: [Constraint]) throws -> (bindings: [DependencyResolver.Binding], state: State) {
internal func solve(root: DependencyResolutionNode, constraints: [Constraint]) throws -> (bindings: [DependencyResolverBinding], state: State) {
// first process inputs
let inputs = try self.processInputs(root: root, with: constraints)

Expand Down Expand Up @@ -241,18 +241,22 @@ public struct PubGrubDependencyResolver {
flattenedAssignments[updatePackage] = (binding: boundVersion, products: products)
}
}
var finalAssignments: [DependencyResolver.Binding]
var finalAssignments: [DependencyResolverBinding]
= flattenedAssignments.keys.sorted(by: { $0.deprecatedName < $1.deprecatedName }).map { package in
let details = flattenedAssignments[package]!
return (package: package, binding: details.binding, products: details.products)
return .init(package: package, boundVersion: details.binding, products: details.products)
}

// Add overridden packages to the result.
for (package, override) in state.overriddenPackages {
// TODO: replace with async/await when available
let container = try temp_await { provider.getContainer(for: package, completion: $0) }
let updatePackage = try container.underlying.loadPackageReference(at: override.version)
finalAssignments.append((updatePackage, override.version, override.products))
finalAssignments.append(.init(
package: updatePackage,
boundVersion: override.version,
products: override.products
))
}

self.delegate?.solved(result: finalAssignments)
Expand Down
51 changes: 26 additions & 25 deletions Sources/Workspace/Workspace+Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import struct PackageGraph.Assignment
import enum PackageGraph.BoundVersion
import enum PackageGraph.ContainerUpdateStrategy
import protocol PackageGraph.CustomPackageContainer
import struct PackageGraph.DependencyResolverBinding
import protocol PackageGraph.DependencyResolverDelegate
import struct PackageGraph.Incompatibility
import struct PackageGraph.MultiplexResolverDelegate
Expand Down Expand Up @@ -631,7 +632,7 @@ extension Workspace {
@discardableResult
fileprivate func updateDependenciesCheckouts(
root: PackageGraphRoot,
updateResults: [(PackageReference, BoundVersion, ProductFilter)],
updateResults: [DependencyResolverBinding],
updateBranches: Bool = false,
observabilityScope: ObservabilityScope
) -> [(PackageReference, PackageStateChange)] {
Expand Down Expand Up @@ -954,7 +955,7 @@ extension Workspace {
/// Computes states of the packages based on last stored state.
fileprivate func computePackageStateChanges(
root: PackageGraphRoot,
resolvedDependencies: [(PackageReference, BoundVersion, ProductFilter)],
resolvedDependencies: [DependencyResolverBinding],
updateBranches: Bool,
observabilityScope: ObservabilityScope
) throws -> [(PackageReference, PackageStateChange)] {
Expand All @@ -963,59 +964,59 @@ extension Workspace {
var packageStateChanges: [PackageIdentity: (PackageReference, PackageStateChange)] = [:]

// Set the states from resolved dependencies results.
for (packageRef, binding, products) in resolvedDependencies {
for binding in resolvedDependencies {
// Get the existing managed dependency for this package ref, if any.

// first find by identity only since edit location may be different by design
var currentDependency = self.state.dependencies[packageRef.identity]
var currentDependency = self.state.dependencies[binding.package.identity]
// Check if this is an edited dependency.
if case .edited(let basedOn, _) = currentDependency?.state, let originalReference = basedOn?.packageRef {
packageStateChanges[originalReference.identity] = (originalReference, .unchanged)
} else {
// if not edited, also compare by location since it may have changed
currentDependency = self.state.dependencies[comparingLocation: packageRef]
currentDependency = self.state.dependencies[comparingLocation: binding.package]
}

switch binding {
switch binding.boundVersion {
case .excluded:
throw InternalError("Unexpected excluded binding")

case .unversioned:
// Ignore the root packages.
if root.packages.keys.contains(packageRef.identity) {
if root.packages.keys.contains(binding.package.identity) {
continue
}

if let currentDependency {
switch currentDependency.state {
case .fileSystem, .edited:
packageStateChanges[packageRef.identity] = (packageRef, .unchanged)
packageStateChanges[binding.package.identity] = (binding.package, .unchanged)
case .sourceControlCheckout:
let newState = PackageStateChange.State(requirement: .unversioned, products: products)
packageStateChanges[packageRef.identity] = (packageRef, .updated(newState))
let newState = PackageStateChange.State(requirement: .unversioned, products: binding.products)
packageStateChanges[binding.package.identity] = (binding.package, .updated(newState))
case .registryDownload:
throw InternalError("Unexpected unversioned binding for downloaded dependency")
case .custom:
throw InternalError("Unexpected unversioned binding for custom dependency")
}
} else {
let newState = PackageStateChange.State(requirement: .unversioned, products: products)
packageStateChanges[packageRef.identity] = (packageRef, .added(newState))
let newState = PackageStateChange.State(requirement: .unversioned, products: binding.products)
packageStateChanges[binding.package.identity] = (binding.package, .added(newState))
}

case .revision(let identifier, let branch):
// Get the latest revision from the container.
// TODO: replace with async/await when available
guard let container = try (temp_await {
packageContainerProvider.getContainer(
for: packageRef,
for: binding.package,
updateStrategy: .never,
observabilityScope: observabilityScope,
on: .sharedConcurrent,
completion: $0
)
}) as? SourceControlPackageContainer else {
throw InternalError("invalid container for \(packageRef) expected a SourceControlPackageContainer")
throw InternalError("invalid container for \(binding.package) expected a SourceControlPackageContainer")
}
var revision = try container.getRevision(forIdentifier: identifier)
let branch = branch ?? (identifier == revision.identifier ? nil : identifier)
Expand All @@ -1024,7 +1025,7 @@ extension Workspace {
// branches, use the revision from pin instead (if present).
if branch != nil, !updateBranches {
if case .branch(branch, let pinRevision) = pinsStore.pins.values
.first(where: { $0.packageRef == packageRef })?.state
.first(where: { $0.packageRef == binding.package })?.state
{
revision = Revision(identifier: pinRevision)
}
Expand All @@ -1043,21 +1044,21 @@ extension Workspace {
if case .sourceControlCheckout(let checkoutState) = currentDependency.state,
checkoutState == newState
{
packageStateChanges[packageRef.identity] = (packageRef, .unchanged)
packageStateChanges[binding.package.identity] = (binding.package, .unchanged)
} else {
// Otherwise, we need to update this dependency to this revision.
let newState = PackageStateChange.State(
requirement: .revision(revision, branch: branch),
products: products
products: binding.products
)
packageStateChanges[packageRef.identity] = (packageRef, .updated(newState))
packageStateChanges[binding.package.identity] = (binding.package, .updated(newState))
}
} else {
let newState = PackageStateChange.State(
requirement: .revision(revision, branch: branch),
products: products
products: binding.products
)
packageStateChanges[packageRef.identity] = (packageRef, .added(newState))
packageStateChanges[binding.package.identity] = (binding.package, .added(newState))
}

case .version(let version):
Expand All @@ -1066,11 +1067,11 @@ extension Workspace {
case .sourceControlCheckout(.version(version, _)), .registryDownload(version), .custom(version, _):
stateChange = .unchanged
case .edited, .fileSystem, .sourceControlCheckout, .registryDownload, .custom:
stateChange = .updated(.init(requirement: .version(version), products: products))
stateChange = .updated(.init(requirement: .version(version), products: binding.products))
case nil:
stateChange = .added(.init(requirement: .version(version), products: products))
stateChange = .added(.init(requirement: .version(version), products: binding.products))
}
packageStateChanges[packageRef.identity] = (packageRef, stateChange)
packageStateChanges[binding.package.identity] = (binding.package, stateChange)
}
}
// Set the state of any old package that might have been removed.
Expand Down Expand Up @@ -1114,7 +1115,7 @@ extension Workspace {
resolver: PubGrubDependencyResolver,
constraints: [PackageContainerConstraint],
observabilityScope: ObservabilityScope
) -> [(package: PackageReference, binding: BoundVersion, products: ProductFilter)] {
) -> [DependencyResolverBinding] {
os_signpost(.begin, name: SignpostName.pubgrub)
let result = resolver.solve(constraints: constraints)
os_signpost(.end, name: SignpostName.pubgrub)
Expand Down Expand Up @@ -1179,7 +1180,7 @@ private struct WorkspaceDependencyResolverDelegate: DependencyResolverDelegate {
difference: Term
) {}
func failedToResolve(incompatibility: Incompatibility) {}
func solved(result: [(package: PackageReference, binding: BoundVersion, products: ProductFilter)]) {}
func solved(result: [DependencyResolverBinding]) {}
}

// FIXME: the manifest loading logic should be changed to use identity instead of location once identity is unique
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class DependencyResolverRealWorldPerfTests: XCTestCasePerf {
switch resolver.solve(constraints: graph.constraints) {
case .success(let result):
let result: [(container: PackageReference, version: Version)] = result.compactMap {
guard case .version(let version) = $0.binding else {
guard case .version(let version) = $0.boundVersion else {
XCTFail("Unexpected result")
return nil
}
Expand Down
Loading