@@ -99,16 +99,48 @@ extension PluginError: CustomStringConvertible {
99
99
100
100
await withThrowingTaskGroup ( of: Void . self) { group in
101
101
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
+
102
122
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)
105
124
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)
111
136
}
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)
112
144
}
113
145
}
114
146
@@ -118,3 +150,13 @@ extension PluginError: CustomStringConvertible {
118
150
}
119
151
}
120
152
}
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