@@ -99,16 +99,48 @@ extension PluginError: CustomStringConvertible {
9999
100100 await withThrowingTaskGroup ( of: Void . self) { group in
101101 group. addTask {
102+ enum LoggingState {
103+ // Normal output is logged at 'progress' level.
104+ case progress
105+
106+ // If an error is detected, all output from that point onwards is logged at 'error' level, which is always printed.
107+ // Errors are reported even without the --verbose flag and cause the build to return a nonzero exit code.
108+ case error
109+
110+ func log( _ msg: String ) {
111+ let trimmed = msg. trimmingCharacters ( in: . newlines)
112+ switch self {
113+ case . progress: Diagnostics . progress ( trimmed)
114+ case . error: Diagnostics . error ( trimmed)
115+ }
116+ }
117+ }
118+
119+ var buf = " "
120+ var logger = LoggingState . progress
121+
102122 for try await line in err. lines {
103- let errorLabel = " Error: " // SwiftArgumentParser adds this prefix to all errors which bubble up
104- let trimmed = line. trimmingCharacters ( in: . whitespacesAndNewlines)
123+ buf. append ( line)
105124
106- if trimmed. starts ( with: errorLabel) {
107- // Errors are reported even without the --verbose flag and cause the build to fail.
108- Diagnostics . error ( String ( trimmed. dropFirst ( errorLabel. count) ) )
109- } else {
110- Diagnostics . progress ( trimmed)
125+ guard let ( before, after) = buf. splitOn ( first: " \n " ) else {
126+ continue
127+ }
128+
129+ var msg = before
130+ buf = String ( after)
131+
132+ let errorLabel = " Error: " // SwiftArgumentParser adds this prefix to all errors which bubble up
133+ if msg. starts ( with: errorLabel) {
134+ logger = . error
135+ msg. trimPrefix ( errorLabel)
111136 }
137+
138+ logger. log ( String ( msg) )
139+ }
140+
141+ // Print any leftover output in the buffer, in case the child exited without sending a final newline.
142+ if !buf. isEmpty {
143+ logger. log ( buf)
112144 }
113145 }
114146
@@ -118,3 +150,13 @@ extension PluginError: CustomStringConvertible {
118150 }
119151 }
120152}
153+
154+ extension Collection where Element: Equatable {
155+ func splitOn( first element: Element ) -> ( before: SubSequence , after: SubSequence ) ? {
156+ guard let idx = self . firstIndex ( of: element) else {
157+ return nil
158+ }
159+
160+ return ( self [ ..< idx] , self [ idx... ] . dropFirst ( ) )
161+ }
162+ }
0 commit comments