@@ -24,7 +24,19 @@ 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
- @_spi ( Testing) public private( set) var inputDependencySourceMap = InputDependencySourceMap ( )
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 > ( )
28
40
29
41
// The set of paths to external dependencies known to be in the graph
30
42
public internal( set) var fingerprintedExternalDependencies = Set < FingerprintedExternalDependency > ( )
@@ -54,20 +66,22 @@ import SwiftOptions
54
66
self . creationPhase = phase
55
67
}
56
68
57
- @_spi ( Testing) public func source( requiredFor input: TypedVirtualPath ,
58
- function: String = #function,
59
- file: String = #file,
60
- line: Int = #line) -> DependencySource {
61
- guard let source = inputDependencySourceMap. sourceIfKnown ( for: input)
62
- else {
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 {
63
79
fatalError ( " \( input. file. basename) not found in inputDependencySourceMap, \( file) : \( line) in \( function) " )
64
80
}
65
81
return source
66
82
}
67
-
68
- @_spi ( Testing) public func input( neededFor source: DependencySource ) -> TypedVirtualPath ? {
69
- guard let input = inputDependencySourceMap. input ( ifKnownFor: source)
70
- else {
83
+ @_spi ( Testing) public func getInput( for source: DependencySource ) -> TypedVirtualPath ? {
84
+ guard let input = inputDependencySourceMap [ source] else {
71
85
info. diagnosticEngine. emit ( warning: " Failed to find source file for ' \( source. file. basename) ', recovering with a full rebuild. Next build will be incremental. " )
72
86
return nil
73
87
}
@@ -141,7 +155,7 @@ extension ModuleDependencyGraph {
141
155
return TransitivelyInvalidatedInputSet ( )
142
156
}
143
157
return collectInputsRequiringCompilationAfterProcessing (
144
- dependencySource: source ( requiredFor : input) )
158
+ dependencySource: getSource ( for : input) )
145
159
}
146
160
}
147
161
@@ -161,17 +175,17 @@ extension ModuleDependencyGraph {
161
175
/// speculatively scheduled in the first wave.
162
176
func collectInputsInvalidatedBy( input: TypedVirtualPath
163
177
) -> TransitivelyInvalidatedInputArray {
164
- let changedSource = source ( requiredFor : input)
178
+ let changedSource = getSource ( for : input)
165
179
let allDependencySourcesToRecompile =
166
180
collectSwiftDepsUsing ( dependencySource: changedSource)
167
181
168
182
return allDependencySourcesToRecompile. compactMap {
169
183
depedencySource in
170
184
guard depedencySource != changedSource else { return nil }
171
- let dependentInput = inputDependencySourceMap. input ( ifKnownFor : depedencySource)
185
+ let dependentSource = inputDependencySourceMap [ depedencySource]
172
186
info. reporter? . report (
173
- " Found dependent of \( input. file. basename) : " , dependentInput )
174
- return dependentInput
187
+ " Found dependent of \( input. file. basename) : " , dependentSource )
188
+ return dependentSource
175
189
}
176
190
}
177
191
@@ -188,7 +202,7 @@ extension ModuleDependencyGraph {
188
202
/// Does the graph contain any dependency nodes for a given source-code file?
189
203
func containsNodes( forSourceFile file: TypedVirtualPath ) -> Bool {
190
204
precondition ( file. type == . swift)
191
- guard let source = inputDependencySourceMap. sourceIfKnown ( for : file) else {
205
+ guard let source = inputDependencySourceMap [ file] else {
192
206
return false
193
207
}
194
208
return containsNodes ( forDependencySource: source)
@@ -198,46 +212,17 @@ extension ModuleDependencyGraph {
198
212
return nodeFinder. findNodes ( for: source) . map { !$0. isEmpty}
199
213
?? false
200
214
}
201
-
202
- /// Returns: false on error
203
- func populateInputDependencySourceMap(
204
- for purpose: InputDependencySourceMap . AdditionPurpose
205
- ) -> Bool {
215
+
216
+ /// Return true on success
217
+ func populateInputDependencySourceMap( ) -> Bool {
206
218
let ofm = info. outputFileMap
207
- let diags = info. diagnosticEngine
208
- var allFound = true
209
- for input in info. inputFiles {
210
- if let source = ofm. dependencySource ( for: input, diagnosticEngine: diags) {
211
- inputDependencySourceMap. addEntry ( input, source, for: purpose)
212
- } else {
213
- // Don't break in order to report all failures.
214
- allFound = false
215
- }
216
- }
217
- return allFound
218
- }
219
- }
220
- extension OutputFileMap {
221
- fileprivate func dependencySource(
222
- for sourceFile: TypedVirtualPath ,
223
- diagnosticEngine: DiagnosticsEngine
224
- ) -> DependencySource ? {
225
- assert ( sourceFile. type == FileType . swift)
226
- guard let swiftDepsPath = existingOutput ( inputFile: sourceFile. fileHandle,
227
- outputType: . swiftDeps)
228
- else {
229
- // The legacy driver fails silently here.
230
- diagnosticEngine. emit (
231
- . remarkDisabled( " \( sourceFile. file. basename) has no swiftDeps file " )
232
- )
233
- return nil
219
+ let de = info. diagnosticEngine
220
+ return info. inputFiles. reduce ( true ) { okSoFar, input in
221
+ ofm. getDependencySource ( for: input, diagnosticEngine: de)
222
+ . map { source in addMapEntry ( input, source) ; return okSoFar } ?? false
234
223
}
235
- assert ( VirtualPath . lookup ( swiftDepsPath) . extension == FileType . swiftDeps. rawValue)
236
- let typedSwiftDepsFile = TypedVirtualPath ( file: swiftDepsPath, type: . swiftDeps)
237
- return DependencySource ( typedSwiftDepsFile)
238
224
}
239
225
}
240
-
241
226
// MARK: - Scheduling the 2nd wave
242
227
extension ModuleDependencyGraph {
243
228
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -247,7 +232,7 @@ extension ModuleDependencyGraph {
247
232
func collectInputsRequiringCompilation( byCompiling input: TypedVirtualPath
248
233
) -> TransitivelyInvalidatedInputSet ? {
249
234
precondition ( input. type == . swift)
250
- let dependencySource = source ( requiredFor : input)
235
+ let dependencySource = getSource ( for : input)
251
236
return collectInputsRequiringCompilationAfterProcessing (
252
237
dependencySource: dependencySource)
253
238
}
@@ -342,7 +327,7 @@ extension ModuleDependencyGraph {
342
327
) -> TransitivelyInvalidatedInputSet ? {
343
328
var invalidatedInputs = TransitivelyInvalidatedInputSet ( )
344
329
for invalidatedSwiftDeps in collectSwiftDepsUsingInvalidated ( nodes: directlyInvalidatedNodes) {
345
- guard let invalidatedInput = input ( neededFor : invalidatedSwiftDeps) else {
330
+ guard let invalidatedInput = getInput ( for : invalidatedSwiftDeps) else {
346
331
return nil
347
332
}
348
333
invalidatedInputs. insert ( invalidatedInput)
@@ -466,6 +451,27 @@ extension ModuleDependencyGraph {
466
451
}
467
452
}
468
453
454
+ extension OutputFileMap {
455
+ fileprivate func getDependencySource(
456
+ for sourceFile: TypedVirtualPath ,
457
+ diagnosticEngine: DiagnosticsEngine
458
+ ) -> DependencySource ? {
459
+ assert ( sourceFile. type == FileType . swift)
460
+ guard let swiftDepsPath = existingOutput ( inputFile: sourceFile. fileHandle,
461
+ outputType: . swiftDeps)
462
+ else {
463
+ // The legacy driver fails silently here.
464
+ diagnosticEngine. emit (
465
+ . remarkDisabled( " \( sourceFile. file. basename) has no swiftDeps file " )
466
+ )
467
+ return nil
468
+ }
469
+ assert ( VirtualPath . lookup ( swiftDepsPath) . extension == FileType . swiftDeps. rawValue)
470
+ let typedSwiftDepsFile = TypedVirtualPath ( file: swiftDepsPath, type: . swiftDeps)
471
+ return DependencySource ( typedSwiftDepsFile)
472
+ }
473
+ }
474
+
469
475
// MARK: - tracking traced nodes
470
476
extension ModuleDependencyGraph {
471
477
@@ -590,11 +596,10 @@ extension ModuleDependencyGraph {
590
596
. record ( def: dependencyKey, use: self . allNodes [ useID] )
591
597
assert ( isNewUse, " Duplicate use def-use arc in graph? " )
592
598
}
593
- for (input, dependencySource) in inputDependencySourceMap {
594
- graph. inputDependencySourceMap. addEntry ( input,
595
- dependencySource,
596
- for: . readingPriors)
599
+ for (input, source) in inputDependencySourceMap {
600
+ graph. addMapEntry ( input, source)
597
601
}
602
+
598
603
return self . graph
599
604
}
600
605
@@ -890,7 +895,7 @@ extension ModuleDependencyGraph {
890
895
}
891
896
}
892
897
893
- graph . inputDependencySourceMap . enumerateToSerializePriors { input, dependencySource in
898
+ for ( input, dependencySource) in graph . inputDependencySourceMap {
894
899
self . addIdentifier ( input. file. name)
895
900
self . addIdentifier ( dependencySource. file. name)
896
901
}
@@ -1035,8 +1040,7 @@ extension ModuleDependencyGraph {
1035
1040
}
1036
1041
}
1037
1042
}
1038
- graph. inputDependencySourceMap. enumerateToSerializePriors {
1039
- input, dependencySource in
1043
+ for (input, dependencySource) in graph. inputDependencySourceMap {
1040
1044
serializer. stream. writeRecord ( serializer. abbreviations [ . mapNode] !) {
1041
1045
$0. append ( RecordID . mapNode)
1042
1046
$0. append ( serializer. lookupIdentifierCode ( for: input. file. name) )
@@ -1154,7 +1158,7 @@ extension Set where Element == ModuleDependencyGraph.Node {
1154
1158
}
1155
1159
}
1156
1160
1157
- extension InputDependencySourceMap {
1161
+ extension BidirectionalMap where T1 == TypedVirtualPath , T2 == DependencySource {
1158
1162
fileprivate func matches( _ other: Self ) -> Bool {
1159
1163
self == other
1160
1164
}
@@ -1172,6 +1176,6 @@ extension ModuleDependencyGraph {
1172
1176
_ mockInput: TypedVirtualPath ,
1173
1177
_ mockDependencySource: DependencySource
1174
1178
) {
1175
- inputDependencySourceMap . addEntry ( mockInput, mockDependencySource, for : . mocking )
1179
+ addMapEntry ( mockInput, mockDependencySource)
1176
1180
}
1177
1181
}
0 commit comments