Skip to content

Commit 8dadcc7

Browse files
committed
Propagate explicit module build commands and arguments to compile jobs
1 parent a1cbb36 commit 8dadcc7

File tree

6 files changed

+145
-64
lines changed

6 files changed

+145
-64
lines changed

Sources/SwiftDriver/Dependency Scanning/ModuleDependencyBuildGeneration.swift

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ extension Driver {
4747
moduleName: String,
4848
dependencyGraph: InterModuleDependencyGraph)
4949
throws -> Job {
50-
// FIXIT: Needs more error handling
5150
guard case .swift(let swiftModuleDetails) = moduleInfo.details else {
5251
throw Error.malformedModuleDependency(moduleName, "no `details` object")
5352
}
@@ -57,7 +56,13 @@ extension Driver {
5756
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath), type: .swiftModule)
5857
]
5958
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
60-
commandLine.appendFlag("-frontend")
59+
// First, take the command line options provided in the dependency information
60+
swiftModuleDetails.commandLine?.forEach { commandLine.appendFlags($0) }
61+
62+
if (swiftModuleDetails.commandLine == nil ||
63+
!swiftModuleDetails.commandLine!.contains("-frontend")) {
64+
commandLine.appendFlag("-frontend")
65+
}
6166

6267
try addModuleDependencies(moduleInfo: moduleInfo,
6368
dependencyGraph: dependencyGraph,
@@ -72,7 +77,6 @@ extension Driver {
7277
inputs.append(TypedVirtualPath(file: try VirtualPath(path: moduleInterfacePath),
7378
type: .swiftInterface))
7479
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)
75-
swiftModuleDetails.commandLine?.forEach { commandLine.appendFlag($0) }
7680

7781
return Job(
7882
moduleName: moduleName,
@@ -101,7 +105,14 @@ extension Driver {
101105
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath), type: .pcm)
102106
]
103107
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
104-
commandLine.appendFlag("-frontend")
108+
109+
// First, take the command line options provided in the dependency information
110+
clangModuleDetails.commandLine?.forEach { commandLine.appendFlags($0) }
111+
112+
if (clangModuleDetails.commandLine == nil ||
113+
!clangModuleDetails.commandLine!.contains("-frontend")) {
114+
commandLine.appendFlag("-frontend")
115+
}
105116
commandLine.appendFlags("-emit-pcm", "-module-name", moduleName)
106117

107118
try addModuleDependencies(moduleInfo: moduleInfo,
@@ -115,7 +126,6 @@ extension Driver {
115126
inputs.append(TypedVirtualPath(file: try VirtualPath(path: clangModuleDetails.moduleMapPath),
116127
type: .clangModuleMap))
117128
try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs)
118-
clangModuleDetails.commandLine?.forEach { commandLine.appendFlags($0) }
119129

120130
return Job(
121131
moduleName: moduleName,
@@ -134,29 +144,15 @@ extension Driver {
134144
dependencyGraph: InterModuleDependencyGraph,
135145
inputs: inout [TypedVirtualPath],
136146
commandLine: inout [Job.ArgTemplate]) throws {
137-
// These options ensure that the frontend only uses explicitly-specified module dependencies
138-
// and the frontend errors if it has to perform any implicit module builds.
147+
// Prohibit the frontend from implicitly building textual modules into binary modules.
139148
commandLine.appendFlags("-disable-implicit-swift-modules", "-disable-implicit-pcms")
140149
for moduleId in moduleInfo.directDependencies {
141150
guard let dependencyInfo = dependencyGraph.modules[moduleId] else {
142151
throw Error.missingModuleDependency(moduleId.getName())
143152
}
144-
switch dependencyInfo.details {
145-
case .swift:
146-
let swiftModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
147-
type: .swiftModule)
148-
commandLine.appendFlag("-swift-module-file=\(swiftModulePath.file.description)")
149-
inputs.append(swiftModulePath)
150-
case .clang(let clangDependencyDetails):
151-
let clangModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
152-
type: .pcm)
153-
let clangModuleMapPath = TypedVirtualPath(file: try VirtualPath(path: clangDependencyDetails.moduleMapPath),
154-
type: .pcm)
155-
commandLine.appendFlag("-clang-module-file=\(clangModulePath.file.description)")
156-
commandLine.appendFlag("-clang-module-map-file=\(clangModuleMapPath.file.description)")
157-
inputs.append(clangModulePath)
158-
inputs.append(clangModuleMapPath)
159-
}
153+
154+
try addModuleAsExplicitDependency(moduleInfo: dependencyInfo, commandLine: &commandLine,
155+
inputs: &inputs)
160156
}
161157
}
162158
}

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public struct Driver {
5050
case subcommandPassedToDriver
5151
case integratedReplRemoved
5252
case conflictingOptions(Option, Option)
53+
// Explicit Module Build Failures
5354
case malformedModuleDependency(String, String)
5455
case missingModuleDependency(String)
5556
case dependencyScanningFailure(Int, String)
@@ -71,6 +72,7 @@ public struct Driver {
7172
return "Compiler-internal integrated REPL has been removed; use the LLDB-enhanced REPL instead."
7273
case .conflictingOptions(let one, let two):
7374
return "conflicting options '\(one.spelling)' and '\(two.spelling)'"
75+
// Explicit Module Build Failures
7476
case .malformedModuleDependency(let moduleName, let errorDescription):
7577
return "Malformed Module Dependency: \(moduleName), \(errorDescription)"
7678
case .missingModuleDependency(let moduleName):
@@ -206,6 +208,11 @@ public struct Driver {
206208
/// This will force the driver to first emit the module and then run compile jobs.
207209
public var forceEmitModuleInSingleInvocation: Bool = false
208210

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+
209216
/// Handler for emitting diagnostics to stderr.
210217
public static let stderrDiagnosticsHandler: DiagnosticsEngine.DiagnosticsHandler = { diagnostic in
211218
let stream = stderrStream

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ extension Driver {
155155
try addCommonFrontendOptions(commandLine: &commandLine)
156156
// FIXME: MSVC runtime flags
157157

158+
if parsedOptions.contains(.driverExplicitModuleBuild) {
159+
try addExplicitModuleBuildArguments(commandLine: &commandLine, inputs: &inputs)
160+
}
161+
158162
if parsedOptions.hasArgument(.parseAsLibrary, .emitLibrary) {
159163
commandLine.appendFlag(.parseAsLibrary)
160164
}

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,49 @@ extension Driver {
304304

305305
return outputs
306306
}
307+
308+
/// Adds the specified module as an explicit module dependency to given
309+
/// inputs and comman line arguments of a compile job.
310+
func addModuleAsExplicitDependency(moduleInfo: ModuleInfo,
311+
commandLine: inout [Job.ArgTemplate],
312+
inputs: inout [TypedVirtualPath]) throws {
313+
switch moduleInfo.details {
314+
case .swift:
315+
let swiftModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
316+
type: .swiftModule)
317+
commandLine.appendFlag("-swift-module-file=\(swiftModulePath.file.description)")
318+
inputs.append(swiftModulePath)
319+
case .clang(let clangDependencyDetails):
320+
let clangModulePath = TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
321+
type: .pcm)
322+
let clangModuleMapPath = TypedVirtualPath(file: try VirtualPath(path: clangDependencyDetails.moduleMapPath),
323+
type: .pcm)
324+
commandLine.appendFlag("-clang-module-file=\(clangModulePath.file.description)")
325+
commandLine.appendFlag("-clang-module-map-file=\(clangModuleMapPath.file.description)")
326+
inputs.append(clangModulePath)
327+
inputs.append(clangModuleMapPath)
328+
}
329+
}
330+
331+
/// Adds all dependecies required for an explicit module build
332+
/// to inputs and comman line arguments of a compile job.
333+
func addExplicitModuleBuildArguments(commandLine: inout [Job.ArgTemplate],
334+
inputs: inout [TypedVirtualPath]) throws {
335+
guard let dependencyGraph = interModuleDependencyGraph else {
336+
fatalError("Inter Module Dependency Graph does not exist in explicit module build mode.")
337+
}
338+
// Prohibit the frontend from implicitly building textual modules into binary modules.
339+
commandLine.appendFlags("-disable-implicit-swift-modules", "-disable-implicit-pcms")
340+
341+
// Provide the frontend with a list of explicitly pre-built modules.
342+
for (moduleId, moduleInfo) in dependencyGraph.modules {
343+
// Skip the main output module as it is not its own dependency
344+
guard moduleId.getName() != dependencyGraph.mainModuleName else {
345+
continue
346+
}
347+
348+
try addModuleAsExplicitDependency(moduleInfo: moduleInfo, commandLine: &commandLine,
349+
inputs: &inputs)
350+
}
351+
}
307352
}

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,6 @@ extension Driver {
3434
private mutating func planStandardCompile() throws -> [Job] {
3535
var jobs = [Job]()
3636

37-
// If we've been asked to prebuild module dependencies,
38-
// for the time being, just print the jobs' compile commands.
39-
if parsedOptions.contains(.driverPrintModuleDependenciesJobs) {
40-
let modulePrebuildJobs = try generateExplicitModuleBuildJobs()
41-
let forceResponseFiles = parsedOptions.contains(.driverForceResponseFiles)
42-
for job in modulePrebuildJobs {
43-
try Self.printJob(job, resolver: try ArgsResolver(),
44-
forceResponseFiles: forceResponseFiles)
45-
}
46-
}
47-
4837
// Keep track of the various outputs we care about from the jobs we build.
4938
var linkerInputs: [TypedVirtualPath] = []
5039
var moduleInputs: [TypedVirtualPath] = []
@@ -63,9 +52,24 @@ extension Driver {
6352
}
6453
}
6554

66-
// Precompile module dependencies, if asked.
67-
if parsedOptions.contains(.driverExplicitModuleBuild) {
68-
jobs.append(contentsOf: try generateExplicitModuleBuildJobs())
55+
// If asked, add jobs to precompile module dependencies
56+
if parsedOptions.contains(.driverExplicitModuleBuild) ||
57+
parsedOptions.contains(.driverPrintModuleDependenciesJobs) {
58+
let modulePrebuildJobs = try generateExplicitModuleBuildJobs()
59+
60+
if parsedOptions.contains(.driverExplicitModuleBuild) {
61+
jobs.append(contentsOf: modulePrebuildJobs)
62+
}
63+
64+
// If we've been asked to prebuild module dependencies,
65+
// for the time being, just print the jobs' compile commands.
66+
if parsedOptions.contains(.driverPrintModuleDependenciesJobs) {
67+
let forceResponseFiles = parsedOptions.contains(.driverForceResponseFiles)
68+
for job in modulePrebuildJobs {
69+
try Self.printJob(job, resolver: try ArgsResolver(),
70+
forceResponseFiles: forceResponseFiles)
71+
}
72+
}
6973
}
7074

7175
// Precompile the bridging header if needed.
@@ -218,13 +222,11 @@ extension Driver {
218222
/// Prescan the source files to produce a module dependency graph and turn it into a set
219223
/// of jobs required to build all dependencies.
220224
public mutating func generateExplicitModuleBuildJobs() throws -> [Job] {
221-
let moduleDependencyGraph = try computeModuleDependencyGraph()
222-
if let dependencyGraph = moduleDependencyGraph {
223-
let modulePrebuildJobs =
224-
try planExplicitModuleDependenciesCompile(dependencyGraph: dependencyGraph)
225-
return modulePrebuildJobs
225+
interModuleDependencyGraph = try computeModuleDependencyGraph()
226+
guard let dependencyGraph = interModuleDependencyGraph else {
227+
fatalError("Attempting to perform Explicit Module Build job generation, but the Inter Module Dependency Graph does not exist.")
226228
}
227-
return []
229+
return try planExplicitModuleDependenciesCompile(dependencyGraph: dependencyGraph)
228230
}
229231

230232
/// Create a job if needed for simple requests that can be immediately

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ private func checkExplicitModuleBuildJob(job: Job,
3838
}
3939
try checkExplicitModuleBuildJobDependencies(job: job, moduleInfo: moduleInfo,
4040
moduleDependencyGraph: moduleDependencyGraph)
41-
4241
}
4342

4443
/// Checks that the build job for the specified module contains the required options and inputs
@@ -54,22 +53,22 @@ throws {
5453
switch dependencyInfo.details {
5554
case .swift:
5655
let swiftDependencyModulePath =
57-
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
56+
TypedVirtualPath(file: try VirtualPath(path: dependencyInfo.modulePath),
5857
type: .swiftModule)
5958
XCTAssertTrue(job.inputs.contains(swiftDependencyModulePath))
6059
XCTAssertTrue(job.commandLine.contains(
61-
.flag(String("-swift-module-file=\(moduleInfo.modulePath)"))))
60+
.flag(String("-swift-module-file=\(dependencyInfo.modulePath)"))))
6261
case .clang(let clangDependencyDetails):
6362
let clangDependencyModulePath =
64-
TypedVirtualPath(file: try VirtualPath(path: moduleInfo.modulePath),
63+
TypedVirtualPath(file: try VirtualPath(path: dependencyInfo.modulePath),
6564
type: .pcm)
6665
let clangDependencyModuleMapPath =
6766
TypedVirtualPath(file: try VirtualPath(path: clangDependencyDetails.moduleMapPath),
6867
type: .pcm)
6968
XCTAssertTrue(job.inputs.contains(clangDependencyModulePath))
7069
XCTAssertTrue(job.inputs.contains(clangDependencyModuleMapPath))
7170
XCTAssertTrue(job.commandLine.contains(
72-
.flag(String("-clang-module-file=\(moduleInfo.modulePath)"))))
71+
.flag(String("-clang-module-file=\(dependencyInfo.modulePath)"))))
7372
XCTAssertTrue(job.commandLine.contains(
7473
.flag(String("-clang-module-map-file=\(clangDependencyDetails.moduleMapPath)"))))
7574
}
@@ -116,8 +115,8 @@ final class ExplicitModuleBuildTests: XCTestCase {
116115
}
117116

118117
/// Test generation of explicit module build jobs for dependency modules when the driver
119-
/// is invoked with -driver-print-module-dependencies-jobs
120-
func testModuleDependencyBuildEndToEnd() throws {
118+
/// is invoked with -experimental-explicit-module-build
119+
func testExplicitModuleBuildJobs() throws {
121120
try withTemporaryDirectory { path in
122121
let main = path.appending(component: "main.swift")
123122
try localFileSystem.writeFileContents(main) {
@@ -131,36 +130,64 @@ final class ExplicitModuleBuildTests: XCTestCase {
131130
let testInputsPath = packageRootPath + "/TestInputs"
132131
let cHeadersPath : String = testInputsPath + "/ExplicitModuleBuilds/CHeaders"
133132
let swiftModuleInterfacesPath : String = testInputsPath + "/ExplicitModuleBuilds/Swift"
134-
var driver = try Driver(args: ["swift",
133+
var driver = try Driver(args: ["swiftc",
135134
"-I", cHeadersPath,
136135
"-I", swiftModuleInterfacesPath,
137-
"-driver-print-module-dependencies-jobs",
136+
"-experimental-explicit-module-build",
138137
main.pathString])
139-
let jobs = try driver.generateExplicitModuleBuildJobs()
140-
XCTAssertEqual(jobs.count, 10)
138+
let jobs = try driver.planBuild()
139+
XCTAssertTrue(driver.parsedOptions.contains(.driverExplicitModuleBuild))
140+
let dependencyGraph = driver.interModuleDependencyGraph!
141+
XCTAssertEqual(jobs.count, 12)
141142
for job in jobs {
142143
XCTAssertEqual(job.outputs.count, 1)
143144
switch (job.outputs[0].file) {
144145
case .relative(RelativePath("A.swiftmodule")):
145-
XCTAssertEqual(job.kind, .emitModule)
146+
try checkExplicitModuleBuildJob(job: job, moduleName: "A",
147+
moduleKind: ModuleDependencyId.CodingKeys.swift,
148+
moduleDependencyGraph: dependencyGraph)
146149
case .relative(RelativePath("E.swiftmodule")):
147-
XCTAssertEqual(job.kind, .emitModule)
150+
try checkExplicitModuleBuildJob(job: job, moduleName: "E",
151+
moduleKind: ModuleDependencyId.CodingKeys.swift,
152+
moduleDependencyGraph: dependencyGraph)
148153
case .relative(RelativePath("G.swiftmodule")):
149-
XCTAssertEqual(job.kind, .emitModule)
154+
try checkExplicitModuleBuildJob(job: job, moduleName: "G",
155+
moduleKind: ModuleDependencyId.CodingKeys.swift,
156+
moduleDependencyGraph: dependencyGraph)
150157
case .relative(RelativePath("A.pcm")):
151-
XCTAssertEqual(job.kind, .generatePCM)
158+
try checkExplicitModuleBuildJob(job: job, moduleName: "A",
159+
moduleKind: ModuleDependencyId.CodingKeys.clang,
160+
moduleDependencyGraph: dependencyGraph)
152161
case .relative(RelativePath("B.pcm")):
153-
XCTAssertEqual(job.kind, .generatePCM)
162+
try checkExplicitModuleBuildJob(job: job, moduleName: "B",
163+
moduleKind: ModuleDependencyId.CodingKeys.clang,
164+
moduleDependencyGraph: dependencyGraph)
154165
case .relative(RelativePath("C.pcm")):
155-
XCTAssertEqual(job.kind, .generatePCM)
166+
try checkExplicitModuleBuildJob(job: job, moduleName: "C",
167+
moduleKind: ModuleDependencyId.CodingKeys.clang,
168+
moduleDependencyGraph: dependencyGraph)
156169
case .relative(RelativePath("G.pcm")):
157-
XCTAssertEqual(job.kind, .generatePCM)
170+
try checkExplicitModuleBuildJob(job: job, moduleName: "G",
171+
moduleKind: ModuleDependencyId.CodingKeys.clang,
172+
moduleDependencyGraph: dependencyGraph)
158173
case .relative(RelativePath("Swift.swiftmodule")):
159-
XCTAssertEqual(job.kind, .emitModule)
174+
try checkExplicitModuleBuildJob(job: job, moduleName: "Swift",
175+
moduleKind: ModuleDependencyId.CodingKeys.swift,
176+
moduleDependencyGraph: dependencyGraph)
160177
case .relative(RelativePath("SwiftOnoneSupport.swiftmodule")):
161-
XCTAssertEqual(job.kind, .emitModule)
178+
try checkExplicitModuleBuildJob(job: job, moduleName: "SwiftOnoneSupport",
179+
moduleKind: ModuleDependencyId.CodingKeys.swift,
180+
moduleDependencyGraph: dependencyGraph)
162181
case .relative(RelativePath("SwiftShims.pcm")):
163-
XCTAssertEqual(job.kind, .generatePCM)
182+
try checkExplicitModuleBuildJob(job: job, moduleName: "SwiftShims",
183+
moduleKind: ModuleDependencyId.CodingKeys.clang,
184+
moduleDependencyGraph: dependencyGraph)
185+
case .temporary(RelativePath("main.o")):
186+
try checkExplicitModuleBuildJobDependencies(job: job,
187+
moduleInfo: dependencyGraph.mainModule,
188+
moduleDependencyGraph: dependencyGraph)
189+
case .relative(RelativePath("main")):
190+
XCTAssertEqual(job.kind, .link)
164191
default:
165192
XCTFail("Unexpected module dependency build job output: \(job.outputs[0].file)")
166193
}

0 commit comments

Comments
 (0)