@@ -24,9 +24,6 @@ extension Driver {
24
24
var commandLine : [ Job . ArgTemplate ] = swiftCompilerPrefixArgs. map { Job . ArgTemplate. flag ( $0) }
25
25
commandLine. appendFlag ( " -frontend " )
26
26
commandLine. appendFlag ( " -scan-dependencies " )
27
- if parsedOptions. hasArgument ( . parseStdlib) {
28
- commandLine. appendFlag ( . disableObjcAttrRequiresFoundationModule)
29
- }
30
27
try addCommonFrontendOptions ( commandLine: & commandLine, inputs: & inputs,
31
28
bridgingHeaderHandling: . precompiled,
32
29
moduleDependencyGraphUse: . dependencyScan)
@@ -75,65 +72,105 @@ extension Driver {
75
72
return placeholderMapFilePath
76
73
}
77
74
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 {
82
118
var inputs : [ TypedVirtualPath ] = [ ]
83
119
84
120
// Aggregate the fast dependency scanner arguments
85
121
var commandLine : [ Job . ArgTemplate ] = swiftCompilerPrefixArgs. map { Job . ArgTemplate. flag ( $0) }
86
122
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 " )
89
126
try addCommonFrontendOptions ( commandLine: & commandLine, inputs: & inputs,
90
127
bridgingHeaderHandling: . precompiled,
91
128
moduleDependencyGraphUse: . dependencyScan)
92
129
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)
107
133
108
134
// This action does not require any input files, but all frontend actions require
109
135
// at least one input so pick any input of the current compilation.
110
136
let inputFile = inputFiles. first { $0. type == . swift }
111
137
commandLine. appendPath ( inputFile!. file)
112
138
inputs. append ( inputFile!)
113
139
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
+
115
152
// Construct the scanning job.
116
153
return Job ( moduleName: moduleOutputInfo. name,
117
- kind: . scanClangDependencies ,
154
+ kind: . scanDependencies ,
118
155
tool: VirtualPath . absolute ( try toolchain. getToolPath ( . swiftCompiler) ) ,
119
156
commandLine: commandLine,
120
157
displayInputs: inputs,
121
158
inputs: inputs,
122
- outputs: [ TypedVirtualPath ( file : . standardOutput , type : . jsonDependencies ) ] ,
159
+ outputs: outputs ,
123
160
supportsResponseFiles: true )
124
161
}
125
162
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 " )
131
169
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
138
175
}
139
176
}
0 commit comments