@@ -24,9 +24,6 @@ extension Driver {
2424 var commandLine : [ Job . ArgTemplate ] = swiftCompilerPrefixArgs. map { Job . ArgTemplate. flag ( $0) }
2525 commandLine. appendFlag ( " -frontend " )
2626 commandLine. appendFlag ( " -scan-dependencies " )
27- if parsedOptions. hasArgument ( . parseStdlib) {
28- commandLine. appendFlag ( . disableObjcAttrRequiresFoundationModule)
29- }
3027 try addCommonFrontendOptions ( commandLine: & commandLine, inputs: & inputs,
3128 bridgingHeaderHandling: . precompiled,
3229 moduleDependencyGraphUse: . dependencyScan)
@@ -75,65 +72,105 @@ extension Driver {
7572 return placeholderMapFilePath
7673 }
7774
78- /// Compute the dependencies for a given Clang module, by invoking the Clang dependency scanning action
79- /// with the given module's name and a set of arguments (including the target version)
80- mutating func clangDependencyScanningJob( moduleId: ModuleDependencyId ,
81- pcmArgs: [ String ] ) throws -> Job {
75+ mutating func performBatchDependencyScan( moduleInfos: [ BatchScanModuleInfo ] )
76+ throws -> [ ModuleDependencyId : [ InterModuleDependencyGraph ] ] {
77+ let batchScanningJob = try batchDependencyScanningJob ( for: moduleInfos)
78+ let forceResponseFiles = parsedOptions. hasArgument ( . driverForceResponseFiles)
79+ let batchScanResult =
80+ try self . executor. execute ( job: batchScanningJob,
81+ forceResponseFiles: forceResponseFiles,
82+ recordedInputModificationDates: recordedInputModificationDates)
83+ let success = batchScanResult. exitStatus == . terminated( code: EXIT_SUCCESS)
84+ guard success else {
85+ throw JobExecutionError . jobFailedWithNonzeroExitCode (
86+ SwiftDriverExecutor . computeReturnCode ( exitStatus: batchScanResult. exitStatus) ,
87+ try batchScanResult. utf8stderrOutput ( ) )
88+ }
89+
90+ // Decode the resulting dependency graphs and build a dictionary from a moduleId to
91+ // a set of dependency graphs that were built for it
92+ let moduleVersionedGraphMap =
93+ try moduleInfos. reduce ( into: [ ModuleDependencyId: [ InterModuleDependencyGraph] ] ( ) ) {
94+ let moduleId : ModuleDependencyId
95+ let dependencyGraphPath : VirtualPath
96+ switch $1 {
97+ case . swift( let swiftModuleBatchScanInfo) :
98+ moduleId = . swift( swiftModuleBatchScanInfo. swiftModuleName)
99+ dependencyGraphPath = try VirtualPath ( path: swiftModuleBatchScanInfo. output)
100+ case . clang( let clangModuleBatchScanInfo) :
101+ moduleId = . clang( clangModuleBatchScanInfo. clangModuleName)
102+ dependencyGraphPath = try VirtualPath ( path: clangModuleBatchScanInfo. output)
103+ }
104+ let contents = try fileSystem. readFileContents ( dependencyGraphPath)
105+ let decodedGraph = try JSONDecoder ( ) . decode ( InterModuleDependencyGraph . self,
106+ from: Data ( contents. contents) )
107+ if $0 [ moduleId] != nil {
108+ $0 [ moduleId] !. append ( decodedGraph)
109+ } else {
110+ $0 [ moduleId] = [ decodedGraph]
111+ }
112+ }
113+ return moduleVersionedGraphMap
114+ }
115+
116+ /// Precompute the dependencies for a given collection of modules using swift frontend's batch scanning mode
117+ mutating func batchDependencyScanningJob( for moduleInfos: [ BatchScanModuleInfo ] ) throws -> Job {
82118 var inputs : [ TypedVirtualPath ] = [ ]
83119
84120 // Aggregate the fast dependency scanner arguments
85121 var commandLine : [ Job . ArgTemplate ] = swiftCompilerPrefixArgs. map { Job . ArgTemplate. flag ( $0) }
86122 commandLine. appendFlag ( " -frontend " )
87- commandLine. appendFlag ( " -scan-clang-dependencies " )
88-
123+ // The dependency scanner automatically operates in batch mode if -batch-scan-input-file
124+ // is present.
125+ commandLine. appendFlag ( " -scan-dependencies " )
89126 try addCommonFrontendOptions ( commandLine: & commandLine, inputs: & inputs,
90127 bridgingHeaderHandling: . precompiled,
91128 moduleDependencyGraphUse: . dependencyScan)
92129
93- // Ensure the `-target` option is inherited from the dependent Swift module's PCM args
94- if let targetOptionIndex = pcmArgs. firstIndex ( of: Option . target. spelling) {
95- // PCM args are formulated as Clang command line options specified with:
96- // -Xcc <option> -Xcc <option_value>
97- assert ( pcmArgs. count > targetOptionIndex + 1 && pcmArgs [ targetOptionIndex + 1 ] == " -Xcc " )
98- let pcmArgTriple = Triple ( pcmArgs [ targetOptionIndex + 2 ] )
99- // Override the invocation's default target argument by appending the one extracted from
100- // the pcmArgs
101- commandLine. appendFlag ( . target)
102- commandLine. appendFlag ( pcmArgTriple. triple)
103- }
104-
105- // Add the PCM args specific to this scan
106- pcmArgs. forEach { commandLine. appendFlags ( $0) }
130+ let batchScanInputFilePath = try serializeBatchScanningModuleArtifacts ( moduleInfos: moduleInfos)
131+ commandLine. appendFlag ( " -batch-scan-input-file " )
132+ commandLine. appendPath ( batchScanInputFilePath)
107133
108134 // This action does not require any input files, but all frontend actions require
109135 // at least one input so pick any input of the current compilation.
110136 let inputFile = inputFiles. first { $0. type == . swift }
111137 commandLine. appendPath ( inputFile!. file)
112138 inputs. append ( inputFile!)
113139
114- commandLine. appendFlags ( " -module-name " , moduleId. moduleName)
140+ // This job's outputs are defined as a set of dependency graph json files
141+ let outputs : [ TypedVirtualPath ] = try moduleInfos. map {
142+ switch $0 {
143+ case . swift( let swiftModuleBatchScanInfo) :
144+ return TypedVirtualPath ( file: try VirtualPath ( path: swiftModuleBatchScanInfo. output) ,
145+ type: . jsonDependencies)
146+ case . clang( let clangModuleBatchScanInfo) :
147+ return TypedVirtualPath ( file: try VirtualPath ( path: clangModuleBatchScanInfo. output) ,
148+ type: . jsonDependencies)
149+ }
150+ }
151+
115152 // Construct the scanning job.
116153 return Job ( moduleName: moduleOutputInfo. name,
117- kind: . scanClangDependencies ,
154+ kind: . scanDependencies ,
118155 tool: VirtualPath . absolute ( try toolchain. getToolPath ( . swiftCompiler) ) ,
119156 commandLine: commandLine,
120157 displayInputs: inputs,
121158 inputs: inputs,
122- outputs: [ TypedVirtualPath ( file : . standardOutput , type : . jsonDependencies ) ] ,
159+ outputs: outputs ,
123160 supportsResponseFiles: true )
124161 }
125162
126- mutating func scanClangModule( moduleId: ModuleDependencyId , pcmArgs: [ String ] )
127- throws -> InterModuleDependencyGraph {
128- let clangDependencyScannerJob = try clangDependencyScanningJob ( moduleId: moduleId,
129- pcmArgs: pcmArgs)
130- let forceResponseFiles = parsedOptions. hasArgument ( . driverForceResponseFiles)
163+ /// Serialize a collection of modules into an input format expected by the batch module dependency scanner.
164+ func serializeBatchScanningModuleArtifacts( moduleInfos: [ BatchScanModuleInfo ] )
165+ throws -> AbsolutePath {
166+ let temporaryDirectory = try determineTempDirectory ( )
167+ let batchScanInputFilePath =
168+ temporaryDirectory. appending ( component: " \( moduleOutputInfo. name) -batch-module-scan.json " )
131169
132- let dependencyGraph =
133- try self . executor. execute ( job: clangDependencyScannerJob,
134- capturingJSONOutputAs: InterModuleDependencyGraph . self,
135- forceResponseFiles: forceResponseFiles,
136- recordedInputModificationDates: recordedInputModificationDates)
137- return dependencyGraph
170+ let encoder = JSONEncoder ( )
171+ encoder. outputFormatting = [ . prettyPrinted]
172+ let contents = try encoder. encode ( moduleInfos)
173+ try fileSystem. writeFileContents ( batchScanInputFilePath, bytes: ByteString ( contents) )
174+ return batchScanInputFilePath
138175 }
139176}
0 commit comments