Skip to content

Commit c258e1f

Browse files
authored
Merge pull request #1164 from aciidb0mb3r/remote-needless-fetch
Misc improvements to Repository Manager etc
2 parents 9c661fa + 211986e commit c258e1f

File tree

6 files changed

+90
-56
lines changed

6 files changed

+90
-56
lines changed

Sources/Commands/SwiftPackageTool.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,14 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
8282

8383
case .fetch:
8484
diagnostics.emit(data: FetchDeprecatedDiagnostic())
85-
let workspace = try getActiveWorkspace()
86-
workspace.resolve(root: try getWorkspaceRoot(), diagnostics: diagnostics)
85+
try resolve()
8786

8887
case .resolve:
8988
let resolveOptions = options.resolveOptions
90-
let workspace = try getActiveWorkspace()
91-
let root = try getWorkspaceRoot()
9289

9390
// If a package is provided, use that to resolve the dependencies.
9491
if let packageName = resolveOptions.packageName {
92+
let workspace = try getActiveWorkspace()
9593
return try workspace.resolve(
9694
packageName: packageName,
9795
root: getWorkspaceRoot(),
@@ -102,18 +100,16 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
102100
}
103101

104102
// Otherwise, run a normal resolve.
105-
workspace.resolve(root: root, diagnostics: diagnostics)
103+
try resolve()
106104

107105
case .edit:
108106
let packageName = options.editOptions.packageName!
109-
// Load the package graph.
110-
try loadPackageGraph()
111-
112-
// Get the current workspace.
107+
try resolve()
113108
let workspace = try getActiveWorkspace()
114109

115110
// Create revision object if provided by user.
116111
let revision = options.editOptions.revision.flatMap({ Revision(identifier: $0) })
112+
117113
// Put the dependency in edit mode.
118114
workspace.edit(
119115
packageName: packageName,
@@ -124,9 +120,7 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
124120

125121
case .unedit:
126122
let packageName = options.editOptions.packageName!
127-
128-
// Load the package graph.
129-
try loadPackageGraph()
123+
try resolve()
130124
let workspace = try getActiveWorkspace()
131125

132126
try workspace.unedit(packageName: packageName, forceRemove: options.editOptions.shouldForceRemove)

Sources/Commands/SwiftTool.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ public class SwiftTool<Options: ToolOptions> {
279279
fatalError("Must be implmented by subclasses")
280280
}
281281

282+
/// Resolve the dependencies.
283+
func resolve() throws {
284+
let workspace = try getActiveWorkspace()
285+
workspace.resolve(root: try getWorkspaceRoot(), diagnostics: diagnostics)
286+
287+
// Throw if there were errors when loading the graph.
288+
// The actual errors will be printed before exiting.
289+
guard !diagnostics.hasErrors else {
290+
throw Error.hasFatalDiagnostics
291+
}
292+
}
293+
282294
/// Fetch and load the complete package graph.
283295
@discardableResult
284296
func loadPackageGraph() throws -> PackageGraph {

Sources/PackageGraph/RepositoryPackageContainerProvider.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,17 @@ public class RepositoryPackageContainerProvider: PackageContainerProvider {
5353
public func getContainer(
5454
for identifier: Container.Identifier,
5555
completion: @escaping (Result<Container, AnyError>) -> Void
56+
) {
57+
getContainer(for: identifier, skipUpdate: false, completion: completion)
58+
}
59+
60+
public func getContainer(
61+
for identifier: Container.Identifier,
62+
skipUpdate: Bool,
63+
completion: @escaping (Result<Container, AnyError>) -> Void
5664
) {
5765
// Resolve the container using the repository manager.
58-
repositoryManager.lookup(repository: identifier) { result in
66+
repositoryManager.lookup(repository: identifier, skipUpdate: skipUpdate) { result in
5967
// Create the container wrapper.
6068
let container = result.mapAny { handle -> Container in
6169
// Open the repository.

Sources/SourceControl/RepositoryManager.swift

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,16 @@ public class RepositoryManager {
203203
///
204204
/// Note: Recursive lookups are not supported i.e. calling lookup inside
205205
/// completion block of another lookup will block.
206-
public func lookup(repository: RepositorySpecifier, completion: @escaping LookupCompletion) {
206+
///
207+
/// - Parameters:
208+
/// - repository: The repository to look up.
209+
/// - skipUpdate: If a repository is availble, skip updating it.
210+
/// - completion: The completion block that should be called after lookup finishes.
211+
public func lookup(
212+
repository: RepositorySpecifier,
213+
skipUpdate: Bool = false,
214+
completion: @escaping LookupCompletion
215+
) {
207216
operationQueue.addOperation {
208217
// First look for the handle.
209218
let handle = self.getHandle(repository: repository)
@@ -217,6 +226,11 @@ public class RepositoryManager {
217226
// Update the repository when it is being looked up.
218227
let repo = try handle.open()
219228

229+
// Skip update if asked to.
230+
if skipUpdate {
231+
return handle
232+
}
233+
220234
self.callbacksQueue.async {
221235
self.delegate.handleWillUpdate(handle: handle)
222236
}
@@ -300,24 +314,6 @@ public class RepositoryManager {
300314
}
301315
}
302316

303-
/// Synchronous variation of lookup(repository:) method.
304-
public func lookupSynchronously(repository: RepositorySpecifier) throws -> RepositoryHandle {
305-
let lookupCondition = Condition()
306-
var result: Result<RepositoryHandle, AnyError>? = nil
307-
lookup(repository: repository) { theResult in
308-
lookupCondition.whileLocked {
309-
result = theResult
310-
lookupCondition.signal()
311-
}
312-
}
313-
lookupCondition.whileLocked {
314-
while result == nil {
315-
lookupCondition.wait()
316-
}
317-
}
318-
return try result!.dematerialize()
319-
}
320-
321317
/// Open a repository from a handle.
322318
private func open(_ handle: RepositoryHandle) throws -> Repository {
323319
return try provider.open(

Sources/Workspace/Workspace.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,9 @@ extension Workspace {
621621
// Otherwise, create a checkout at the destination from our repository store.
622622
//
623623
// Get handle to the repository.
624-
let handle = try repositoryManager.lookupSynchronously(repository: dependency.repository)
624+
let handle = try await {
625+
repositoryManager.lookup(repository: dependency.repository, skipUpdate: true, completion: $0)
626+
}
625627
let repo = try handle.open()
626628

627629
// Do preliminary checks on branch and revision, if provided.
@@ -1077,7 +1079,9 @@ extension Workspace {
10771079

10781080
case .revision(let identifier):
10791081
// Get the latest revision from the container.
1080-
let container = try await { containerProvider.getContainer(for: specifier, completion: $0) }
1082+
let container = try await {
1083+
containerProvider.getContainer(for: specifier, skipUpdate: true, completion: $0)
1084+
}
10811085
var revision = try container.getRevision(forIdentifier: identifier)
10821086
let branch = identifier == revision.identifier ? nil : identifier
10831087

@@ -1256,7 +1260,9 @@ extension Workspace {
12561260
}
12571261

12581262
// If not, we need to get the repository from the checkouts.
1259-
let handle = try repositoryManager.lookupSynchronously(repository: repository)
1263+
let handle = try await {
1264+
repositoryManager.lookup(repository: repository, skipUpdate: true, completion: $0)
1265+
}
12601266

12611267
// Clone the repository into the checkouts.
12621268
let path = checkoutsPath.appending(component: repository.fileSystemIdentifier)
@@ -1329,7 +1335,7 @@ extension Workspace {
13291335
// way to get it back out of the resolver which is very
13301336
// annoying. Maybe we should make an SPI on the provider for
13311337
// this?
1332-
let container = try await { containerProvider.getContainer(for: specifier, completion: $0) }
1338+
let container = try await { containerProvider.getContainer(for: specifier, skipUpdate: true, completion: $0) }
13331339
let checkoutState: CheckoutState
13341340

13351341
switch requirement {

Tests/SourceControlTests/RepositoryManagerTests.swift

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import TestSupport
1717

1818
@testable import class SourceControl.RepositoryManager
1919

20+
extension RepositoryManager {
21+
fileprivate func lookupSynchronously(repository: RepositorySpecifier) throws -> RepositoryHandle {
22+
return try await { self.lookup(repository: repository, completion: $0) }
23+
}
24+
}
25+
2026
private enum DummyError: Swift.Error {
2127
case invalidRepository
2228
}
@@ -239,25 +245,6 @@ class RepositoryManagerTests: XCTestCase {
239245
}
240246
}
241247

242-
func testSyncLookup() throws {
243-
mktmpdir { path in
244-
let provider = DummyRepositoryProvider()
245-
let delegate = DummyRepositoryManagerDelegate()
246-
let manager = RepositoryManager(path: path, provider: provider, delegate: delegate)
247-
let dummyRepo = RepositorySpecifier(url: "dummy")
248-
let handle = try manager.lookupSynchronously(repository: dummyRepo)
249-
// Relookup should return same instance.
250-
XCTAssert(handle === (try? manager.lookupSynchronously(repository: dummyRepo)))
251-
// And async lookup should also return same instance.
252-
let lookupExpectation = expectation(description: "Repository lookup expectation")
253-
manager.lookup(repository: dummyRepo) { result in
254-
XCTAssert(handle === (try? result.dematerialize()))
255-
lookupExpectation.fulfill()
256-
}
257-
waitForExpectations(timeout: 1)
258-
}
259-
}
260-
261248
/// Check that the manager is persistent.
262249
func testPersistence() {
263250
mktmpdir { path in
@@ -343,11 +330,42 @@ class RepositoryManagerTests: XCTestCase {
343330
}
344331
}
345332

333+
func testSkipUpdate() throws {
334+
mktmpdir { path in
335+
let repos = path.appending(component: "repo")
336+
let provider = DummyRepositoryProvider()
337+
let delegate = DummyRepositoryManagerDelegate()
338+
try localFileSystem.createDirectory(repos, recursive: true)
339+
340+
let manager = RepositoryManager(path: repos, provider: provider, delegate: delegate)
341+
let dummyRepo = RepositorySpecifier(url: "dummy")
342+
343+
_ = try await { manager.lookup(repository: dummyRepo, completion: $0) }
344+
XCTAssertEqual(delegate.willFetch.count, 1)
345+
XCTAssertEqual(delegate.didFetch.count, 1)
346+
XCTAssertEqual(delegate.willUpdate.count, 0)
347+
XCTAssertEqual(delegate.didUpdate.count, 0)
348+
349+
_ = try await { manager.lookup(repository: dummyRepo, completion: $0) }
350+
_ = try await { manager.lookup(repository: dummyRepo, completion: $0) }
351+
XCTAssertEqual(delegate.willFetch.count, 1)
352+
XCTAssertEqual(delegate.didFetch.count, 1)
353+
XCTAssertEqual(delegate.willUpdate.count, 2)
354+
XCTAssertEqual(delegate.didUpdate.count, 2)
355+
356+
_ = try await { manager.lookup(repository: dummyRepo, skipUpdate: true, completion: $0) }
357+
XCTAssertEqual(delegate.willFetch.count, 1)
358+
XCTAssertEqual(delegate.didFetch.count, 1)
359+
XCTAssertEqual(delegate.willUpdate.count, 2)
360+
XCTAssertEqual(delegate.didUpdate.count, 2)
361+
}
362+
}
363+
346364
static var allTests = [
347365
("testBasics", testBasics),
348366
("testParallelLookups", testParallelLookups),
349367
("testPersistence", testPersistence),
350-
("testSyncLookup", testSyncLookup),
351368
("testReset", testReset),
369+
("testSkipUpdate", testSkipUpdate),
352370
]
353371
}

0 commit comments

Comments
 (0)