Skip to content

Commit 2424c5a

Browse files
authored
Merge pull request #113 from artemcm/ExplicitModuleBuild
Propagate Explicit Module Build command arguments to compile jobs.
2 parents 78806f2 + 5092bdb commit 2424c5a

27 files changed

+452
-57
lines changed

Sources/SwiftDriver/Dependency Scanning/ModuleDependencyBuildGeneration.swift

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import Foundation
1616
extension Driver {
1717
/// For the current moduleDependencyGraph, plan the order and generate jobs
1818
/// for explicitly building all dependency modules.
19-
mutating func planExplicitModuleDependenciesCompile(dependencyGraph: InterModuleDependencyGraph)
20-
throws -> [Job] {
19+
mutating func planExplicitModuleDependenciesCompile(
20+
dependencyGraph: InterModuleDependencyGraph
21+
) throws -> [Job] {
2122
var jobs: [Job] = []
2223
for (id, moduleInfo) in dependencyGraph.modules {
2324
// The generation of the main module file will be handled elsewhere in the driver.
@@ -26,23 +27,27 @@ extension Driver {
2627
}
2728
switch id {
2829
case .swift(let moduleName):
29-
let swiftModuleBuildJob = try genSwiftModuleDependencyBuildJob(moduleInfo: moduleInfo,
30-
moduleName: moduleName)
30+
let swiftModuleBuildJob =
31+
try genSwiftModuleDependencyBuildJob(moduleInfo: moduleInfo,
32+
moduleName: moduleName,
33+
dependencyGraph: dependencyGraph)
3134
jobs.append(swiftModuleBuildJob)
3235
case .clang(let moduleName):
33-
let clangModuleBuildJob = try genClangModuleDependencyBuildJob(moduleInfo: moduleInfo,
34-
moduleName: moduleName)
36+
let clangModuleBuildJob =
37+
try genClangModuleDependencyBuildJob(moduleInfo: moduleInfo,
38+
moduleName: moduleName,
39+
dependencyGraph: dependencyGraph)
3540
jobs.append(clangModuleBuildJob)
36-
3741
}
3842
}
3943
return jobs
4044
}
4145

4246
/// For a given swift module dependency, generate a build job
4347
mutating private func genSwiftModuleDependencyBuildJob(moduleInfo: ModuleInfo,
44-
moduleName: String) throws -> Job {
45-
// FIXIT: Needs more error handling
48+
moduleName: String,
49+
dependencyGraph: InterModuleDependencyGraph
50+
) throws -> Job {
4651
guard case .swift(let swiftModuleDetails) = moduleInfo.details else {
4752
throw Error.malformedModuleDependency(moduleName, "no `details` object")
4853
}
@@ -52,7 +57,18 @@ extension Driver {
5257
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath), type: .swiftModule)
5358
]
5459
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
55-
commandLine.appendFlag("-frontend")
60+
// First, take the command line options provided in the dependency information
61+
swiftModuleDetails.commandLine?.forEach { commandLine.appendFlags($0) }
62+
63+
if (swiftModuleDetails.commandLine == nil ||
64+
!swiftModuleDetails.commandLine!.contains("-frontend")) {
65+
commandLine.appendFlag("-frontend")
66+
}
67+
68+
try addModuleDependencies(moduleInfo: moduleInfo,
69+
dependencyGraph: dependencyGraph,
70+
inputs: &inputs,
71+
commandLine: &commandLine)
5672

5773
// Build the .swiftinterfaces file using a list of command line options specified in the
5874
// `details` field.
@@ -62,7 +78,6 @@ extension Driver {
6278
inputs.append(TypedVirtualPath(file: try VirtualPath(path: moduleInterfacePath),
6379
type: .swiftInterface))
6480
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)
65-
swiftModuleDetails.commandLine?.forEach { commandLine.appendFlag($0) }
6681

6782
return Job(
6883
moduleName: moduleName,
@@ -76,7 +91,9 @@ extension Driver {
7691

7792
/// For a given clang module dependency, generate a build job
7893
mutating private func genClangModuleDependencyBuildJob(moduleInfo: ModuleInfo,
79-
moduleName: String) throws -> Job {
94+
moduleName: String,
95+
dependencyGraph: InterModuleDependencyGraph
96+
) throws -> Job {
8097
// For clang modules, the Fast Dependency Scanner emits a list of source
8198
// files (with a .modulemap among them), and a list of compile command
8299
// options.
@@ -89,15 +106,27 @@ extension Driver {
89106
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath), type: .pcm)
90107
]
91108
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
92-
commandLine.appendFlag("-frontend")
109+
110+
// First, take the command line options provided in the dependency information
111+
clangModuleDetails.commandLine?.forEach { commandLine.appendFlags($0) }
112+
113+
if (clangModuleDetails.commandLine == nil ||
114+
!clangModuleDetails.commandLine!.contains("-frontend")) {
115+
commandLine.appendFlag("-frontend")
116+
}
93117
commandLine.appendFlags("-emit-pcm", "-module-name", moduleName)
94118

119+
try addModuleDependencies(moduleInfo: moduleInfo,
120+
dependencyGraph: dependencyGraph,
121+
inputs: &inputs,
122+
commandLine: &commandLine)
123+
95124
// The only required input is the .modulemap for this module.
96-
commandLine.append(Job.ArgTemplate.path(try VirtualPath(path: clangModuleDetails.moduleMapPath)))
125+
commandLine.append(Job.ArgTemplate.path(
126+
try VirtualPath(path: clangModuleDetails.moduleMapPath)))
97127
inputs.append(TypedVirtualPath(file: try VirtualPath(path: clangModuleDetails.moduleMapPath),
98128
type: .clangModuleMap))
99129
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)
100-
clangModuleDetails.commandLine?.forEach { commandLine.appendFlags("-Xcc", $0) }
101130

102131
return Job(
103132
moduleName: moduleName,
@@ -108,4 +137,24 @@ extension Driver {
108137
outputs: outputs
109138
)
110139
}
140+
141+
142+
/// For the specified module, update its command line flags and inputs
143+
/// to use explicitly-built module dependencies.
144+
private func addModuleDependencies(moduleInfo: ModuleInfo,
145+
dependencyGraph: InterModuleDependencyGraph,
146+
inputs: inout [TypedVirtualPath],
147+
commandLine: inout [Job.ArgTemplate]) throws {
148+
// Prohibit the frontend from implicitly building textual modules into binary modules.
149+
commandLine.appendFlags("-disable-implicit-swift-modules", "-Xcc", "-Xclang", "-Xcc",
150+
"-fno-implicit-modules")
151+
for moduleId in moduleInfo.directDependencies {
152+
guard let dependencyInfo = dependencyGraph.modules[moduleId] else {
153+
throw Error.missingModuleDependency(moduleId.moduleName)
154+
}
155+
try addModuleAsExplicitDependency(moduleInfo: dependencyInfo,
156+
dependencyGraph: dependencyGraph,
157+
commandLine: &commandLine, inputs: &inputs)
158+
}
159+
}
111160
}

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ public struct Driver {
5050
case subcommandPassedToDriver
5151
case integratedReplRemoved
5252
case conflictingOptions(Option, Option)
53+
// Explicit Module Build Failures
5354
case malformedModuleDependency(String, String)
55+
case missingModuleDependency(String)
56+
case dependencyScanningFailure(Int, String)
5457

5558
public var description: String {
5659
switch self {
@@ -69,8 +72,13 @@ public struct Driver {
6972
return "Compiler-internal integrated REPL has been removed; use the LLDB-enhanced REPL instead."
7073
case .conflictingOptions(let one, let two):
7174
return "conflicting options '\(one.spelling)' and '\(two.spelling)'"
75+
// Explicit Module Build Failures
7276
case .malformedModuleDependency(let moduleName, let errorDescription):
7377
return "Malformed Module Dependency: \(moduleName), \(errorDescription)"
78+
case .missingModuleDependency(let moduleName):
79+
return "Missing Module Dependency Info: \(moduleName)"
80+
case .dependencyScanningFailure(let code, let error):
81+
return "Module Dependency Scanner returned with non-zero exit status: \(code), \(error)"
7482
}
7583
}
7684
}
@@ -200,6 +208,11 @@ public struct Driver {
200208
/// This will force the driver to first emit the module and then run compile jobs.
201209
public var forceEmitModuleInSingleInvocation: Bool = false
202210

211+
/// The module dependency graph, which is populated during the planning phase
212+
/// only when all modules will be prebuilt and treated as explicit by the
213+
/// various compilation jobs.
214+
var interModuleDependencyGraph: InterModuleDependencyGraph? = nil
215+
203216
/// Handler for emitting diagnostics to stderr.
204217
public static let stderrDiagnosticsHandler: DiagnosticsEngine.DiagnosticsHandler = { diagnostic in
205218
let stream = stderrStream

Sources/SwiftDriver/Driver/ModuleDependencyScanning.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extension Driver {
2323
let resolver = try ArgsResolver()
2424
let compilerPath = VirtualPath.absolute(try toolchain.getToolPath(.swiftCompiler))
2525
let tool = try resolver.resolve(.path(compilerPath))
26+
var inputs: [TypedVirtualPath] = []
2627

2728
// Aggregate the fast dependency scanner arguments
2829
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
@@ -31,7 +32,9 @@ extension Driver {
3132
if parsedOptions.hasArgument(.parseStdlib) {
3233
commandLine.appendFlag(.disableObjcAttrRequiresFoundationModule)
3334
}
34-
try addCommonFrontendOptions(commandLine: &commandLine)
35+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs,
36+
bridgingHeaderHandling: .precompiled,
37+
moduleDependencyGraphUse: .dependencyScan)
3538
// FIXME: MSVC runtime flags
3639

3740
// Pass on the input files
@@ -41,6 +44,17 @@ extension Driver {
4144
let arguments = [tool] + (try commandLine.map { try resolver.resolve($0) })
4245
let scanProcess = try Process.launchProcess(arguments: arguments, env: env)
4346
let result = try scanProcess.waitUntilExit()
47+
// Error on dependency scanning failure
48+
if (result.exitStatus != .terminated(code: 0)) {
49+
var returnCode = 0
50+
switch result.exitStatus {
51+
case .terminated(let code):
52+
returnCode = Int(code)
53+
case .signalled(let signal):
54+
returnCode = Int(signal)
55+
}
56+
throw Error.dependencyScanningFailure(returnCode, try result.utf8stderrOutput())
57+
}
4458
guard let outputData = try? Data(result.utf8Output().utf8) else {
4559
return nil
4660
}

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ extension Driver {
152152
commandLine.appendFlag(.disableObjcAttrRequiresFoundationModule)
153153
}
154154

155-
try addCommonFrontendOptions(commandLine: &commandLine)
155+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs)
156156
// FIXME: MSVC runtime flags
157157

158158
if parsedOptions.hasArgument(.parseAsLibrary, .emitLibrary) {

Sources/SwiftDriver/Jobs/EmitModuleJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extension Driver {
6060
inputs.append(input)
6161
}
6262

63-
try addCommonFrontendOptions(commandLine: &commandLine)
63+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs)
6464
// FIXME: Add MSVC runtime library flags
6565

6666
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,21 @@ extension Driver {
3535
/// Use the precompiled bridging header.
3636
case precompiled
3737
}
38+
/// Whether the driver has already constructed a module dependency graph or is in the process
39+
/// of doing so
40+
enum ModuleDependencyGraphUse {
41+
/// Even though the driver may be in ExplicitModuleBuild mode, the dependency graph has not yet
42+
/// been constructed, omit processing module dependencies
43+
case dependencyScan
44+
/// If the driver is in Explicit Module Build mode, the dependency graph has been computed
45+
case computed
46+
}
3847
/// Add frontend options that are common to different frontend invocations.
3948
mutating func addCommonFrontendOptions(
4049
commandLine: inout [Job.ArgTemplate],
41-
bridgingHeaderHandling: BridgingHeaderHandling = .precompiled
50+
inputs: inout [TypedVirtualPath],
51+
bridgingHeaderHandling: BridgingHeaderHandling = .precompiled,
52+
moduleDependencyGraphUse: ModuleDependencyGraphUse = .computed
4253
) throws {
4354
// Only pass -target to the REPL or immediate modes if it was explicitly
4455
// specified on the command line.
@@ -54,6 +65,18 @@ extension Driver {
5465
}
5566
}
5667

68+
// If in ExplicitModuleBuild mode and the dependency graph has been computed, add module
69+
// dependencies.
70+
// May also be used for generation of the dependency graph itself in ExplicitModuleBuild mode.
71+
if (parsedOptions.contains(.driverExplicitModuleBuild) &&
72+
moduleDependencyGraphUse == .computed) {
73+
guard let dependencyGraph = interModuleDependencyGraph else {
74+
fatalError("Attempting to add Explicit Module job dependencies, but the Inter Module Dependency Graph does not exist.")
75+
}
76+
try addExplicitModuleBuildArguments(dependencyGraph: dependencyGraph,
77+
commandLine: &commandLine, inputs: &inputs)
78+
}
79+
5780
if let variant = parsedOptions.getLastArgument(.targetVariant)?.asSingle {
5881
commandLine.appendFlag(.targetVariant)
5982
commandLine.appendFlag(Triple(variant, normalizing: true).triple)
@@ -304,4 +327,62 @@ extension Driver {
304327

305328
return outputs
306329
}
330+
331+
/// Adds the specified module as an explicit module dependency to given
332+
/// inputs and command line arguments of a compile job.
333+
/// Also adds transitive dependencies that arise from dependencies of this module.
334+
func addModuleAsExplicitDependency(moduleInfo: ModuleInfo,
335+
dependencyGraph: InterModuleDependencyGraph,
336+
commandLine: inout [Job.ArgTemplate],
337+
inputs: inout [TypedVirtualPath]) throws {
338+
switch moduleInfo.details {
339+
case .swift:
340+
let swiftModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
341+
type: .swiftModule)
342+
commandLine.appendFlags("-swift-module-file")
343+
commandLine.appendPath(swiftModulePath.file)
344+
inputs.append(swiftModulePath)
345+
case .clang(let clangDependencyDetails):
346+
let clangModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
347+
type: .pcm)
348+
let clangModuleMapPath = TypedVirtualPath(file: try VirtualPath(path: clangDependencyDetails.moduleMapPath),
349+
type: .pcm)
350+
commandLine.appendFlags("-Xcc", "-Xclang", "-Xcc",
351+
"-fmodule-map-file=\(clangModuleMapPath.file.description)")
352+
commandLine.appendFlags("-Xcc", "-Xclang", "-Xcc",
353+
"-fmodule-file=\(clangModulePath.file.description)")
354+
inputs.append(clangModulePath)
355+
inputs.append(clangModuleMapPath)
356+
}
357+
// Add transitive dependencies to the command line as well
358+
for transitiveDependencyId in moduleInfo.directDependencies {
359+
guard let transitiveDependencyInfo = dependencyGraph.modules[transitiveDependencyId] else {
360+
throw Error.missingModuleDependency(transitiveDependencyId.moduleName)
361+
}
362+
try addModuleAsExplicitDependency(moduleInfo: transitiveDependencyInfo,
363+
dependencyGraph: dependencyGraph,
364+
commandLine: &commandLine,
365+
inputs: &inputs)
366+
}
367+
}
368+
369+
/// Adds all dependecies required for an explicit module build
370+
/// to inputs and comman line arguments of a compile job.
371+
func addExplicitModuleBuildArguments(dependencyGraph: InterModuleDependencyGraph,
372+
commandLine: inout [Job.ArgTemplate],
373+
inputs: inout [TypedVirtualPath]) throws {
374+
// Prohibit the frontend from implicitly building textual modules into binary modules.
375+
commandLine.appendFlags("-disable-implicit-swift-modules", "-Xcc", "-Xclang", "-Xcc",
376+
"-fno-implicit-modules")
377+
378+
// Provide the frontend with a list of explicitly pre-built modules.
379+
for (moduleId, moduleInfo) in dependencyGraph.modules {
380+
// Skip the main output module as it is not its own dependency
381+
guard moduleId.moduleName != dependencyGraph.mainModuleName else {
382+
continue
383+
}
384+
try addModuleAsExplicitDependency(moduleInfo: moduleInfo, dependencyGraph: dependencyGraph,
385+
commandLine: &commandLine, inputs: &inputs)
386+
}
387+
}
307388
}

Sources/SwiftDriver/Jobs/GeneratePCHJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension Driver {
2323
commandLine.appendFlag("-frontend")
2424

2525
try addCommonFrontendOptions(
26-
commandLine: &commandLine, bridgingHeaderHandling: .parsed)
26+
commandLine: &commandLine, inputs: &inputs, bridgingHeaderHandling: .parsed)
2727

2828
try commandLine.appendLast(.indexStorePath, from: &parsedOptions)
2929

Sources/SwiftDriver/Jobs/GeneratePCMJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extension Driver {
4848
commandLine.appendPath(output.file)
4949

5050
try addCommonFrontendOptions(
51-
commandLine: &commandLine, bridgingHeaderHandling: .ignored)
51+
commandLine: &commandLine, inputs: &inputs, bridgingHeaderHandling: .ignored)
5252

5353
try commandLine.appendLast(.indexStorePath, from: &parsedOptions)
5454

Sources/SwiftDriver/Jobs/InterpretJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension Driver {
2727
commandLine.appendFlag(.disableObjcAttrRequiresFoundationModule)
2828
}
2929

30-
try addCommonFrontendOptions(commandLine: &commandLine)
30+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs)
3131
// FIXME: MSVC runtime flags
3232

3333
try commandLine.appendLast(.parseSil, from: &parsedOptions)

Sources/SwiftDriver/Jobs/MergeModuleJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extension Driver {
4141
commandLine.appendFlag(.disableDiagnosticPasses)
4242
commandLine.appendFlag(.disableSilPerfOptzns)
4343

44-
try addCommonFrontendOptions(commandLine: &commandLine)
44+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs)
4545
// FIXME: Add MSVC runtime library flags
4646

4747
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)

0 commit comments

Comments
 (0)