Skip to content

Commit da52352

Browse files
authored
Merge pull request #730 from rconnell9/rconnell/async-crash-6-2
[6.2] Fix crash when running swift test for swift-build
2 parents e0559ea + b5603bb commit da52352

File tree

1 file changed

+54
-42
lines changed

1 file changed

+54
-42
lines changed

Sources/SWBUtil/Process.swift

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -82,61 +82,73 @@ extension Process {
8282
extension Process {
8383
public static func getOutput(url: URL, arguments: [String], currentDirectoryURL: URL? = nil, environment: Environment? = nil, interruptible: Bool = true) async throws -> Processes.ExecutionResult {
8484
if #available(macOS 15, iOS 18, tvOS 18, watchOS 11, visionOS 2, *) {
85+
let stdoutPipe = Pipe()
86+
let stderrPipe = Pipe()
87+
8588
// Extend the lifetime of the pipes to avoid file descriptors being closed until the AsyncStream is finished being consumed.
86-
return try await withExtendedLifetime((Pipe(), Pipe())) { (stdoutPipe, stderrPipe) in
87-
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
88-
let stdoutStream = process.makeStream(for: \.standardOutputPipe, using: stdoutPipe)
89-
let stderrStream = process.makeStream(for: \.standardErrorPipe, using: stderrPipe)
90-
return (stdoutStream, stderrStream)
91-
} collect: { (stdoutStream, stderrStream) in
92-
let stdoutData = try await stdoutStream.collect()
93-
let stderrData = try await stderrStream.collect()
94-
return (stdoutData: stdoutData, stderrData: stderrData)
95-
}
96-
return Processes.ExecutionResult(exitStatus: exitStatus, stdout: Data(output.stdoutData), stderr: Data(output.stderrData))
89+
defer { withExtendedLifetime(stdoutPipe) {} }
90+
defer { withExtendedLifetime(stderrPipe) {} }
91+
92+
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
93+
let stdoutStream = process.makeStream(for: \.standardOutputPipe, using: stdoutPipe)
94+
let stderrStream = process.makeStream(for: \.standardErrorPipe, using: stderrPipe)
95+
return (stdoutStream, stderrStream)
96+
} collect: { (stdoutStream, stderrStream) in
97+
let stdoutData = try await stdoutStream.collect()
98+
let stderrData = try await stderrStream.collect()
99+
return (stdoutData: stdoutData, stderrData: stderrData)
97100
}
101+
return Processes.ExecutionResult(exitStatus: exitStatus, stdout: Data(output.stdoutData), stderr: Data(output.stderrData))
98102
} else {
103+
let stdoutPipe = Pipe()
104+
let stderrPipe = Pipe()
105+
99106
// Extend the lifetime of the pipes to avoid file descriptors being closed until the AsyncStream is finished being consumed.
100-
return try await withExtendedLifetime((Pipe(), Pipe())) { (stdoutPipe, stderrPipe) in
101-
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
102-
let stdoutStream = process._makeStream(for: \.standardOutputPipe, using: stdoutPipe)
103-
let stderrStream = process._makeStream(for: \.standardErrorPipe, using: stderrPipe)
104-
return (stdoutStream, stderrStream)
105-
} collect: { (stdoutStream, stderrStream) in
106-
let stdoutData = try await stdoutStream.collect()
107-
let stderrData = try await stderrStream.collect()
108-
return (stdoutData: stdoutData, stderrData: stderrData)
109-
}
110-
return Processes.ExecutionResult(exitStatus: exitStatus, stdout: Data(output.stdoutData), stderr: Data(output.stderrData))
107+
defer { withExtendedLifetime(stdoutPipe) {} }
108+
defer { withExtendedLifetime(stderrPipe) {} }
109+
110+
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
111+
let stdoutStream = process._makeStream(for: \.standardOutputPipe, using: stdoutPipe)
112+
let stderrStream = process._makeStream(for: \.standardErrorPipe, using: stderrPipe)
113+
return (stdoutStream, stderrStream)
114+
} collect: { (stdoutStream, stderrStream) in
115+
let stdoutData = try await stdoutStream.collect()
116+
let stderrData = try await stderrStream.collect()
117+
return (stdoutData: stdoutData, stderrData: stderrData)
111118
}
119+
return Processes.ExecutionResult(exitStatus: exitStatus, stdout: Data(output.stdoutData), stderr: Data(output.stderrData))
112120
}
113121
}
114122

115123
public static func getMergedOutput(url: URL, arguments: [String], currentDirectoryURL: URL? = nil, environment: Environment? = nil, interruptible: Bool = true) async throws -> (exitStatus: Processes.ExitStatus, output: Data) {
116124
if #available(macOS 15, iOS 18, tvOS 18, watchOS 11, visionOS 2, *) {
117-
// Extend the lifetime of the pipe to avoid file descriptors being closed until the AsyncStream is finished being consumed.
118-
return try await withExtendedLifetime(Pipe()) { pipe in
119-
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
120-
process.standardOutputPipe = pipe
121-
process.standardErrorPipe = pipe
122-
return pipe.fileHandleForReading.bytes(on: .global())
123-
} collect: { stream in
124-
try await stream.collect()
125-
}
126-
return (exitStatus: exitStatus, output: Data(output))
125+
let pipe = Pipe()
126+
127+
// Extend the lifetime of the pipes to avoid file descriptors being closed until the AsyncStream is finished being consumed.
128+
defer { withExtendedLifetime(pipe) {} }
129+
130+
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
131+
process.standardOutputPipe = pipe
132+
process.standardErrorPipe = pipe
133+
return pipe.fileHandleForReading.bytes(on: .global())
134+
} collect: { stream in
135+
try await stream.collect()
127136
}
137+
return (exitStatus: exitStatus, output: Data(output))
128138
} else {
129-
// Extend the lifetime of the pipe to avoid file descriptors being closed until the AsyncStream is finished being consumed.
130-
return try await withExtendedLifetime(Pipe()) { pipe in
131-
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
132-
process.standardOutputPipe = pipe
133-
process.standardErrorPipe = pipe
134-
return pipe.fileHandleForReading._bytes(on: .global())
135-
} collect: { stream in
136-
try await stream.collect()
137-
}
138-
return (exitStatus: exitStatus, output: Data(output))
139+
let pipe = Pipe()
140+
141+
// Extend the lifetime of the pipes to avoid file descriptors being closed until the AsyncStream is finished being consumed.
142+
defer { withExtendedLifetime(pipe) {} }
143+
144+
let (exitStatus, output) = try await _getOutput(url: url, arguments: arguments, currentDirectoryURL: currentDirectoryURL, environment: environment, interruptible: interruptible) { process in
145+
process.standardOutputPipe = pipe
146+
process.standardErrorPipe = pipe
147+
return pipe.fileHandleForReading._bytes(on: .global())
148+
} collect: { stream in
149+
try await stream.collect()
139150
}
151+
return (exitStatus: exitStatus, output: Data(output))
140152
}
141153
}
142154

0 commit comments

Comments
 (0)