Skip to content

Commit 0c2ef21

Browse files
committed
[Explicit Module Builds] Centralize computing *all* dependencies of a given 'ModuleInfo' and use throughout
This is in preparation of dependency scanner turning the 'directDependencies' field to contain only actually-directly-imported modules.
1 parent e7753dd commit 0c2ef21

File tree

7 files changed

+75
-88
lines changed

7 files changed

+75
-88
lines changed

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
510510
clangDependencyArtifacts: &clangDependencyArtifacts,
511511
swiftDependencyArtifacts: &swiftDependencyArtifacts)
512512
let depInfo = try dependencyGraph.moduleInfo(of: bridgingHeaderDepID)
513-
dependenciesWorklist.append(contentsOf: depInfo.directDependencies ?? [])
513+
dependenciesWorklist.append(contentsOf: depInfo.allDependencies)
514514
}
515515

516516
// Clang module dependencies are specified on the command line explicitly

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift

+9-44
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,7 @@ import protocol TSCBasic.FileSystem
5959
extension InterModuleDependencyGraph {
6060
var topologicalSorting: [ModuleDependencyId] {
6161
get throws {
62-
try topologicalSort(Array(modules.keys),
63-
successors: {
64-
var dependencies: [ModuleDependencyId] = []
65-
let moduleInfo = try moduleInfo(of: $0)
66-
dependencies.append(contentsOf: moduleInfo.directDependencies ?? [])
67-
if case .swift(let swiftModuleDetails) = moduleInfo.details {
68-
dependencies.append(contentsOf: swiftModuleDetails.swiftOverlayDependencies ?? [])
69-
}
70-
return dependencies
71-
})
62+
try topologicalSort(Array(modules.keys), successors: { try Array(moduleInfo(of: $0).allDependencies) })
7263
}
7364
}
7465

@@ -91,22 +82,9 @@ extension InterModuleDependencyGraph {
9182
}
9283
// Traverse the set of modules in reverse topological order, assimilating transitive closures
9384
for moduleId in topologicalIdList.reversed() {
94-
let moduleInfo = try moduleInfo(of: moduleId)
95-
for dependencyId in moduleInfo.directDependencies! {
85+
for dependencyId in try moduleInfo(of: moduleId).allDependencies {
9686
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
9787
}
98-
// For Swift dependencies, their corresponding Swift Overlay dependencies
99-
// and bridging header dependencies are equivalent to direct dependencies.
100-
if case .swift(let swiftModuleDetails) = moduleInfo.details {
101-
let swiftOverlayDependencies = swiftModuleDetails.swiftOverlayDependencies ?? []
102-
for dependencyId in swiftOverlayDependencies {
103-
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
104-
}
105-
let bridgingHeaderDependencies = swiftModuleDetails.bridgingHeaderDependencies ?? []
106-
for dependencyId in bridgingHeaderDependencies {
107-
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
108-
}
109-
}
11088
}
11189
// For ease of use down-the-line, remove the node's self from its set of reachable nodes
11290
for (key, _) in transitiveClosureMap {
@@ -167,7 +145,7 @@ internal extension InterModuleDependencyGraph {
167145
var visited: Set<ModuleDependencyId> = []
168146
// Scan from the main module's dependencies to avoid reporting
169147
// the main module itself in the results.
170-
for dependencyId in mainModuleInfo.directDependencies ?? [] {
148+
for dependencyId in mainModuleInfo.allDependencies {
171149
try outOfDateModuleScan(from: dependencyId, visited: &visited,
172150
modulesRequiringRebuild: &modulesRequiringRebuild,
173151
fileSystem: fileSystem, cas: cas, forRebuild: forRebuild,
@@ -225,7 +203,7 @@ internal extension InterModuleDependencyGraph {
225203
let sourceModuleInfo = try moduleInfo(of: sourceModuleId)
226204
// Visit the module's dependencies
227205
var hasOutOfDateModuleDependency = false
228-
for dependencyId in sourceModuleInfo.directDependencies ?? [] {
206+
for dependencyId in sourceModuleInfo.allDependencies {
229207
// If we have not already visited this module, recurse.
230208
if !visited.contains(dependencyId) {
231209
try outOfDateModuleScan(from: dependencyId, visited: &visited,
@@ -319,7 +297,7 @@ internal extension InterModuleDependencyGraph {
319297
}
320298

321299
// Check if a dependency of this module has a newer output than this module
322-
for dependencyId in checkedModuleInfo.directDependencies ?? [] {
300+
for dependencyId in checkedModuleInfo.allDependencies {
323301
let dependencyInfo = try moduleInfo(of: dependencyId)
324302
if !verifyInputOlderThanOutputModTime(moduleID.moduleName,
325303
VirtualPath.lookup(dependencyInfo.modulePath.path),
@@ -409,21 +387,14 @@ internal extension InterModuleDependencyGraph {
409387
// depends on a corresponding Clang module with the same name.
410388
// If it does, add it to the path as well.
411389
var completePath = pathSoFar
412-
if let dependencies = sourceInfo.directDependencies,
413-
dependencies.contains(.clang(source.moduleName)) {
390+
if sourceInfo.allDependencies.contains(.clang(source.moduleName)) {
414391
completePath.append(.clang(source.moduleName))
415392
}
416393
result = completePath
417394
return true
418395
}
419396

420-
var allDependencies = sourceInfo.directDependencies ?? []
421-
if case .swift(let swiftModuleDetails) = sourceInfo.details,
422-
let overlayDependencies = swiftModuleDetails.swiftOverlayDependencies {
423-
allDependencies.append(contentsOf: overlayDependencies)
424-
}
425-
426-
for dependency in allDependencies {
397+
for dependency in sourceInfo.allDependencies {
427398
if !visited.contains(dependency),
428399
try findAPath(source: dependency,
429400
pathSoFar: pathSoFar + [dependency],
@@ -446,20 +417,14 @@ internal extension InterModuleDependencyGraph {
446417
// depends on a corresponding Clang module with the same name.
447418
// If it does, add it to the path as well.
448419
var completePath = pathSoFar
449-
if let dependencies = sourceInfo.directDependencies,
450-
dependencies.contains(.clang(source.moduleName)) {
420+
if sourceInfo.allDependencies.contains(.clang(source.moduleName)) {
451421
completePath.append(.clang(source.moduleName))
452422
}
453423
results.insert(completePath)
454424
return
455425
}
456426

457-
var allDependencies = sourceInfo.directDependencies ?? []
458-
if case .swift(let swiftModuleDetails) = sourceInfo.details,
459-
let overlayDependencies = swiftModuleDetails.swiftOverlayDependencies {
460-
allDependencies.append(contentsOf: overlayDependencies)
461-
}
462-
for dependency in allDependencies {
427+
for dependency in sourceInfo.allDependencies {
463428
try findAllPaths(source: dependency,
464429
pathSoFar: pathSoFar + [dependency],
465430
results: &results,

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

+33-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ public struct ModuleInfo: Codable, Hashable {
203203
/// The source files used to build this module.
204204
public var sourceFiles: [String]?
205205

206-
/// The set of direct module dependencies of this module.
206+
/// The set of directly-imported module dependencies of this module.
207+
/// For the complete set of all module dependencies of this module,
208+
/// including Swift overlay dependencies and bridging header dependenceis,
209+
/// use the `allDependencies` computed property.
207210
public var directDependencies: [ModuleDependencyId]?
208211

209212
/// The set of libraries that need to be linked
@@ -301,6 +304,35 @@ extension ModuleInfo {
301304
}
302305
}
303306

307+
public extension ModuleInfo {
308+
// Directly-imported dependencies plus additional dependency
309+
// kinds for Swift modules:
310+
// - Swift overlay dependencies
311+
// - Bridging Header dependencies
312+
var allDependencies: [ModuleDependencyId] {
313+
var result: [ModuleDependencyId] = directDependencies ?? []
314+
if case .swift(let swiftModuleDetails) = details {
315+
// Ensure the dependnecies emitted are unique and follow a predictable ordering:
316+
// 1. directDependencies in the order reported by the scanner
317+
// 2. swift overlay dependencies
318+
// 3. briding header dependencies
319+
var addedSoFar: Set<ModuleDependencyId> = []
320+
addedSoFar.formUnion(directDependencies ?? [])
321+
for depId in swiftModuleDetails.swiftOverlayDependencies ?? [] {
322+
if addedSoFar.insert(depId).inserted {
323+
result.append(depId)
324+
}
325+
}
326+
for depId in swiftModuleDetails.bridgingHeaderDependencies ?? [] {
327+
if addedSoFar.insert(depId).inserted {
328+
result.append(depId)
329+
}
330+
}
331+
}
332+
return result
333+
}
334+
}
335+
304336
/// Describes the complete set of dependencies for a Swift module, including
305337
/// all of the Swift and C modules and source files it depends on.
306338
public struct InterModuleDependencyGraph: Codable {

Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift

+25-26
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ extension InterModuleDependencyGraph {
633633
if !includingPCM && isPCM(key) {
634634
break
635635
}
636-
modules[key]!.directDependencies?.forEach { dep in
636+
modules[key]!.allDependencies.forEach { dep in
637637
if !includingPCM && isPCM(dep) {
638638
return
639639
}
@@ -814,7 +814,8 @@ extension Driver {
814814

815815
func getSwiftDependencies(for module: String) -> [String] {
816816
let info = dependencyGraph.modules[.swift(module)]!
817-
guard let dependencies = info.directDependencies else {
817+
let dependencies = info.allDependencies
818+
guard !dependencies.isEmpty else {
818819
return []
819820
}
820821
return collectSwiftModuleNames(dependencies)
@@ -859,31 +860,29 @@ extension Driver {
859860
}
860861
// Keep track of modules we haven't handled.
861862
var unhandledModules = Set<String>(inputMap.keys)
862-
if let importedModules = dependencyGraph.mainModule.directDependencies {
863-
// Start from those modules explicitly imported into the file under scanning
864-
var openModules = collectSwiftModuleNames(importedModules)
865-
var idx = 0
866-
while idx != openModules.count {
867-
let module = openModules[idx]
868-
let dependencies = getSwiftDependencies(for: module)
869-
try forEachInputOutputPair(module) { input, output in
870-
jobs.append(contentsOf: try generateSingleModuleBuildingJob(module,
871-
prebuiltModuleDir, input, output,
872-
try getOutputPaths(withName: dependencies, loadableFor: input.arch),
873-
currentABIDir, baselineABIDir))
874-
}
875-
// For each dependency, add to the list to handle if the list doesn't
876-
// contain this dependency.
877-
dependencies.forEach({ newModule in
878-
if !openModules.contains(newModule) {
879-
diagnosticEngine.emit(.note("\(newModule) is discovered."),
880-
location: nil)
881-
openModules.append(newModule)
882-
}
883-
})
884-
unhandledModules.remove(module)
885-
idx += 1
863+
// Start from those modules explicitly imported into the file under scanning
864+
var openModules = collectSwiftModuleNames(dependencyGraph.mainModule.allDependencies)
865+
var idx = 0
866+
while idx != openModules.count {
867+
let module = openModules[idx]
868+
let dependencies = getSwiftDependencies(for: module)
869+
try forEachInputOutputPair(module) { input, output in
870+
jobs.append(contentsOf: try generateSingleModuleBuildingJob(module,
871+
prebuiltModuleDir, input, output,
872+
try getOutputPaths(withName: dependencies, loadableFor: input.arch),
873+
currentABIDir, baselineABIDir))
886874
}
875+
// For each dependency, add to the list to handle if the list doesn't
876+
// contain this dependency.
877+
dependencies.forEach({ newModule in
878+
if !openModules.contains(newModule) {
879+
diagnosticEngine.emit(.note("\(newModule) is discovered."),
880+
location: nil)
881+
openModules.append(newModule)
882+
}
883+
})
884+
unhandledModules.remove(module)
885+
idx += 1
887886
}
888887

889888
// We are done if we don't need to handle all inputs exhaustively.

Sources/SwiftDriver/Utilities/DOTModuleDependencyGraphSerializer.swift

+1-4
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ import TSCBasic
6464
stream.write("digraph Modules {\n")
6565
for (moduleId, moduleInfo) in graph.modules {
6666
stream.write(outputNode(for: moduleId))
67-
guard let dependencies = moduleInfo.directDependencies else {
68-
continue
69-
}
70-
for dependencyId in dependencies {
67+
for dependencyId in moduleInfo.allDependencies {
7168
stream.write(" \(quoteName(label(for: moduleId))) -> \(quoteName(label(for: dependencyId))) [color=black];\n")
7269
}
7370
}

Tests/SwiftDriverTests/CachingBuildTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private func checkCachingBuildJobDependencies(job: Job,
172172
XCTAssertTrue(job.commandLine.contains(.flag(String(cacheKey))))
173173
}
174174

175-
for dependencyId in moduleInfo.directDependencies! {
175+
for dependencyId in moduleInfo.allDependencies {
176176
let dependencyInfo = try dependencyGraph.moduleInfo(of: dependencyId)
177177
switch dependencyInfo.details {
178178
case .swift(let swiftDependencyDetails):
@@ -186,7 +186,7 @@ private func checkCachingBuildJobDependencies(job: Job,
186186
}
187187

188188
// Ensure all transitive dependencies got added as well.
189-
for transitiveDependencyId in dependencyInfo.directDependencies! {
189+
for transitiveDependencyId in dependencyInfo.allDependencies {
190190
try checkCachingBuildJobDependencies(job: job,
191191
moduleInfo: try dependencyGraph.moduleInfo(of: transitiveDependencyId),
192192
dependencyGraph: dependencyGraph)

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

+4-10
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private func checkExplicitModuleBuildJobDependencies(job: Job,
8888
XCTAssertJobInvocationMatches(job, .flag("-fmodule-file=\(dependencyId.moduleName)=\(clangDependencyModulePathString)"))
8989
}
9090

91-
for dependencyId in moduleInfo.directDependencies! {
91+
for dependencyId in moduleInfo.allDependencies {
9292
let dependencyInfo = try dependencyGraph.moduleInfo(of: dependencyId)
9393
switch dependencyInfo.details {
9494
case .swift(_):
@@ -102,7 +102,7 @@ private func checkExplicitModuleBuildJobDependencies(job: Job,
102102
}
103103

104104
// Ensure all transitive dependencies got added as well.
105-
for transitiveDependencyId in dependencyInfo.directDependencies! {
105+
for transitiveDependencyId in dependencyInfo.allDependencies {
106106
try checkExplicitModuleBuildJobDependencies(job: job,
107107
moduleInfo: try dependencyGraph.moduleInfo(of: transitiveDependencyId),
108108
dependencyGraph: dependencyGraph)
@@ -2442,13 +2442,10 @@ final class ExplicitModuleBuildTests: XCTestCase {
24422442
main.nativePathString(escaped: true)
24432443
] + sdkArgumentsForTesting) { driver, diagnostics in
24442444
diagnostics.forbidUnexpected(.error, .warning, .note, .remark)
2445-
2446-
let jobs = try driver.planBuild()
2447-
try driver.run(jobs: jobs)
2448-
24492445
diagnostics.expect(.remark("Module 'testTraceDependency' depends on 'A'"))
24502446
diagnostics.expect(.note("[testTraceDependency] -> [A] -> [A](ObjC)"))
24512447
diagnostics.expect(.note("[testTraceDependency] -> [C](ObjC) -> [B](ObjC) -> [A](ObjC)"))
2448+
try driver.run(jobs: driver.planBuild())
24522449
}
24532450
}
24542451

@@ -2466,13 +2463,10 @@ final class ExplicitModuleBuildTests: XCTestCase {
24662463
main.nativePathString(escaped: true)
24672464
] + sdkArgumentsForTesting) { driver, diagnostics in
24682465
diagnostics.forbidUnexpected(.error, .warning, .note, .remark)
2469-
2470-
let jobs = try driver.planBuild()
2471-
try driver.run(jobs: jobs)
2472-
24732466
diagnostics.expect(.remark("Module 'testTraceDependency' depends on 'A'"))
24742467
diagnostics.expect(.note("[testTraceDependency] -> [A] -> [A](ObjC)"),
24752468
alternativeMessage: .note("[testTraceDependency] -> [C](ObjC) -> [B](ObjC) -> [A](ObjC)"))
2469+
try driver.run(jobs: driver.planBuild())
24762470
}
24772471
}
24782472
}

0 commit comments

Comments
 (0)