Skip to content

Commit c8c72b9

Browse files
author
David Ungar
authored
Merge pull request #799 from davidungar/add-source-file-type
[Incremental] Add SwiftSourceFile type
2 parents 71b1a53 + ceab9e4 commit c8c72b9

13 files changed

+186
-101
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ add_library(SwiftDriver
5151
"IncrementalCompilation/KeyAndFingerprintHolder.swift"
5252
"IncrementalCompilation/ModuleDependencyGraph.swift"
5353
"IncrementalCompilation/Multidictionary.swift"
54+
"IncrementalCompilation/SwiftSourceFile.swift"
5455
"IncrementalCompilation/SourceFileDependencyGraph.swift"
5556
"IncrementalCompilation/TwoDMap.swift"
5657

Sources/SwiftDriver/IncrementalCompilation/DirectAndTransitiveCollections.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,7 @@ public typealias TransitivelyInvalidatedNodeArray = InvalidatedArray<Transitivel
8585
public typealias TransitivelyInvalidatedSourceSet = InvalidatedSet<Transitively, DependencySource>
8686
public typealias TransitivelyInvalidatedInputArray = InvalidatedArray<Transitively, TypedVirtualPath>
8787
public typealias TransitivelyInvalidatedInputSet = InvalidatedSet<Transitively, TypedVirtualPath>
88+
public typealias TransitivelyInvalidatedSwiftSourceFileArray = InvalidatedArray<Transitively, SwiftSourceFile>
89+
public typealias TransitivelyInvalidatedSwiftSourceFileSet = InvalidatedSet<Transitively, SwiftSourceFile>
8890
public typealias DirectlyInvalidatedNodeArray = InvalidatedArray<Directly, ModuleDependencyGraph.Node>
8991
public typealias DirectlyInvalidatedNodeSet = InvalidatedSet<Directly, ModuleDependencyGraph.Node>

Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extension IncrementalCompilationState {
1717
struct FirstWaveComputer {
1818
let moduleDependencyGraph: ModuleDependencyGraph
1919
let jobsInPhases: JobsInPhases
20-
let inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet
20+
let inputsInvalidatedByExternals: TransitivelyInvalidatedSwiftSourceFileSet
2121
let inputFiles: [TypedVirtualPath]
2222
let sourceFiles: SourceFiles
2323
let buildRecordInfo: BuildRecordInfo
@@ -74,20 +74,26 @@ extension IncrementalCompilationState.FirstWaveComputer {
7474
Dictionary(uniqueKeysWithValues:
7575
jobsInPhases.compileGroups.map { ($0.primaryInput, $0) })
7676
guard let buildRecord = maybeBuildRecord else {
77-
let mandatoryCompileGroupsInOrder = sourceFiles.currentInOrder.compactMap {
78-
input -> CompileJobGroup? in
79-
compileGroups[input]
80-
}
77+
func everythingIsMandatory()
78+
throws -> (skippedCompileGroups: [TypedVirtualPath: CompileJobGroup],
79+
mandatoryJobsInOrder: [Job])
80+
{
81+
let mandatoryCompileGroupsInOrder = sourceFiles.currentInOrder.compactMap {
82+
input -> CompileJobGroup? in
83+
compileGroups[input.typedFile]
84+
}
8185

82-
let mandatoryJobsInOrder = try
86+
let mandatoryJobsInOrder = try
8387
jobsInPhases.beforeCompiles +
8488
batchJobFormer.formBatchedJobs(
8589
mandatoryCompileGroupsInOrder.flatMap {$0.allJobs()},
8690
showJobLifecycle: showJobLifecycle)
8791

88-
moduleDependencyGraph.phase = .buildingAfterEachCompilation
89-
return (skippedCompileGroups: [:],
90-
mandatoryJobsInOrder: mandatoryJobsInOrder)
92+
moduleDependencyGraph.phase = .buildingAfterEachCompilation
93+
return (skippedCompileGroups: [:],
94+
mandatoryJobsInOrder: mandatoryJobsInOrder)
95+
}
96+
return try everythingIsMandatory()
9197
}
9298
moduleDependencyGraph.phase = .updatingAfterCompilation
9399

@@ -117,7 +123,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
117123

118124
// Figure out which compilation inputs are *not* mandatory
119125
private func computeSkippedCompilationInputs(
120-
inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet,
126+
inputsInvalidatedByExternals: TransitivelyInvalidatedSwiftSourceFileSet,
121127
_ moduleDependencyGraph: ModuleDependencyGraph,
122128
_ buildRecord: BuildRecord
123129
) -> Set<TypedVirtualPath> {
@@ -153,11 +159,11 @@ extension IncrementalCompilationState.FirstWaveComputer {
153159
}
154160

155161
// Combine to obtain the inputs that definitely must be recompiled.
156-
let definitelyRequiredInputs =
157-
Set(changedInputs.map({ $0.filePath }) +
158-
inputsInvalidatedByExternals +
159-
inputsMissingFromGraph +
160-
inputsMissingOutputs)
162+
var definitelyRequiredInputs = Set(changedInputs.lazy.map {$0.typedFile})
163+
definitelyRequiredInputs.formUnion(inputsInvalidatedByExternals.lazy.map {$0.typedFile})
164+
definitelyRequiredInputs.formUnion(inputsMissingFromGraph.lazy.map {$0.typedFile})
165+
definitelyRequiredInputs.formUnion(inputsMissingOutputs)
166+
161167
if let reporter = reporter {
162168
for scheduledInput in sortByCommandLineOrder(definitelyRequiredInputs) {
163169
reporter.report("Queuing (initial):", scheduledInput)
@@ -173,14 +179,15 @@ extension IncrementalCompilationState.FirstWaveComputer {
173179
externalDependents: inputsInvalidatedByExternals,
174180
inputsMissingOutputs: Set(inputsMissingOutputs),
175181
moduleDependencyGraph)
176-
.subtracting(definitelyRequiredInputs)
182+
.subtracting(definitelyRequiredInputs.swiftSourceFiles)
183+
177184

178185
if let reporter = reporter {
179186
for dependent in sortByCommandLineOrder(speculativeInputs) {
180187
reporter.report("Queuing because of the initial set:", dependent)
181188
}
182189
}
183-
let immediatelyCompiledInputs = definitelyRequiredInputs.union(speculativeInputs)
190+
let immediatelyCompiledInputs = definitelyRequiredInputs.union(speculativeInputs.lazy.map {$0.typedFile})
184191

185192
let skippedInputs = Set(buildRecordInfo.compilationInputModificationDates.keys)
186193
.subtracting(immediatelyCompiledInputs)
@@ -198,11 +205,17 @@ extension IncrementalCompilationState.FirstWaveComputer {
198205
inputFiles.lazy.filter(inputs.contains)
199206
}
200207

208+
private func sortByCommandLineOrder(
209+
_ inputs: Set<SwiftSourceFile>
210+
) -> LazyFilterSequence<[TypedVirtualPath]> {
211+
inputFiles.lazy.filter {inputs.contains(SwiftSourceFile($0))}
212+
}
213+
201214
/// Encapsulates information about an input the driver has determined has
202215
/// changed in a way that requires an incremental rebuild.
203216
struct ChangedInput {
204217
/// The path to the input file.
205-
let filePath: TypedVirtualPath
218+
let typedFile: TypedVirtualPath
206219
/// The status of the input file.
207220
let status: InputInfo.Status
208221
/// If `true`, the modification time of this input matches the modification
@@ -244,7 +257,7 @@ extension IncrementalCompilationState.FirstWaveComputer {
244257
case .needsNonCascadingBuild:
245258
reporter?.report("Scheduling noncascading build", input)
246259
}
247-
return ChangedInput(filePath: input,
260+
return ChangedInput(typedFile: input,
248261
status: previousCompilationStatus,
249262
datesMatch: datesMatch)
250263
}
@@ -256,22 +269,29 @@ extension IncrementalCompilationState.FirstWaveComputer {
256269
// before the whole frontend job finished.
257270
private func collectInputsToBeSpeculativelyRecompiled(
258271
changedInputs: [ChangedInput],
259-
externalDependents: TransitivelyInvalidatedInputSet,
272+
externalDependents: TransitivelyInvalidatedSwiftSourceFileSet,
260273
inputsMissingOutputs: Set<TypedVirtualPath>,
261274
_ moduleDependencyGraph: ModuleDependencyGraph
262-
) -> Set<TypedVirtualPath> {
275+
) -> Set<SwiftSourceFile> {
263276
let cascadingChangedInputs = computeCascadingChangedInputs(
264277
from: changedInputs,
265278
inputsMissingOutputs: inputsMissingOutputs)
266279

267-
var inputsToBeCertainlyRecompiled = alwaysRebuildDependents ? externalDependents : TransitivelyInvalidatedInputSet()
268-
inputsToBeCertainlyRecompiled.formUnion(cascadingChangedInputs)
280+
var inputsToBeCertainlyRecompiled = Set(cascadingChangedInputs)
281+
if alwaysRebuildDependents {
282+
inputsToBeCertainlyRecompiled.formUnion(externalDependents.lazy.map {$0.typedFile})
283+
}
269284

270285
return inputsToBeCertainlyRecompiled.reduce(into: Set()) {
271286
speculativelyRecompiledInputs, certainlyRecompiledInput in
272-
let speculativeDependents = moduleDependencyGraph.collectInputsInvalidatedBy(changedInput: certainlyRecompiledInput)
287+
guard let certainlyRecompiledSwiftSourceFile = SwiftSourceFile(ifSource: certainlyRecompiledInput)
288+
else {
289+
return
290+
}
291+
let speculativeDependents = moduleDependencyGraph.collectInputsInvalidatedBy(changedInput: certainlyRecompiledSwiftSourceFile)
292+
273293
for speculativeDependent in speculativeDependents
274-
where !inputsToBeCertainlyRecompiled.contains(speculativeDependent) {
294+
where !inputsToBeCertainlyRecompiled.contains(speculativeDependent.typedFile) {
275295
if speculativelyRecompiledInputs.insert(speculativeDependent).inserted {
276296
reporter?.report(
277297
"Immediately scheduling dependent on \(certainlyRecompiledInput.file.basename)",
@@ -288,8 +308,8 @@ extension IncrementalCompilationState.FirstWaveComputer {
288308
) -> [TypedVirtualPath] {
289309
changedInputs.compactMap { changedInput in
290310
let inputIsUpToDate =
291-
changedInput.datesMatch && !inputsMissingOutputs.contains(changedInput.filePath)
292-
let basename = changedInput.filePath.file.basename
311+
changedInput.datesMatch && !inputsMissingOutputs.contains(changedInput.typedFile)
312+
let basename = changedInput.typedFile.file.basename
293313

294314
// If we're asked to always rebuild dependents, all we need to do is
295315
// return inputs whose modification times have changed.
@@ -301,15 +321,15 @@ extension IncrementalCompilationState.FirstWaveComputer {
301321
} else {
302322
reporter?.report(
303323
"scheduling dependents of \(basename); -driver-always-rebuild-dependents")
304-
return changedInput.filePath
324+
return changedInput.typedFile
305325
}
306326
}
307327

308328
switch changedInput.status {
309329
case .needsCascadingBuild:
310330
reporter?.report(
311331
"scheduling dependents of \(basename); needed cascading build")
312-
return changedInput.filePath
332+
return changedInput.typedFile
313333
case .upToDate:
314334
reporter?.report(
315335
"not scheduling dependents of \(basename); unknown changes")

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ extension IncrementalCompilationState {
126126
/// Record about existence and time of the last compile.
127127
let maybeBuildRecord: BuildRecord?
128128
/// A set of inputs invalidated by external chagnes.
129-
let inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet
129+
let inputsInvalidatedByExternals: TransitivelyInvalidatedSwiftSourceFileSet
130130
/// Compiler options related to incremental builds.
131131
let incrementalOptions: IncrementalCompilationState.Options
132132
/// The last time this compilation was started. Used to compare against e.g. input file mod dates.
@@ -146,6 +146,7 @@ extension IncrementalCompilationState {
146146
let skippedCompileGroups: [TypedVirtualPath: CompileJobGroup]
147147
/// All of the pre-compile or compilation job (groups) known to be required
148148
/// for the first wave to execute.
149+
/// The primaries could be other than .swift files, i.e. .sib
149150
let mandatoryJobsInOrder: [Job]
150151
}
151152
}
@@ -218,7 +219,7 @@ extension IncrementalCompilationState {
218219
return try self.confinementQueue.sync {
219220
// Find and deal with inputs that now need to be compiled
220221
let invalidatedInputs = collectInputsInvalidatedByRunning(finishedJob)
221-
assert(Set(invalidatedInputs).isDisjoint(with: finishedJob.primaryInputs),
222+
assert(invalidatedInputs.isDisjoint(with: finishedJob.primarySwiftSourceFiles),
222223
"Primaries should not overlap secondaries.")
223224

224225
if let reporter = self.reporter {
@@ -239,37 +240,39 @@ extension IncrementalCompilationState {
239240
}
240241

241242
/// After `job` finished find out which inputs must compiled that were not known to need compilation before
242-
private func collectInputsInvalidatedByRunning(_ job: Job)-> Set<TypedVirtualPath> {
243+
private func collectInputsInvalidatedByRunning(_ job: Job)-> Set<SwiftSourceFile> {
243244
dispatchPrecondition(condition: .onQueue(self.confinementQueue))
244245
guard job.kind == .compile else {
245-
return Set<TypedVirtualPath>()
246+
return Set<SwiftSourceFile>()
246247
}
247248
return job.primaryInputs.reduce(into: Set()) { invalidatedInputs, primaryInput in
248-
invalidatedInputs.formUnion(collectInputsInvalidated(byCompiling: primaryInput))
249+
if let primary = SwiftSourceFile(ifSource: primaryInput) {
250+
invalidatedInputs.formUnion(collectInputsInvalidated(byCompiling: primary))
251+
}
249252
}
250-
.subtracting(job.primaryInputs) // have already compiled these
253+
.subtracting(job.primarySwiftSourceFiles) // have already compiled these
251254
}
252255

253256
private func collectInputsInvalidated(
254-
byCompiling input: TypedVirtualPath
255-
) -> TransitivelyInvalidatedInputSet {
257+
byCompiling input: SwiftSourceFile
258+
) -> TransitivelyInvalidatedSwiftSourceFileSet {
256259
dispatchPrecondition(condition: .onQueue(self.confinementQueue))
257260
if let found = moduleDependencyGraph.collectInputsRequiringCompilation(byCompiling: input) {
258261
return found
259262
}
260263
self.reporter?.report(
261264
"Failed to read some dependencies source; compiling everything", input)
262-
return TransitivelyInvalidatedInputSet(skippedCompileGroups.keys)
265+
return TransitivelyInvalidatedSwiftSourceFileSet(skippedCompileGroups.keys.swiftSourceFiles)
263266
}
264267

265268
/// Find the jobs that now must be run that were not originally known to be needed.
266269
private func getJobs(
267-
for invalidatedInputs: Set<TypedVirtualPath>
270+
for invalidatedInputs: Set<SwiftSourceFile>
268271
) throws -> [Job] {
269272
dispatchPrecondition(condition: .onQueue(self.confinementQueue))
270273
let unbatched = invalidatedInputs.flatMap { input -> [Job] in
271-
if let group = skippedCompileGroups.removeValue(forKey: input) {
272-
let primaryInputs = group.compileJob.primaryInputs
274+
if let group = skippedCompileGroups.removeValue(forKey: input.typedFile) {
275+
let primaryInputs = group.compileJob.primarySwiftSourceFiles
273276
assert(primaryInputs.count == 1)
274277
assert(primaryInputs[0] == input)
275278
self.reporter?.report("Scheduling invalidated", input)
@@ -399,6 +402,10 @@ extension IncrementalCompilationState {
399402
diagnosticEngine.emit(.remark_incremental_compilation(because: "\(message) \(compiling)"))
400403
}
401404

405+
public func report(_ message: String, _ ifh: SwiftSourceFile) {
406+
report(message, ifh.typedFile)
407+
}
408+
402409
/// Entry point for a simple path, won't print the compile job, path could be anything.
403410
public func report(_ message: String, _ path: VirtualPath?) {
404411
guard let path = path
@@ -545,43 +552,41 @@ extension OutputFileMap {
545552

546553
/// Handy information about the source files in the current invocation
547554
@_spi(Testing) public struct SourceFiles {
548-
/// The current files in same order as the invocation
549-
let currentInOrder: [TypedVirtualPath]
555+
/// The current (.swift) files in same order as the invocation
556+
let currentInOrder: [SwiftSourceFile]
550557

551558
/// The set of current files (actually the handles)
552-
let currentSet: Set<VirtualPath.Handle>
559+
let currentSet: Set<SwiftSourceFile>
553560

554561
/// Handles of the input files in the previous invocation
555-
private let previousSet: Set<VirtualPath.Handle>
562+
private let previousSet: Set<SwiftSourceFile>
556563

557564
/// The files that were in the previous but not in the current invocation
558-
let disappeared: [VirtualPath]
565+
let disappeared: [SwiftSourceFile]
559566

560567
init(inputFiles: [TypedVirtualPath], buildRecord: BuildRecord?) {
561-
self.currentInOrder = inputFiles.filter {$0.type == .swift}
562-
self.currentSet = currentInOrder.reduce(into: Set()) {
563-
currentSet, currentFile in
564-
currentSet.insert(currentFile.fileHandle)
565-
}
568+
self.currentInOrder = inputFiles.swiftSourceFiles
569+
self.currentSet = Set(currentInOrder)
566570
guard let buildRecord = buildRecord else {
567571
self.previousSet = Set()
568572
self.disappeared = []
569573
return
570574
}
571-
var previous = Set<VirtualPath.Handle>()
572-
var disappeared = [VirtualPath]()
575+
var previous = Set<SwiftSourceFile>()
576+
var disappeared = [SwiftSourceFile]()
573577
for prevPath in buildRecord.inputInfos.keys {
574-
let handle = prevPath.intern()
578+
let handle = SwiftSourceFile(prevPath)
575579
previous.insert(handle)
576580
if !currentSet.contains(handle) {
577-
disappeared.append(prevPath)
581+
disappeared.append(handle)
578582
}
579583
}
580584
self.previousSet = previous
581-
self.disappeared = disappeared.sorted {$0.name < $1.name}
585+
self.disappeared = disappeared.sorted {
586+
VirtualPath.lookup($0.fileHandle).name < VirtualPath.lookup($1.fileHandle).name}
582587
}
583588

584-
func isANewInput(_ file: VirtualPath) -> Bool {
585-
!previousSet.contains(file.intern())
589+
func isANewInput(_ file: SwiftSourceFile) -> Bool {
590+
!previousSet.contains(file)
586591
}
587592
}

Sources/SwiftDriver/IncrementalCompilation/IncrementalDependencyAndInputSetup.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ extension IncrementalCompilationState {
166166
if let reporter = reporter {
167167
reporter.report(
168168
"Incremental compilation has been disabled, "
169-
+ "because the following inputs were used in the previous compilation but not in this one: "
170-
+ sourceFiles.disappeared.map { $0.basename }.joined(separator: ", "))
169+
+ "because the following inputs were used in the previous compilation but not in this one: "
170+
+ sourceFiles.disappeared.map { $0.typedFile.file.basename }.joined(separator: ", "))
171171
}
172172
return nil
173173
}
@@ -197,7 +197,7 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
197197
/// For inputs with swiftDeps in OFM, but no readable file, puts input in graph map, but no nodes in graph:
198198
/// caller must ensure scheduling of those
199199
private func computeGraphAndInputsInvalidatedByExternals()
200-
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
200+
-> (ModuleDependencyGraph, TransitivelyInvalidatedSwiftSourceFileSet)?
201201
{
202202
precondition(
203203
sourceFiles.disappeared.isEmpty,
@@ -211,7 +211,7 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
211211
}
212212

213213
private func readPriorGraphAndCollectInputsInvalidatedByChangedOrAddedExternals(
214-
) -> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
214+
) -> (ModuleDependencyGraph, TransitivelyInvalidatedSwiftSourceFileSet)?
215215
{
216216
let dependencyGraphPath = buildRecordInfo.dependencyGraphPath
217217
let graphIfPresent: ModuleDependencyGraph?
@@ -269,10 +269,10 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
269269
/// For externalDependencies, puts then in graph.fingerprintedExternalDependencies, but otherwise
270270
/// does nothing special.
271271
private func buildInitialGraphFromSwiftDepsAndCollectInputsInvalidatedByChangedExternals()
272-
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet)?
272+
-> (ModuleDependencyGraph, TransitivelyInvalidatedSwiftSourceFileSet)?
273273
{
274274
let graph = ModuleDependencyGraph(self, .buildingFromSwiftDeps)
275-
var inputsInvalidatedByChangedExternals = TransitivelyInvalidatedInputSet()
275+
var inputsInvalidatedByChangedExternals = TransitivelyInvalidatedSwiftSourceFileSet()
276276
for input in sourceFiles.currentInOrder {
277277
guard let invalidatedInputs =
278278
graph.collectInputsRequiringCompilationFromExternalsFoundByCompiling(input: input)
@@ -286,8 +286,8 @@ extension IncrementalCompilationState.IncrementalDependencyAndInputSetup {
286286
}
287287

288288
private func bulidEmptyGraphAndCompileEverything()
289-
-> (ModuleDependencyGraph, TransitivelyInvalidatedInputSet) {
289+
-> (ModuleDependencyGraph, TransitivelyInvalidatedSwiftSourceFileSet) {
290290
let graph = ModuleDependencyGraph(self, .buildingAfterEachCompilation)
291-
return (graph, TransitivelyInvalidatedInputSet())
291+
return (graph, TransitivelyInvalidatedSwiftSourceFileSet())
292292
}
293293
}

0 commit comments

Comments
 (0)