Skip to content

Commit db7515c

Browse files
author
David Ungar
committed
Add more incremental logic
1 parent cf6356f commit db7515c

File tree

2 files changed

+136
-41
lines changed

2 files changed

+136
-41
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 24 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ public enum ModuleOutput: Equatable {
1818
return path
1919
}
2020
}
21+
22+
public var isTopLevel: Bool {
23+
switch self {
24+
case .topLevel: return true
25+
default: return false
26+
}
27+
}
2128
}
2229

2330
/// The Swift driver.
@@ -63,12 +70,6 @@ public struct Driver {
6370
/// The mode in which the compiler will execute.
6471
public let compilerMode: CompilerMode
6572

66-
/// Whether to print out incremental build decisions
67-
public let showIncrementalBuildDecisions: Bool
68-
69-
/// Is the build incremental?
70-
public let isIncremental: Bool
71-
7273
/// The type of the primary output generated by the compiler.
7374
public let compilerOutputType: FileType?
7475

@@ -88,6 +89,9 @@ public struct Driver {
8889
/// \c nil if no module should be emitted.
8990
public let moduleOutput: ModuleOutput?
9091

92+
/// Code & data for incremental compilation
93+
public let incrementalCompilation: IncrementalCompilation
94+
9195
/// The name of the Swift module being built.
9296
public let moduleName: String
9397

@@ -196,24 +200,17 @@ public struct Driver {
196200
self.inputFiles = try Self.collectInputFiles(&self.parsedOptions)
197201

198202
// Initialize an empty output file map, which will be populated when we start creating jobs.
199-
let outputFileMap: OutputFileMap?
200203
if let outputFileMapArg = parsedOptions.getLastArgument(.output_file_map)?.asSingle {
201204
let path = try AbsolutePath(validating: outputFileMapArg)
202-
outputFileMap = try .load(file: path, diagnosticEngine: diagnosticEngine)
205+
self.outputFileMap = try .load(file: path, diagnosticEngine: diagnosticEngine)
203206
}
204207
else {
205-
outputFileMap = nil
208+
self.outputFileMap = nil
206209
}
207-
self.outputFileMap = outputFileMap
208210

209211
// Determine the compilation mode.
210212
self.compilerMode = Self.computeCompilerMode(&parsedOptions, driverKind: driverKind)
211213

212-
// Determine whether the compilation should be incremental
213-
(showIncrementalBuildDecisions: self.showIncrementalBuildDecisions,
214-
shouldBeIncremental: self.isIncremental) =
215-
Self.computeIncrementalPredicates(&parsedOptions, driverKind: driverKind)
216-
217214
// Figure out the primary outputs from the driver.
218215
(self.compilerOutputType, self.linkerOutputType) = Self.determinePrimaryOutputs(&parsedOptions, driverKind: driverKind, diagnosticsEngine: diagnosticEngine)
219216

@@ -224,9 +221,20 @@ public struct Driver {
224221
(self.debugInfoLevel, self.debugInfoFormat) = Self.computeDebugInfo(&parsedOptions, diagnosticsEngine: diagnosticEngine)
225222

226223
// Determine the module we're building and whether/how the module file itself will be emitted.
227-
(self.moduleOutput, self.moduleName) = try Self.computeModuleInfo(
224+
let moduleOutput: ModuleOutput?
225+
(moduleOutput, self.moduleName) = try Self.computeModuleInfo(
228226
&parsedOptions, compilerOutputType: compilerOutputType, compilerMode: compilerMode, linkerOutputType: linkerOutputType,
229227
debugInfoLevel: debugInfoLevel, diagnosticsEngine: diagnosticEngine)
228+
self.moduleOutput = moduleOutput
229+
230+
// Determine the state for incremental compilation
231+
self.incrementalCompilation = IncrementalCompilation(
232+
&parsedOptions,
233+
compilerMode: compilerMode,
234+
outputFileMap: self.outputFileMap,
235+
compilerOutputType: self.compilerOutputType,
236+
moduleOutput: moduleOutput,
237+
diagnosticEngine: diagnosticEngine)
230238

231239
self.sdkPath = Self.computeSDKPath(&parsedOptions, compilerMode: compilerMode, toolchain: toolchain, diagnosticsEngine: diagnosticEngine)
232240

@@ -402,31 +410,6 @@ extension Driver {
402410
}
403411
}
404412

405-
extension Driver {
406-
/// Compute whether the compilation should be incremental
407-
private static func computeIncrementalPredicates(
408-
_ parsedOptions: inout ParsedOptions,
409-
driverKind: DriverKind) -> (showIncrementalBuildDecisions: Bool, shouldBeIncremental: Bool) {
410-
let showIncrementalBuildDecisions = parsedOptions.hasArgument(.driver_show_incremental))
411-
guard (parsedOptions.hasArgument(.incremental) else {
412-
return (showIncrementalBuildDecisions: showIncrementalBuildDecisions, shouldBeIncremental: false)
413-
}
414-
guard let reasonToDisable = parsedOptions.hasArgument(.whole_module_optimization)
415-
? "is not compatible with whole module optimization."
416-
: parsedOptions.hasArgument(.embed_bitcode)
417-
? "is not currently compatible with embedding LLVM IR bitcode."
418-
: nil
419-
else {
420-
return (showIncrementalBuildDecisions: showIncrementalBuildDecisions, shouldBeIncremental: true)
421-
}
422-
if (showIncrementalBuildDecisions) {
423-
stderrStream <<<"Incremental compilation has been disabled, because it \(reasonToDisable)\n"
424-
stderrStream.flush()
425-
}
426-
return (showIncrementalBuildDecisions: showIncrementalBuildDecisions, shouldBeIncremental: false)
427-
}
428-
}
429-
430413
/// Input and output file handling.
431414
extension Driver {
432415
/// Apply the given working directory to all paths in the parsed options.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import TSCBasic
2+
import TSCUtility
3+
4+
public struct IncrementalCompilation {
5+
public let showIncrementalBuildDecisions: Bool
6+
public let shouldCompileIncrementally: Bool
7+
public let buildRecordPath: VirtualPath?
8+
public let outputBuildRecordForModuleOnlyBuild: Bool
9+
10+
public init(_ parsedOptions: inout ParsedOptions,
11+
compilerMode: CompilerMode,
12+
outputFileMap: OutputFileMap?,
13+
compilerOutputType: FileType?,
14+
moduleOutput: ModuleOutput?,
15+
diagnosticEngine: DiagnosticsEngine
16+
) {
17+
let showIncrementalBuildDecisions = Self.getShowIncrementalBuildDecisions(&parsedOptions)
18+
self.showIncrementalBuildDecisions = showIncrementalBuildDecisions
19+
20+
let shouldCompileIncrementally = Self.computeAndExplainShouldCompileIncrementally(
21+
&parsedOptions,
22+
showIncrementalBuildDecisions: showIncrementalBuildDecisions,
23+
compilerMode: compilerMode)
24+
25+
self.shouldCompileIncrementally = shouldCompileIncrementally
26+
27+
self.buildRecordPath = Self.computeBuildRecordPath(
28+
outputFileMap: outputFileMap,
29+
compilerOutputType: compilerOutputType,
30+
diagnosticEngine: shouldCompileIncrementally ? diagnosticEngine : nil)
31+
32+
// If we emit module along with full compilation, emit build record
33+
// file for '-emit-module' only mode as well.
34+
self.outputBuildRecordForModuleOnlyBuild = self.buildRecordPath != nil &&
35+
moduleOutput?.isTopLevel ?? false
36+
}
37+
38+
private static func getShowIncrementalBuildDecisions(_ parsedOptions: inout ParsedOptions) -> Bool {
39+
parsedOptions.hasArgument(.driver_show_incremental)
40+
}
41+
42+
private static func computeAndExplainShouldCompileIncrementally(
43+
_ parsedOptions: inout ParsedOptions,
44+
showIncrementalBuildDecisions: Bool,
45+
compilerMode: CompilerMode
46+
)
47+
-> Bool
48+
{
49+
func explain(disabledBecause why: String) {
50+
stdoutStream <<< "Incremental compilation has been disabled, because it \(why).\n"
51+
stdoutStream.flush()
52+
}
53+
guard parsedOptions.hasArgument(.incremental) else {
54+
return false
55+
}
56+
guard compilerMode.supportsIncrementalCompilation else {
57+
explain(disabledBecause: "is not compatible with \(compilerMode)")
58+
return false
59+
}
60+
guard !parsedOptions.hasArgument(.embed_bitcode) else {
61+
explain(disabledBecause: "is not currently compatible with embedding LLVM IR bitcode")
62+
return false
63+
}
64+
return true
65+
}
66+
67+
private static func computeBuildRecordPath(
68+
outputFileMap: OutputFileMap?,
69+
compilerOutputType: FileType?,
70+
diagnosticEngine: DiagnosticsEngine?
71+
) -> VirtualPath? {
72+
// FIXME: This should work without an output file map. We should have
73+
// another way to specify a build record and where to put intermediates.
74+
guard let ofm = outputFileMap else {
75+
diagnosticEngine.map { $0.emit(.warning_incremental_requires_output_file_map) }
76+
return nil
77+
}
78+
guard let partialBuildRecordPath = ofm.existingOutputForSingleInput(outputType: .swiftDeps)
79+
else {
80+
diagnosticEngine.map { $0.emit(.warning_incremental_requires_build_record_entry) }
81+
return nil
82+
}
83+
// In 'emit-module' only mode, use build-record filename suffixed with
84+
// '~moduleonly'. So that module-only mode doesn't mess up build-record
85+
// file for full compilation.
86+
return try! compilerOutputType == .swiftModule
87+
? VirtualPath(path: partialBuildRecordPath.name + "~moduleonly")
88+
: partialBuildRecordPath
89+
}
90+
}
91+
92+
93+
fileprivate extension CompilerMode {
94+
var supportsIncrementalCompilation: Bool {
95+
switch self {
96+
case .standardCompile, .immediate, .repl: return true
97+
case .singleCompile: return false
98+
}
99+
}
100+
}
101+
102+
public extension Diagnostic.Message {
103+
static var warning_incremental_requires_output_file_map: Diagnostic.Message {
104+
.warning("ignoring -incremental (currently requires an output file map)")
105+
}
106+
static var warning_incremental_requires_build_record_entry: Diagnostic.Message {
107+
.warning(
108+
"ignoring -incremental; " +
109+
"output file map has no master dependencies entry under \(FileType.swiftDeps)"
110+
)
111+
}
112+
}

0 commit comments

Comments
 (0)