Skip to content

Commit c643884

Browse files
committed
[Explicit Module Builds] Rely on the dependency scanner to produce complete compile commands for Swift dependencies
This change removes instances where the driver modified command-line invocations used to build Swift module dependencies, as produced by the dependency scanner. Instead, the scanner will now emit command lines that are sufficient to build all such module dependencies, including explicit dependency inputs.
1 parent ab39e87 commit c643884

File tree

6 files changed

+137
-221
lines changed

6 files changed

+137
-221
lines changed

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

Lines changed: 30 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -162,33 +162,11 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
162162
// Add precompiled module candidates, if present
163163
if let compiledCandidateList = moduleDetails.compiledModuleCandidates {
164164
for compiledCandidate in compiledCandidateList {
165-
commandLine.appendFlag("-candidate-module-file")
166-
let compiledCandidatePath = compiledCandidate
167-
commandLine.appendPath(VirtualPath.lookup(compiledCandidatePath.path))
168-
inputs.append(TypedVirtualPath(file: compiledCandidatePath.path,
165+
inputs.append(TypedVirtualPath(file: compiledCandidate.path,
169166
type: .swiftModule))
170167
}
171168
}
172169

173-
if supportsExplicitInterfaceBuild {
174-
// Ensure the compiler flags specified in the interface are ignored
175-
// because they are already captured in the dependency scanner output
176-
commandLine.appendFlag(.explicitInterfaceModuleBuild)
177-
}
178-
179-
// FIXME: This is a temporary measure meant to be deleted once supported toolchains'
180-
// scanners always output commands with '-o'.
181-
//
182-
// If the dependency scanner did not append its own "-o", add it here.
183-
// This is temporary and is meant to handle both: the scanner that
184-
// appends '-o' and one that doesn't, until we have a toolchain snapshot with the scanner
185-
// that appends '-o' always.
186-
let outputFlagIndeces = commandLine.enumerated().compactMap { $1 == .flag("-o") ? $0 : nil }
187-
if outputFlagIndeces.isEmpty {
188-
commandLine.appendFlag(.o)
189-
commandLine.appendPath(VirtualPath.lookup(moduleInfo.modulePath.path))
190-
}
191-
192170
jobs.append(Job(
193171
moduleName: moduleId.moduleName,
194172
kind: .compileModuleFromInterface,
@@ -241,23 +219,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
241219
let moduleMapPath = moduleDetails.moduleMapPath.path
242220
let modulePCMPath = moduleInfo.modulePath
243221
outputs.append(TypedVirtualPath(file: modulePCMPath.path, type: .pcm))
244-
245-
// TODO: Remove this once toolchain is updated
246-
// If the dependency scanner did not append its own "-o", add it here.
247-
// This is temporary and is meant to handle both: the scanner that
248-
// appends '-o' and one that doesn't, until we have a toolchain snapshot with the scanner
249-
// that appends '-o' always.
250-
let outputFlagIndeces = commandLine.enumerated().compactMap { $1 == .flag("-o") ? $0 : nil }
251-
// One '-o' is expected as an `-Xcc` flag.
252-
if outputFlagIndeces.count <= 1 {
253-
commandLine.appendFlags("-o", modulePCMPath.path.description)
254-
}
255-
256-
// TODO: Remove this once toolchain is updated
257-
// Fixup "-o -Xcc '<replace-me>'"
258-
if let outputIndex = commandLine.firstIndex(of: .flag("<replace-me>")) {
259-
commandLine[outputIndex] = .path(VirtualPath.lookup(modulePCMPath.path))
260-
}
261222

262223
// The only required input is the .modulemap for this module.
263224
// Command line options in the dependency scanner output will include the
@@ -285,53 +246,42 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
285246
inputs: inout [TypedVirtualPath],
286247
commandLine: inout [Job.ArgTemplate]) throws {
287248
// Prohibit the frontend from implicitly building textual modules into binary modules.
288-
commandLine.appendFlags("-disable-implicit-swift-modules",
289-
"-Xcc", "-fno-implicit-modules",
290-
"-Xcc", "-fno-implicit-module-maps")
291249
var swiftDependencyArtifacts: [SwiftModuleArtifactInfo] = []
292250
var clangDependencyArtifacts: [ClangModuleArtifactInfo] = []
293251
try addModuleDependencies(moduleId: moduleId, pcmArgs: pcmArgs,
294252
clangDependencyArtifacts: &clangDependencyArtifacts,
295253
swiftDependencyArtifacts: &swiftDependencyArtifacts)
296254

297-
// Swift Module dependencies are passed encoded in a JSON file as described by
298-
// SwiftModuleArtifactInfo
299-
if !swiftDependencyArtifacts.isEmpty {
300-
let dependencyFile =
301-
try serializeModuleDependencies(for: moduleId, dependencyArtifacts: swiftDependencyArtifacts)
302-
commandLine.appendFlag("-explicit-swift-module-map-file")
303-
commandLine.appendPath(dependencyFile)
304-
inputs.append(TypedVirtualPath(file: dependencyFile.intern(),
305-
type: .jsonSwiftArtifacts))
306-
// Each individual module binary is still an "input" to ensure the build system gets the
307-
// order correctly.
308-
for dependencyModule in swiftDependencyArtifacts {
309-
inputs.append(TypedVirtualPath(file: dependencyModule.modulePath.path,
310-
type: .swiftModule))
311-
}
255+
// Each individual module binary is still an "input" to ensure the build system gets the
256+
// order correctly.
257+
for dependencyModule in swiftDependencyArtifacts {
258+
inputs.append(TypedVirtualPath(file: dependencyModule.modulePath.path,
259+
type: .swiftModule))
312260
}
261+
313262
// Clang module dependencies are specified on the command line explicitly
314263
for moduleArtifactInfo in clangDependencyArtifacts {
315264
let clangModulePath =
316-
TypedVirtualPath(file: moduleArtifactInfo.modulePath.path,
265+
TypedVirtualPath(file: moduleArtifactInfo.clangModulePath.path,
317266
type: .pcm)
318267
let clangModuleMapPath =
319-
TypedVirtualPath(file: moduleArtifactInfo.moduleMapPath.path,
268+
TypedVirtualPath(file: moduleArtifactInfo.clangModuleMapPath.path,
320269
type: .clangModuleMap)
321270
inputs.append(clangModulePath)
322271
inputs.append(clangModuleMapPath)
272+
}
323273

324-
// If an existing dependency module path stub exists, replace it.
325-
if let existingIndex = commandLine.firstIndex(of: .flag("-fmodule-file=" + moduleArtifactInfo.moduleName + "=<replace-me>")) {
326-
commandLine[existingIndex] = .flag("-fmodule-file=\(moduleArtifactInfo.moduleName)=\(clangModulePath.file.description)")
327-
} else if case .swift(_) = moduleId {
328-
commandLine.appendFlags("-Xcc",
329-
"-fmodule-file=\(moduleArtifactInfo.moduleName)=\(clangModulePath.file.description)")
330-
}
331-
if case .swift(_) = moduleId {
332-
commandLine.appendFlags("-Xcc",
333-
"-fmodule-map-file=\(clangModuleMapPath.file.description)")
334-
}
274+
// Swift Main Module dependencies are passed encoded in a JSON file as described by
275+
// SwiftModuleArtifactInfo
276+
if moduleId.moduleName == mainModuleName {
277+
let dependencyFile =
278+
try serializeModuleDependencies(for: moduleId,
279+
swiftDependencyArtifacts: swiftDependencyArtifacts,
280+
clangDependencyArtifacts: clangDependencyArtifacts)
281+
commandLine.appendFlag("-explicit-swift-module-map-file")
282+
commandLine.appendPath(dependencyFile)
283+
inputs.append(TypedVirtualPath(file: dependencyFile.intern(),
284+
type: .jsonSwiftArtifacts))
335285
}
336286
}
337287

@@ -410,6 +360,9 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
410360
public mutating func resolveMainModuleDependencies(inputs: inout [TypedVirtualPath],
411361
commandLine: inout [Job.ArgTemplate]) throws {
412362
let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName)
363+
commandLine.appendFlags("-disable-implicit-swift-modules",
364+
"-Xcc", "-fno-implicit-modules",
365+
"-Xcc", "-fno-implicit-module-maps")
413366
try resolveExplicitModuleDependencies(moduleId: mainModuleId,
414367
pcmArgs:
415368
try dependencyGraph.swiftModulePCMArgs(of: mainModuleId),
@@ -419,11 +372,15 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
419372

420373
/// Store the output file artifacts for a given module in a JSON file, return the file's path.
421374
private func serializeModuleDependencies(for moduleId: ModuleDependencyId,
422-
dependencyArtifacts: [SwiftModuleArtifactInfo]
375+
swiftDependencyArtifacts: [SwiftModuleArtifactInfo],
376+
clangDependencyArtifacts: [ClangModuleArtifactInfo]
423377
) throws -> VirtualPath {
378+
let allDependencyArtifacts: [ModuleDependencyArtifactInfo] =
379+
swiftDependencyArtifacts.map {ModuleDependencyArtifactInfo.swift($0)} +
380+
clangDependencyArtifacts.map {ModuleDependencyArtifactInfo.clang($0)}
424381
let encoder = JSONEncoder()
425382
encoder.outputFormatting = [.prettyPrinted]
426-
let contents = try encoder.encode(dependencyArtifacts)
383+
let contents = try encoder.encode(allDependencyArtifacts)
427384
return VirtualPath.createUniqueTemporaryFileWithKnownContents(.init("\(moduleId.moduleName)-dependencies.json"), contents)
428385
}
429386

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -77,78 +77,6 @@ extension Diagnostic.Message {
7777

7878
return dependencyGraph
7979
}
80-
81-
/// Update the given inter-module dependency graph to set module paths to be within the module cache,
82-
/// if one is present, and for Swift modules to use the context hash in the file name.
83-
private mutating func resolveDependencyModulePaths(dependencyGraph: inout InterModuleDependencyGraph)
84-
throws {
85-
// If a module cache path is specified, update all module dependencies
86-
// to be output into it.
87-
if let moduleCachePath = parsedOptions.getLastArgument(.moduleCachePath)?.asSingle {
88-
try resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: &dependencyGraph,
89-
moduleCachePath: moduleCachePath)
90-
}
91-
92-
// Set the output path to include the module's context hash
93-
try resolveDependencyModuleFileNamesWithContextHash(dependencyGraph: &dependencyGraph)
94-
}
95-
96-
/// For Swift module dependencies, set the output path to include the module's context hash
97-
private mutating func resolveDependencyModuleFileNamesWithContextHash(dependencyGraph: inout InterModuleDependencyGraph)
98-
throws {
99-
for (moduleId, moduleInfo) in dependencyGraph.modules {
100-
// Output path on the main module is determined by the invocation arguments.
101-
guard moduleId.moduleName != dependencyGraph.mainModuleName else {
102-
continue
103-
}
104-
105-
let plainPath = VirtualPath.lookup(dependencyGraph.modules[moduleId]!.modulePath.path)
106-
if case .swift(let swiftDetails) = moduleInfo.details {
107-
guard let contextHash = swiftDetails.contextHash else {
108-
throw Driver.Error.missingContextHashOnSwiftDependency(moduleId.moduleName)
109-
}
110-
let updatedPath = plainPath.parentDirectory.appending(component: "\(plainPath.basenameWithoutExt)-\(contextHash).\(plainPath.extension!)")
111-
dependencyGraph.modules[moduleId]!.modulePath = TextualVirtualPath(path: updatedPath.intern())
112-
}
113-
// TODO: Remove this once toolchain is updated
114-
else if case .clang(let clangDetails) = moduleInfo.details {
115-
if !moduleInfo.modulePath.path.description.contains(clangDetails.contextHash) {
116-
let contextHash = clangDetails.contextHash
117-
let updatedPath = plainPath.parentDirectory.appending(component: "\(plainPath.basenameWithoutExt)-\(contextHash).\(plainPath.extension!)")
118-
dependencyGraph.modules[moduleId]!.modulePath = TextualVirtualPath(path: updatedPath.intern())
119-
}
120-
}
121-
}
122-
}
123-
124-
/// Resolve all paths to dependency binary module files to be relative to the module cache path.
125-
private mutating func resolveDependencyModulePathsRelativeToModuleCache(dependencyGraph: inout InterModuleDependencyGraph,
126-
moduleCachePath: String)
127-
throws {
128-
for (moduleId, moduleInfo) in dependencyGraph.modules {
129-
// Output path on the main module is determined by the invocation arguments.
130-
if case .swift(let name) = moduleId {
131-
if name == dependencyGraph.mainModuleName {
132-
continue
133-
}
134-
let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path)
135-
// Use VirtualPath to get the OS-specific path separators right.
136-
let modulePathInCache =
137-
try VirtualPath(path: moduleCachePath).appending(component: modulePath.basename)
138-
dependencyGraph.modules[moduleId]!.modulePath =
139-
TextualVirtualPath(path: modulePathInCache.intern())
140-
}
141-
// TODO: Remove this once toolchain is updated
142-
else if case .clang(_) = moduleId {
143-
let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path)
144-
// Use VirtualPath to get the OS-specific path separators right.
145-
let modulePathInCache =
146-
try VirtualPath(path: moduleCachePath).appending(component: modulePath.basename)
147-
dependencyGraph.modules[moduleId]!.modulePath =
148-
TextualVirtualPath(path: modulePathInCache.intern())
149-
}
150-
}
151-
}
15280
}
15381

15482
public extension Driver {

Sources/SwiftDriver/ExplicitModuleBuilds/SerializableModuleArtifacts.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,32 @@
4545
/// The module's name
4646
public let moduleName: String
4747
/// The path for the module's .pcm file
48-
public let modulePath: TextualVirtualPath
48+
public let clangModulePath: TextualVirtualPath
4949
/// The path for this module's .modulemap file
50-
public let moduleMapPath: TextualVirtualPath
50+
public let clangModuleMapPath: TextualVirtualPath
51+
/// A flag to indicate whether this module is a framework
52+
public let isFramework: Bool
5153

5254
init(name: String, modulePath: TextualVirtualPath, moduleMapPath: TextualVirtualPath) {
5355
self.moduleName = name
54-
self.modulePath = modulePath
55-
self.moduleMapPath = moduleMapPath
56+
self.clangModulePath = modulePath
57+
self.clangModuleMapPath = moduleMapPath
58+
self.isFramework = false
59+
}
60+
}
61+
62+
enum ModuleDependencyArtifactInfo: Codable {
63+
case clang(ClangModuleArtifactInfo)
64+
case swift(SwiftModuleArtifactInfo)
65+
66+
func encode(to encoder: Encoder) throws {
67+
var container = encoder.singleValueContainer()
68+
switch self {
69+
case .swift(let swiftInfo):
70+
try container.encode(swiftInfo)
71+
case .clang(let clangInfo):
72+
try container.encode(clangInfo)
73+
}
5674
}
5775
}
5876

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ struct CompileJobGroup {
7777
let compileGroups: [CompileJobGroup]
7878
let afterCompiles: [Job]
7979

80-
var allJobs: [Job] {
80+
@_spi(Testing) public var allJobs: [Job] {
8181
var r = beforeCompiles
8282
compileGroups.forEach { r.append(contentsOf: $0.allJobs()) }
8383
r.append(contentsOf: afterCompiles)
@@ -152,7 +152,7 @@ extension Driver {
152152

153153
/// Construct a build plan consisting of *all* jobs required for building the current module (non-incrementally).
154154
/// At build time, incremental state will be used to distinguish which of these jobs must run.
155-
mutating private func computeJobsForPhasedStandardBuild(with dependencyGraph: InterModuleDependencyGraph?)
155+
@_spi(Testing) public mutating func computeJobsForPhasedStandardBuild(with dependencyGraph: InterModuleDependencyGraph?)
156156
throws -> JobsInPhases {
157157
// Centralize job accumulation here.
158158
// For incremental compilation, must separate jobs happening before,

0 commit comments

Comments
 (0)