Skip to content

Commit 6c83210

Browse files
committed
[NFC] Move 'InterModuleDependencyGraph' out-of-date analysis into an extension on the graph itself
1 parent 02b896d commit 6c83210

File tree

4 files changed

+80
-72
lines changed

4 files changed

+80
-72
lines changed

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import func TSCBasic.topologicalSort
14+
import protocol TSCBasic.FileSystem
1415

1516
@_spi(Testing) public extension InterModuleDependencyGraph {
1617
/// For targets that are built alongside the driver's current module, the scanning action will report them as
@@ -249,6 +250,82 @@ extension InterModuleDependencyGraph {
249250
}
250251
}
251252

253+
/// Incremental Build Machinery
254+
internal extension InterModuleDependencyGraph {
255+
/// We must determine if any of the module dependencies require re-compilation
256+
/// Since we know that a prior dependency graph was not completely up-to-date,
257+
/// there must be at least *some* dependencies that require being re-built.
258+
///
259+
/// If a dependency is deemed as requiring a re-build, then every module
260+
/// between it and the root (source module being built by this driver
261+
/// instance) must also be re-built.
262+
func computeInvalidatedModuleDependencies(fileSystem: FileSystem,
263+
reporter: IncrementalCompilationState.Reporter? = nil)
264+
throws -> Set<ModuleDependencyId> {
265+
let mainModuleInfo = mainModule
266+
var modulesRequiringRebuild: Set<ModuleDependencyId> = []
267+
var visited: Set<ModuleDependencyId> = []
268+
// Scan from the main module's dependencies to avoid reporting
269+
// the main module itself in the results.
270+
for dependencyId in mainModuleInfo.directDependencies ?? [] {
271+
try outOfDateModuleScan(from: dependencyId, visited: &visited,
272+
modulesRequiringRebuild: &modulesRequiringRebuild,
273+
fileSystem: fileSystem, reporter: reporter)
274+
}
275+
276+
reporter?.reportExplicitDependencyReBuildSet(Array(modulesRequiringRebuild))
277+
return modulesRequiringRebuild
278+
}
279+
280+
/// Perform a postorder DFS to locate modules which are out-of-date with respect
281+
/// to their inputs. Upon encountering such a module, add it to the set of invalidated
282+
/// modules, along with the path from the root to this module.
283+
func outOfDateModuleScan(from sourceModuleId: ModuleDependencyId,
284+
visited: inout Set<ModuleDependencyId>,
285+
modulesRequiringRebuild: inout Set<ModuleDependencyId>,
286+
fileSystem: FileSystem,
287+
reporter: IncrementalCompilationState.Reporter? = nil) throws {
288+
let sourceModuleInfo = try moduleInfo(of: sourceModuleId)
289+
// Visit the module's dependencies
290+
var hasOutOfDateModuleDependency = false
291+
var mostRecentlyUpdatedDependencyOutput: TimePoint = .zero
292+
for dependencyId in sourceModuleInfo.directDependencies ?? [] {
293+
// If we have not already visited this module, recurse.
294+
if !visited.contains(dependencyId) {
295+
try outOfDateModuleScan(from: dependencyId, visited: &visited,
296+
modulesRequiringRebuild: &modulesRequiringRebuild,
297+
fileSystem: fileSystem, reporter: reporter)
298+
}
299+
// Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
300+
hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild.contains(dependencyId)
301+
302+
// Keep track of dependencies' output file time stamp to determine if it is newer than the current module.
303+
if let depOutputTimeStamp = try? fileSystem.lastModificationTime(for: VirtualPath.lookup(moduleInfo(of: dependencyId).modulePath.path)),
304+
depOutputTimeStamp > mostRecentlyUpdatedDependencyOutput {
305+
mostRecentlyUpdatedDependencyOutput = depOutputTimeStamp
306+
}
307+
}
308+
309+
if hasOutOfDateModuleDependency {
310+
reporter?.reportExplicitDependencyWillBeReBuilt(sourceModuleId.moduleNameForDiagnostic, reason: "Invalidated by downstream dependency")
311+
modulesRequiringRebuild.insert(sourceModuleId)
312+
} else if try !IncrementalCompilationState.IncrementalDependencyAndInputSetup.verifyModuleDependencyUpToDate(moduleID: sourceModuleId, moduleInfo: sourceModuleInfo,
313+
fileSystem: fileSystem, reporter: reporter) {
314+
reporter?.reportExplicitDependencyWillBeReBuilt(sourceModuleId.moduleNameForDiagnostic, reason: "Out-of-date")
315+
modulesRequiringRebuild.insert(sourceModuleId)
316+
} else if let outputModTime = try? fileSystem.lastModificationTime(for: VirtualPath.lookup(sourceModuleInfo.modulePath.path)),
317+
outputModTime < mostRecentlyUpdatedDependencyOutput {
318+
// If a prior variant of this module dependnecy exists, and is older than any of its direct or transitive
319+
// module dependency outputs, it must also be re-built.
320+
reporter?.reportExplicitDependencyWillBeReBuilt(sourceModuleId.moduleNameForDiagnostic, reason: "Has newer module dependency inputs")
321+
modulesRequiringRebuild.insert(sourceModuleId)
322+
}
323+
324+
// Now that we've determined if this module must be rebuilt, mark it as visited.
325+
visited.insert(sourceModuleId)
326+
}
327+
}
328+
252329
internal extension InterModuleDependencyGraph {
253330
func explainDependency(dependencyModuleName: String) throws -> [[ModuleDependencyId]]? {
254331
guard modules.contains(where: { $0.key.moduleName == dependencyModuleName }) else { return nil }

Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ import class Dispatch.DispatchQueue
184184
try? fileSystem.removeFileTree(absPath)
185185
}
186186

187-
func readOutOfDateInterModuleDependencyGraph(
187+
func readPriorInterModuleDependencyGraph(
188188
reporter: IncrementalCompilationState.Reporter?
189189
) -> InterModuleDependencyGraph? {
190190
let decodedGraph: InterModuleDependencyGraph

Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -138,75 +138,6 @@ extension IncrementalCompilationState.FirstWaveComputer {
138138
mandatoryJobsInOrder: mandatoryJobsInOrder)
139139
}
140140

141-
/// We must determine if any of the module dependencies require re-compilation
142-
/// Since we know that a prior dependency graph was not completely up-to-date,
143-
/// there must be at least *some* dependencies that require being re-built.
144-
///
145-
/// If a dependency is deemed as requiring a re-build, then every module
146-
/// between it and the root (source module being built by this driver
147-
/// instance) must also be re-built.
148-
private func computeInvalidatedModuleDependencies(on moduleDependencyGraph: InterModuleDependencyGraph)
149-
throws -> Set<ModuleDependencyId> {
150-
let mainModuleInfo = moduleDependencyGraph.mainModule
151-
var modulesRequiringRebuild: Set<ModuleDependencyId> = []
152-
var visited: Set<ModuleDependencyId> = []
153-
// Scan from the main module's dependencies to avoid reporting
154-
// the main module itself in the results.
155-
for dependencyId in mainModuleInfo.directDependencies ?? [] {
156-
try outOfDateModuleScan(on: moduleDependencyGraph, from: dependencyId,
157-
visited: &visited, modulesRequiringRebuild: &modulesRequiringRebuild)
158-
}
159-
160-
reporter?.reportExplicitDependencyReBuildSet(Array(modulesRequiringRebuild))
161-
return modulesRequiringRebuild
162-
}
163-
164-
/// Perform a postorder DFS to locate modules which are out-of-date with respect
165-
/// to their inputs. Upon encountering such a module, add it to the set of invalidated
166-
/// modules, along with the path from the root to this module.
167-
private func outOfDateModuleScan(on moduleDependencyGraph: InterModuleDependencyGraph,
168-
from moduleId: ModuleDependencyId,
169-
visited: inout Set<ModuleDependencyId>,
170-
modulesRequiringRebuild: inout Set<ModuleDependencyId>) throws {
171-
let moduleInfo = try moduleDependencyGraph.moduleInfo(of: moduleId)
172-
// Visit the module's dependencies
173-
var hasOutOfDateModuleDependency = false
174-
var mostRecentlyUpdatedDependencyOutput: TimePoint = .zero
175-
for dependencyId in moduleInfo.directDependencies ?? [] {
176-
// If we have not already visited this module, recurse.
177-
if !visited.contains(dependencyId) {
178-
try outOfDateModuleScan(on: moduleDependencyGraph, from: dependencyId,
179-
visited: &visited, modulesRequiringRebuild: &modulesRequiringRebuild)
180-
}
181-
// Even if we're not revisiting a dependency, we must check if it's already known to be out of date.
182-
hasOutOfDateModuleDependency = hasOutOfDateModuleDependency || modulesRequiringRebuild.contains(dependencyId)
183-
184-
// Keep track of dependencies' output file time stamp to determine if it is newer than the current module.
185-
if let depOutputTimeStamp = try? fileSystem.lastModificationTime(for: VirtualPath.lookup(moduleDependencyGraph.moduleInfo(of: dependencyId).modulePath.path)),
186-
depOutputTimeStamp > mostRecentlyUpdatedDependencyOutput {
187-
mostRecentlyUpdatedDependencyOutput = depOutputTimeStamp
188-
}
189-
}
190-
191-
if hasOutOfDateModuleDependency {
192-
reporter?.reportExplicitDependencyWillBeReBuilt(moduleId.moduleNameForDiagnostic, reason: "Invalidated by downstream dependency")
193-
modulesRequiringRebuild.insert(moduleId)
194-
} else if try !IncrementalCompilationState.IncrementalDependencyAndInputSetup.verifyModuleDependencyUpToDate(moduleID: moduleId, moduleInfo: moduleInfo,
195-
fileSystem: fileSystem, reporter: reporter) {
196-
reporter?.reportExplicitDependencyWillBeReBuilt(moduleId.moduleNameForDiagnostic, reason: "Out-of-date")
197-
modulesRequiringRebuild.insert(moduleId)
198-
} else if let outputModTime = try? fileSystem.lastModificationTime(for: VirtualPath.lookup(moduleInfo.modulePath.path)),
199-
outputModTime < mostRecentlyUpdatedDependencyOutput {
200-
// If a prior variant of this module dependnecy exists, and is older than any of its direct or transitive
201-
// module dependency outputs, it must also be re-built.
202-
reporter?.reportExplicitDependencyWillBeReBuilt(moduleId.moduleNameForDiagnostic, reason: "Has newer module dependency inputs")
203-
modulesRequiringRebuild.insert(moduleId)
204-
}
205-
206-
// Now that we've determined if this module must be rebuilt, mark it as visited.
207-
visited.insert(moduleId)
208-
}
209-
210141
/// In an explicit module build, filter out dependency module pre-compilation tasks
211142
/// for modules up-to-date from a prior compile.
212143
private func computeMandatoryBeforeCompilesJobs() throws -> [Job] {
@@ -224,7 +155,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
224155

225156
// Determine which module pre-build jobs must be re-run
226157
let modulesRequiringReBuild =
227-
try computeInvalidatedModuleDependencies(on: moduleDependencyGraph)
158+
try moduleDependencyGraph.computeInvalidatedModuleDependencies(fileSystem: fileSystem, reporter: reporter)
228159

229160
// Filter the `.generatePCM` and `.compileModuleFromInterface` jobs for
230161
// modules which do *not* need re-building.

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
106106
) throws -> InterModuleDependencyGraph? {
107107
// Attempt to read a serialized inter-module dependency graph from a prior build
108108
guard let priorInterModuleDependencyGraph =
109-
buildRecordInfo.readOutOfDateInterModuleDependencyGraph(reporter: reporter),
109+
buildRecordInfo.readPriorInterModuleDependencyGraph(reporter: reporter),
110110
let priorImports = priorInterModuleDependencyGraph.mainModule.directDependencies?.map({ $0.moduleName }) else {
111111
reporter?.reportExplicitBuildMustReScan("Could not read inter-module dependency graph at \(buildRecordInfo.interModuleDependencyGraphPath)")
112112
return nil

0 commit comments

Comments
 (0)