@@ -113,6 +113,9 @@ public struct ProcessResult: CustomStringConvertible {
113113 }
114114}
115115
116+ /// Alias to make the type's name closer to its purpose.
117+ public typealias ProcessStdinStream = OutputByteStream
118+
116119/// Process allows spawning new subprocesses and working with them.
117120///
118121/// Note: This class is thread safe.
@@ -331,8 +334,10 @@ public final class Process: ObjectIdentifierProtocol {
331334 }
332335 }
333336
334- /// Launch the subprocess.
335- public func launch( ) throws {
337+ /// Launch the subprocess. Returns a ProcessStdinStream object that can be used to communicate to the process's
338+ /// stdin.
339+ @discardableResult
340+ public func launch( ) throws -> ProcessStdinStream {
336341 precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
337342 precondition ( !launched, " It is not allowed to launch the same process object again. " )
338343
@@ -351,12 +356,15 @@ public final class Process: ObjectIdentifierProtocol {
351356 throw Process . Error. missingExecutableProgram ( program: executable)
352357 }
353358
354- #if os(Windows)
359+ #if os(Windows)
355360 _process = Foundation . Process ( )
356361 _process? . arguments = Array ( arguments. dropFirst ( ) ) // Avoid including the executable URL twice.
357362 _process? . executableURL = executablePath. asURL
358363 _process? . environment = environment
359364
365+ let stdinPipe = Pipe ( )
366+ _process? . standardInput = stdinPipe
367+
360368 if outputRedirection. redirectsOutput {
361369 let stdoutPipe = Pipe ( )
362370 let stderrPipe = Pipe ( )
@@ -379,6 +387,8 @@ public final class Process: ObjectIdentifierProtocol {
379387 }
380388
381389 try _process? . run ( )
390+
391+ return stdinPipe. fileHandleForWriting
382392 #else
383393 // Initialize the spawn attributes.
384394 #if canImport(Darwin) || os(Android)
@@ -453,14 +463,17 @@ public final class Process: ObjectIdentifierProtocol {
453463 #endif
454464 }
455465
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 )
466+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
467+ try open ( pipe: & stdinPipe)
468+
469+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
470+
471+ // Dupe the read portion of the remote to 0.
472+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
473+
474+ // Close the other side's pipe since it was dupped to 0.
475+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
476+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
464477
465478 var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
466479 var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -471,7 +484,7 @@ public final class Process: ObjectIdentifierProtocol {
471484 // Open the write end of the pipe.
472485 posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
473486
474- // Close the other ends of the pipe.
487+ // Close the other ends of the pipe since they were dupped to 1 .
475488 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
476489 posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
477490
@@ -483,7 +496,7 @@ public final class Process: ObjectIdentifierProtocol {
483496 try open ( pipe: & stderrPipe)
484497 posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
485498
486- // Close the other ends of the pipe.
499+ // Close the other ends of the pipe since they were dupped to 2 .
487500 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
488501 posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
489502 }
@@ -500,11 +513,14 @@ public final class Process: ObjectIdentifierProtocol {
500513 throw SystemError . posix_spawn ( rv, arguments)
501514 }
502515
516+ // Close the local read end of the input pipe.
517+ try close ( fd: stdinPipe [ 0 ] )
518+
503519 if outputRedirection. redirectsOutput {
504520 let outputClosures = outputRedirection. outputClosures
505521
506- // Close the write end of the output pipe.
507- try close ( fd: & outputPipe[ 1 ] )
522+ // Close the local write end of the output pipe.
523+ try close ( fd: outputPipe [ 1 ] )
508524
509525 // Create a thread and start reading the output on it.
510526 var thread = Thread { [ weak self] in
@@ -517,8 +533,8 @@ public final class Process: ObjectIdentifierProtocol {
517533
518534 // Only schedule a thread for stderr if no redirect was requested.
519535 if !outputRedirection. redirectStderr {
520- // Close the write end of the stderr pipe.
521- try close ( fd: & stderrPipe[ 1 ] )
536+ // Close the local write end of the stderr pipe.
537+ try close ( fd: stderrPipe [ 1 ] )
522538
523539 // Create a thread and start reading the stderr output on it.
524540 thread = Thread { [ weak self] in
@@ -531,6 +547,8 @@ public final class Process: ObjectIdentifierProtocol {
531547 }
532548 }
533549 #endif // POSIX implementation
550+
551+ return stdinStream
534552 }
535553
536554 /// Blocks the calling process until the subprocess finishes execution.
@@ -731,11 +749,15 @@ private func open(pipe: inout [Int32]) throws {
731749}
732750
733751/// 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)
752+ private func close( fd: Int32 ) throws {
753+ func innerClose( _ fd: inout Int32 ) throws {
754+ let rv = TSCLibc . close ( fd)
755+ guard rv == 0 else {
756+ throw SystemError . close ( rv)
757+ }
738758 }
759+ var innerFd = fd
760+ try innerClose ( & innerFd)
739761}
740762
741763extension Process . Error : CustomStringConvertible {
@@ -788,3 +810,23 @@ extension ProcessResult.Error: CustomStringConvertible {
788810 }
789811}
790812#endif
813+
814+ #if os(Windows)
815+ extension FileHandle : ProcessStdinStream {
816+ public var position : Int {
817+ return Int ( offsetInFile)
818+ }
819+
820+ public func write( _ byte: UInt8 ) {
821+ write ( Data ( [ byte] ) )
822+ }
823+
824+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
825+ write ( Data ( bytes) )
826+ }
827+
828+ public func flush( ) {
829+ synchronizeFile ( )
830+ }
831+ }
832+ #endif
0 commit comments