Skip to content

Commit b6fdae0

Browse files
committed
[Workspace] Switch manifest loading to use DFS instead of findCycles+topologicalSort
It's too early to diagnose cycles here because cyclic package-level dependencies don't necessary lead to cycles in build graph, only target/product element cycles are important.
1 parent 07bd625 commit b6fdae0

File tree

2 files changed

+21
-43
lines changed

2 files changed

+21
-43
lines changed

Sources/Workspace/Workspace+Manifests.swift

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import struct Basics.InternalError
1616
import class Basics.ObservabilityScope
1717
import struct Basics.SwiftVersion
1818
import func Basics.temp_await
19+
import func Basics.DFS
1920
import class Basics.ThreadSafeKeyValueStore
2021
import class Dispatch.DispatchGroup
2122
import struct Dispatch.DispatchTime
@@ -494,12 +495,7 @@ extension Workspace {
494495
// Continue to load the rest of the manifest for this graph
495496
// Creates a map of loaded manifests. We do this to avoid reloading the shared nodes.
496497
var loadedManifests = firstLevelManifests
497-
// Compute the transitive closure of available dependencies.
498-
let topologicalSortInput = topLevelManifests.map { identity, manifest in KeyedPair(
499-
manifest,
500-
key: Key(identity: identity, productFilter: .everything)
501-
) }
502-
let topologicalSortSuccessors: (KeyedPair<Manifest, Key>) throws -> [KeyedPair<Manifest, Key>] = { pair in
498+
let successorManifests: (KeyedPair<Manifest, Key>) throws -> [KeyedPair<Manifest, Key>] = { pair in
503499
// optimization: preload manifest we know about in parallel
504500
let dependenciesRequired = pair.item.dependenciesRequired(for: pair.key.productFilter)
505501
let dependenciesToLoad = dependenciesRequired.map(\.packageRef)
@@ -527,36 +523,26 @@ extension Workspace {
527523
}
528524
}
529525

530-
// Look for any cycle in the dependencies.
531-
if let cycle = try findCycle(topologicalSortInput, successors: topologicalSortSuccessors) {
532-
observabilityScope.emit(
533-
error: "cyclic dependency declaration found: " +
534-
(cycle.path + cycle.cycle).map(\.key.identity.description).joined(separator: " -> ") +
535-
" -> " + cycle.cycle[0].key.identity.description
536-
)
537-
// return partial results
538-
return DependencyManifests(
539-
root: root,
540-
dependencies: [],
541-
workspace: self,
542-
observabilityScope: observabilityScope
543-
)
544-
}
545-
let allManifestsWithPossibleDuplicates = try topologicalSort(
546-
topologicalSortInput,
547-
successors: topologicalSortSuccessors
548-
)
549-
550-
// merge the productFilter of the same package (by identity)
551-
var deduplication = [PackageIdentity: Int]()
552526
var allManifests = [(identity: PackageIdentity, manifest: Manifest, productFilter: ProductFilter)]()
553-
for pair in allManifestsWithPossibleDuplicates {
554-
if let index = deduplication[pair.key.identity] {
555-
let productFilter = allManifests[index].productFilter.merge(pair.key.productFilter)
556-
allManifests[index] = (pair.key.identity, pair.item, productFilter)
557-
} else {
527+
do {
528+
let manifestGraphRoots = topLevelManifests.map { identity, manifest in
529+
KeyedPair(
530+
manifest,
531+
key: Key(identity: identity, productFilter: .everything)
532+
)
533+
}
534+
535+
var deduplication = [PackageIdentity: Int]()
536+
try DFS(
537+
manifestGraphRoots,
538+
successors: successorManifests
539+
) { pair in
558540
deduplication[pair.key.identity] = allManifests.count
559541
allManifests.append((pair.key.identity, pair.item, pair.key.productFilter))
542+
} onDuplicate: { old, new in
543+
let index = deduplication[old.key.identity]!
544+
let productFilter = allManifests[index].productFilter.merge(new.key.productFilter)
545+
allManifests[index] = (new.key.identity, new.item, productFilter)
560546
}
561547
}
562548

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,7 @@ final class WorkspaceTests: XCTestCase {
19711971
// Ensure that the order of the manifests is stable.
19721972
XCTAssertEqual(
19731973
manifests.allDependencyManifests.map(\.value.manifest.displayName),
1974-
["Foo", "Baz", "Bam", "Bar"]
1974+
["Foo", "Bar", "Baz", "Bam"]
19751975
)
19761976
XCTAssertNoDiagnostics(diagnostics)
19771977
}
@@ -12111,15 +12111,7 @@ final class WorkspaceTests: XCTestCase {
1211112111
try workspace.checkPackageGraph(roots: ["Root1", "Root2"]) { _, diagnostics in
1211212112
testDiagnostics(diagnostics) { result in
1211312113
result.check(
12114-
diagnostic: .regex("cyclic dependency declaration found: root[1|2] -> *"),
12115-
severity: .error
12116-
)
12117-
result.check(
12118-
diagnostic: """
12119-
exhausted attempts to resolve the dependencies graph, with the following dependencies unresolved:
12120-
* 'bar' from http://scm.com/org/bar
12121-
* 'foo' from http://scm.com/org/foo
12122-
""",
12114+
diagnostic: .regex("cyclic dependency declaration found: Root[1|2] -> *"),
1212312115
severity: .error
1212412116
)
1212512117
}

0 commit comments

Comments
 (0)