@@ -374,8 +374,11 @@ public final class Process: ObjectIdentifierProtocol {
374
374
}
375
375
}
376
376
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 {
379
382
precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
380
383
381
384
self . launchedLock. withLock {
@@ -401,6 +404,9 @@ public final class Process: ObjectIdentifierProtocol {
401
404
_process? . executableURL = executablePath. asURL
402
405
_process? . environment = environment
403
406
407
+ let stdinPipe = Pipe ( )
408
+ _process? . standardInput = stdinPipe
409
+
404
410
if outputRedirection. redirectsOutput {
405
411
let stdoutPipe = Pipe ( )
406
412
let stderrPipe = Pipe ( )
@@ -423,6 +429,8 @@ public final class Process: ObjectIdentifierProtocol {
423
429
}
424
430
425
431
try _process? . run ( )
432
+
433
+ return stdinPipe. fileHandleForWriting
426
434
#else
427
435
// Initialize the spawn attributes.
428
436
#if canImport(Darwin) || os(Android)
@@ -497,14 +505,27 @@ public final class Process: ObjectIdentifierProtocol {
497
505
#endif
498
506
}
499
507
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)
508
529
509
530
var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
510
531
var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -515,7 +536,7 @@ public final class Process: ObjectIdentifierProtocol {
515
536
// Open the write end of the pipe.
516
537
posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
517
538
518
- // Close the other ends of the pipe.
539
+ // Close the other ends of the pipe since they were dupped to 1 .
519
540
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
520
541
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
521
542
@@ -527,7 +548,7 @@ public final class Process: ObjectIdentifierProtocol {
527
548
try open ( pipe: & stderrPipe)
528
549
posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
529
550
530
- // Close the other ends of the pipe.
551
+ // Close the other ends of the pipe since they were dupped to 2 .
531
552
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
532
553
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
533
554
}
@@ -548,6 +569,9 @@ public final class Process: ObjectIdentifierProtocol {
548
569
throw SystemError . posix_spawn ( rv, arguments)
549
570
}
550
571
572
+ // Close the local read end of the input pipe.
573
+ try close ( fd: stdinPipe [ 0 ] )
574
+
551
575
if !outputRedirection. redirectsOutput {
552
576
// no stdout or stderr in this case
553
577
self . stateLock. withLock {
@@ -559,8 +583,8 @@ public final class Process: ObjectIdentifierProtocol {
559
583
560
584
let outputClosures = outputRedirection. outputClosures
561
585
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 ] )
564
588
565
589
// Create a thread and start reading the output on it.
566
590
let stdoutThread = Thread { [ weak self] in
@@ -585,8 +609,8 @@ public final class Process: ObjectIdentifierProtocol {
585
609
// Only schedule a thread for stderr if no redirect was requested.
586
610
var stderrThread : Thread ? = nil
587
611
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 ] )
590
614
591
615
// Create a thread and start reading the stderr output on it.
592
616
stderrThread = Thread { [ weak self] in
@@ -619,6 +643,7 @@ public final class Process: ObjectIdentifierProtocol {
619
643
stdoutThread. start ( )
620
644
stderrThread? . start ( )
621
645
}
646
+ return stdinStream
622
647
#endif // POSIX implementation
623
648
}
624
649
@@ -830,11 +855,15 @@ private func open(pipe: inout [Int32]) throws {
830
855
}
831
856
832
857
/// 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
+ }
837
864
}
865
+ var innerFd = fd
866
+ try innerClose ( & innerFd)
838
867
}
839
868
840
869
extension Process . Error : CustomStringConvertible {
@@ -900,3 +929,27 @@ extension ProcessResult.Error: CustomNSError {
900
929
return [ NSLocalizedDescriptionKey: self . description]
901
930
}
902
931
}
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