@@ -374,8 +374,11 @@ public final class Process: ObjectIdentifierProtocol {
374374 }
375375 }
376376
377- /// Launch the subprocess.
378- public func launch( ) throws {
377+ /// Launch the subprocess. Returns a WritableByteStream object that can be used to communicate to the process's
378+ /// stdin. If needed, the stream can be closed using the close() API. Otherwise, the stream will be closed
379+ /// automatically.
380+ @discardableResult
381+ public func launch( ) throws -> WritableByteStream {
379382 precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
380383
381384 self . launchedLock. withLock {
@@ -401,6 +404,9 @@ public final class Process: ObjectIdentifierProtocol {
401404 _process? . executableURL = executablePath. asURL
402405 _process? . environment = environment
403406
407+ let stdinPipe = Pipe ( )
408+ _process? . standardInput = stdinPipe
409+
404410 if outputRedirection. redirectsOutput {
405411 let stdoutPipe = Pipe ( )
406412 let stderrPipe = Pipe ( )
@@ -423,6 +429,8 @@ public final class Process: ObjectIdentifierProtocol {
423429 }
424430
425431 try _process? . run ( )
432+
433+ return stdinPipe. fileHandleForWriting
426434 #else
427435 // Initialize the spawn attributes.
428436 #if canImport(Darwin) || os(Android)
@@ -497,14 +505,27 @@ public final class Process: ObjectIdentifierProtocol {
497505 #endif
498506 }
499507
500- // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
501- // Change allowing for newer version of glibc
502- guard let devNull = strdup ( " /dev/null " ) else {
503- throw SystemError . posix_spawn ( 0 , arguments)
504- }
505- defer { free ( devNull) }
506- // Open /dev/null as stdin.
507- posix_spawn_file_actions_addopen ( & fileActions, 0 , devNull, O_RDONLY, 0 )
508+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
509+ try open ( pipe: & stdinPipe)
510+
511+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) ,
512+ closeOnDeinit: true )
513+
514+ // Dupe the read portion of the remote to 0.
515+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , STDIN_FILENO)
516+
517+ // Close the other side's pipe since it was dupped to 0.
518+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
519+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
520+
521+ // // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
522+ // // Change allowing for newer version of glibc
523+ // guard let devNull = strdup("/dev/null") else {
524+ // throw SystemError.posix_spawn(0, arguments)
525+ // }
526+ // defer { free(devNull) }
527+ // // Open /dev/null as stdin.
528+ // posix_spawn_file_actions_addopen(&fileActions, 0, devNull, O_RDONLY, 0)
508529
509530 var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
510531 var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -515,7 +536,7 @@ public final class Process: ObjectIdentifierProtocol {
515536 // Open the write end of the pipe.
516537 posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
517538
518- // Close the other ends of the pipe.
539+ // Close the other ends of the pipe since they were dupped to 1 .
519540 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
520541 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
521542
@@ -527,7 +548,7 @@ public final class Process: ObjectIdentifierProtocol {
527548 try open ( pipe: & stderrPipe)
528549 posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
529550
530- // Close the other ends of the pipe.
551+ // Close the other ends of the pipe since they were dupped to 2 .
531552 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
532553 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
533554 }
@@ -548,6 +569,9 @@ public final class Process: ObjectIdentifierProtocol {
548569 throw SystemError . posix_spawn ( rv, arguments)
549570 }
550571
572+ // Close the local read end of the input pipe.
573+ try close ( fd: stdinPipe [ 0 ] )
574+
551575 if !outputRedirection. redirectsOutput {
552576 // no stdout or stderr in this case
553577 self . stateLock. withLock {
@@ -559,8 +583,8 @@ public final class Process: ObjectIdentifierProtocol {
559583
560584 let outputClosures = outputRedirection. outputClosures
561585
562- // Close the write end of the output pipe.
563- try close ( fd: & outputPipe[ 1 ] )
586+ // Close the local write end of the output pipe.
587+ try close ( fd: outputPipe [ 1 ] )
564588
565589 // Create a thread and start reading the output on it.
566590 let stdoutThread = Thread { [ weak self] in
@@ -585,8 +609,8 @@ public final class Process: ObjectIdentifierProtocol {
585609 // Only schedule a thread for stderr if no redirect was requested.
586610 var stderrThread : Thread ? = nil
587611 if !outputRedirection. redirectStderr {
588- // Close the write end of the stderr pipe.
589- try close ( fd: & stderrPipe[ 1 ] )
612+ // Close the local write end of the stderr pipe.
613+ try close ( fd: stderrPipe [ 1 ] )
590614
591615 // Create a thread and start reading the stderr output on it.
592616 stderrThread = Thread { [ weak self] in
@@ -619,6 +643,7 @@ public final class Process: ObjectIdentifierProtocol {
619643 stdoutThread. start ( )
620644 stderrThread? . start ( )
621645 }
646+ return stdinStream
622647 #endif // POSIX implementation
623648 }
624649
@@ -830,11 +855,15 @@ private func open(pipe: inout [Int32]) throws {
830855}
831856
832857/// Close the given fd.
833- private func close( fd: inout Int32 ) throws {
834- let rv = TSCLibc . close ( fd)
835- guard rv == 0 else {
836- throw SystemError . close ( rv)
858+ private func close( fd: Int32 ) throws {
859+ func innerClose( _ fd: inout Int32 ) throws {
860+ let rv = TSCLibc . close ( fd)
861+ guard rv == 0 else {
862+ throw SystemError . close ( rv)
863+ }
837864 }
865+ var innerFd = fd
866+ try innerClose ( & innerFd)
838867}
839868
840869extension Process . Error : CustomStringConvertible {
@@ -900,3 +929,27 @@ extension ProcessResult.Error: CustomNSError {
900929 return [ NSLocalizedDescriptionKey: self . description]
901930 }
902931}
932+
933+ #if os(Windows)
934+ extension FileHandle : WritableByteStream {
935+ public var position : Int {
936+ return Int ( offsetInFile)
937+ }
938+
939+ public func write( _ byte: UInt8 ) {
940+ write ( Data ( [ byte] ) )
941+ }
942+
943+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
944+ write ( Data ( bytes) )
945+ }
946+
947+ public func flush( ) {
948+ synchronizeFile ( )
949+ }
950+
951+ public func close( ) throws {
952+ closeFile ( )
953+ }
954+ }
955+ #endif
0 commit comments