Skip to content

Commit 3959e74

Browse files
diogotclaude
andcommitted
Use shell redirection to temp files instead of pipes
Pipes have buffer limits (~64KB) that can cause deadlocks when reading stdout/stderr sequentially. Using shell redirection to temp files avoids this entirely - no pipes, no buffer issues. The shell handles argument passing via "$@" which avoids escaping. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent ef8cf35 commit 3959e74

1 file changed

Lines changed: 26 additions & 17 deletions

File tree

Sources/XCResultParser/XCResultTool.swift

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,43 @@ actor XCResultTool {
1010

1111
/// Execute xcresulttool and return the JSON data
1212
private func execute(arguments: [String]) async throws -> Data {
13-
let process = Process()
14-
process.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
15-
process.arguments = ["xcresulttool"] + arguments + ["--path", path, "--compact"]
13+
let fileManager = FileManager.default
14+
let tempDir = fileManager.temporaryDirectory
15+
let uuid = UUID().uuidString
16+
let stdoutFile = tempDir.appendingPathComponent("xcresulttool-\(uuid).stdout")
17+
let stderrFile = tempDir.appendingPathComponent("xcresulttool-\(uuid).stderr")
18+
19+
defer {
20+
try? fileManager.removeItem(at: stdoutFile)
21+
try? fileManager.removeItem(at: stderrFile)
22+
}
1623

17-
let stdout = Pipe()
18-
let stderr = Pipe()
19-
process.standardOutput = stdout
20-
process.standardError = stderr
24+
let process = Process()
25+
process.executableURL = URL(fileURLWithPath: "/bin/sh")
26+
process.arguments = [
27+
"-c",
28+
"xcrun \"$@\" > \"\(stdoutFile.path)\" 2> \"\(stderrFile.path)\"",
29+
"--",
30+
"xcresulttool"
31+
] + arguments + ["--path", path, "--compact"]
2132

2233
do {
2334
try process.run()
2435
} catch {
2536
throw XCResultParserError.xcresulttoolNotFound
2637
}
2738

28-
// Read output BEFORE waiting to avoid pipe buffer deadlock
29-
// (if output exceeds ~64KB, the process blocks waiting to write)
30-
let outputData = stdout.fileHandleForReading.readDataToEndOfFile()
31-
let errorData = stderr.fileHandleForReading.readDataToEndOfFile()
39+
process.waitUntilExit()
3240

33-
// Wait for process termination on a detached task to avoid:
34-
// 1. Blocking the cooperative thread pool (waitUntilExit blocks)
35-
// 2. Race condition with terminationHandler (process may have exited before handler was registered)
36-
await Task.detached {
37-
process.waitUntilExit()
38-
}.value
41+
let outputData: Data
42+
do {
43+
outputData = try Data(contentsOf: stdoutFile)
44+
} catch {
45+
throw XCResultParserError.xcresulttoolFailed("Failed to read stdout: \(error.localizedDescription)")
46+
}
3947

4048
if process.terminationStatus != 0 {
49+
let errorData = (try? Data(contentsOf: stderrFile)) ?? Data()
4150
let errorMessage = String(data: errorData, encoding: .utf8) ?? "Unknown error"
4251
throw XCResultParserError.xcresulttoolFailed(errorMessage.trimmingCharacters(in: .whitespacesAndNewlines))
4352
}

0 commit comments

Comments
 (0)