Skip to content

Commit faba79c

Browse files
authored
Merge pull request #697 from aciidb0mb3r/dependency-manifests
[Workspace] Refactor dependencyManifests into its own struct
2 parents d8e15e0 + 00685b8 commit faba79c

File tree

2 files changed

+54
-32
lines changed

2 files changed

+54
-32
lines changed

Sources/Commands/Workspace.swift

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,43 @@ public class Workspace {
150150
}
151151
}
152152

153+
/// A struct representing all the current manifests (root + external) in a package graph.
154+
struct DependencyManifests {
155+
/// The root manifest.
156+
let root: Manifest
157+
158+
/// The dependency manifests in the transitive closure of root manifest.
159+
let dependencies: [Manifest]
160+
161+
/// Computes the URLs which are declared in the manifests but aren't present in dependencies.
162+
func missingURLs() -> Set<String> {
163+
let manifestsMap = Dictionary<String, Manifest>(
164+
items: [(root.url, root)] + dependencies.map{ ($0.url, $0) })
165+
166+
var requiredURLs = transitiveClosure([root.url]) { url in
167+
guard let manifest = manifestsMap[url] else { return [] }
168+
return manifest.package.dependencies.map{ $0.url }
169+
}
170+
requiredURLs.insert(root.url)
171+
172+
let availableURLs = Set<String>(manifestsMap.keys)
173+
// We should never have loaded a manifest we don't need.
174+
assert(availableURLs.isSubset(of: requiredURLs))
175+
// These are the missing URLs.
176+
return requiredURLs.subtracting(availableURLs)
177+
}
178+
179+
/// Find a manifest given its name.
180+
func lookup(_ name: String) -> Manifest? {
181+
return dependencies.first(where: { $0.name == name })
182+
}
183+
184+
init(root: Manifest, dependencies: [Manifest]) {
185+
self.root = root
186+
self.dependencies = dependencies
187+
}
188+
}
189+
153190
/// The delegate interface.
154191
public let delegate: WorkspaceDelegate
155192

@@ -430,7 +467,7 @@ public class Workspace {
430467
/// current dependencies from the working checkouts.
431468
///
432469
/// Throws: If the root manifest could not be loaded.
433-
func loadDependencyManifests() throws -> (root: Manifest, dependencies: [Manifest]) {
470+
func loadDependencyManifests() throws -> DependencyManifests {
434471
// Load the root manifest.
435472
let rootManifest = try loadRootManifest()
436473

@@ -456,7 +493,7 @@ public class Workspace {
456493
}
457494
}
458495

459-
return (root: rootManifest, dependencies: dependencies.map{ $0.item })
496+
return DependencyManifests(root: rootManifest, dependencies: dependencies.map{ $0.item })
460497
}
461498

462499
/// Fetch and load the complete package at the given path.
@@ -474,42 +511,29 @@ public class Workspace {
474511
/// - Throws: Rethrows errors from dependency resolution (if required) and package graph loading.
475512
public func loadPackageGraph() throws -> PackageGraph {
476513
// First, load the active manifest sets.
477-
let (rootManifest, currentExternalManifests) = try loadDependencyManifests()
478-
479-
// Check for missing checkouts.
480-
let manifestsMap = Dictionary<String, Manifest>(
481-
items: [(rootManifest.url, rootManifest)] + currentExternalManifests.map{ ($0.url, $0) })
482-
let availableURLs = Set<String>(manifestsMap.keys)
483-
var requiredURLs = transitiveClosure([rootManifest.url]) { url in
484-
guard let manifest = manifestsMap[url] else { return [] }
485-
return manifest.package.dependencies.map{ $0.url }
486-
}
487-
requiredURLs.insert(rootManifest.url)
488-
489-
// We should never have loaded a manifest we don't need.
490-
assert(availableURLs.isSubset(of: requiredURLs))
514+
let currentManifests = try loadDependencyManifests()
491515

492-
// Check if there are any missing URLs.
493-
let missingURLs = requiredURLs.subtracting(availableURLs)
516+
// Look for any missing URLs.
517+
let missingURLs = currentManifests.missingURLs()
494518
if missingURLs.isEmpty {
495519
// If not, we are done.
496-
return try PackageGraphLoader().load(rootManifest: rootManifest, externalManifests: currentExternalManifests)
520+
return try PackageGraphLoader().load(rootManifest: currentManifests.root, externalManifests: currentManifests.dependencies)
497521
}
498522

499523
// If so, we need to resolve and fetch them. Start by informing the
500524
// delegate of what is happening.
501525
delegate.fetchingMissingRepositories(missingURLs)
502526

503527
// First, add the root package constraints.
504-
var constraints = computeRootPackageConstraints(rootManifest)
528+
var constraints = computeRootPackageConstraints(currentManifests.root)
505529

506530
// Add constraints to pin to *exactly* all the checkouts we have.
507531
//
508532
// FIXME: We may need a better way to tell the resolution algorithm that
509533
// certain repositories are pinned to the current checkout. We might be
510534
// able to do that simply by overriding the view presented by the
511535
// repository container provider.
512-
for externalManifest in currentExternalManifests {
536+
for externalManifest in currentManifests.dependencies {
513537
let specifier = RepositorySpecifier(url: externalManifest.url)
514538
let managedDependency = dependencyMap[specifier]!
515539

@@ -534,7 +558,7 @@ public class Workspace {
534558
// currently provide constraints, but if we provided only the root and
535559
// then the restrictions (to the current assignment) it would be
536560
// possible.
537-
var externalManifests = currentExternalManifests
561+
var externalManifests = currentManifests.dependencies
538562
for (specifier, state) in packageStateChanges {
539563
switch state {
540564
case .added(let version):
@@ -551,7 +575,7 @@ public class Workspace {
551575
}
552576

553577
// We've loaded the complete set of manifests, load the graph.
554-
return try PackageGraphLoader().load(rootManifest: rootManifest, externalManifests: externalManifests)
578+
return try PackageGraphLoader().load(rootManifest: currentManifests.root, externalManifests: externalManifests)
555579
}
556580

557581
/// Removes the clone and checkout of the provided specifier.

Tests/CommandsTests/WorkspaceTests.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,15 @@ final class WorkspaceTests: XCTestCase {
157157
// Load the "current" manifests.
158158
let manifests = try workspace.loadDependencyManifests()
159159
XCTAssertEqual(manifests.root.package, graph.rootManifest.package)
160-
var dependencyManifests: [String: Manifest] = [:]
161-
for manifest in manifests.dependencies {
162-
dependencyManifests[manifest.package.name] = manifest
163-
}
164-
XCTAssertEqual(dependencyManifests.keys.sorted(), ["A", "AA"])
160+
// B should be missing.
161+
XCTAssertEqual(manifests.missingURLs(), ["//B"])
162+
XCTAssertEqual(manifests.dependencies.map{$0.name}.sorted(), ["A", "AA"])
165163
let aManifest = graph.manifest("A", version: v1)
166-
XCTAssertEqual(dependencyManifests["A"]?.package, aManifest.package)
167-
XCTAssertEqual(dependencyManifests["A"]?.version, aManifest.version)
164+
XCTAssertEqual(manifests.lookup("A")?.package, aManifest.package)
165+
XCTAssertEqual(manifests.lookup("A")?.version, aManifest.version)
168166
let aaManifest = graph.manifest("AA", version: v1)
169-
XCTAssertEqual(dependencyManifests["AA"]?.package, aaManifest.package)
170-
XCTAssertEqual(dependencyManifests["AA"]?.version, aaManifest.version)
167+
XCTAssertEqual(manifests.lookup("AA")?.package, aaManifest.package)
168+
XCTAssertEqual(manifests.lookup("AA")?.version, aaManifest.version)
171169
}
172170
}
173171

0 commit comments

Comments
 (0)