@@ -331,8 +331,10 @@ public final class Process: ObjectIdentifierProtocol {
331331 }
332332 }
333333
334- /// Launch the subprocess.
335- public func launch( ) throws {
334+ /// Launch the subprocess. Returns an OutputByteStream object that can be used to communicate to the process's
335+ /// stdin.
336+ @discardableResult
337+ public func launch( ) throws -> OutputByteStream {
336338 precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
337339 precondition ( !launched, " It is not allowed to launch the same process object again. " )
338340
@@ -351,12 +353,15 @@ public final class Process: ObjectIdentifierProtocol {
351353 throw Process . Error. missingExecutableProgram ( program: executable)
352354 }
353355
354- #if os(Windows)
356+ #if os(Windows)
355357 _process = Foundation . Process ( )
356358 _process? . arguments = Array ( arguments. dropFirst ( ) ) // Avoid including the executable URL twice.
357359 _process? . executableURL = executablePath. asURL
358360 _process? . environment = environment
359361
362+ let stdinPipe = Pipe ( )
363+ _process? . standardInput = stdinPipe
364+
360365 if outputRedirection. redirectsOutput {
361366 let stdoutPipe = Pipe ( )
362367 let stderrPipe = Pipe ( )
@@ -379,6 +384,8 @@ public final class Process: ObjectIdentifierProtocol {
379384 }
380385
381386 try _process? . run ( )
387+
388+ return stdinPipe. fileHandleForWriting
382389 #else
383390 // Initialize the spawn attributes.
384391 #if canImport(Darwin) || os(Android)
@@ -453,14 +460,17 @@ public final class Process: ObjectIdentifierProtocol {
453460 #endif
454461 }
455462
456- // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
457- // Change allowing for newer version of glibc
458- guard let devNull = strdup ( " /dev/null " ) else {
459- throw SystemError . posix_spawn ( 0 , arguments)
460- }
461- defer { free ( devNull) }
462- // Open /dev/null as stdin.
463- posix_spawn_file_actions_addopen ( & fileActions, 0 , devNull, O_RDONLY, 0 )
463+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
464+ try open ( pipe: & stdinPipe)
465+
466+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
467+
468+ // Dupe the read portion of the remote to 0.
469+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
470+
471+ // Close the other side's pipe since it was dupped to 0.
472+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
473+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
464474
465475 var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
466476 var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -471,7 +481,7 @@ public final class Process: ObjectIdentifierProtocol {
471481 // Open the write end of the pipe.
472482 posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
473483
474- // Close the other ends of the pipe.
484+ // Close the other ends of the pipe since they were dupped to 1 .
475485 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
476486 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
477487
@@ -483,7 +493,7 @@ public final class Process: ObjectIdentifierProtocol {
483493 try open ( pipe: & stderrPipe)
484494 posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
485495
486- // Close the other ends of the pipe.
496+ // Close the other ends of the pipe since they were dupped to 2 .
487497 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
488498 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
489499 }
@@ -500,11 +510,14 @@ public final class Process: ObjectIdentifierProtocol {
500510 throw SystemError . posix_spawn ( rv, arguments)
501511 }
502512
513+ // Close the local read end of the input pipe.
514+ try close ( fd: stdinPipe [ 0 ] )
515+
503516 if outputRedirection. redirectsOutput {
504517 let outputClosures = outputRedirection. outputClosures
505518
506- // Close the write end of the output pipe.
507- try close ( fd: & outputPipe[ 1 ] )
519+ // Close the local write end of the output pipe.
520+ try close ( fd: outputPipe [ 1 ] )
508521
509522 // Create a thread and start reading the output on it.
510523 var thread = Thread { [ weak self] in
@@ -517,8 +530,8 @@ public final class Process: ObjectIdentifierProtocol {
517530
518531 // Only schedule a thread for stderr if no redirect was requested.
519532 if !outputRedirection. redirectStderr {
520- // Close the write end of the stderr pipe.
521- try close ( fd: & stderrPipe[ 1 ] )
533+ // Close the local write end of the stderr pipe.
534+ try close ( fd: stderrPipe [ 1 ] )
522535
523536 // Create a thread and start reading the stderr output on it.
524537 thread = Thread { [ weak self] in
@@ -531,6 +544,8 @@ public final class Process: ObjectIdentifierProtocol {
531544 }
532545 }
533546 #endif // POSIX implementation
547+
548+ return stdinStream
534549 }
535550
536551 /// Blocks the calling process until the subprocess finishes execution.
@@ -731,11 +746,15 @@ private func open(pipe: inout [Int32]) throws {
731746}
732747
733748/// Close the given fd.
734- private func close( fd: inout Int32 ) throws {
735- let rv = TSCLibc . close ( fd)
736- guard rv == 0 else {
737- throw SystemError . close ( rv)
749+ private func close( fd: Int32 ) throws {
750+ func innerClose( _ fd: inout Int32 ) throws {
751+ let rv = TSCLibc . close ( fd)
752+ guard rv == 0 else {
753+ throw SystemError . close ( rv)
754+ }
738755 }
756+ var innerFd = fd
757+ try innerClose ( & innerFd)
739758}
740759
741760extension Process . Error : CustomStringConvertible {
@@ -788,3 +807,23 @@ extension ProcessResult.Error: CustomStringConvertible {
788807 }
789808}
790809#endif
810+
811+ #if os(Windows)
812+ extension FileHandle : OutputByteStream {
813+ public var position : Int {
814+ return Int ( offsetInFile)
815+ }
816+
817+ public func write( _ byte: UInt8 ) {
818+ write ( Data ( [ byte] ) )
819+ }
820+
821+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
822+ write ( Data ( bytes) )
823+ }
824+
825+ public func flush( ) {
826+ synchronizeFile ( )
827+ }
828+ }
829+ #endif
0 commit comments