Skip to content

Commit b588e0d

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

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
@@ -255,6 +256,82 @@ extension InterModuleDependencyGraph {
255256
}
256257
}
257258

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