@@ -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,39 @@ 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
+ let specs = fileSpecs
157
+ await withTaskGroup ( of: ( URL, Error? ) . self) { group in
158
+ for index in 0 ..< fileSpecs. count {
159
+ group. addTask {
160
+ let fileSpec = specs [ index]
161
+ do {
162
+ var destination = destination
163
+ for component in fileSpec. pathComponents {
164
+ destination = destination. appendingPathComponent ( component)
165
+ }
166
+ do {
167
+ try generateFile (
168
+ contents: fileSpec. contents,
169
+ destination: destination,
170
+ verbose: verbose
171
+ )
172
+ } catch {
173
+ // If we throw from here, we'll lose the URL,
174
+ // and we'll end up removing a file that is still
175
+ // included in the files we intend to generate,
176
+ // even if we failed to do so on this run.
177
+ return ( destination, error)
178
+ }
179
+ return ( destination, nil )
180
+ }
181
+ }
182
+ }
183
+ for await result in group {
184
+ _ = previouslyGeneratedFiles. remove ( result. 0 )
185
+ if let error = result. 1 {
186
+ print ( error)
164
187
}
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
188
}
178
- }
179
-
180
- if let firstError = errors. first {
181
- // TODO: It would be nice if we could emit all errors
182
- throw firstError
183
189
}
184
190
185
191
for file in previouslyGeneratedFiles {
@@ -189,24 +195,25 @@ struct GenerateSwiftSyntax: ParsableCommand {
189
195
}
190
196
}
191
197
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
- )
198
+ }
202
199
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
- }
200
+ func generateFile(
201
+ contents: @autoclosure ( ) -> String ,
202
+ destination: URL ,
203
+ verbose: Bool
204
+ ) throws {
205
+ try FileManager . default. createDirectory (
206
+ atPath: destination. deletingLastPathComponent ( ) . path,
207
+ withIntermediateDirectories: true ,
208
+ attributes: nil
209
+ )
210
+
211
+ if verbose {
212
+ print ( " Generating \( destination. path) ... " )
213
+ }
214
+ let start = Date ( )
215
+ try contents ( ) . write ( to: destination, atomically: true , encoding: . utf8)
216
+ if verbose {
217
+ print ( " Generated \( destination. path) in \( ( Date ( ) . timeIntervalSince ( start) * 1000 ) . rounded ( ) / 1000 ) s " )
211
218
}
212
219
}
0 commit comments