@@ -31,9 +31,23 @@ public struct ExplicitModuleBuildHandler {
3131 /// The toolchain to be used for frontend job generation.
3232 private let toolchain : Toolchain
3333
34- public init ( dependencyGraph: InterModuleDependencyGraph , toolchain: Toolchain ) throws {
34+ /// The file system which we should interact with.
35+ private let fileSystem : FileSystem
36+
37+ /// Path to the directory that will contain the temporary files.
38+ /// e.g. Explicit Swift module artifact files
39+ private let temporaryDirectory : AbsolutePath
40+
41+ public init ( dependencyGraph: InterModuleDependencyGraph , toolchain: Toolchain ,
42+ fileSystem: FileSystem ) throws {
3543 self . dependencyGraph = dependencyGraph
3644 self . toolchain = toolchain
45+ self . fileSystem = fileSystem
46+ self . temporaryDirectory = try withTemporaryDirectory ( removeTreeOnDeinit: false ) { path in
47+ // FIXME: TSC removes empty directories even when removeTreeOnDeinit is false. This seems like a bug.
48+ try fileSystem. writeFileContents ( path. appending ( component: " .keep-directory " ) ) { $0 <<< " " }
49+ return path
50+ }
3751 }
3852
3953 /// Generate build jobs for all dependencies of the main module.
@@ -207,6 +221,19 @@ public struct ExplicitModuleBuildHandler {
207221 )
208222 }
209223
224+ /// Store the output file artifacts for a given module in a JSON file, return the file's path.
225+ private func serializeModuleDependencies( for moduleId: ModuleDependencyId ,
226+ dependencyArtifacts: [ SwiftModuleArtifactInfo ]
227+ ) throws -> AbsolutePath {
228+ let dependencyFilePath =
229+ temporaryDirectory. appending ( component: " \( moduleId. moduleName) -dependencies.json " )
230+ let encoder = JSONEncoder ( )
231+ encoder. outputFormatting = [ . prettyPrinted]
232+ let contents = try encoder. encode ( dependencyArtifacts)
233+ try fileSystem. writeFileContents ( dependencyFilePath, bytes: ByteString ( contents) )
234+ return dependencyFilePath
235+ }
236+
210237 /// For the specified module, update the given command line flags and inputs
211238 /// to use explicitly-built module dependencies.
212239 ///
@@ -217,27 +244,43 @@ public struct ExplicitModuleBuildHandler {
217244 inputs: inout [ TypedVirtualPath ] ,
218245 commandLine: inout [ Job . ArgTemplate ]
219246 ) throws {
247+
220248 // Prohibit the frontend from implicitly building textual modules into binary modules.
221249 commandLine. appendFlags ( " -disable-implicit-swift-modules " , " -Xcc " , " -Xclang " , " -Xcc " ,
222250 " -fno-implicit-modules " )
251+ var swiftDependencyArtifacts : [ SwiftModuleArtifactInfo ] = [ ]
223252 try addModuleDependencies ( moduleId: moduleId, pcmArgs: pcmArgs, inputs: & inputs,
224- commandLine: & commandLine)
253+ commandLine: & commandLine,
254+ swiftDependencyArtifacts: & swiftDependencyArtifacts)
255+
256+ if ( swiftDependencyArtifacts. isEmpty) {
257+ let dependencyFile = try serializeModuleDependencies ( for: moduleId,
258+ dependencyArtifacts: swiftDependencyArtifacts)
259+ commandLine. appendFlag ( " -explicit-swift-module-map-file " )
260+ commandLine. appendPath ( dependencyFile)
261+ inputs. append ( TypedVirtualPath ( file: try VirtualPath ( path: dependencyFile. pathString) ,
262+ type: . jsonSwiftArtifacts) )
263+ }
225264 }
226265
227266 /// Add a specific module dependency as an input and a corresponding command
228267 /// line flag. Dispatches to clang and swift-specific variants.
229268 mutating private func addModuleDependencies( moduleId: ModuleDependencyId ,
230269 pcmArgs: [ String ] ,
231270 inputs: inout [ TypedVirtualPath ] ,
232- commandLine: inout [ Job . ArgTemplate ] ) throws {
271+ commandLine: inout [ Job . ArgTemplate ] ,
272+ swiftDependencyArtifacts: inout [ SwiftModuleArtifactInfo ]
273+ ) throws {
233274 for dependencyId in try dependencyGraph. moduleInfo ( of: moduleId) . directDependencies {
234275 switch dependencyId {
235276 case . swift:
236277 try addSwiftModuleDependency ( moduleId: moduleId, dependencyId: dependencyId,
237- pcmArgs: pcmArgs, inputs: & inputs, commandLine: & commandLine)
278+ pcmArgs: pcmArgs, inputs: & inputs, commandLine: & commandLine,
279+ swiftDependencyArtifacts: & swiftDependencyArtifacts)
238280 case . clang:
239281 try addClangModuleDependency ( moduleId: moduleId, dependencyId: dependencyId,
240- pcmArgs: pcmArgs, inputs: & inputs, commandLine: & commandLine)
282+ pcmArgs: pcmArgs, inputs: & inputs, commandLine: & commandLine,
283+ swiftDependencyArtifacts: & swiftDependencyArtifacts)
241284 }
242285 }
243286 }
@@ -250,7 +293,9 @@ public struct ExplicitModuleBuildHandler {
250293 dependencyId: ModuleDependencyId ,
251294 pcmArgs: [ String ] ,
252295 inputs: inout [ TypedVirtualPath ] ,
253- commandLine: inout [ Job . ArgTemplate ] ) throws {
296+ commandLine: inout [ Job . ArgTemplate ] ,
297+ swiftDependencyArtifacts: inout [ SwiftModuleArtifactInfo ]
298+ ) throws {
254299 // Generate a build job for the dependency module, if not already generated
255300 if swiftModuleBuildCache [ dependencyId] == nil {
256301 try genSwiftModuleBuildJob ( moduleId: dependencyId)
@@ -261,13 +306,17 @@ public struct ExplicitModuleBuildHandler {
261306 let dependencyInfo = try dependencyGraph. moduleInfo ( of: dependencyId)
262307 let swiftModulePath = TypedVirtualPath ( file: try VirtualPath ( path: dependencyInfo. modulePath) ,
263308 type: . swiftModule)
264- commandLine. appendFlags ( " -swift-module-file " )
265- commandLine. appendPath ( swiftModulePath. file)
266- inputs. append ( swiftModulePath)
309+
310+ // Collect the requried information about this module
311+ // TODO: add .swiftdoc and .swiftsourceinfo for this module.
312+ swiftDependencyArtifacts. append (
313+ SwiftModuleArtifactInfo ( name: dependencyId. moduleName,
314+ modulePath: swiftModulePath. file. description) )
267315
268316 // Process all transitive dependencies as direct
269- try addModuleDependencies ( moduleId: dependencyId, pcmArgs: pcmArgs, inputs: & inputs,
270- commandLine: & commandLine)
317+ try addModuleDependencies ( moduleId: dependencyId, pcmArgs: pcmArgs,
318+ inputs: & inputs, commandLine: & commandLine,
319+ swiftDependencyArtifacts: & swiftDependencyArtifacts)
271320 }
272321
273322 /// Add a specific Clang module dependency as an input and a corresponding command
@@ -278,7 +327,9 @@ public struct ExplicitModuleBuildHandler {
278327 dependencyId: ModuleDependencyId ,
279328 pcmArgs: [ String ] ,
280329 inputs: inout [ TypedVirtualPath ] ,
281- commandLine: inout [ Job . ArgTemplate ] ) throws {
330+ commandLine: inout [ Job . ArgTemplate ] ,
331+ swiftDependencyArtifacts: inout [ SwiftModuleArtifactInfo ]
332+ ) throws {
282333 // Generate a build job for the dependency module at the given target, if not already generated
283334 if clangTargetModuleBuildCache [ ( dependencyId, pcmArgs) ] == nil {
284335 try genClangModuleBuildJob ( moduleId: dependencyId, pcmArgs: pcmArgs)
@@ -302,8 +353,9 @@ public struct ExplicitModuleBuildHandler {
302353 inputs. append ( clangModuleMapPath)
303354
304355 // Process all transitive dependencies as direct
305- try addModuleDependencies ( moduleId: dependencyId, pcmArgs: pcmArgs, inputs: & inputs,
306- commandLine: & commandLine)
356+ try addModuleDependencies ( moduleId: dependencyId, pcmArgs: pcmArgs,
357+ inputs: & inputs, commandLine: & commandLine,
358+ swiftDependencyArtifacts: & swiftDependencyArtifacts)
307359 }
308360}
309361
0 commit comments