diff --git a/Sources/PackageGraph/CMakeLists.txt b/Sources/PackageGraph/CMakeLists.txt index c0910900ec9..6a1695a0a4d 100644 --- a/Sources/PackageGraph/CMakeLists.txt +++ b/Sources/PackageGraph/CMakeLists.txt @@ -19,7 +19,6 @@ add_library(PackageGraph PackageGraphRoot.swift PackageModel+Extensions.swift PackageRequirement.swift - PrebuiltPackageContainer.swift PinsStore.swift Resolution/PubGrub/Assignment.swift Resolution/PubGrub/ContainerProvider.swift diff --git a/Sources/PackageGraph/PrebuiltPackageContainer.swift b/Sources/PackageGraph/PrebuiltPackageContainer.swift deleted file mode 100644 index 870487d1781..00000000000 --- a/Sources/PackageGraph/PrebuiltPackageContainer.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2024 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 Basics -import PackageModel -import struct TSCUtility.Version - -/// A package container that can represent a prebuilt library from a package. -public struct PrebuiltPackageContainer: PackageContainer { - private let chosenIdentity: LibraryMetadata.Identity - private let metadata: LibraryMetadata - - public init(metadata: LibraryMetadata) throws { - self.metadata = metadata - - // FIXME: Unclear what is supposed to happen if we have multiple identities. - if let identity = metadata.identities.first { - self.chosenIdentity = identity - } else { - let name = metadata.productName.map { "'\($0)' " } ?? "" - throw InternalError("provided library \(name)does not specifiy any identities") - } - } - - public var package: PackageReference { - return .init(identity: chosenIdentity.identity, kind: chosenIdentity.kind) - } - - public func isToolsVersionCompatible(at version: Version) -> Bool { - return true - } - - public func toolsVersion(for version: Version) throws -> ToolsVersion { - return .v4 - } - - public func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { - return try versionsAscending() - } - - public func versionsAscending() throws -> [Version] { - return [.init(stringLiteral: metadata.version)] - } - - public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return [] - } - - public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { - return package - } -} diff --git a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift index 74735c9ccf9..d73e41ffdf9 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift @@ -49,8 +49,8 @@ final class ContainerProvider { } func removeCachedContainers(for packages: [PackageReference]) { - packages.forEach { - self.containersCache[$0] = nil + for package in packages { + self.containersCache[package] = nil } } @@ -65,7 +65,6 @@ final class ContainerProvider { /// Get the container for the given identifier, loading it if necessary. func getContainer( for package: PackageReference, - availableLibraries: [LibraryMetadata], completion: @escaping (Result) -> Void ) { // Return the cached container, if available. @@ -73,17 +72,6 @@ final class ContainerProvider { return completion(.success(container)) } - if let metadata = package.matchingPrebuiltLibrary(in: availableLibraries) { - do { - let prebuiltPackageContainer = try PrebuiltPackageContainer(metadata: metadata) - let pubGrubContainer = PubGrubPackageContainer(underlying: prebuiltPackageContainer, pins: self.pins) - self.containersCache[package] = pubGrubContainer - return completion(.success(pubGrubContainer)) - } catch { - return completion(.failure(error)) - } - } - if let prefetchSync = self.prefetches[package] { // If this container is already being prefetched, wait for that to complete prefetchSync.notify(queue: .sharedConcurrent) { @@ -93,7 +81,7 @@ final class ContainerProvider { } else { // if prefetch failed, remove from list of prefetches and try again self.prefetches[package] = nil - return self.getContainer(for: package, availableLibraries: availableLibraries, completion: completion) + return self.getContainer(for: package, completion: completion) } } } else { @@ -101,7 +89,10 @@ final class ContainerProvider { self.underlying.getContainer( for: package, updateStrategy: self.skipUpdate ? .never : .always, // TODO: make this more elaborate - observabilityScope: self.observabilityScope.makeChildScope(description: "getting package container", metadata: package.diagnosticsMetadata), + observabilityScope: self.observabilityScope.makeChildScope( + description: "getting package container", + metadata: package.diagnosticsMetadata + ), on: .sharedConcurrent ) { result in let result = result.tryMap { container -> PubGrubPackageContainer in @@ -118,7 +109,7 @@ final class ContainerProvider { /// Starts prefetching the given containers. func prefetch(containers identifiers: [PackageReference], availableLibraries: [LibraryMetadata]) { let filteredIdentifiers = identifiers.filter { - return $0.matchingPrebuiltLibrary(in: availableLibraries) == nil + $0.matchingPrebuiltLibrary(in: availableLibraries) == nil } // Process each container. for identifier in filteredIdentifiers { @@ -133,13 +124,19 @@ final class ContainerProvider { self.underlying.getContainer( for: identifier, updateStrategy: self.skipUpdate ? .never : .always, // TODO: make this more elaborate - observabilityScope: self.observabilityScope.makeChildScope(description: "prefetching package container", metadata: identifier.diagnosticsMetadata), + observabilityScope: self.observabilityScope.makeChildScope( + description: "prefetching package container", + metadata: identifier.diagnosticsMetadata + ), on: .sharedConcurrent ) { result in defer { self.prefetches[identifier]?.leave() } // only cache positive results if case .success(let container) = result { - self.containersCache[identifier] = PubGrubPackageContainer(underlying: container, pins: self.pins) + self.containersCache[identifier] = PubGrubPackageContainer( + underlying: container, + pins: self.pins + ) } } } diff --git a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift index 2d1993bbcbe..ffee1492d1e 100644 --- a/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift +++ b/Sources/PackageGraph/Resolution/PubGrub/PubGrubDependencyResolver.swift @@ -25,7 +25,7 @@ public struct PubGrubDependencyResolver { public typealias Constraint = PackageContainerConstraint /// the mutable state that get computed - internal final class State { + final class State { /// The root package reference. let root: DependencyResolutionNode @@ -43,10 +43,11 @@ public struct PubGrubDependencyResolver { private let lock = NSLock() - init(root: DependencyResolutionNode, - overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)] = [:], - solution: PartialSolution = PartialSolution()) - { + init( + root: DependencyResolutionNode, + overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)] = [:], + solution: PartialSolution = PartialSolution() + ) { self.root = root self.overriddenPackages = overriddenPackages self.solution = solution @@ -103,6 +104,9 @@ public struct PubGrubDependencyResolver { /// Reference to the pins store, if provided. private let pins: PinsStore.Pins + /// The packages that are available in a prebuilt form in SDK or a toolchain + private let availableLibraries: [LibraryMetadata] + /// The container provider used to load package containers. private let provider: ContainerProvider @@ -121,6 +125,7 @@ public struct PubGrubDependencyResolver { public init( provider: PackageContainerProvider, pins: PinsStore.Pins = [:], + availableLibraries: [LibraryMetadata] = [], skipDependenciesUpdates: Bool = false, prefetchBasedOnResolvedFile: Bool = false, observabilityScope: ObservabilityScope, @@ -128,6 +133,7 @@ public struct PubGrubDependencyResolver { ) { self.packageContainerProvider = provider self.pins = pins + self.availableLibraries = availableLibraries self.skipDependenciesUpdates = skipDependenciesUpdates self.prefetchBasedOnResolvedFile = prefetchBasedOnResolvedFile self.provider = ContainerProvider( @@ -140,11 +146,7 @@ public struct PubGrubDependencyResolver { } /// Execute the resolution algorithm to find a valid assignment of versions. - public func solve(constraints: [Constraint], availableLibraries: [LibraryMetadata], preferPrebuiltLibraries: Bool) -> Result<[DependencyResolverBinding], Error> { - if !preferPrebuiltLibraries { - self.provider.removeCachedContainers(for: availableLibraries.flatMap { $0.identities.map { $0.ref } }) - } - + 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 { @@ -159,26 +161,13 @@ public struct PubGrubDependencyResolver { } do { - // Use empty `availableLibraries` for the rest of resolving if we don't prefer them. - let availableLibraries = preferPrebuiltLibraries ? availableLibraries : [] - // strips state - let bindings = try self.solve(root: root, constraints: constraints, availableLibraries: availableLibraries).bindings.filter { - return $0.package.matchingPrebuiltLibrary(in: availableLibraries) == nil - } + let bindings = try self.solve(root: root, constraints: constraints).bindings return .success(bindings) } catch { // If version solving failing, build the user-facing diagnostic. - if let pubGrubError = error as? PubgrubError, let rootCause = pubGrubError.rootCause, let incompatibilities = pubGrubError.incompatibilities { - let incompatiblePackages = incompatibilities.map({ $0.key.package }) - if incompatiblePackages.contains(where: { $0.matchingPrebuiltLibrary(in: availableLibraries) != nil }) { - return .failure( - PubgrubError.potentiallyUnresovableDueToPrebuiltLibrary( - incompatiblePackages, - pubGrubError.description - ) - ) - } - + if let pubGrubError = error as? PubgrubError, let rootCause = pubGrubError.rootCause, + let incompatibilities = pubGrubError.incompatibilities + { do { var builder = DiagnosticReportBuilder( root: root, @@ -199,12 +188,12 @@ 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], availableLibraries: [LibraryMetadata] = []) throws -> ( + func solve(root: DependencyResolutionNode, constraints: [Constraint]) throws -> ( bindings: [DependencyResolverBinding], state: State ) { // first process inputs - let inputs = try self.processInputs(root: root, with: constraints, availableLibraries: availableLibraries) + let inputs = try self.processInputs(root: root, with: constraints) // Prefetch the containers if prefetching is enabled. if self.prefetchBasedOnResolvedFile { @@ -214,7 +203,7 @@ public struct PubGrubDependencyResolver { let pins = self.pins.values .map(\.packageRef) .filter { !inputs.overriddenPackages.keys.contains($0) } - self.provider.prefetch(containers: pins, availableLibraries: availableLibraries) + self.provider.prefetch(containers: pins, availableLibraries: self.availableLibraries) } let state = State(root: root, overriddenPackages: inputs.overriddenPackages) @@ -223,14 +212,17 @@ public struct PubGrubDependencyResolver { state.decide(state.root, at: "1.0.0") // Add the root incompatibility. - state.addIncompatibility(Incompatibility(terms: [Term(not: root, .exact("1.0.0"))], cause: .root), at: .topLevel) + state.addIncompatibility( + Incompatibility(terms: [Term(not: root, .exact("1.0.0"))], cause: .root), + at: .topLevel + ) // Add inputs root incompatibilities. for incompatibility in inputs.rootIncompatibilities { state.addIncompatibility(incompatibility, at: .topLevel) } - try self.run(state: state, availableLibraries: availableLibraries) + try self.run(state: state) let decisions = state.solution.assignments.filter(\.isDecision) var flattenedAssignments: [PackageReference: (binding: BoundVersion, products: ProductFilter)] = [:] @@ -239,6 +231,8 @@ public struct PubGrubDependencyResolver { continue } + let package = assignment.term.node.package + let boundVersion: BoundVersion switch assignment.term.requirement { case .exact(let version): @@ -247,15 +241,29 @@ public struct PubGrubDependencyResolver { throw InternalError("unexpected requirement value for assignment \(assignment.term)") } + // Strip packages that have prebuilt libraries only if they match library version. + // + // FIXME: This is built on assumption that libraries are part of the SDK and are + // always available in include/library paths, but what happens if they are + // part of a toolchain instead? Builder needs an indicator that certain path + // has to be included when building packages that depend on prebuilt libraries. + if let library = package.matchingPrebuiltLibrary(in: availableLibraries), + boundVersion == .version(.init(stringLiteral: library.version)) + { + continue + } + let products = assignment.term.node.productFilter // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: assignment.term.node.package, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: boundVersion) if var existing = flattenedAssignments[updatePackage] { guard existing.binding == boundVersion else { - throw InternalError("Two products in one package resolved to different versions: \(existing.products)@\(existing.binding) vs \(products)@\(boundVersion)") + throw InternalError( + "Two products in one package resolved to different versions: \(existing.products)@\(existing.binding) vs \(products)@\(boundVersion)" + ) } existing.products.formUnion(products) flattenedAssignments[updatePackage] = existing @@ -272,12 +280,12 @@ public struct PubGrubDependencyResolver { // 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, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } let updatePackage = try container.underlying.loadPackageReference(at: override.version) finalAssignments.append(.init( - package: updatePackage, - boundVersion: override.version, - products: override.products + package: updatePackage, + boundVersion: override.version, + products: override.products )) } @@ -288,8 +296,7 @@ public struct PubGrubDependencyResolver { private func processInputs( root: DependencyResolutionNode, - with constraints: [Constraint], - availableLibraries: [LibraryMetadata] + with constraints: [Constraint] ) throws -> ( overriddenPackages: [PackageReference: (version: BoundVersion, products: ProductFilter)], rootIncompatibilities: [Incompatibility] @@ -307,7 +314,10 @@ public struct PubGrubDependencyResolver { // The list of version-based references reachable via local and branch-based references. // These are added as top-level incompatibilities since they always need to be satisfied. // Some of these might be overridden as we discover local and branch-based references. - var versionBasedDependencies = OrderedCollections.OrderedDictionary() + var versionBasedDependencies = OrderedCollections.OrderedDictionary< + DependencyResolutionNode, + [VersionBasedConstraint] + >() // Process unversioned constraints in first phase. We go through all of the unversioned packages // and collect them and their dependencies. This gives us the complete list of unversioned @@ -319,7 +329,9 @@ public struct PubGrubDependencyResolver { // Mark the package as overridden. if var existing = overriddenPackages[constraint.package] { guard existing.version == .unversioned else { - throw InternalError("Overridden package is not unversioned: \(constraint.package)@\(existing.version)") + throw InternalError( + "Overridden package is not unversioned: \(constraint.package)@\(existing.version)" + ) } existing.products.formUnion(constraint.products) overriddenPackages[constraint.package] = existing @@ -331,11 +343,13 @@ public struct PubGrubDependencyResolver { // Process dependencies of this package. // // We collect all version-based dependencies in a separate structure so they can - // be process at the end. This allows us to override them when there is a non-version + // be processed at the end. This allows us to override them when there is a non-version // based (unversioned/branch-based) constraint present in the graph. // TODO: replace with async/await when available - let container = try temp_await { provider.getContainer(for: node.package, availableLibraries: availableLibraries, completion: $0) } - for dependency in try container.underlying.getUnversionedDependencies(productFilter: node.productFilter) { + let container = try temp_await { self.provider.getContainer(for: node.package, completion: $0) } + for dependency in try container.underlying + .getUnversionedDependencies(productFilter: node.productFilter) + { if let versionedBasedConstraints = VersionBasedConstraint.constraints(dependency) { for constraint in versionedBasedConstraints { versionBasedDependencies[node, default: []].append(constraint) @@ -370,9 +384,13 @@ public struct PubGrubDependencyResolver { case .revision(let existingRevision, let branch)?: // If this branch-based package was encountered before, ensure the references match. if (branch ?? existingRevision) != revision { - throw PubgrubError.unresolvable("\(package.identity) is required using two different revision-based requirements (\(existingRevision) and \(revision)), which is not supported") + throw PubgrubError + .unresolvable( + "\(package.identity) is required using two different revision-based requirements (\(existingRevision) and \(revision)), which is not supported" + ) } else { - // Otherwise, continue since we've already processed this constraint. Any cycles will be diagnosed separately. + // Otherwise, continue since we've already processed this constraint. Any cycles will be diagnosed + // separately. continue } case nil: @@ -382,7 +400,7 @@ public struct PubGrubDependencyResolver { // Process dependencies of this package, similar to the first phase but branch-based dependencies // are not allowed to contain local/unversioned packages. // TODO: replace with async/await when avail - let container = try temp_await { provider.getContainer(for: package, availableLibraries: availableLibraries, completion: $0) } + let container = try temp_await { self.provider.getContainer(for: package, completion: $0) } // If there is a pin for this revision-based dependency, get // the dependencies at the pinned revision instead of using @@ -393,7 +411,10 @@ public struct PubGrubDependencyResolver { revisionForDependencies = pinRevision // Mark the package as overridden with the pinned revision and record the branch as well. - overriddenPackages[package] = (version: .revision(revisionForDependencies, branch: revision), products: constraint.products) + overriddenPackages[package] = ( + version: .revision(revisionForDependencies, branch: revision), + products: constraint.products + ) } else { revisionForDependencies = revision @@ -414,7 +435,8 @@ public struct PubGrubDependencyResolver { case .versionSet(let req): for node in dependency.nodes() { let versionedBasedConstraint = VersionBasedConstraint(node: node, req: req) - versionBasedDependencies[.root(package: constraint.package), default: []].append(versionedBasedConstraint) + versionBasedDependencies[.root(package: constraint.package), default: []] + .append(versionedBasedConstraint) } case .revision: constraints.append(dependency) @@ -438,12 +460,15 @@ public struct PubGrubDependencyResolver { versionBasedDependencies[root, default: []].append(versionedBasedConstraint) } case .revision, .unversioned: - throw InternalError("Unexpected revision/unversioned requirement in the constraints list: \(constraints)") + throw InternalError( + "Unexpected revision/unversioned requirement in the constraints list: \(constraints)" + ) } } // Finally, compute the root incompatibilities (which will be all version-based). - // note versionBasedDependencies may point to the root package dependencies, or the dependencies of root's non-versioned dependencies + // note versionBasedDependencies may point to the root package dependencies, or the dependencies of root's + // non-versioned dependencies var rootIncompatibilities: [Incompatibility] = [] for (node, constraints) in versionBasedDependencies { for constraint in constraints { @@ -465,7 +490,7 @@ public struct PubGrubDependencyResolver { /// decisions if nothing else is left to be done. /// After this method returns `solution` is either populated with a list of /// final version assignments or an error is thrown. - private func run(state: State, availableLibraries: [LibraryMetadata]) throws { + private func run(state: State) throws { var next: DependencyResolutionNode? = state.root while let nxt = next { @@ -474,13 +499,13 @@ public struct PubGrubDependencyResolver { // initiate prefetch of known packages that will be used to make the decision on the next step self.provider.prefetch( containers: state.solution.undecided.map(\.node.package), - availableLibraries: availableLibraries + availableLibraries: self.availableLibraries ) // If decision making determines that no more decisions are to be // made, it returns nil to signal that version solving is done. // TODO: replace with async/await when available - next = try temp_await { self.makeDecision(state: state, availableLibraries: availableLibraries, completion: $0) } + next = try temp_await { self.makeDecision(state: state, completion: $0) } } } @@ -488,7 +513,7 @@ public struct PubGrubDependencyResolver { /// partial solution. /// If a conflict is found, the conflicting incompatibility is returned to /// resolve the conflict on. - internal func propagate(state: State, node: DependencyResolutionNode) throws { + func propagate(state: State, node: DependencyResolutionNode) throws { var changed: OrderedCollections.OrderedSet = [node] while !changed.isEmpty { @@ -543,7 +568,6 @@ public struct PubGrubDependencyResolver { return .conflict } - state.derive(unsatisfiedTerm.inverse, cause: incompatibility) self.delegate?.derived(term: unsatisfiedTerm.inverse) return .almostSatisfied(node: unsatisfiedTerm.node) @@ -552,7 +576,7 @@ public struct PubGrubDependencyResolver { // Based on: // https://github.com/dart-lang/pub/tree/master/doc/solver.md#conflict-resolution // https://github.com/dart-lang/pub/blob/master/lib/src/solver/version_solver.dart#L201 - internal func resolve(state: State, conflict: Incompatibility) throws -> Incompatibility { + func resolve(state: State, conflict: Incompatibility) throws -> Incompatibility { self.delegate?.conflict(conflict: conflict) var incompatibility = conflict @@ -563,7 +587,7 @@ public struct PubGrubDependencyResolver { let maxIterations = 1000 var iterations = 0 - while !isCompleteFailure(incompatibility, root: state.root) { + while !self.isCompleteFailure(incompatibility, root: state.root) { var mostRecentTerm: Term? var mostRecentSatisfier: Assignment? var difference: Term? @@ -592,7 +616,10 @@ public struct PubGrubDependencyResolver { if mostRecentTerm == term { difference = mostRecentSatisfier?.term.difference(with: term) if let difference { - previousSatisfierLevel = max(previousSatisfierLevel, try state.solution.satisfier(for: difference.inverse).decisionLevel) + previousSatisfierLevel = try max( + previousSatisfierLevel, + state.solution.satisfier(for: difference.inverse).decisionLevel + ) } } } @@ -631,9 +658,18 @@ public struct PubGrubDependencyResolver { if let mostRecentTerm { if let difference { - self.delegate?.partiallySatisfied(term: mostRecentTerm, by: _mostRecentSatisfier, incompatibility: incompatibility, difference: difference) + self.delegate?.partiallySatisfied( + term: mostRecentTerm, + by: _mostRecentSatisfier, + incompatibility: incompatibility, + difference: difference + ) } else { - self.delegate?.satisfied(term: mostRecentTerm, by: _mostRecentSatisfier, incompatibility: incompatibility) + self.delegate?.satisfied( + term: mostRecentTerm, + by: _mostRecentSatisfier, + incompatibility: incompatibility + ) } } @@ -658,7 +694,6 @@ public struct PubGrubDependencyResolver { private func computeCounts( for terms: [Term], - availableLibraries: [LibraryMetadata], completion: @escaping (Result<[Term: Int], Error>) -> Void ) { if terms.isEmpty { @@ -668,26 +703,26 @@ public struct PubGrubDependencyResolver { let sync = DispatchGroup() let results = ThreadSafeKeyValueStore>() - terms.forEach { term in + for term in terms { sync.enter() - provider.getContainer(for: term.node.package, availableLibraries: availableLibraries) { result in + self.provider.getContainer(for: term.node.package) { result in defer { sync.leave() } - results[term] = result.flatMap { container in Result(catching: { try container.versionCount(term.requirement) }) } + results[term] = result + .flatMap { container in Result(catching: { try container.versionCount(term.requirement) }) } } } sync.notify(queue: .sharedConcurrent) { do { - completion(.success(try results.mapValues { try $0.get() })) + try completion(.success(results.mapValues { try $0.get() })) } catch { completion(.failure(error)) } } } - internal func makeDecision( + func makeDecision( state: State, - availableLibraries: [LibraryMetadata] = [], completion: @escaping (Result) -> Void ) { // If there are no more undecided terms, version solving is complete. @@ -696,9 +731,33 @@ public struct PubGrubDependencyResolver { return completion(.success(nil)) } + // If prebuilt libraries are available, let's attempt their versions first before going for + // the latest viable version in the package. This way we archive multiple goals - prioritize + // prebuilt libraries if they satisfy all requirements, avoid counting and building package + // manifests and avoid (re-)building packages. + // + // Since the conflict resolution learns from incorrect terms this wouldn't be re-attempted. + if !self.availableLibraries.isEmpty { + let start = DispatchTime.now() + for pkgTerm in undecided { + let package = pkgTerm.node.package + guard let library = package.matchingPrebuiltLibrary(in: self.availableLibraries) else { + continue + } + + let version = Version(stringLiteral: library.version) + + if pkgTerm.requirement.contains(version) { + self.delegate?.didResolve(term: pkgTerm, version: version, duration: start.distance(to: .now())) + state.decide(pkgTerm.node, at: version) + return completion(.success(pkgTerm.node)) + } + } + } + // Prefer packages with least number of versions that fit the current requirements so we // get conflicts (if any) sooner. - self.computeCounts(for: undecided, availableLibraries: availableLibraries) { result in + self.computeCounts(for: undecided) { result in do { let start = DispatchTime.now() let counts = try result.get() @@ -710,7 +769,10 @@ public struct PubGrubDependencyResolver { // Get the best available version for this package. guard let version = try container.getBestAvailableVersion(for: pkgTerm) else { - state.addIncompatibility(try Incompatibility(pkgTerm, root: state.root, cause: .noAvailableVersion), at: .decisionMaking) + try state.addIncompatibility( + Incompatibility(pkgTerm, root: state.root, cause: .noAvailableVersion), + at: .decisionMaking + ) return completion(.success(pkgTerm.node)) } @@ -751,18 +813,17 @@ public struct PubGrubDependencyResolver { } } -internal enum LogLocation: String { +enum LogLocation: String { case topLevel = "top level" case unitPropagation = "unit propagation" case decisionMaking = "decision making" case conflictResolution = "conflict resolution" } -public extension PubGrubDependencyResolver { - enum PubgrubError: Swift.Error, CustomStringConvertible { +extension PubGrubDependencyResolver { + public enum PubgrubError: Swift.Error, CustomStringConvertible { case _unresolvable(Incompatibility, [DependencyResolutionNode: [Incompatibility]]) case unresolvable(String) - case potentiallyUnresovableDueToPrebuiltLibrary([PackageReference], String) public var description: String { switch self { @@ -770,8 +831,6 @@ public extension PubGrubDependencyResolver { return rootCause.description case .unresolvable(let error): return error - case .potentiallyUnresovableDueToPrebuiltLibrary(_, let error): - return error } } @@ -781,8 +840,6 @@ public extension PubGrubDependencyResolver { return rootCause case .unresolvable: return nil - case .potentiallyUnresovableDueToPrebuiltLibrary: - return nil } } @@ -792,8 +849,6 @@ public extension PubGrubDependencyResolver { return incompatibilities case .unresolvable: return nil - case .potentiallyUnresovableDueToPrebuiltLibrary: - return nil } } } @@ -809,7 +864,7 @@ extension PubGrubDependencyResolver { self.requirement = req } - internal static func constraints(_ constraint: Constraint) -> [VersionBasedConstraint]? { + static func constraints(_ constraint: Constraint) -> [VersionBasedConstraint]? { switch constraint.requirement { case .versionSet(let req): return constraint.nodes().map { VersionBasedConstraint(node: $0, req: req) } @@ -828,8 +883,8 @@ private enum PropagationResult { case none } -private extension PackageRequirement { - var isRevision: Bool { +extension PackageRequirement { + fileprivate var isRevision: Bool { switch self { case .versionSet, .unversioned: return false @@ -852,8 +907,10 @@ extension PackageReference { scope: registryIdentity.scope.description, name: registryIdentity.name.description ) - }) - }) + } + ) + } + ) } else { return nil } diff --git a/Sources/SPMTestSupport/Resolver.swift b/Sources/SPMTestSupport/Resolver.swift deleted file mode 100644 index 1e1282b7552..00000000000 --- a/Sources/SPMTestSupport/Resolver.swift +++ /dev/null @@ -1,19 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2024 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 PackageGraph - -extension PubGrubDependencyResolver { - package func solve(constraints: [Constraint]) -> Result<[DependencyResolverBinding], Error> { - return solve(constraints: constraints, availableLibraries: [], preferPrebuiltLibraries: false) - } -} diff --git a/Sources/Workspace/ResolverPrecomputationProvider.swift b/Sources/Workspace/ResolverPrecomputationProvider.swift index 3d9803adea7..7308bbcfcac 100644 --- a/Sources/Workspace/ResolverPrecomputationProvider.swift +++ b/Sources/Workspace/ResolverPrecomputationProvider.swift @@ -44,19 +44,14 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { /// The tools version currently in use. let currentToolsVersion: ToolsVersion - /// The available libraries in the SDK. - let availableLibraries: [LibraryMetadata] - init( root: PackageGraphRoot, dependencyManifests: Workspace.DependencyManifests, - currentToolsVersion: ToolsVersion = ToolsVersion.current, - availableLibraries: [LibraryMetadata] + currentToolsVersion: ToolsVersion = ToolsVersion.current ) { self.root = root self.dependencyManifests = dependencyManifests self.currentToolsVersion = currentToolsVersion - self.availableLibraries = availableLibraries } func getContainer( @@ -68,7 +63,9 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { ) { queue.async { // Start by searching manifests from the Workspace's resolved dependencies. - if let manifest = self.dependencyManifests.dependencies.first(where: { _, managed, _, _ in managed.packageRef == package }) { + if let manifest = self.dependencyManifests.dependencies + .first(where: { _, managed, _, _ in managed.packageRef == package }) + { let container = LocalPackageContainer( package: package, manifest: manifest.manifest, @@ -89,15 +86,6 @@ struct ResolverPrecomputationProvider: PackageContainerProvider { return completion(.success(container)) } - // Match against available prebuilt libraries. - if let matchingPrebuiltLibrary = package.matchingPrebuiltLibrary(in: availableLibraries) { - do { - return completion(.success(try PrebuiltPackageContainer(metadata: matchingPrebuiltLibrary))) - } catch { - return completion(.failure(error)) - } - } - // As we don't have anything else locally, error out. completion(.failure(ResolverPrecomputationError.missingPackage(package: package))) } @@ -113,7 +101,7 @@ private struct LocalPackageContainer: PackageContainer { let shouldInvalidatePinnedVersions = false func versionsAscending() throws -> [Version] { - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.version(let version, revision: _)): return [version] case .registryDownload(let version): @@ -125,7 +113,10 @@ private struct LocalPackageContainer: PackageContainer { func isToolsVersionCompatible(at version: Version) -> Bool { do { - try manifest.toolsVersion.validateToolsVersion(currentToolsVersion, packageIdentity: .plain("unknown")) + try self.manifest.toolsVersion.validateToolsVersion( + self.currentToolsVersion, + packageIdentity: .plain("unknown") + ) return true } catch { return false @@ -133,31 +124,36 @@ private struct LocalPackageContainer: PackageContainer { } func toolsVersion(for version: Version) throws -> ToolsVersion { - return currentToolsVersion + self.currentToolsVersion } func toolsVersionsAppropriateVersionsDescending() throws -> [Version] { - return try self.versionsDescending() + try self.versionsDescending() } func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { // Because of the implementation of `reversedVersions`, we should only get the exact same version. - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.version(version, revision: _)): - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) case .registryDownload(version: version): - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: - throw InternalError("expected version based state, but state was \(String(describing: dependency?.state))") + throw InternalError( + "expected version based state, but state was \(String(describing: self.dependency?.state))" + ) } } - func getDependencies(at revisionString: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + func getDependencies( + at revisionString: String, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { let revision = Revision(identifier: revisionString) - switch dependency?.state { + switch self.dependency?.state { case .sourceControlCheckout(.branch(_, revision: revision)), .sourceControlCheckout(.revision(revision)): // Return the dependencies if the checkout state matches the revision. - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: // Throw an error when the dependency is not revision based to fail resolution. throw ResolverPrecomputationError.differentRequirement( @@ -169,14 +165,14 @@ private struct LocalPackageContainer: PackageContainer { } func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - switch dependency?.state { + switch self.dependency?.state { case .none, .fileSystem, .edited: - return try manifest.dependencyConstraints(productFilter: productFilter) + return try self.manifest.dependencyConstraints(productFilter: productFilter) default: // Throw an error when the dependency is not unversioned to fail resolution. throw ResolverPrecomputationError.differentRequirement( - package: package, - state: dependency?.state, + package: self.package, + state: self.dependency?.state, requirement: .unversioned ) } diff --git a/Sources/Workspace/Workspace+Dependencies.swift b/Sources/Workspace/Workspace+Dependencies.swift index 3157260e268..5ece297dc4a 100644 --- a/Sources/Workspace/Workspace+Dependencies.swift +++ b/Sources/Workspace/Workspace+Dependencies.swift @@ -121,12 +121,15 @@ extension Workspace { } // Resolve the dependencies. - let resolver = try self.createResolver(pins: pins, observabilityScope: observabilityScope) + let resolver = try self.createResolver( + pins: pins, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) self.activeResolver = resolver let updateResults = self.resolveDependencies( resolver: resolver, - availableLibraries: availableLibraries, constraints: updateConstraints, observabilityScope: observabilityScope ) @@ -591,12 +594,15 @@ extension Workspace { computedConstraints += try graphRoot.constraints() + constraints // Perform dependency resolution. - let resolver = try self.createResolver(pins: pinsStore.pins, observabilityScope: observabilityScope) + let resolver = try self.createResolver( + pins: pinsStore.pins, + availableLibraries: availableLibraries, + observabilityScope: observabilityScope + ) self.activeResolver = resolver let result = self.resolveDependencies( resolver: resolver, - availableLibraries: availableLibraries, constraints: computedConstraints, observabilityScope: observabilityScope ) @@ -842,19 +848,15 @@ extension Workspace { let precomputationProvider = ResolverPrecomputationProvider( root: root, - dependencyManifests: dependencyManifests, - availableLibraries: availableLibraries + dependencyManifests: dependencyManifests ) let resolver = PubGrubDependencyResolver( provider: precomputationProvider, pins: pinsStore.pins, - observabilityScope: observabilityScope - ) - let result = resolver.solve( - constraints: computedConstraints, availableLibraries: availableLibraries, - preferPrebuiltLibraries: true + observabilityScope: observabilityScope ) + let result = resolver.solve(constraints: computedConstraints) guard !observabilityScope.errorsReported else { return .required(reason: .errorsPreviouslyReported) @@ -1051,7 +1053,9 @@ extension Workspace { completion: $0 ) }) as? SourceControlPackageContainer else { - throw InternalError("invalid container for \(binding.package) 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) @@ -1122,6 +1126,7 @@ extension Workspace { /// Creates resolver for the workspace. fileprivate func createResolver( pins: PinsStore.Pins, + availableLibraries: [LibraryMetadata], observabilityScope: ObservabilityScope ) throws -> PubGrubDependencyResolver { var delegate: DependencyResolverDelegate @@ -1138,6 +1143,7 @@ extension Workspace { return PubGrubDependencyResolver( provider: packageContainerProvider, pins: pins, + availableLibraries: availableLibraries, skipDependenciesUpdates: self.configuration.skipDependenciesUpdates, prefetchBasedOnResolvedFile: self.configuration.prefetchBasedOnResolvedFile, observabilityScope: observabilityScope, @@ -1148,24 +1154,12 @@ extension Workspace { /// Runs the dependency resolver based on constraints provided and returns the results. fileprivate func resolveDependencies( resolver: PubGrubDependencyResolver, - availableLibraries: [LibraryMetadata], constraints: [PackageContainerConstraint], observabilityScope: ObservabilityScope ) -> [DependencyResolverBinding] { os_signpost(.begin, name: SignpostName.pubgrub) - var result = resolver.solve( - constraints: constraints, - availableLibraries: availableLibraries, - preferPrebuiltLibraries: true - ) - // If the initial resolution failed due to prebuilt libraries, we try to resolve again without prebuilt libraries. - if case let Result.failure(error as PubGrubDependencyResolver.PubgrubError) = result, case .potentiallyUnresovableDueToPrebuiltLibrary = error { - result = resolver.solve( - constraints: constraints, - availableLibraries: availableLibraries, - preferPrebuiltLibraries: false - ) - } + let result = resolver.solve(constraints: constraints) + os_signpost(.end, name: SignpostName.pubgrub) // Take an action based on the result. diff --git a/Tests/PackageGraphTests/PubgrubTests.swift b/Tests/PackageGraphTests/PubgrubTests.swift index b05bd760340..7684413fa48 100644 --- a/Tests/PackageGraphTests/PubgrubTests.swift +++ b/Tests/PackageGraphTests/PubgrubTests.swift @@ -54,11 +54,11 @@ private let v1_1: Version = "1.1.0" private let v1_5: Version = "1.5.0" private let v2: Version = "2.0.0" private let v3: Version = "3.0.0" -private let v1Range: VersionSetSpecifier = .range(v1..=1.0.0 <1.5.0 XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("¬a@1.5.0")), - Term("a-1.0.0-1.5.0")) + Term("a-1.0.0-1.5.0") + ) // a^1.0.0 ∩ a >=1.5.0 <3.0.0 → a^1.5.0 XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("a-1.5.0-3.0.0")), - Term("a^1.5.0")) + Term("a^1.5.0") + ) // ¬a^1.0.0 ∩ ¬a >=1.5.0 <3.0.0 → ¬a >=1.0.0 <3.0.0 XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a-1.5.0-3.0.0")), - Term("¬a-1.0.0-3.0.0")) + Term("¬a-1.0.0-3.0.0") + ) XCTAssertEqual( Term("a^1.0.0").intersect(with: Term("a^1.0.0")), - Term("a^1.0.0")) + Term("a^1.0.0") + ) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a^1.0.0")), - Term("¬a^1.0.0")) + Term("¬a^1.0.0") + ) XCTAssertNil(Term("a^1.0.0").intersect(with: Term("¬a^1.0.0"))) XCTAssertNil(Term("a@1.0.0").difference(with: Term("a@1.0.0"))) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("a^2.0.0")), - Term("a^2.0.0")) + Term("a^2.0.0") + ) XCTAssertEqual( Term("a^2.0.0").intersect(with: Term("¬a^1.0.0")), - Term("a^2.0.0")) + Term("a^2.0.0") + ) XCTAssertEqual( Term("¬a^1.0.0").intersect(with: Term("¬a^1.0.0")), - Term("¬a^1.0.0")) + Term("¬a^1.0.0") + ) XCTAssertEqual( Term("¬a@1.0.0").intersect(with: Term("¬a@1.0.0")), - Term("¬a@1.0.0")) + Term("¬a@1.0.0") + ) // Check difference. let anyA = Term(.empty(package: "a"), .any) @@ -184,7 +193,7 @@ final class PubgrubTests: XCTestCase { func testTermIsValidDecision() { let solution100_150 = PartialSolution(assignments: [ .derivation("a^1.0.0", cause: _cause, decisionLevel: 1), - .derivation("a^1.5.0", cause: _cause, decisionLevel: 2) + .derivation("a^1.5.0", cause: _cause, decisionLevel: 2), ]) let allSatisfied = Term("a@1.6.0") @@ -194,26 +203,33 @@ final class PubgrubTests: XCTestCase { } func testIncompatibilityNormalizeTermsOnInit() throws { - let i1 = try Incompatibility(Term("a^1.0.0"), Term("a^1.5.0"), Term("¬b@1.0.0"), - root: rootNode) + let i1 = try Incompatibility( + Term("a^1.0.0"), + Term("a^1.5.0"), + Term("¬b@1.0.0"), + root: rootNode + ) XCTAssertEqual(i1.terms.count, 2) let a1 = i1.terms.first { $0.node.package == "a" } let b1 = i1.terms.first { $0.node.package == "b" } XCTAssertEqual(a1?.requirement, v1_5Range) XCTAssertEqual(b1?.requirement, .exact(v1)) - let i2 = try Incompatibility(Term("¬a^1.0.0"), Term("a^2.0.0"), - root: rootNode) + let i2 = try Incompatibility( + Term("¬a^1.0.0"), + Term("a^2.0.0"), + root: rootNode + ) XCTAssertEqual(i2.terms.count, 1) let a2 = i2.terms.first XCTAssertEqual(a2?.requirement, v2Range) } func testSolutionPositive() { - let s1 = PartialSolution(assignments:[ + let s1 = PartialSolution(assignments: [ .derivation("a^1.5.0", cause: _cause, decisionLevel: 0), .derivation("b@2.0.0", cause: _cause, decisionLevel: 0), - .derivation("a^1.0.0", cause: _cause, decisionLevel: 0) + .derivation("a^1.0.0", cause: _cause, decisionLevel: 0), ]) let a1 = s1._positive.first { $0.key.package.identity == PackageIdentity("a") }?.value XCTAssertEqual(a1?.requirement, v1_5Range) @@ -222,10 +238,10 @@ final class PubgrubTests: XCTestCase { let s2 = PartialSolution(assignments: [ .derivation("¬a^1.5.0", cause: _cause, decisionLevel: 0), - .derivation("a^1.0.0", cause: _cause, decisionLevel: 0) + .derivation("a^1.0.0", cause: _cause, decisionLevel: 0), ]) let a2 = s2._positive.first { $0.key.package.identity == PackageIdentity("a") }?.value - XCTAssertEqual(a2?.requirement, .range(v1.. non-versioned -> version func testHappyPath2() throws { try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) - try builder.serve("config", at: v1) - try builder.serve("config", at: v1_1) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) + try builder.serve("config", at: v1) + try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) @@ -997,14 +1091,18 @@ final class PubgrubTests: XCTestCase { func testHappyPath3() throws { try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1027,7 +1125,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) + "foo": (.versionSet(v1Range), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1041,14 +1139,18 @@ final class PubgrubTests: XCTestCase { // root -> version // root -> non-versioned -> version func testHappyPath5() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1059,19 +1161,22 @@ final class PubgrubTests: XCTestCase { ]) } - // root -> version // root -> non-versioned -> non-versioned -> version func testHappyPath6() throws { try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1090,15 +1195,16 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1_1, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1110,7 +1216,6 @@ final class PubgrubTests: XCTestCase { ]) } - // top level package -> version // top level package -> non-versioned -> version func testHappyPath8() throws { @@ -1118,15 +1223,20 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1145,17 +1255,22 @@ final class PubgrubTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v1_1Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v1_1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1175,10 +1290,19 @@ final class PubgrubTests: XCTestCase { let package = PackageReference.root(identity: .plain("package"), path: .root) try builder.serve(package, at: .unversioned, with: [ "module": [ - "foo": (.versionSet(.range("1.0.0-alpha" ..< "2.0.0")), .specific(["foo"])) - ]]) - try builder.serve("foo", at: "1.0.0-alpha.1", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) - try builder.serve("foo", at: "1.0.0-alpha.2", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + "foo": (.versionSet(.range("1.0.0-alpha" ..< "2.0.0")), .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: "1.0.0-alpha.1", + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) + try builder.serve( + "foo", + at: "1.0.0-alpha.2", + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("foo", at: "1.0.0-beta.1", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) try builder.serve("foo", at: "1.0.0-beta.2", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) try builder.serve("foo", at: "1.0.0-beta.3", with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) @@ -1188,7 +1312,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -1196,29 +1320,37 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("package", .unversioned), ("foo", .version("1.0.0-beta.3")), - ("bar", .version(v1_5)) + ("bar", .version(v1_5)), ]) } func testResolutionWithSimpleBranchBasedDependency() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("bar", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.revision("master"), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } func testResolutionWithSimpleBranchBasedDependency2() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) try builder.serve("bar", at: v1) let resolver = builder.create() @@ -1229,7 +1361,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } @@ -1247,7 +1379,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } @@ -1264,17 +1396,25 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("foo", .revision("master")), - ("bar", .version(v1)) + ("bar", .version(v1)), ]) } func testResolutionWithOverridingBranchBasedDependency3() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .revision("master")) try builder.serve("bar", at: v1) - try builder.serve("baz", at: .revision("master"), with: ["baz": ["bar": (.versionSet(v1Range), .specific(["bar"]))]]) + try builder.serve( + "baz", + at: .revision("master"), + with: ["baz": ["bar": (.versionSet(v1Range), .specific(["bar"]))]] + ) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ @@ -1295,7 +1435,7 @@ final class PubgrubTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "foo": (.revision("master"), .specific(["foo"])) + "foo": (.revision("master"), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -1303,7 +1443,11 @@ final class PubgrubTests: XCTestCase { } func testResolutionWithRevisionConflict() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .version(v1)) try builder.serve("bar", at: .revision("master")) @@ -1341,7 +1485,7 @@ final class PubgrubTests: XCTestCase { AssertResult(result, [ ("swift-nio-ssl", .revision("master")), ("swift-nio", .revision("master")), - ("foo", .version(v1)) + ("foo", .version(v1)), ]) } @@ -1355,13 +1499,13 @@ final class PubgrubTests: XCTestCase { "nio-postgres": [ "swift-nio": (.revision("master"), .specific(["swift-nio"])), "swift-nio-ssl": (.revision("master"), .specific(["swift-nio-ssl"])), - ] + ], ]) try builder.serve("http-client", at: v1, with: [ "http-client": [ "swift-nio": (.versionSet(v1Range), .specific(["swift-nio"])), "boring-ssl": (.versionSet(v1Range), .specific(["boring-ssl"])), - ] + ], ]) try builder.serve("boring-ssl", at: v1, with: [ "boring-ssl": ["swift-nio": (.versionSet(v1Range), .specific(["swift-nio"]))], @@ -1386,7 +1530,7 @@ final class PubgrubTests: XCTestCase { func testNonVersionDependencyInVersionDependency2() throws { try builder.serve("foo", at: v1_1, with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: v1) let resolver = builder.create() @@ -1420,12 +1564,15 @@ final class PubgrubTests: XCTestCase { let result = try resolver.solve(root: rootNode, constraints: dependencies) // Since a was pinned, we shouldn't have computed bounds for its incomaptibilities. - let aIncompat = result.state.positiveIncompatibilities(for: .product("a", package: try builder.reference(for: "a")))![0] + let aIncompat = try result.state.positiveIncompatibilities(for: .product( + "a", + package: builder.reference(for: "a") + ))![0] XCTAssertEqual(aIncompat.terms[0].requirement, .exact("1.0.0")) AssertResult(Result.success(result.bindings), [ ("a", .version(v1)), - ("b", .version(v1)) + ("b", .version(v1)), ]) } @@ -1436,7 +1583,7 @@ final class PubgrubTests: XCTestCase { try builder.serve("a", at: v1_1) try builder.serve("b", at: v1) try builder.serve("b", at: v1_1) - try builder.serve("c", at: v1, with: ["c": ["b": (.versionSet(.range(v1_1..= 1.0.0 practically depends on 'baz' 3.0.0..<4.0.0. - 'bar' >= 2.0.0 practically depends on 'baz' 3.0.0..<4.0.0 because 'bar' 2.0.0 depends on 'baz' 3.0.0..<4.0.0 and no versions of 'bar' match the requirement 2.0.1..<3.0.0. - 'foo' >= 1.0.0 practically depends on 'bar' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'bar' 2.0.0..<3.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'baz' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'baz' 3.0.0..<4.0.0. + 'bar' >= 2.0.0 practically depends on 'baz' 3.0.0..<4.0.0 because 'bar' 2.0.0 depends on 'baz' 3.0.0..<4.0.0 and no versions of 'bar' match the requirement 2.0.1..<3.0.0. + 'foo' >= 1.0.0 practically depends on 'bar' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'bar' 2.0.0..<3.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + """) } func testResolutionBranchingErrorReporting() throws { try builder.serve("foo", at: v1, with: [ "foo": [ "a": (.versionSet(v1Range), .specific(["a"])), - "b": (.versionSet(v1Range), .specific(["b"])) - ] + "b": (.versionSet(v1Range), .specific(["b"])), + ], ]) try builder.serve("foo", at: v1_1, with: [ "foo": [ "x": (.versionSet(v1Range), .specific(["x"])), - "y": (.versionSet(v1Range), .specific(["y"])) - ] + "y": (.versionSet(v1Range), .specific(["y"])), + ], ]) try builder.serve("a", at: v1, with: ["a": ["b": (.versionSet(v2Range), .specific(["b"]))]]) try builder.serve("b", at: v1) @@ -2047,14 +2303,14 @@ final class PubGrubDiagnosticsTests: XCTestCase { print(result.errorMsg!) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 cannot be used because 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used (1). - 'foo' 1.1.0 cannot be used because 'foo' 1.1.0 depends on 'x' 1.0.0..<2.0.0 and 'foo' 1.1.0 depends on 'y' 1.0.0..<2.0.0. - 'x' >= 1.0.0 practically depends on 'y' 2.0.0..<3.0.0 because 'x' 1.0.0 depends on 'y' 2.0.0..<3.0.0 and no versions of 'x' match the requirement 1.0.1..<2.0.0. - 'foo' 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'a' 1.0.0..<2.0.0. - 'a' >= 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'a' 1.0.0 depends on 'b' 2.0.0..<3.0.0 and no versions of 'a' match the requirement 1.0.1..<2.0.0. - (1) As a result, 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because 'foo' 1.0.0 depends on 'b' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0}. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 cannot be used because 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used (1). + 'foo' 1.1.0 cannot be used because 'foo' 1.1.0 depends on 'x' 1.0.0..<2.0.0 and 'foo' 1.1.0 depends on 'y' 1.0.0..<2.0.0. + 'x' >= 1.0.0 practically depends on 'y' 2.0.0..<3.0.0 because 'x' 1.0.0 depends on 'y' 2.0.0..<3.0.0 and no versions of 'x' match the requirement 1.0.1..<2.0.0. + 'foo' 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'foo' 1.0.0 depends on 'a' 1.0.0..<2.0.0. + 'a' >= 1.0.0 practically depends on 'b' 2.0.0..<3.0.0 because 'a' 1.0.0 depends on 'b' 2.0.0..<3.0.0 and no versions of 'a' match the requirement 1.0.1..<2.0.0. + (1) As a result, 'foo' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because 'foo' 1.0.0 depends on 'b' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0}. + """) } func testConflict1() throws { @@ -2066,15 +2322,15 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. - 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. + 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } func testConflict2() throws { @@ -2093,9 +2349,9 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result1 = resolver1.solve(constraints: dependencies1) XCTAssertEqual(result1.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) let dependencies2 = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), @@ -2106,9 +2362,9 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result2 = resolver2.solve(constraints: dependencies2) XCTAssertEqual(result2.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'config' 2.0.0..<3.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'config' 2.0.0..<3.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + """) } func testConflict3() throws { @@ -2123,16 +2379,16 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and root depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and root depends on 'config' 2.0.0..<3.0.0. + """) } func testConflict4() throws { try builder.serve("foo", at: v1, with: [ - "foo": ["shared": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["shared"]))], + "foo": ["shared": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["shared"]))], ]) try builder.serve("bar", at: v1, with: [ - "bar": ["shared": (.versionSet(.range("2.9.0"..<"4.0.0")), .specific(["shared"]))], + "bar": ["shared": (.versionSet(.range("2.9.0" ..< "4.0.0")), .specific(["shared"]))], ]) try builder.serve("shared", at: "2.5.0") try builder.serve("shared", at: "3.5.0") @@ -2146,10 +2402,10 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'bar' 1.0.0 and root depends on 'foo' 1.0.0. - 'foo' is incompatible with 'bar' because 'foo' 1.0.0 depends on 'shared' 2.0.0..<3.0.0. - 'bar' 1.0.0 practically depends on 'shared' 3.0.0..<4.0.0 because 'bar' 1.0.0 depends on 'shared' 2.9.0..<4.0.0 and no versions of 'shared' match the requirement 2.9.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'bar' 1.0.0 and root depends on 'foo' 1.0.0. + 'foo' is incompatible with 'bar' because 'foo' 1.0.0 depends on 'shared' 2.0.0..<3.0.0. + 'bar' 1.0.0 practically depends on 'shared' 3.0.0..<4.0.0 because 'bar' 1.0.0 depends on 'shared' 2.9.0..<4.0.0 and no versions of 'shared' match the requirement 2.9.0..<3.0.0. + """) } func testConflict5() throws { @@ -2168,19 +2424,19 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 0.0.0..<5.0.0. - 'a' cannot be used. - 'a' 2.0.0 cannot be used because 'b' 2.0.0 depends on 'a' 1.0.0 and 'a' 2.0.0 depends on 'b' 2.0.0. - 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} cannot be used because 'b' 1.0.0 depends on 'a' 2.0.0. - 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} practically depends on 'b' 1.0.0 because no versions of 'a' match the requirement {0.0.0..<1.0.0, 1.0.1..<2.0.0, 2.0.1..<5.0.0} and 'a' 1.0.0 depends on 'b' 1.0.0. - """) + Dependencies could not be resolved because root depends on 'a' 0.0.0..<5.0.0. + 'a' cannot be used. + 'a' 2.0.0 cannot be used because 'b' 2.0.0 depends on 'a' 1.0.0 and 'a' 2.0.0 depends on 'b' 2.0.0. + 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} cannot be used because 'b' 1.0.0 depends on 'a' 2.0.0. + 'a' {0.0.0..<2.0.0, 2.0.1..<5.0.0} practically depends on 'b' 1.0.0 because no versions of 'a' match the requirement {0.0.0..<1.0.0, 1.0.1..<2.0.0, 2.0.1..<5.0.0} and 'a' 1.0.0 depends on 'b' 1.0.0. + """) } // root -> version -> version @@ -2194,38 +2450,42 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.versionSet(v1Range), .specific(["bar"])) + "bar": (.versionSet(v1Range), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. - 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. - 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0 and root depends on 'bar' 1.0.0..<2.0.0. + 'bar' is incompatible with 'foo' because 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0 and no versions of 'foo' match the requirement 1.0.1..<2.0.0. + 'bar' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'bar' match the requirement 1.0.1..<2.0.0 and 'bar' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // root -> version -> version // root -> non-versioned -> conflicting version func testConflict7() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'bar' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'bar' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version -> version @@ -2233,81 +2493,97 @@ final class PubGrubDiagnosticsTests: XCTestCase { func testConflict8() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 1.0.0..<2.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version -> version // root -> non-versioned -> non-existing version func testConflict9() throws { try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v1Range), .specific(["config"]))]]) - try builder.serve("bar", at: .unversioned, with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "bar", + at: .unversioned, + with: ["bar": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "foo": (.versionSet(v1Range), .specific(["foo"])), - "bar": (.unversioned, .specific(["bar"])) + "bar": (.unversioned, .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and 'bar' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because no versions of 'config' match the requirement 2.0.0..<3.0.0 and 'bar' depends on 'config' 2.0.0..<3.0.0. + """) } // root -> version // root -> non-versioned -> conflicting version func testConflict10() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version // root -> non-versioned -> non-existing version func testConflict11() throws { - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'foo' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // root -> version @@ -2315,20 +2591,24 @@ final class PubGrubDiagnosticsTests: XCTestCase { func testConflict12() throws { try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) + "foo": (.unversioned, .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. - """) + Dependencies could not be resolved because 'baz' depends on 'config' 2.0.0..<3.0.0 and root depends on 'config' 1.0.0..<2.0.0. + """) } // top level package -> version @@ -2338,23 +2618,24 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2364,22 +2645,23 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.versionSet(v1Range), .specific(["foo"])) - ]]) + "foo": (.versionSet(v1Range), .specific(["foo"])), + ], + ]) try builder.serve("foo", at: v1, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. - 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and root depends on 'foo' 1.0.0..<2.0.0. + 'foo' >= 1.0.0 practically depends on 'config' 2.0.0..<3.0.0 because no versions of 'foo' match the requirement 1.0.1..<2.0.0 and 'foo' 1.0.0 depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2389,22 +2671,27 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2414,21 +2701,26 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) - try builder.serve("foo", at: .unversioned, with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) + try builder.serve( + "foo", + at: .unversioned, + with: ["foo": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'foo' depends on 'config' 2.0.0..<3.0.0. + """) } // top level package -> version @@ -2438,49 +2730,65 @@ final class PubGrubDiagnosticsTests: XCTestCase { try builder.serve(package, at: .unversioned, with: [ "module": [ "config": (.versionSet(v1Range), .specific(["config"])), - "foo": (.unversioned, .specific(["foo"])) - ]]) + "foo": (.unversioned, .specific(["foo"])), + ], + ]) try builder.serve("foo", at: .unversioned, with: ["foo": ["bar": (.unversioned, .specific(["bar"]))]]) try builder.serve("bar", at: .unversioned, with: ["bar": ["baz": (.unversioned, .specific(["baz"]))]]) - try builder.serve("baz", at: .unversioned, with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]]) + try builder.serve( + "baz", + at: .unversioned, + with: ["baz": ["config": (.versionSet(v2Range), .specific(["config"]))]] + ) try builder.serve("config", at: v1) try builder.serve("config", at: v2) let resolver = builder.create() let dependencies = builder.create(dependencies: [ - package: (.unversioned, .everything) + package: (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'baz' depends on 'config' 2.0.0..<3.0.0. - """) + Dependencies could not be resolved because root depends on 'config' 1.0.0..<2.0.0 and 'baz' depends on 'config' 2.0.0..<3.0.0. + """) } func testUnversioned6() throws { try builder.serve("foo", at: .unversioned) try builder.serve("bar", at: .revision("master"), with: [ - "bar": ["foo": (.unversioned, .specific(["foo"]))] + "bar": ["foo": (.unversioned, .specific(["foo"]))], ]) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "bar": (.revision("master"), .specific(["bar"])) + "bar": (.revision("master"), .specific(["bar"])), ]) let result = resolver.solve(constraints: dependencies) - XCTAssertEqual(result.errorMsg, "package 'bar' is required using a revision-based requirement and it depends on local package 'foo', which is not supported") + XCTAssertEqual( + result.errorMsg, + "package 'bar' is required using a revision-based requirement and it depends on local package 'foo', which is not supported" + ) } func testResolutionWithOverridingBranchBasedDependency4() throws { - try builder.serve("foo", at: .revision("master"), with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]]) + try builder.serve( + "foo", + at: .revision("master"), + with: ["foo": ["bar": (.revision("master"), .specific(["bar"]))]] + ) try builder.serve("bar", at: .revision("master")) try builder.serve("bar", at: v1) - try builder.serve("baz", at: .revision("master"), with: ["baz": ["bar": (.revision("develop"), .specific(["baz"]))]]) + try builder.serve( + "baz", + at: .revision("master"), + with: ["baz": ["bar": (.revision("develop"), .specific(["baz"]))]] + ) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ @@ -2489,12 +2797,15 @@ final class PubGrubDiagnosticsTests: XCTestCase { ]) let result = resolver.solve(constraints: dependencies) - XCTAssertEqual(result.errorMsg, "bar is required using two different revision-based requirements (master and develop), which is not supported") + XCTAssertEqual( + result.errorMsg, + "bar is required using two different revision-based requirements (master and develop), which is not supported" + ) } func testNonVersionDependencyInVersionDependency1() throws { try builder.serve("foo", at: v1_1, with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("bar", at: .revision("master")) @@ -2505,14 +2816,14 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. - 'foo' cannot be used because no versions of 'foo' match the requirement {1.0.0..<1.1.0, 1.1.1..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - """) + Dependencies could not be resolved because root depends on 'foo' 1.0.0..<2.0.0. + 'foo' cannot be used because no versions of 'foo' match the requirement {1.0.0..<1.1.0, 1.1.1..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + """) } func testNonVersionDependencyInVersionDependency2() throws { try builder.serve("foo", at: v1, with: [ - "foo": ["bar": (.unversioned, .specific(["bar"]))] + "foo": ["bar": (.unversioned, .specific(["bar"]))], ]) try builder.serve("bar", at: .unversioned) @@ -2523,19 +2834,19 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0. - """) + Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0. + """) } func testNonVersionDependencyInVersionDependency3() throws { try builder.serve("foo", at: "1.0.0-beta.1", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: "1.0.0-beta.2", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("foo", at: "1.0.0-beta.3", with: [ - "foo": ["bar": (.revision("master"), .specific(["bar"]))] + "foo": ["bar": (.revision("master"), .specific(["bar"]))], ]) try builder.serve("bar", at: .revision("master")) @@ -2546,10 +2857,10 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0-beta..<2.0.0. - 'foo' {1.0.0-beta..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - 'foo' {1.0.0-beta..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because no versions of 'foo' match the requirement {1.0.0-beta..<1.0.0-beta.1, 1.0.0-beta.1.0..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. - """) + Dependencies could not be resolved because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar' and root depends on 'foo' 1.0.0-beta..<2.0.0. + 'foo' {1.0.0-beta..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + 'foo' {1.0.0-beta..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} cannot be used because no versions of 'foo' match the requirement {1.0.0-beta..<1.0.0-beta.1, 1.0.0-beta.1.0..<1.0.0-beta.2, 1.0.0-beta.2.0..<1.0.0-beta.3, 1.0.0-beta.3.0..<2.0.0} and package 'foo' is required using a stable-version but 'foo' depends on an unstable-version package 'bar'. + """) } func testIncompatibleToolsVersion1() throws { @@ -2563,14 +2874,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0. - 'a' >= 1.0.0 cannot be used because no versions of 'a' match the requirement 1.0.1..<2.0.0 and 'a' 1.0.0 contains incompatible tools version (\(ToolsVersion.v5)). - """) + Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0. + 'a' >= 1.0.0 cannot be used because no versions of 'a' match the requirement 1.0.1..<2.0.0 and 'a' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v5 + )). + """) } func testIncompatibleToolsVersion3() throws { try builder.serve("a", at: v1_1, with: [ - "a": ["b": (.versionSet(v1Range), .specific(["b"]))] + "a": ["b": (.versionSet(v1Range), .specific(["b"]))], ]) try builder.serve("a", at: v1, toolsVersion: .v4) @@ -2586,10 +2900,13 @@ final class PubGrubDiagnosticsTests: XCTestCase { let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0 and root depends on 'b' 2.0.0..<3.0.0. - 'a' >= 1.0.0 practically depends on 'b' 1.0.0..<2.0.0 because 'a' 1.1.0 depends on 'b' 1.0.0..<2.0.0. - 'a' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because no versions of 'a' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0} and 'a' 1.0.0 contains incompatible tools version (\(ToolsVersion.v4)). - """) + Dependencies could not be resolved because root depends on 'a' 1.0.0..<2.0.0 and root depends on 'b' 2.0.0..<3.0.0. + 'a' >= 1.0.0 practically depends on 'b' 1.0.0..<2.0.0 because 'a' 1.1.0 depends on 'b' 1.0.0..<2.0.0. + 'a' {1.0.0..<1.1.0, 1.1.1..<2.0.0} cannot be used because no versions of 'a' match the requirement {1.0.1..<1.1.0, 1.1.1..<2.0.0} and 'a' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v4 + )). + """) } func testIncompatibleToolsVersion4() throws { @@ -2599,14 +2916,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' contains incompatible tools version (\(ToolsVersion.v3)) and root depends on 'a' 3.2.0..<4.0.0. - """) + Dependencies could not be resolved because 'a' contains incompatible tools version (\( + ToolsVersion + .v3 + )) and root depends on 'a' 3.2.0..<4.0.0. + """) } func testIncompatibleToolsVersion5() throws { @@ -2616,14 +2936,17 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' contains incompatible tools version (\(ToolsVersion.v5)) and root depends on 'a' 3.2.0..<4.0.0. - """) + Dependencies could not be resolved because 'a' contains incompatible tools version (\( + ToolsVersion + .v5 + )) and root depends on 'a' 3.2.0..<4.0.0. + """) } func testIncompatibleToolsVersion6() throws { @@ -2636,47 +2959,53 @@ final class PubGrubDiagnosticsTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("3.2.0"..<"4.0.0")), .specific(["a"])), + "a": (.versionSet(.range("3.2.0" ..< "4.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) XCTAssertEqual(result.errorMsg, """ - Dependencies could not be resolved because 'a' >= 3.2.1 contains incompatible tools version (\(ToolsVersion.v4)) and root depends on 'a' 3.2.0..<4.0.0. - 'a' 3.2.0 cannot be used because 'a' 3.2.0 depends on 'b' 1.0.0..<2.0.0. - 'b' >= 1.0.0 cannot be used because 'b' 1.0.0 contains incompatible tools version (\(ToolsVersion.v3)) and no versions of 'b' match the requirement 1.0.1..<2.0.0. - """) + Dependencies could not be resolved because 'a' >= 3.2.1 contains incompatible tools version (\( + ToolsVersion + .v4 + )) and root depends on 'a' 3.2.0..<4.0.0. + 'a' 3.2.0 cannot be used because 'a' 3.2.0 depends on 'b' 1.0.0..<2.0.0. + 'b' >= 1.0.0 cannot be used because 'b' 1.0.0 contains incompatible tools version (\( + ToolsVersion + .v3 + )) and no versions of 'b' match the requirement 1.0.1..<2.0.0. + """) } func testProductsCannotResolveToDifferentVersions() throws { try builder.serve("package", at: .unversioned, with: [ "package": [ "intermediate_a": (.versionSet(v1Range), .specific(["Intermediate A"])), - "intermediate_b": (.versionSet(v1Range), .specific(["Intermediate B"])) - ] + "intermediate_b": (.versionSet(v1Range), .specific(["Intermediate B"])), + ], ]) try builder.serve("intermediate_a", at: v1, with: [ "Intermediate A": [ - "transitive": (.versionSet(.exact(v1)), .specific(["Product A"])) - ] + "transitive": (.versionSet(.exact(v1)), .specific(["Product A"])), + ], ]) try builder.serve("intermediate_b", at: v1, with: [ "Intermediate B": [ - "transitive": (.versionSet(.exact(v1_1)), .specific(["Product B"])) - ] + "transitive": (.versionSet(.exact(v1_1)), .specific(["Product B"])), + ], ]) try builder.serve("transitive", at: v1, with: [ "Product A": [:], - "Product B": [:] + "Product B": [:], ]) try builder.serve("transitive", at: v1_1, with: [ "Product A": [:], - "Product B": [:] + "Product B": [:], ]) let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "package": (.unversioned, .everything) + "package": (.unversioned, .everything), ]) let result = resolver.solve(constraints: dependencies) @@ -2705,7 +3034,7 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["a"])), + "a": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["a"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2718,14 +3047,14 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack2() throws { try builder.serve("a", at: v1) try builder.serve("a", at: "2.0.0", with: [ - "a": ["c": (.versionSet(.range("1.0.0"..<"2.0.0")), .specific(["c"]))], + "a": ["c": (.versionSet(.range("1.0.0" ..< "2.0.0")), .specific(["c"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["c": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["c"]))], + "b": ["c": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["c"]))], ]) try builder.serve("b", at: "2.0.0", with: [ - "b": ["c": (.versionSet(.range("3.0.0"..<"4.0.0")), .specific(["c"]))], + "b": ["c": (.versionSet(.range("3.0.0" ..< "4.0.0")), .specific(["c"]))], ]) try builder.serve("c", at: "1.0.0") @@ -2734,8 +3063,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["a"])), - "b": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["b"])), + "a": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["a"])), + "b": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["b"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2749,18 +3078,18 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack3() throws { try builder.serve("a", at: "1.0.0", with: [ - "a": ["x": (.versionSet(.range("1.0.0"..<"5.0.0")), .specific(["x"]))], + "a": ["x": (.versionSet(.range("1.0.0" ..< "5.0.0")), .specific(["x"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["x": (.versionSet(.range("0.0.0"..<"2.0.0")), .specific(["x"]))], + "b": ["x": (.versionSet(.range("0.0.0" ..< "2.0.0")), .specific(["x"]))], ]) try builder.serve("c", at: "1.0.0") try builder.serve("c", at: "2.0.0", with: [ "c": [ - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - ] + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + ], ]) try builder.serve("x", at: "0.0.0") @@ -2774,8 +3103,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "c": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["c"])), - "y": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["y"])), + "c": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["c"])), + "y": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["y"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2788,18 +3117,18 @@ final class PubGrubBacktrackTests: XCTestCase { func testBacktrack4() throws { try builder.serve("a", at: "1.0.0", with: [ - "a": ["x": (.versionSet(.range("1.0.0"..<"5.0.0")), .specific(["x"]))], + "a": ["x": (.versionSet(.range("1.0.0" ..< "5.0.0")), .specific(["x"]))], ]) try builder.serve("b", at: "1.0.0", with: [ - "b": ["x": (.versionSet(.range("0.0.0"..<"2.0.0")), .specific(["x"]))], + "b": ["x": (.versionSet(.range("0.0.0" ..< "2.0.0")), .specific(["x"]))], ]) try builder.serve("c", at: "1.0.0") try builder.serve("c", at: "2.0.0", with: [ "c": [ - "a": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["a"])), - "b": (.versionSet(.range("0.0.0"..<"5.0.0")), .specific(["b"])), - ] + "a": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["a"])), + "b": (.versionSet(.range("0.0.0" ..< "5.0.0")), .specific(["b"])), + ], ]) try builder.serve("x", at: "0.0.0") @@ -2813,8 +3142,8 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "c": (.versionSet(.range("1.0.0"..<"3.0.0")), .specific(["c"])), - "y": (.versionSet(.range("2.0.0"..<"3.0.0")), .specific(["y"])), + "c": (.versionSet(.range("1.0.0" ..< "3.0.0")), .specific(["c"])), + "y": (.versionSet(.range("2.0.0" ..< "3.0.0")), .specific(["y"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2837,7 +3166,7 @@ final class PubGrubBacktrackTests: XCTestCase { ]) try builder.serve("bar", at: "1.0.0", with: [ - "bar": ["baz": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["baz"]))], + "bar": ["baz": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["baz"]))], ]) try builder.serve("bar", at: "2.0.0", with: [ "bar": ["baz": (.versionSet(.exact("3.0.0")), .specific(["baz"]))], @@ -2850,7 +3179,7 @@ final class PubGrubBacktrackTests: XCTestCase { let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "foo": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["foo"])), + "foo": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["foo"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2869,16 +3198,16 @@ final class PubGrubBacktrackTests: XCTestCase { "b": ["a": (.versionSet(.exact("1.0.0")), .specific(["a"]))], ]) try builder.serve("c", at: "1.0.0", with: [ - "c": ["b": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["b"]))], + "c": ["b": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["b"]))], ]) try builder.serve("d", at: "1.0.0") try builder.serve("d", at: "2.0.0") let resolver = builder.create() let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["a"])), - "c": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["c"])), - "d": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["d"])), + "a": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["a"])), + "c": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["c"])), + "d": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["d"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2898,18 +3227,21 @@ final class PubGrubBacktrackTests: XCTestCase { "b": ["a": (.versionSet(.exact("1.0.0")), .specific(["a"]))], ]) try builder.serve("c", at: "1.5.2", with: [ - "c": ["b": (.versionSet(.range("0.0.0"..<"3.0.0")), .specific(["b"]))], + "c": ["b": (.versionSet(.range("0.0.0" ..< "3.0.0")), .specific(["b"]))], ]) try builder.serve("d", at: "1.0.1") try builder.serve("d", at: "2.3.0") let observability = ObservabilitySystem.makeForTesting() - let resolver = builder.create(pins: [:], delegate: ObservabilityDependencyResolverDelegate(observabilityScope: observability.topScope)) + let resolver = builder.create( + pins: [:], + delegate: ObservabilityDependencyResolverDelegate(observabilityScope: observability.topScope) + ) let dependencies = try builder.create(dependencies: [ - "a": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["a"])), - "c": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["c"])), - "d": (.versionSet(.range("1.0.0"..<"4.0.0")), .specific(["d"])), + "a": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["a"])), + "c": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["c"])), + "d": (.versionSet(.range("1.0.0" ..< "4.0.0")), .specific(["d"])), ]) let result = resolver.solve(constraints: dependencies) @@ -2923,16 +3255,28 @@ final class PubGrubBacktrackTests: XCTestCase { observability.diagnostics.forEach { print("\($0)") } - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'a' @ '1.0.0'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'b' @ '1.0.1'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'c' @ '1.5.2'" })) - XCTAssertTrue(observability.diagnostics.contains(where: { $0.message == "[DependencyResolver] resolved 'd' @ '2.3.0'" })) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'a' @ '1.0.0'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'b' @ '1.0.1'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'c' @ '1.5.2'" }) + ) + XCTAssertTrue( + observability.diagnostics + .contains(where: { $0.message == "[DependencyResolver] resolved 'd' @ '2.3.0'" }) + ) } } -fileprivate extension PinsStore.PinState { +extension PinsStore.PinState { /// Creates a checkout state with the given version and a mocked revision. - static func version(_ version: Version) -> Self { + fileprivate static func version(_ version: Version) -> Self { .version(version, revision: .none) } } @@ -2952,9 +3296,13 @@ private func AssertBindings( pkg.identity != binding.package.identity }) } - .map { $0.package.identity } + .map(\.package.identity) - XCTFail("Unexpected binding(s) found for \(unexpectedBindings.map { $0.description }.joined(separator: ", ")).", file: file, line: line) + XCTFail( + "Unexpected binding(s) found for \(unexpectedBindings.map(\.description).joined(separator: ", ")).", + file: file, + line: line + ) } for package in packages { guard let binding = bindings.first(where: { $0.package.identity == package.identity }) else { @@ -2963,7 +3311,11 @@ private func AssertBindings( } if binding.boundVersion != package.version { - XCTFail("Expected \(package.version) for \(package.identity), found \(binding.boundVersion) instead.", file: file, line: line) + XCTFail( + "Expected \(package.version) for \(package.identity), found \(binding.boundVersion) instead.", + file: file, + line: line + ) } } } @@ -3001,7 +3353,11 @@ private func AssertError( // FIXME: this is not thread-safe public class MockContainer: PackageContainer { - public typealias Dependency = (container: PackageReference, requirement: PackageRequirement, productFilter: ProductFilter) + public typealias Dependency = ( + container: PackageReference, + requirement: PackageRequirement, + productFilter: ProductFilter + ) public var package: PackageReference var manifestName: PackageReference? @@ -3011,7 +3367,7 @@ public class MockContainer: PackageContainer { public var unversionedDeps: [PackageContainerConstraint] = [] /// The list of versions that have incompatible tools version. - var toolsVersion: ToolsVersion = ToolsVersion.current + var toolsVersion: ToolsVersion = .current var versionsToolsVersions = [Version: ToolsVersion]() private var _versions: [BoundVersion] @@ -3026,7 +3382,7 @@ public class MockContainer: PackageContainer { return versions } - public func versionsAscending() throws -> [Version] { + public func versionsAscending() throws -> [Version] { var versions: [Version] = [] for version in self._versions { guard case .version(let v) = version else { continue } @@ -3053,11 +3409,17 @@ public class MockContainer: PackageContainer { return version } - public func getDependencies(at version: Version, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { - return try getDependencies(at: version.description, productFilter: productFilter) + public func getDependencies( + at version: Version, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { + try self.getDependencies(at: version.description, productFilter: productFilter) } - public func getDependencies(at revision: String, productFilter: ProductFilter) throws -> [PackageContainerConstraint] { + public func getDependencies( + at revision: String, + productFilter: ProductFilter + ) throws -> [PackageContainerConstraint] { guard let revisionDependencies = dependencies[revision] else { throw _MockLoadingError.unknownRevision } @@ -3065,18 +3427,18 @@ public class MockContainer: PackageContainer { for (product, productDependencies) in revisionDependencies where productFilter.contains(product) { filteredDependencies.append(contentsOf: productDependencies) } - return filteredDependencies.map({ value in + return filteredDependencies.map { value in let (package, requirement, filter) = value return PackageContainerConstraint(package: package, requirement: requirement, products: filter) - }) + } } public func getUnversionedDependencies(productFilter: ProductFilter) throws -> [PackageContainerConstraint] { // FIXME: This is messy, remove unversionedDeps property. - if !unversionedDeps.isEmpty { - return unversionedDeps + if !self.unversionedDeps.isEmpty { + return self.unversionedDeps } - return try getDependencies(at: PackageRequirement.unversioned.description, productFilter: productFilter) + return try self.getDependencies(at: PackageRequirement.unversioned.description, productFilter: productFilter) } public func loadPackageReference(at boundVersion: BoundVersion) throws -> PackageReference { @@ -3099,11 +3461,19 @@ public class MockContainer: PackageContainer { public convenience init( package: PackageReference, - unversionedDependencies: [(package: PackageReference, requirement: PackageRequirement, productFilter: ProductFilter)] + unversionedDependencies: [( + package: PackageReference, + requirement: PackageRequirement, + productFilter: ProductFilter + )] ) { self.init(package: package) self.unversionedDeps = unversionedDependencies - .map { PackageContainerConstraint(package: $0.package, requirement: $0.requirement, products: $0.productFilter) } + .map { PackageContainerConstraint( + package: $0.package, + requirement: $0.requirement, + products: $0.productFilter + ) } } public convenience init( @@ -3112,16 +3482,17 @@ public class MockContainer: PackageContainer { package: PackageReference, requirement: VersionSetSpecifier, productFilter: ProductFilter - )]]]) { + )]]] + ) { var dependencies: [String: [String: [Dependency]]] = [:] for (version, productDependencies) in dependenciesByVersion { if dependencies[version.description] == nil { dependencies[version.description] = [:] } for (product, deps) in productDependencies { - dependencies[version.description, default: [:]][product] = deps.map({ + dependencies[version.description, default: [:]][product] = deps.map { ($0.package, .versionSet($0.requirement), $0.productFilter) - }) + } } } self.init(package: package, dependencies: dependencies) @@ -3146,13 +3517,12 @@ public enum _MockLoadingError: Error { } public struct MockProvider: PackageContainerProvider { - public let containers: [MockContainer] public let containersByIdentifier: [PackageReference: MockContainer] public init(containers: [MockContainer]) { self.containers = containers - self.containersByIdentifier = Dictionary(uniqueKeysWithValues: containers.map({ ($0.package, $0) })) + self.containersByIdentifier = Dictionary(uniqueKeysWithValues: containers.map { ($0.package, $0) }) } public func getContainer( @@ -3160,11 +3530,15 @@ public struct MockProvider: PackageContainerProvider { updateStrategy: ContainerUpdateStrategy, observabilityScope: ObservabilityScope, on queue: DispatchQueue, - completion: @escaping (Result - ) -> Void) { + completion: @escaping ( + Result + ) -> Void + ) { queue.async { - completion(self.containersByIdentifier[package].map{ .success($0) } ?? - .failure(_MockLoadingError.unknownModule)) + completion( + self.containersByIdentifier[package].map { .success($0) } ?? + .failure(_MockLoadingError.unknownModule) + ) } } } @@ -3177,7 +3551,10 @@ class DependencyGraphBuilder { if let reference = self.references[packageName] { return reference } - let newReference = PackageReference.localSourceControl(identity: .plain(packageName), path: try .init(validating: "/\(packageName)")) + let newReference = try PackageReference.localSourceControl( + identity: .plain(packageName), + path: .init(validating: "/\(packageName)") + ) self.references[packageName] = newReference return newReference } @@ -3185,9 +3562,12 @@ class DependencyGraphBuilder { func create( dependencies: OrderedCollections.OrderedDictionary ) throws -> [PackageContainerConstraint] { - var refDependencies = OrderedCollections.OrderedDictionary() + var refDependencies = OrderedCollections.OrderedDictionary< + PackageReference, + (PackageRequirement, ProductFilter) + >() for dependency in dependencies { - try refDependencies[reference(for: dependency.key)] = dependency.value + try refDependencies[self.reference(for: dependency.key)] = dependency.value } return self.create(dependencies: refDependencies) } @@ -3195,7 +3575,7 @@ class DependencyGraphBuilder { func create( dependencies: OrderedCollections.OrderedDictionary ) -> [PackageContainerConstraint] { - return dependencies.map { + dependencies.map { PackageContainerConstraint(package: $0, requirement: $1.0, products: $1.1) } } @@ -3204,16 +3584,22 @@ class DependencyGraphBuilder { _ package: String, at versions: [Version], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { - try self.serve(package, at: versions.map{ .version($0) }, toolsVersion: toolsVersion, with: dependencies) + try self.serve(package, at: versions.map { .version($0) }, toolsVersion: toolsVersion, with: dependencies) } func serve( _ package: String, at version: Version, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { try self.serve(package, at: .version(version), toolsVersion: toolsVersion, with: dependencies) } @@ -3222,7 +3608,10 @@ class DependencyGraphBuilder { _ package: String, at versions: [BoundVersion], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { let packageReference = try reference(for: package) try self.serve( @@ -3237,7 +3626,10 @@ class DependencyGraphBuilder { _ package: String, at version: BoundVersion, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { let packageReference = try reference(for: package) try self.serve( @@ -3252,10 +3644,13 @@ class DependencyGraphBuilder { _ packageReference: PackageReference, at versions: [BoundVersion], toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { for version in versions { - try serve(packageReference, at: version, toolsVersion: toolsVersion, with: dependencies) + try self.serve(packageReference, at: version, toolsVersion: toolsVersion, with: dependencies) } } @@ -3263,9 +3658,13 @@ class DependencyGraphBuilder { _ packageReference: PackageReference, at version: BoundVersion, toolsVersion: ToolsVersion? = nil, - with dependencies: KeyValuePairs> = [:] + with dependencies: KeyValuePairs< + String, + OrderedCollections.OrderedDictionary + > = [:] ) throws { - let container = self.containers[packageReference.identity.description] ?? MockContainer(package: packageReference) + let container = self + .containers[packageReference.identity.description] ?? MockContainer(package: packageReference) if case .version(let v) = version { container.versionsToolsVersions[v] = toolsVersion ?? container.toolsVersion @@ -3277,8 +3676,8 @@ class DependencyGraphBuilder { container.dependencies[version.description] = [:] } for (product, filteredDependencies) in dependencies { - let packageDependencies: [MockContainer.Dependency] = try filteredDependencies.map { - (container: try reference(for: $0), requirement: $1.0, productFilter: $1.1) + let packageDependencies: [MockContainer.Dependency] = filteredDependencies.map { + (container: $0, requirement: $1.0, productFilter: $1.1) } container.dependencies[version.description, default: [:]][product, default: []] += packageDependencies } @@ -3288,24 +3687,38 @@ class DependencyGraphBuilder { /// Creates a pins store with the given pins. func create(pinsStore pins: [String: (PinsStore.PinState, ProductFilter)]) throws -> PinsStore { let fs = InMemoryFileSystem() - let store = try! PinsStore(pinsFile: "/tmp/Package.resolved", workingDirectory: .root, fileSystem: fs, mirrors: .init()) + let store = try! PinsStore( + pinsFile: "/tmp/Package.resolved", + workingDirectory: .root, + fileSystem: fs, + mirrors: .init() + ) for (package, pin) in pins { - store.pin(packageRef: try reference(for: package), state: pin.0) + try store.pin(packageRef: self.reference(for: package), state: pin.0) } try! store.saveState(toolsVersion: ToolsVersion.current, originHash: .none) return store } - - func create(pins: PinsStore.Pins = [:], delegate: DependencyResolverDelegate? = .none) -> PubGrubDependencyResolver { + func create( + pins: PinsStore.Pins = [:], + availableLibraries: [LibraryMetadata] = [], + delegate: DependencyResolverDelegate? = .none + ) -> PubGrubDependencyResolver { defer { self.containers = [:] self.references = [:] } let provider = MockProvider(containers: Array(self.containers.values)) - return PubGrubDependencyResolver(provider :provider, pins: pins, observabilityScope: ObservabilitySystem.NOOP, delegate: delegate) + return PubGrubDependencyResolver( + provider: provider, + pins: pins, + availableLibraries: availableLibraries, + observabilityScope: ObservabilitySystem.NOOP, + delegate: delegate + ) } } @@ -3334,26 +3747,35 @@ extension Term { } else if value.contains("^") { components = value.split(separator: "^").map(String.init) let upperMajor = Int(String(components[1].split(separator: ".").first!))! + 1 - requirement = .versionSet(.range(Version(stringLiteral: components[1])..