@@ -32,6 +32,9 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
3232
3333 /// The process had a non zero exit.
3434 case nonZeroExit( ProcessResult )
35+
36+ /// The process failed with a `SystemError` (this is used to still provide context on the process that was launched).
37+ case systemError( arguments: [ String ] , underlyingError: Swift . Error )
3538 }
3639
3740 public enum ExitStatus : Equatable , Sendable {
@@ -728,91 +731,95 @@ public final class Process {
728731 throw SystemError . posix_spawn ( rv, arguments)
729732 }
730733
731- // Close the local read end of the input pipe.
732- try close ( fd: stdinPipe [ 0 ] )
733-
734- let group = DispatchGroup ( )
735- if !outputRedirection. redirectsOutput {
736- // no stdout or stderr in this case
737- self . stateLock. withLock {
738- self . state = . outputReady( stdout: . success( [ ] ) , stderr: . success( [ ] ) )
739- }
740- } else {
741- var pending : Result < [ UInt8 ] , Swift . Error > ?
742- let pendingLock = NSLock ( )
743-
744- let outputClosures = outputRedirection. outputClosures
745-
746- // Close the local write end of the output pipe.
747- try close ( fd: outputPipe [ 1 ] )
748-
749- // Create a thread and start reading the output on it.
750- group. enter ( )
751- let stdoutThread = Thread { [ weak self] in
752- if let readResult = self ? . readOutput ( onFD: outputPipe [ 0 ] , outputClosure: outputClosures? . stdoutClosure) {
753- pendingLock. withLock {
754- if let stderrResult = pending {
755- self ? . stateLock. withLock {
756- self ? . state = . outputReady( stdout: readResult, stderr: stderrResult)
757- }
758- } else {
759- pending = readResult
760- }
761- }
762- group. leave ( )
763- } else if let stderrResult = ( pendingLock. withLock { pending } ) {
764- // TODO: this is more of an error
765- self ? . stateLock. withLock {
766- self ? . state = . outputReady( stdout: . success( [ ] ) , stderr: stderrResult)
767- }
768- group. leave ( )
734+ do {
735+ // Close the local read end of the input pipe.
736+ try close ( fd: stdinPipe [ 0 ] )
737+
738+ let group = DispatchGroup ( )
739+ if !outputRedirection. redirectsOutput {
740+ // no stdout or stderr in this case
741+ self . stateLock. withLock {
742+ self . state = . outputReady( stdout: . success( [ ] ) , stderr: . success( [ ] ) )
769743 }
770- }
744+ } else {
745+ var pending : Result < [ UInt8 ] , Swift . Error > ?
746+ let pendingLock = NSLock ( )
771747
772- // Only schedule a thread for stderr if no redirect was requested.
773- var stderrThread : Thread ? = nil
774- if !outputRedirection. redirectStderr {
775- // Close the local write end of the stderr pipe.
776- try close ( fd: stderrPipe [ 1 ] )
748+ let outputClosures = outputRedirection. outputClosures
777749
778- // Create a thread and start reading the stderr output on it.
750+ // Close the local write end of the output pipe.
751+ try close ( fd: outputPipe [ 1 ] )
752+
753+ // Create a thread and start reading the output on it.
779754 group. enter ( )
780- stderrThread = Thread { [ weak self] in
781- if let readResult = self ? . readOutput ( onFD: stderrPipe [ 0 ] , outputClosure: outputClosures? . stderrClosure ) {
755+ let stdoutThread = Thread { [ weak self] in
756+ if let readResult = self ? . readOutput ( onFD: outputPipe [ 0 ] , outputClosure: outputClosures? . stdoutClosure ) {
782757 pendingLock. withLock {
783- if let stdoutResult = pending {
758+ if let stderrResult = pending {
784759 self ? . stateLock. withLock {
785- self ? . state = . outputReady( stdout: stdoutResult , stderr: readResult )
760+ self ? . state = . outputReady( stdout: readResult , stderr: stderrResult )
786761 }
787- } else {
762+ } else {
788763 pending = readResult
789764 }
790765 }
791766 group. leave ( )
792- } else if let stdoutResult = ( pendingLock. withLock { pending } ) {
767+ } else if let stderrResult = ( pendingLock. withLock { pending } ) {
793768 // TODO: this is more of an error
794769 self ? . stateLock. withLock {
795- self ? . state = . outputReady( stdout: stdoutResult , stderr : . success( [ ] ) )
770+ self ? . state = . outputReady( stdout: . success( [ ] ) , stderr : stderrResult )
796771 }
797772 group. leave ( )
798773 }
799774 }
800- } else {
801- pendingLock. withLock {
802- pending = . success( [ ] ) // no stderr in this case
775+
776+ // Only schedule a thread for stderr if no redirect was requested.
777+ var stderrThread : Thread ? = nil
778+ if !outputRedirection. redirectStderr {
779+ // Close the local write end of the stderr pipe.
780+ try close ( fd: stderrPipe [ 1 ] )
781+
782+ // Create a thread and start reading the stderr output on it.
783+ group. enter ( )
784+ stderrThread = Thread { [ weak self] in
785+ if let readResult = self ? . readOutput ( onFD: stderrPipe [ 0 ] , outputClosure: outputClosures? . stderrClosure) {
786+ pendingLock. withLock {
787+ if let stdoutResult = pending {
788+ self ? . stateLock. withLock {
789+ self ? . state = . outputReady( stdout: stdoutResult, stderr: readResult)
790+ }
791+ } else {
792+ pending = readResult
793+ }
794+ }
795+ group. leave ( )
796+ } else if let stdoutResult = ( pendingLock. withLock { pending } ) {
797+ // TODO: this is more of an error
798+ self ? . stateLock. withLock {
799+ self ? . state = . outputReady( stdout: stdoutResult, stderr: . success( [ ] ) )
800+ }
801+ group. leave ( )
802+ }
803+ }
804+ } else {
805+ pendingLock. withLock {
806+ pending = . success( [ ] ) // no stderr in this case
807+ }
803808 }
809+
810+ // first set state then start reading threads
811+ self . stateLock. withLock {
812+ self . state = . readingOutput( sync: group)
813+ }
814+
815+ stdoutThread. start ( )
816+ stderrThread? . start ( )
804817 }
805-
806- // first set state then start reading threads
807- self . stateLock. withLock {
808- self . state = . readingOutput( sync: group)
809- }
810-
811- stdoutThread. start ( )
812- stderrThread? . start ( )
813- }
814818
815- return stdinStream
819+ return stdinStream
820+ } catch {
821+ throw ProcessResult . Error. systemError ( arguments: arguments, underlyingError: error)
822+ }
816823 #else
817824 preconditionFailure ( " Process spawning is not available " )
818825 #endif // POSIX implementation
@@ -1273,6 +1280,8 @@ extension Process.Error: CustomNSError {
12731280extension ProcessResult . Error : CustomStringConvertible {
12741281 public var description : String {
12751282 switch self {
1283+ case . systemError( let arguments, let underlyingError) :
1284+ return " error while executing ` \( arguments. joined ( separator: " " ) ) `: \( underlyingError) "
12761285 case . illegalUTF8Sequence:
12771286 return " illegal UTF8 sequence output "
12781287 case . nonZeroExit( let result) :
0 commit comments