Skip to content

Commit 49becc2

Browse files
committed
plugin: handle chunks of output correctly
1 parent b0a23d0 commit 49becc2

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

Examples/HelloWorldHummingbird/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let package = Package(
2121
platforms: [.macOS(.v14)],
2222
dependencies: [
2323
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.1.0"),
24-
.package(url: "https://github.com/apple/swift-container-plugin", from: "0.4.0"),
24+
.package(path: "../.."),
2525
],
2626
targets: [
2727
.executableTarget(name: "hello-world", dependencies: [.product(name: "Hummingbird", package: "hummingbird")])

Plugins/ContainerImageBuilder/main.swift

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)