@@ -331,8 +331,11 @@ public final class Process: ObjectIdentifierProtocol {
331331 }
332332 }
333333
334- /// Launch the subprocess.
335- public func launch( ) throws {
334+ /// Launch the subprocess. Returns a WritableByteStream object that can be used to communicate to the process's
335+ /// stdin. If needed, the stream can be closed using the close() API. Otherwise, the stream will be closed
336+ /// automatically.
337+ @discardableResult
338+ public func launch( ) throws -> WritableByteStream {
336339 precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
337340 precondition ( !launched, " It is not allowed to launch the same process object again. " )
338341
@@ -351,12 +354,15 @@ public final class Process: ObjectIdentifierProtocol {
351354 throw Process . Error. missingExecutableProgram ( program: executable)
352355 }
353356
354- #if os(Windows)
357+ #if os(Windows)
355358 _process = Foundation . Process ( )
356359 _process? . arguments = Array ( arguments. dropFirst ( ) ) // Avoid including the executable URL twice.
357360 _process? . executableURL = executablePath. asURL
358361 _process? . environment = environment
359362
363+ let stdinPipe = Pipe ( )
364+ _process? . standardInput = stdinPipe
365+
360366 if outputRedirection. redirectsOutput {
361367 let stdoutPipe = Pipe ( )
362368 let stderrPipe = Pipe ( )
@@ -379,6 +385,8 @@ public final class Process: ObjectIdentifierProtocol {
379385 }
380386
381387 try _process? . run ( )
388+
389+ return stdinPipe. fileHandleForWriting
382390 #else
383391 // Initialize the spawn attributes.
384392 #if canImport(Darwin) || os(Android)
@@ -453,14 +461,17 @@ public final class Process: ObjectIdentifierProtocol {
453461 #endif
454462 }
455463
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 )
464+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
465+ try open ( pipe: & stdinPipe)
466+
467+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
468+
469+ // Dupe the read portion of the remote to 0.
470+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
471+
472+ // Close the other side's pipe since it was dupped to 0.
473+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
474+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
464475
465476 var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
466477 var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -471,7 +482,7 @@ public final class Process: ObjectIdentifierProtocol {
471482 // Open the write end of the pipe.
472483 posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
473484
474- // Close the other ends of the pipe.
485+ // Close the other ends of the pipe since they were dupped to 1 .
475486 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
476487 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
477488
@@ -483,7 +494,7 @@ public final class Process: ObjectIdentifierProtocol {
483494 try open ( pipe: & stderrPipe)
484495 posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
485496
486- // Close the other ends of the pipe.
497+ // Close the other ends of the pipe since they were dupped to 2 .
487498 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
488499 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
489500 }
@@ -500,11 +511,14 @@ public final class Process: ObjectIdentifierProtocol {
500511 throw SystemError . posix_spawn ( rv, arguments)
501512 }
502513
514+ // Close the local read end of the input pipe.
515+ try close ( fd: stdinPipe [ 0 ] )
516+
503517 if outputRedirection. redirectsOutput {
504518 let outputClosures = outputRedirection. outputClosures
505519
506- // Close the write end of the output pipe.
507- try close ( fd: & outputPipe[ 1 ] )
520+ // Close the local write end of the output pipe.
521+ try close ( fd: outputPipe [ 1 ] )
508522
509523 // Create a thread and start reading the output on it.
510524 var thread = Thread { [ weak self] in
@@ -517,8 +531,8 @@ public final class Process: ObjectIdentifierProtocol {
517531
518532 // Only schedule a thread for stderr if no redirect was requested.
519533 if !outputRedirection. redirectStderr {
520- // Close the write end of the stderr pipe.
521- try close ( fd: & stderrPipe[ 1 ] )
534+ // Close the local write end of the stderr pipe.
535+ try close ( fd: stderrPipe [ 1 ] )
522536
523537 // Create a thread and start reading the stderr output on it.
524538 thread = Thread { [ weak self] in
@@ -531,6 +545,8 @@ public final class Process: ObjectIdentifierProtocol {
531545 }
532546 }
533547 #endif // POSIX implementation
548+
549+ return stdinStream
534550 }
535551
536552 /// Blocks the calling process until the subprocess finishes execution.
@@ -731,11 +747,15 @@ private func open(pipe: inout [Int32]) throws {
731747}
732748
733749/// 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)
750+ private func close( fd: Int32 ) throws {
751+ func innerClose( _ fd: inout Int32 ) throws {
752+ let rv = TSCLibc . close ( fd)
753+ guard rv == 0 else {
754+ throw SystemError . close ( rv)
755+ }
738756 }
757+ var innerFd = fd
758+ try innerClose ( & innerFd)
739759}
740760
741761extension Process . Error : CustomStringConvertible {
@@ -788,3 +808,27 @@ extension ProcessResult.Error: CustomStringConvertible {
788808 }
789809 }
790810}
811+
812+ #if os(Windows)
813+ extension FileHandle : WritableByteStream {
814+ public var position : Int {
815+ return Int ( offsetInFile)
816+ }
817+
818+ public func write( _ byte: UInt8 ) {
819+ write ( Data ( [ byte] ) )
820+ }
821+
822+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
823+ write ( Data ( bytes) )
824+ }
825+
826+ public func flush( ) {
827+ synchronizeFile ( )
828+ }
829+
830+ public func close( ) throws {
831+ close ( )
832+ }
833+ }
834+ #endif
0 commit comments