@@ -32,6 +32,9 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
32
32
33
33
/// The process had a non zero exit.
34
34
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 )
35
38
}
36
39
37
40
public enum ExitStatus : Equatable , Sendable {
@@ -728,91 +731,95 @@ public final class Process {
728
731
throw SystemError . posix_spawn ( rv, arguments)
729
732
}
730
733
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( [ ] ) )
769
743
}
770
- }
744
+ } else {
745
+ var pending : Result < [ UInt8 ] , Swift . Error > ?
746
+ let pendingLock = NSLock ( )
771
747
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
777
749
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.
779
754
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 ) {
782
757
pendingLock. withLock {
783
- if let stdoutResult = pending {
758
+ if let stderrResult = pending {
784
759
self ? . stateLock. withLock {
785
- self ? . state = . outputReady( stdout: stdoutResult , stderr: readResult )
760
+ self ? . state = . outputReady( stdout: readResult , stderr: stderrResult )
786
761
}
787
- } else {
762
+ } else {
788
763
pending = readResult
789
764
}
790
765
}
791
766
group. leave ( )
792
- } else if let stdoutResult = ( pendingLock. withLock { pending } ) {
767
+ } else if let stderrResult = ( pendingLock. withLock { pending } ) {
793
768
// TODO: this is more of an error
794
769
self ? . stateLock. withLock {
795
- self ? . state = . outputReady( stdout: stdoutResult , stderr : . success( [ ] ) )
770
+ self ? . state = . outputReady( stdout: . success( [ ] ) , stderr : stderrResult )
796
771
}
797
772
group. leave ( )
798
773
}
799
774
}
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
+ }
803
808
}
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 ( )
804
817
}
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
- }
814
818
815
- return stdinStream
819
+ return stdinStream
820
+ } catch {
821
+ throw ProcessResult . Error. systemError ( arguments: arguments, underlyingError: error)
822
+ }
816
823
#else
817
824
preconditionFailure ( " Process spawning is not available " )
818
825
#endif // POSIX implementation
@@ -1273,6 +1280,8 @@ extension Process.Error: CustomNSError {
1273
1280
extension ProcessResult . Error : CustomStringConvertible {
1274
1281
public var description : String {
1275
1282
switch self {
1283
+ case . systemError( let arguments, let underlyingError) :
1284
+ return " error while executing ` \( arguments. joined ( separator: " " ) ) `: \( underlyingError) "
1276
1285
case . illegalUTF8Sequence:
1277
1286
return " illegal UTF8 sequence output "
1278
1287
case . nonZeroExit( let result) :
0 commit comments