@@ -361,8 +361,11 @@ public final class Process: ObjectIdentifierProtocol {
361361 }
362362 }
363363
364- /// Launch the subprocess.
365- public func launch( ) throws {
364+ /// Launch the subprocess. Returns a WritableByteStream object that can be used to communicate to the process's
365+ /// stdin. If needed, the stream can be closed using the close() API. Otherwise, the stream will be closed
366+ /// automatically.
367+ @discardableResult
368+ public func launch( ) throws -> WritableByteStream {
366369 precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
367370 precondition ( !launched, " It is not allowed to launch the same process object again. " )
368371
@@ -387,6 +390,9 @@ public final class Process: ObjectIdentifierProtocol {
387390 _process? . executableURL = executablePath. asURL
388391 _process? . environment = environment
389392
393+ let stdinPipe = Pipe ( )
394+ _process? . standardInput = stdinPipe
395+
390396 if outputRedirection. redirectsOutput {
391397 let stdoutPipe = Pipe ( )
392398 let stderrPipe = Pipe ( )
@@ -409,6 +415,7 @@ public final class Process: ObjectIdentifierProtocol {
409415 }
410416
411417 try _process? . run ( )
418+ return stdinPipe. fileHandleForWriting
412419 #else
413420 // Initialize the spawn attributes.
414421 #if canImport(Darwin) || os(Android)
@@ -483,14 +490,17 @@ public final class Process: ObjectIdentifierProtocol {
483490 #endif
484491 }
485492
486- // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
487- // Change allowing for newer version of glibc
488- guard let devNull = strdup ( " /dev/null " ) else {
489- throw SystemError . posix_spawn ( 0 , arguments)
490- }
491- defer { free ( devNull) }
492- // Open /dev/null as stdin.
493- posix_spawn_file_actions_addopen ( & fileActions, 0 , devNull, O_RDONLY, 0 )
493+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
494+ try open ( pipe: & stdinPipe)
495+
496+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
497+
498+ // Dupe the read portion of the remote to 0.
499+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
500+
501+ // Close the other side's pipe since it was dupped to 0.
502+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
503+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
494504
495505 var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
496506 var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -501,7 +511,7 @@ public final class Process: ObjectIdentifierProtocol {
501511 // Open the write end of the pipe.
502512 posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
503513
504- // Close the other ends of the pipe.
514+ // Close the other ends of the pipe since they were dupped to 1 .
505515 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
506516 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
507517
@@ -513,7 +523,7 @@ public final class Process: ObjectIdentifierProtocol {
513523 try open ( pipe: & stderrPipe)
514524 posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
515525
516- // Close the other ends of the pipe.
526+ // Close the other ends of the pipe since they were dupped to 2 .
517527 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
518528 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
519529 }
@@ -534,11 +544,14 @@ public final class Process: ObjectIdentifierProtocol {
534544 throw SystemError . posix_spawn ( rv, arguments)
535545 }
536546
547+ // Close the local read end of the input pipe.
548+ try close ( fd: stdinPipe [ 0 ] )
549+
537550 if outputRedirection. redirectsOutput {
538551 let outputClosures = outputRedirection. outputClosures
539552
540- // Close the write end of the output pipe.
541- try close ( fd: & outputPipe[ 1 ] )
553+ // Close the local write end of the output pipe.
554+ try close ( fd: outputPipe [ 1 ] )
542555
543556 // Create a thread and start reading the output on it.
544557 var thread = Thread { [ weak self] in
@@ -551,8 +564,8 @@ public final class Process: ObjectIdentifierProtocol {
551564
552565 // Only schedule a thread for stderr if no redirect was requested.
553566 if !outputRedirection. redirectStderr {
554- // Close the write end of the stderr pipe.
555- try close ( fd: & stderrPipe[ 1 ] )
567+ // Close the local write end of the stderr pipe.
568+ try close ( fd: stderrPipe [ 1 ] )
556569
557570 // Create a thread and start reading the stderr output on it.
558571 thread = Thread { [ weak self] in
@@ -564,7 +577,8 @@ public final class Process: ObjectIdentifierProtocol {
564577 self . stderr. thread = thread
565578 }
566579 }
567- #endif // POSIX implementation
580+ return stdinStream
581+ #endif // POSIX implementation
568582 }
569583
570584 /// Blocks the calling process until the subprocess finishes execution.
@@ -771,11 +785,15 @@ private func open(pipe: inout [Int32]) throws {
771785}
772786
773787/// Close the given fd.
774- private func close( fd: inout Int32 ) throws {
775- let rv = TSCLibc . close ( fd)
776- guard rv == 0 else {
777- throw SystemError . close ( rv)
788+ private func close( fd: Int32 ) throws {
789+ func innerClose( _ fd: inout Int32 ) throws {
790+ let rv = TSCLibc . close ( fd)
791+ guard rv == 0 else {
792+ throw SystemError . close ( rv)
793+ }
778794 }
795+ var innerFd = fd
796+ try innerClose ( & innerFd)
779797}
780798
781799extension Process . Error : CustomStringConvertible {
@@ -836,8 +854,26 @@ extension ProcessResult.Error: CustomStringConvertible {
836854 }
837855}
838856
839- extension ProcessResult . Error : CustomNSError {
840- public var errorUserInfo : [ String : Any ] {
841- return [ NSLocalizedDescriptionKey: self . description]
857+ #if os(Windows)
858+ extension FileHandle : WritableByteStream {
859+ public var position : Int {
860+ return Int ( offsetInFile)
861+ }
862+
863+ public func write( _ byte: UInt8 ) {
864+ write ( Data ( [ byte] ) )
865+ }
866+
867+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
868+ write ( Data ( bytes) )
869+ }
870+
871+ public func flush( ) {
872+ synchronizeFile ( )
873+ }
874+
875+ public func close( ) throws {
876+ closeFile ( )
842877 }
843878}
879+ #endif
0 commit comments