@@ -68,7 +68,7 @@ struct TemplateSpec {
68
68
}
69
69
70
70
@main
71
- struct GenerateSwiftSyntax : ParsableCommand {
71
+ struct GenerateSwiftSyntax : AsyncParsableCommand {
72
72
@Argument ( help: " The path to the source directory (i.e. 'swift-syntax/Sources') where the source files are to be generated " )
73
73
var destination : String = {
74
74
let sourcesURL = URL ( fileURLWithPath: #filePath)
@@ -83,7 +83,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
83
83
@Flag ( help: " Enable verbose output " )
84
84
var verbose : Bool = false
85
85
86
- func run( ) throws {
86
+ func run( ) async throws {
87
87
let destination = URL ( fileURLWithPath: self . destination) . standardizedFileURL
88
88
89
89
var fileSpecs : [ GeneratedFileSpec ] = [
@@ -139,7 +139,6 @@ struct GenerateSwiftSyntax: ParsableCommand {
139
139
140
140
let modules = Set ( fileSpecs. compactMap { $0. pathComponents. first } )
141
141
142
- let previouslyGeneratedFilesLock = NSLock ( )
143
142
var previouslyGeneratedFiles = Set (
144
143
modules. flatMap { ( module) -> [ URL ] in
145
144
let generatedDir =
@@ -154,32 +153,37 @@ struct GenerateSwiftSyntax: ParsableCommand {
154
153
}
155
154
)
156
155
157
- var errors : [ Error ] = [ ]
158
- DispatchQueue . concurrentPerform ( iterations: fileSpecs. count) { index in
159
- let fileSpec = fileSpecs [ index]
160
- do {
161
- var destination = destination
162
- for component in fileSpec. pathComponents {
163
- destination = destination. appendingPathComponent ( component)
156
+ await withTaskGroup ( of: ( url: URL, error: Error? ) . self) { group in
157
+ for fileSpec in fileSpecs {
158
+ group. addTask {
159
+ do {
160
+ var destination = destination
161
+ for component in fileSpec. pathComponents {
162
+ destination = destination. appendingPathComponent ( component)
163
+ }
164
+ do {
165
+ try generateFile (
166
+ contents: fileSpec. contents,
167
+ destination: destination,
168
+ verbose: verbose
169
+ )
170
+ } catch {
171
+ // If we throw from here, we'll lose the URL,
172
+ // and we'll end up removing a file that is still
173
+ // included in the files we intend to generate,
174
+ // even if we failed to do so on this run.
175
+ return ( destination, error)
176
+ }
177
+ return ( destination, nil )
178
+ }
179
+ }
180
+ }
181
+ for await result in group {
182
+ _ = previouslyGeneratedFiles. remove ( result. url)
183
+ if let error = result. error {
184
+ print ( " Error when generating file at \( result. url) : " , error)
164
185
}
165
-
166
- previouslyGeneratedFilesLock. lock ( ) ;
167
- _ = previouslyGeneratedFiles. remove ( destination)
168
- previouslyGeneratedFilesLock. unlock ( )
169
-
170
- try generateFile (
171
- contents: fileSpec. contents,
172
- destination: destination,
173
- verbose: verbose
174
- )
175
- } catch {
176
- errors. append ( error)
177
186
}
178
- }
179
-
180
- if let firstError = errors. first {
181
- // TODO: It would be nice if we could emit all errors
182
- throw firstError
183
187
}
184
188
185
189
for file in previouslyGeneratedFiles {
@@ -189,24 +193,25 @@ struct GenerateSwiftSyntax: ParsableCommand {
189
193
}
190
194
}
191
195
192
- private func generateFile(
193
- contents: @autoclosure ( ) -> String ,
194
- destination: URL ,
195
- verbose: Bool
196
- ) throws {
197
- try FileManager . default. createDirectory (
198
- atPath: destination. deletingLastPathComponent ( ) . path,
199
- withIntermediateDirectories: true ,
200
- attributes: nil
201
- )
196
+ }
202
197
203
- if verbose {
204
- print ( " Generating \( destination. path) ... " )
205
- }
206
- let start = Date ( )
207
- try contents ( ) . write ( to: destination, atomically: true , encoding: . utf8)
208
- if verbose {
209
- print ( " Generated \( destination. path) in \( ( Date ( ) . timeIntervalSince ( start) * 1000 ) . rounded ( ) / 1000 ) s " )
210
- }
198
+ fileprivate func generateFile(
199
+ contents: @autoclosure ( ) -> String ,
200
+ destination: URL ,
201
+ verbose: Bool
202
+ ) throws {
203
+ try FileManager . default. createDirectory (
204
+ atPath: destination. deletingLastPathComponent ( ) . path,
205
+ withIntermediateDirectories: true ,
206
+ attributes: nil
207
+ )
208
+
209
+ if verbose {
210
+ print ( " Generating \( destination. path) ... " )
211
+ }
212
+ let start = Date ( )
213
+ try contents ( ) . write ( to: destination, atomically: true , encoding: . utf8)
214
+ if verbose {
215
+ print ( " Generated \( destination. path) in \( ( Date ( ) . timeIntervalSince ( start) * 1000 ) . rounded ( ) / 1000 ) s " )
211
216
}
212
217
}
0 commit comments