@@ -24,19 +24,7 @@ import SwiftOptions
24
24
@_spi ( Testing) public var nodeFinder = NodeFinder ( )
25
25
26
26
/// Maps input files (e.g. .swift) to and from the DependencySource object.
27
- ///
28
- // FIXME: The map between swiftdeps and swift files is absolutely *not*
29
- // a bijection. In particular, more than one swiftdeps file can be encountered
30
- // in the course of deserializing priors *and* reading the output file map
31
- // *and* re-reading swiftdeps files after frontends complete
32
- // that correspond to the same swift file. These cause two problems:
33
- // - overwrites in this data structure that lose data and
34
- // - cache misses in `getInput(for:)` that cause the incremental build to
35
- // turn over when e.g. entries in the output file map change. This should be
36
- // replaced by a multi-map from swift files to dependency sources,
37
- // and a regular map from dependency sources to swift files -
38
- // since that direction really is one-to-one.
39
- @_spi ( Testing) public private( set) var inputDependencySourceMap = BidirectionalMap < TypedVirtualPath , DependencySource > ( )
27
+ @_spi ( Testing) public private( set) var inputDependencySourceMap : InputDependencySourceMap
40
28
41
29
// The set of paths to external dependencies known to be in the graph
42
30
public internal( set) var fingerprintedExternalDependencies = Set < FingerprintedExternalDependency > ( )
@@ -64,34 +52,18 @@ import SwiftOptions
64
52
: nil
65
53
self . phase = phase
66
54
self . creationPhase = phase
55
+ self . inputDependencySourceMap = InputDependencySourceMap ( simulateGetInputFailure: info. simulateGetInputFailure)
67
56
}
68
57
69
- private func addMapEntry( _ input: TypedVirtualPath , _ dependencySource: DependencySource ) {
70
- assert ( input. type == . swift && dependencySource. typedFile. type == . swiftDeps)
71
- inputDependencySourceMap [ input] = dependencySource
72
- }
73
-
74
- @_spi ( Testing) public func getSource( for input: TypedVirtualPath ,
75
- function: String = #function,
76
- file: String = #file,
77
- line: Int = #line) -> DependencySource {
78
- guard let source = inputDependencySourceMap [ input] else {
79
- fatalError ( " \( input. file) not found in map: \( inputDependencySourceMap) , \( file) : \( line) in \( function) " )
80
- }
81
- return source
82
- }
83
-
84
- @_spi ( Testing) public func getInput( for source: DependencySource ) -> TypedVirtualPath ? {
85
- guard let input =
86
- info. simulateGetInputFailure ? nil
87
- : inputDependencySourceMap [ source]
58
+ @_spi ( Testing) public func sourceRequired( for input: TypedVirtualPath ,
59
+ function: String = #function,
60
+ file: String = #file,
61
+ line: Int = #line) -> DependencySource {
62
+ guard let source = inputDependencySourceMap. sourceIfKnown ( for: input)
88
63
else {
89
- info. diagnosticEngine. emit ( warning: " Failed to find source file for ' \( source. file. basename) ', recovering with a full rebuild. Next build will be incremental. " )
90
- info. reporter? . report (
91
- " \( info. simulateGetInputFailure ? " Simulating i " : " I " ) nput not found in inputDependencySourceMap; created for: \( creationPhase) , now: \( phase) " )
92
- return nil
64
+ fatalError ( " \( input. file. basename) not found in inputDependencySourceMap, \( file) : \( line) in \( function) " )
93
65
}
94
- return input
66
+ return source
95
67
}
96
68
}
97
69
@@ -161,7 +133,7 @@ extension ModuleDependencyGraph {
161
133
return TransitivelyInvalidatedInputSet ( )
162
134
}
163
135
return collectInputsRequiringCompilationAfterProcessing (
164
- dependencySource: getSource ( for: input) )
136
+ dependencySource: sourceRequired ( for: input) )
165
137
}
166
138
}
167
139
@@ -183,17 +155,16 @@ extension ModuleDependencyGraph {
183
155
/// speculatively scheduled in the first wave.
184
156
func collectInputsInvalidatedBy( input: TypedVirtualPath
185
157
) -> TransitivelyInvalidatedInputArray {
186
- let changedSource = getSource ( for: input)
158
+ let changedSource = sourceRequired ( for: input)
187
159
let allDependencySourcesToRecompile =
188
160
collectSwiftDepsUsing ( dependencySource: changedSource)
189
161
190
162
return allDependencySourcesToRecompile. compactMap {
191
163
dependencySource in
192
164
guard dependencySource != changedSource else { return nil }
193
- let dependentSource = inputDependencySourceMap [ dependencySource]
194
- info. reporter? . report (
195
- " Found dependent of \( input. file. basename) : " , dependentSource)
196
- return dependentSource
165
+ let inputToRecompile = inputDependencySourceMap. inputIfKnown ( for: dependencySource)
166
+ info. reporter? . report ( " Found dependent of \( input. file. basename) : " , inputToRecompile)
167
+ return inputToRecompile
197
168
}
198
169
}
199
170
@@ -210,7 +181,7 @@ extension ModuleDependencyGraph {
210
181
/// Does the graph contain any dependency nodes for a given source-code file?
211
182
func containsNodes( forSourceFile file: TypedVirtualPath ) -> Bool {
212
183
precondition ( file. type == . swift)
213
- guard let source = inputDependencySourceMap [ file] else {
184
+ guard let source = inputDependencySourceMap. sourceIfKnown ( for : file) else {
214
185
return false
215
186
}
216
187
return containsNodes ( forDependencySource: source)
@@ -220,17 +191,46 @@ extension ModuleDependencyGraph {
220
191
return nodeFinder. findNodes ( for: source) . map { !$0. isEmpty}
221
192
?? false
222
193
}
223
-
224
- /// Return true on success
225
- func populateInputDependencySourceMap( ) -> Bool {
194
+
195
+ /// - Returns: false on error
196
+ func populateInputDependencySourceMap(
197
+ `for` purpose: InputDependencySourceMap . AdditionPurpose
198
+ ) -> Bool {
226
199
let ofm = info. outputFileMap
227
- let de = info. diagnosticEngine
228
- return info. inputFiles. reduce ( true ) { okSoFar, input in
229
- ofm. getDependencySource ( for: input, diagnosticEngine: de)
230
- . map { source in addMapEntry ( input, source) ; return okSoFar } ?? false
200
+ let diags = info. diagnosticEngine
201
+ var allFound = true
202
+ for input in info. inputFiles {
203
+ if let source = ofm. dependencySource ( for: input, diagnosticEngine: diags) {
204
+ inputDependencySourceMap. addEntry ( input, source, for: purpose)
205
+ } else {
206
+ // Don't break in order to report all failures.
207
+ allFound = false
208
+ }
209
+ }
210
+ return allFound
211
+ }
212
+ }
213
+ extension OutputFileMap {
214
+ fileprivate func dependencySource(
215
+ for sourceFile: TypedVirtualPath ,
216
+ diagnosticEngine: DiagnosticsEngine
217
+ ) -> DependencySource ? {
218
+ assert ( sourceFile. type == FileType . swift)
219
+ guard let swiftDepsPath = existingOutput ( inputFile: sourceFile. fileHandle,
220
+ outputType: . swiftDeps)
221
+ else {
222
+ // The legacy driver fails silently here.
223
+ diagnosticEngine. emit (
224
+ . remarkDisabled( " \( sourceFile. file. basename) has no swiftDeps file " )
225
+ )
226
+ return nil
231
227
}
228
+ assert ( VirtualPath . lookup ( swiftDepsPath) . extension == FileType . swiftDeps. rawValue)
229
+ let typedSwiftDepsFile = TypedVirtualPath ( file: swiftDepsPath, type: . swiftDeps)
230
+ return DependencySource ( typedSwiftDepsFile)
232
231
}
233
232
}
233
+
234
234
// MARK: - Scheduling the 2nd wave
235
235
extension ModuleDependencyGraph {
236
236
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -240,7 +240,7 @@ extension ModuleDependencyGraph {
240
240
func collectInputsRequiringCompilation( byCompiling input: TypedVirtualPath
241
241
) -> TransitivelyInvalidatedInputSet ? {
242
242
precondition ( input. type == . swift)
243
- let dependencySource = getSource ( for: input)
243
+ let dependencySource = sourceRequired ( for: input)
244
244
return collectInputsRequiringCompilationAfterProcessing (
245
245
dependencySource: dependencySource)
246
246
}
@@ -327,8 +327,10 @@ extension ModuleDependencyGraph {
327
327
) -> TransitivelyInvalidatedInputSet ? {
328
328
var invalidatedInputs = TransitivelyInvalidatedInputSet ( )
329
329
for invalidatedSwiftDeps in collectSwiftDepsUsingInvalidated ( nodes: directlyInvalidatedNodes) {
330
- guard let invalidatedInput = getInput ( for: invalidatedSwiftDeps)
330
+ guard let invalidatedInput = inputDependencySourceMap . inputIfKnown ( for: invalidatedSwiftDeps)
331
331
else {
332
+ info. diagnosticEngine. emit (
333
+ warning: " Failed to find source file for ' \( invalidatedSwiftDeps. file. basename) ', recovering with a full rebuild. Next build will be incremental. " )
332
334
return nil
333
335
}
334
336
invalidatedInputs. insert ( invalidatedInput)
@@ -405,27 +407,6 @@ extension ModuleDependencyGraph {
405
407
}
406
408
}
407
409
408
- extension OutputFileMap {
409
- fileprivate func getDependencySource(
410
- for sourceFile: TypedVirtualPath ,
411
- diagnosticEngine: DiagnosticsEngine
412
- ) -> DependencySource ? {
413
- assert ( sourceFile. type == FileType . swift)
414
- guard let swiftDepsPath = existingOutput ( inputFile: sourceFile. fileHandle,
415
- outputType: . swiftDeps)
416
- else {
417
- // The legacy driver fails silently here.
418
- diagnosticEngine. emit (
419
- . remarkDisabled( " \( sourceFile. file. basename) has no swiftDeps file " )
420
- )
421
- return nil
422
- }
423
- assert ( VirtualPath . lookup ( swiftDepsPath) . extension == FileType . swiftDeps. rawValue)
424
- let typedSwiftDepsFile = TypedVirtualPath ( file: swiftDepsPath, type: . swiftDeps)
425
- return DependencySource ( typedSwiftDepsFile)
426
- }
427
- }
428
-
429
410
// MARK: - tracking traced nodes
430
411
extension ModuleDependencyGraph {
431
412
@@ -550,10 +531,11 @@ extension ModuleDependencyGraph {
550
531
. record ( def: dependencyKey, use: self . allNodes [ useID] )
551
532
assert ( isNewUse, " Duplicate use def-use arc in graph? " )
552
533
}
553
- for (input, source) in inputDependencySourceMap {
554
- graph. addMapEntry ( input, source)
534
+ for (input, dependencySource) in inputDependencySourceMap {
535
+ graph. inputDependencySourceMap. addEntry ( input,
536
+ dependencySource,
537
+ for: . readingPriors)
555
538
}
556
-
557
539
return self . graph
558
540
}
559
541
@@ -849,7 +831,7 @@ extension ModuleDependencyGraph {
849
831
}
850
832
}
851
833
852
- for ( input, dependencySource) in graph . inputDependencySourceMap {
834
+ graph . inputDependencySourceMap . enumerateToSerializePriors { input, dependencySource in
853
835
self . addIdentifier ( input. file. name)
854
836
self . addIdentifier ( dependencySource. file. name)
855
837
}
@@ -994,7 +976,8 @@ extension ModuleDependencyGraph {
994
976
}
995
977
}
996
978
}
997
- for (input, dependencySource) in graph. inputDependencySourceMap {
979
+ graph. inputDependencySourceMap. enumerateToSerializePriors {
980
+ input, dependencySource in
998
981
serializer. stream. writeRecord ( serializer. abbreviations [ . mapNode] !) {
999
982
$0. append ( RecordID . mapNode)
1000
983
$0. append ( serializer. lookupIdentifierCode ( for: input. file. name) )
@@ -1112,7 +1095,7 @@ extension Set where Element == ModuleDependencyGraph.Node {
1112
1095
}
1113
1096
}
1114
1097
1115
- extension BidirectionalMap where T1 == TypedVirtualPath , T2 == DependencySource {
1098
+ extension InputDependencySourceMap {
1116
1099
fileprivate func matches( _ other: Self ) -> Bool {
1117
1100
self == other
1118
1101
}
@@ -1130,6 +1113,6 @@ extension ModuleDependencyGraph {
1130
1113
_ mockInput: TypedVirtualPath ,
1131
1114
_ mockDependencySource: DependencySource
1132
1115
) {
1133
- addMapEntry ( mockInput, mockDependencySource)
1116
+ inputDependencySourceMap . addEntry ( mockInput, mockDependencySource, for : . mocking )
1134
1117
}
1135
1118
}
0 commit comments