@@ -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 > ( )
@@ -66,27 +54,16 @@ import SwiftOptions
66
54
self . creationPhase = phase
67
55
}
68
56
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 {
57
+ @_spi ( Testing) public func sourceRequired( for 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 {
79
63
fatalError ( " \( input. file. basename) not found in inputDependencySourceMap, \( file) : \( line) in \( function) " )
80
64
}
81
65
return source
82
66
}
83
- @_spi ( Testing) public func getInput( for source: DependencySource ) -> TypedVirtualPath ? {
84
- guard let input = inputDependencySourceMap [ source] else {
85
- info. diagnosticEngine. emit ( warning: " Failed to find source file for ' \( source. file. basename) ', recovering with a full rebuild. Next build will be incremental. " )
86
- return nil
87
- }
88
- return input
89
- }
90
67
}
91
68
92
69
extension ModuleDependencyGraph {
@@ -155,7 +132,7 @@ extension ModuleDependencyGraph {
155
132
return TransitivelyInvalidatedInputSet ( )
156
133
}
157
134
return collectInputsRequiringCompilationAfterProcessing (
158
- dependencySource: getSource ( for: input) )
135
+ dependencySource: sourceRequired ( for: input) )
159
136
}
160
137
}
161
138
@@ -175,17 +152,16 @@ extension ModuleDependencyGraph {
175
152
/// speculatively scheduled in the first wave.
176
153
func collectInputsInvalidatedBy( input: TypedVirtualPath
177
154
) -> TransitivelyInvalidatedInputArray {
178
- let changedSource = getSource ( for: input)
155
+ let changedSource = sourceRequired ( for: input)
179
156
let allDependencySourcesToRecompile =
180
157
collectSwiftDepsUsing ( dependencySource: changedSource)
181
158
182
159
return allDependencySourcesToRecompile. compactMap {
183
160
dependencySource in
184
161
guard dependencySource != changedSource else { return nil }
185
- let dependentSource = inputDependencySourceMap [ dependencySource]
186
- info. reporter? . report (
187
- " Found dependent of \( input. file. basename) : " , dependentSource)
188
- return dependentSource
162
+ let inputToRecompile = inputDependencySourceMap. inputIfKnown ( for: dependencySource)
163
+ info. reporter? . report ( " Found dependent of \( input. file. basename) : " , inputToRecompile)
164
+ return inputToRecompile
189
165
}
190
166
}
191
167
@@ -202,7 +178,7 @@ extension ModuleDependencyGraph {
202
178
/// Does the graph contain any dependency nodes for a given source-code file?
203
179
func containsNodes( forSourceFile file: TypedVirtualPath ) -> Bool {
204
180
precondition ( file. type == . swift)
205
- guard let source = inputDependencySourceMap [ file] else {
181
+ guard let source = inputDependencySourceMap. sourceIfKnown ( for : file) else {
206
182
return false
207
183
}
208
184
return containsNodes ( forDependencySource: source)
@@ -212,17 +188,46 @@ extension ModuleDependencyGraph {
212
188
return nodeFinder. findNodes ( for: source) . map { !$0. isEmpty}
213
189
?? false
214
190
}
215
-
216
- /// Return true on success
217
- func populateInputDependencySourceMap( ) -> Bool {
191
+
192
+ /// Returns: false on error
193
+ func populateInputDependencySourceMap(
194
+ `for` purpose: InputDependencySourceMap . AdditionPurpose
195
+ ) -> Bool {
218
196
let ofm = info. outputFileMap
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
197
+ let diags = info. diagnosticEngine
198
+ var allFound = true
199
+ for input in info. inputFiles {
200
+ if let source = ofm. dependencySource ( for: input, diagnosticEngine: diags) {
201
+ inputDependencySourceMap. addEntry ( input, source, for: purpose)
202
+ } else {
203
+ // Don't break in order to report all failures.
204
+ allFound = false
205
+ }
223
206
}
207
+ return allFound
224
208
}
225
209
}
210
+ extension OutputFileMap {
211
+ fileprivate func dependencySource(
212
+ for sourceFile: TypedVirtualPath ,
213
+ diagnosticEngine: DiagnosticsEngine
214
+ ) -> DependencySource ? {
215
+ assert ( sourceFile. type == FileType . swift)
216
+ guard let swiftDepsPath = existingOutput ( inputFile: sourceFile. fileHandle,
217
+ outputType: . swiftDeps)
218
+ else {
219
+ // The legacy driver fails silently here.
220
+ diagnosticEngine. emit (
221
+ . remarkDisabled( " \( sourceFile. file. basename) has no swiftDeps file " )
222
+ )
223
+ return nil
224
+ }
225
+ assert ( VirtualPath . lookup ( swiftDepsPath) . extension == FileType . swiftDeps. rawValue)
226
+ let typedSwiftDepsFile = TypedVirtualPath ( file: swiftDepsPath, type: . swiftDeps)
227
+ return DependencySource ( typedSwiftDepsFile)
228
+ }
229
+ }
230
+
226
231
// MARK: - Scheduling the 2nd wave
227
232
extension ModuleDependencyGraph {
228
233
/// After `source` has been compiled, figure out what other source files need compiling.
@@ -232,7 +237,7 @@ extension ModuleDependencyGraph {
232
237
func collectInputsRequiringCompilation( byCompiling input: TypedVirtualPath
233
238
) -> TransitivelyInvalidatedInputSet ? {
234
239
precondition ( input. type == . swift)
235
- let dependencySource = getSource ( for: input)
240
+ let dependencySource = sourceRequired ( for: input)
236
241
return collectInputsRequiringCompilationAfterProcessing (
237
242
dependencySource: dependencySource)
238
243
}
@@ -327,7 +332,10 @@ extension ModuleDependencyGraph {
327
332
) -> TransitivelyInvalidatedInputSet ? {
328
333
var invalidatedInputs = TransitivelyInvalidatedInputSet ( )
329
334
for invalidatedSwiftDeps in collectSwiftDepsUsingInvalidated ( nodes: directlyInvalidatedNodes) {
330
- guard let invalidatedInput = getInput ( for: invalidatedSwiftDeps) else {
335
+ guard let invalidatedInput = inputDependencySourceMap. inputIfKnown ( for: invalidatedSwiftDeps)
336
+ else {
337
+ info. diagnosticEngine. emit (
338
+ warning: " Failed to find source file for ' \( invalidatedSwiftDeps. file. basename) ', recovering with a full rebuild. Next build will be incremental. " )
331
339
return nil
332
340
}
333
341
invalidatedInputs. insert ( invalidatedInput)
@@ -451,27 +459,6 @@ extension ModuleDependencyGraph {
451
459
}
452
460
}
453
461
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
-
475
462
// MARK: - tracking traced nodes
476
463
extension ModuleDependencyGraph {
477
464
@@ -596,10 +583,11 @@ extension ModuleDependencyGraph {
596
583
. record ( def: dependencyKey, use: self . allNodes [ useID] )
597
584
assert ( isNewUse, " Duplicate use def-use arc in graph? " )
598
585
}
599
- for (input, source) in inputDependencySourceMap {
600
- graph. addMapEntry ( input, source)
586
+ for (input, dependencySource) in inputDependencySourceMap {
587
+ graph. inputDependencySourceMap. addEntry ( input,
588
+ dependencySource,
589
+ for: . readingPriors)
601
590
}
602
-
603
591
return self . graph
604
592
}
605
593
@@ -895,7 +883,7 @@ extension ModuleDependencyGraph {
895
883
}
896
884
}
897
885
898
- for ( input, dependencySource) in graph . inputDependencySourceMap {
886
+ graph . inputDependencySourceMap . enumerateToSerializePriors { input, dependencySource in
899
887
self . addIdentifier ( input. file. name)
900
888
self . addIdentifier ( dependencySource. file. name)
901
889
}
@@ -1040,7 +1028,8 @@ extension ModuleDependencyGraph {
1040
1028
}
1041
1029
}
1042
1030
}
1043
- for (input, dependencySource) in graph. inputDependencySourceMap {
1031
+ graph. inputDependencySourceMap. enumerateToSerializePriors {
1032
+ input, dependencySource in
1044
1033
serializer. stream. writeRecord ( serializer. abbreviations [ . mapNode] !) {
1045
1034
$0. append ( RecordID . mapNode)
1046
1035
$0. append ( serializer. lookupIdentifierCode ( for: input. file. name) )
@@ -1158,7 +1147,7 @@ extension Set where Element == ModuleDependencyGraph.Node {
1158
1147
}
1159
1148
}
1160
1149
1161
- extension BidirectionalMap where T1 == TypedVirtualPath , T2 == DependencySource {
1150
+ extension InputDependencySourceMap {
1162
1151
fileprivate func matches( _ other: Self ) -> Bool {
1163
1152
self == other
1164
1153
}
@@ -1176,6 +1165,6 @@ extension ModuleDependencyGraph {
1176
1165
_ mockInput: TypedVirtualPath ,
1177
1166
_ mockDependencySource: DependencySource
1178
1167
) {
1179
- addMapEntry ( mockInput, mockDependencySource)
1168
+ inputDependencySourceMap . addEntry ( mockInput, mockDependencySource, for : . mocking )
1180
1169
}
1181
1170
}
0 commit comments