Skip to content

Commit ccc4024

Browse files
committed
Refactor IncrementalCompilationState to compute initial state before job-generation.
In the near future, planning of `PrecompileModuleDependenciesJobs`, for explicit module dependencies, will require access to the incremental state in order to incrementalize expensive actions such as dependency scanning. This change refactors the creation of incremental compilation state in order to move computation of the initial state (dependency graph) to occur before job-generation. The first wave of jobs is then computed after job-generation, using the initial state, and the set of jobs in the plan, as input. - Separate `InitialIncrementalStateComputer` to run early, by the `Driver`, to compute the dependency graph and the set of changed inputs. Rename `InitialIncrementalStateComputer` into `IncrementalDependencyAndInputSetup`. - Introduce `FirstWaveComputer` which the constructor of the `IncrementalCompilationContext` uses to compute the `mandatoryJobsInOrder` set of jobs the executors *must* run.
1 parent 9e2c870 commit ccc4024

10 files changed

+423
-317
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ add_library(SwiftDriver
4747
"IncrementalCompilation/DirectAndTransitiveCollections.swift"
4848
"IncrementalCompilation/ExternalDependencyAndFingerprintEnforcer.swift"
4949
"IncrementalCompilation/IncrementalCompilationState.swift"
50-
"IncrementalCompilation/InitialStateComputer.swift"
50+
"IncrementalCompilation/IncrementalDependencyAndInputSetup.swift"
51+
"IncrementalCompilation/FirstWaveComputer.swift"
5152
"IncrementalCompilation/InputInfo.swift"
5253
"IncrementalCompilation/KeyAndFingerprintHolder.swift"
5354
"IncrementalCompilation/ModuleDependencyGraph.swift"

Sources/SwiftDriver/IncrementalCompilation/DependencyGraphDotFileWriter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import TSCBasic
1414
// MARK: - Asking to write dot files / interface
1515
public class DependencyGraphDotFileWriter {
1616
/// Holds file-system and options
17-
private let info: IncrementalCompilationState.InitialStateComputer
17+
private let info: IncrementalCompilationState.IncrementalDependencyAndInputSetup
1818

1919
private var versionNumber = 0
2020

21-
init(_ info: IncrementalCompilationState.InitialStateComputer) {
21+
init(_ info: IncrementalCompilationState.IncrementalDependencyAndInputSetup) {
2222
self.info = info
2323
}
2424

Sources/SwiftDriver/IncrementalCompilation/InitialStateComputer.swift renamed to Sources/SwiftDriver/IncrementalCompilation/FirstWaveComputer.swift

Lines changed: 56 additions & 190 deletions
Large diffs are not rendered by default.

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState.swift

Lines changed: 40 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -69,81 +69,48 @@ public final class IncrementalCompilationState {
6969
/// be protected by the confinement queue.
7070
private var skippedCompileGroups = [TypedVirtualPath: CompileJobGroup]()
7171

72-
// MARK: - Creating IncrementalCompilationState if possible
72+
// MARK: - Creating IncrementalCompilationState
7373
/// Return nil if not compiling incrementally
74-
init?(
74+
internal init(
7575
driver: inout Driver,
76-
options: Options,
77-
jobsInPhases: JobsInPhases
76+
jobsInPhases: JobsInPhases,
77+
initialState: InitialStateForPlanning
7878
) throws {
79-
guard driver.shouldAttemptIncrementalCompilation else { return nil }
80-
81-
if options.contains(.showIncremental) {
79+
if initialState.incrementalOptions.contains(.showIncremental) {
8280
self.reporter = Reporter(diagnosticEngine: driver.diagnosticEngine,
8381
outputFileMap: driver.outputFileMap)
8482
} else {
8583
self.reporter = nil
8684
}
8785

88-
let enablingOrDisabling = options.contains(.enableCrossModuleIncrementalBuild)
86+
let enablingOrDisabling =
87+
initialState.incrementalOptions.contains(.enableCrossModuleIncrementalBuild)
8988
? "Enabling"
9089
: "Disabling"
9190
reporter?.report(
9291
"\(enablingOrDisabling) incremental cross-module building")
9392

93+
let firstWave =
94+
try FirstWaveComputer(initialState: initialState, jobsInPhases: jobsInPhases,
95+
driver: driver, reporter: reporter).compute(batchJobFormer: &driver)
9496

95-
guard let outputFileMap = driver.outputFileMap else {
96-
driver.diagnosticEngine.emit(.warning_incremental_requires_output_file_map)
97-
return nil
98-
}
99-
100-
guard let buildRecordInfo = driver.buildRecordInfo else {
101-
reporter?.reportDisablingIncrementalBuild("no build record path")
102-
return nil
103-
}
104-
105-
// FIXME: This should work without an output file map. We should have
106-
// another way to specify a build record and where to put intermediates.
107-
let maybeBuildRecord = buildRecordInfo.populateOutOfDateBuildRecord(
108-
inputFiles: driver.inputFiles, reporter: reporter)
109-
110-
// Forming batch jobs requires passing in the driver "inout". But that's the
111-
// only "inout" use needed, among many other values needed from the driver.
112-
// So, pass the other values individually, and pass the driver "inout" as
113-
// the "batchJobFormer". Maybe someday there will be a better way.
114-
guard
115-
let initial = try InitialStateComputer(
116-
options,
117-
jobsInPhases,
118-
outputFileMap,
119-
buildRecordInfo,
120-
maybeBuildRecord,
121-
self.reporter,
122-
driver.inputFiles,
123-
driver.fileSystem,
124-
showJobLifecycle: driver.showJobLifecycle,
125-
driver.diagnosticEngine)
126-
.compute(batchJobFormer: &driver)
127-
else {
128-
return nil
129-
}
130-
131-
self.skippedCompileGroups = initial.skippedCompileGroups
132-
self.mandatoryJobsInOrder = initial.mandatoryJobsInOrder
97+
self.skippedCompileGroups = firstWave.skippedCompileGroups
98+
self.mandatoryJobsInOrder = firstWave.mandatoryJobsInOrder
13399
self.jobsAfterCompiles = jobsInPhases.afterCompiles
134-
self.moduleDependencyGraph = initial.graph
135-
self.buildStartTime = initial.buildStartTime
136-
self.buildEndTime = initial.buildEndTime
100+
self.moduleDependencyGraph = initialState.graph
101+
self.buildStartTime = initialState.buildStartTime
102+
self.buildEndTime = initialState.buildEndTime
137103
self.fileSystem = driver.fileSystem
138104
self.driver = driver
139105
}
140106
}
141107

142108
// MARK: - Initial State
143-
144109
extension IncrementalCompilationState {
145-
/// The initial state of an incremental compilation plan.
146-
@_spi(Testing) public struct InitialState {
110+
/// The initial state of an incremental compilation plan that consists of the module dependency graph
111+
/// and computes which inputs were invalidated by external changes.
112+
/// This set of incremental information is used during planning - job-generation, and is computed early.
113+
@_spi(Testing) public struct InitialStateForPlanning {
147114
/// The dependency graph.
148115
///
149116
/// In a status quo build, the dependency graph is derived from the state
@@ -154,17 +121,32 @@ extension IncrementalCompilationState {
154121
/// In a cross-module build, the dependency graph is derived from prior
155122
/// state that is serialized alongside the build record.
156123
let graph: ModuleDependencyGraph
124+
/// Information about the last known compilation, incl. the location of build artifacts such as the dependency graph.
125+
let buildRecordInfo: BuildRecordInfo
126+
/// Record about existence and time of the last compile.
127+
let maybeBuildRecord: BuildRecord?
128+
/// A set of inputs invalidated by external chagnes.
129+
let inputsInvalidatedByExternals: TransitivelyInvalidatedInputSet
130+
/// Compiler options related to incremental builds.
131+
let incrementalOptions: IncrementalCompilationState.Options
132+
/// The last time this compilation was started. Used to compare against e.g. input file mod dates.
133+
let buildStartTime: Date
134+
/// The last time this compilation finished. Used to compare against output file mod dates
135+
let buildEndTime: Date
136+
}
137+
}
138+
139+
// MARK: - First Wave
140+
extension IncrementalCompilationState {
141+
/// The first set of mandatory jobs for inputs which *must* be built
142+
struct FirstWave {
157143
/// The set of compile jobs we can definitely skip given the state of the
158144
/// incremental dependency graph and the status of the input files for this
159145
/// incremental build.
160146
let skippedCompileGroups: [TypedVirtualPath: CompileJobGroup]
161147
/// All of the pre-compile or compilation job (groups) known to be required
162148
/// for the first wave to execute.
163149
let mandatoryJobsInOrder: [Job]
164-
/// The last time this compilation was started. Used to compare against e.g. input file mod dates.
165-
let buildStartTime: Date
166-
/// The last time this compilation finished. Used to compare against output file mod dates
167-
let buildEndTime: Date
168150
}
169151
}
170152

@@ -204,7 +186,7 @@ fileprivate extension CompilerMode {
204186
}
205187

206188
extension Diagnostic.Message {
207-
fileprivate static var warning_incremental_requires_output_file_map: Diagnostic.Message {
189+
static var warning_incremental_requires_output_file_map: Diagnostic.Message {
208190
.warning("ignoring -incremental (currently requires an output file map)")
209191
}
210192
static var warning_incremental_requires_build_record_entry: Diagnostic.Message {
@@ -220,7 +202,7 @@ extension Diagnostic.Message {
220202
return .remark("Incremental compilation has been disabled: \(why)")
221203
}
222204

223-
fileprivate static func remark_incremental_compilation(because why: String) -> Diagnostic.Message {
205+
static func remark_incremental_compilation(because why: String) -> Diagnostic.Message {
224206
.remark("Incremental compilation: \(why)")
225207
}
226208
}

0 commit comments

Comments
 (0)