Skip to content

Commit 3a2ccf4

Browse files
authored
Merge pull request #1282 from artemcm/SelfContainedExplicitSwiftDeps
[Explicit Module Builds] Rely on the dependency scanner to produce complete compile commands for Swift dependencies
2 parents 929a926 + c643884 commit 3a2ccf4

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)