@@ -361,8 +361,11 @@ public final class Process: ObjectIdentifierProtocol {
361
361
}
362
362
}
363
363
364
- /// Launch the subprocess.
365
- public func launch( ) throws {
364
+ /// Launch the subprocess. Returns a WritableByteStream object that can be used to communicate to the process's
365
+ /// stdin. If needed, the stream can be closed using the close() API. Otherwise, the stream will be closed
366
+ /// automatically.
367
+ @discardableResult
368
+ public func launch( ) throws -> WritableByteStream {
366
369
precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
367
370
precondition ( !launched, " It is not allowed to launch the same process object again. " )
368
371
@@ -387,6 +390,9 @@ public final class Process: ObjectIdentifierProtocol {
387
390
_process? . executableURL = executablePath. asURL
388
391
_process? . environment = environment
389
392
393
+ let stdinPipe = Pipe ( )
394
+ _process? . standardInput = stdinPipe
395
+
390
396
if outputRedirection. redirectsOutput {
391
397
let stdoutPipe = Pipe ( )
392
398
let stderrPipe = Pipe ( )
@@ -409,6 +415,7 @@ public final class Process: ObjectIdentifierProtocol {
409
415
}
410
416
411
417
try _process? . run ( )
418
+ return stdinPipe. fileHandleForWriting
412
419
#else
413
420
// Initialize the spawn attributes.
414
421
#if canImport(Darwin) || os(Android)
@@ -483,14 +490,17 @@ public final class Process: ObjectIdentifierProtocol {
483
490
#endif
484
491
}
485
492
486
- // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362
487
- // Change allowing for newer version of glibc
488
- guard let devNull = strdup ( " /dev/null " ) else {
489
- throw SystemError . posix_spawn ( 0 , arguments)
490
- }
491
- defer { free ( devNull) }
492
- // Open /dev/null as stdin.
493
- posix_spawn_file_actions_addopen ( & fileActions, 0 , devNull, O_RDONLY, 0 )
493
+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
494
+ try open ( pipe: & stdinPipe)
495
+
496
+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
497
+
498
+ // Dupe the read portion of the remote to 0.
499
+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
500
+
501
+ // Close the other side's pipe since it was dupped to 0.
502
+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
503
+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
494
504
495
505
var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
496
506
var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -501,7 +511,7 @@ public final class Process: ObjectIdentifierProtocol {
501
511
// Open the write end of the pipe.
502
512
posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
503
513
504
- // Close the other ends of the pipe.
514
+ // Close the other ends of the pipe since they were dupped to 1 .
505
515
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
506
516
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
507
517
@@ -513,7 +523,7 @@ public final class Process: ObjectIdentifierProtocol {
513
523
try open ( pipe: & stderrPipe)
514
524
posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
515
525
516
- // Close the other ends of the pipe.
526
+ // Close the other ends of the pipe since they were dupped to 2 .
517
527
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
518
528
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
519
529
}
@@ -534,11 +544,14 @@ public final class Process: ObjectIdentifierProtocol {
534
544
throw SystemError . posix_spawn ( rv, arguments)
535
545
}
536
546
547
+ // Close the local read end of the input pipe.
548
+ try close ( fd: stdinPipe [ 0 ] )
549
+
537
550
if outputRedirection. redirectsOutput {
538
551
let outputClosures = outputRedirection. outputClosures
539
552
540
- // Close the write end of the output pipe.
541
- try close ( fd: & outputPipe[ 1 ] )
553
+ // Close the local write end of the output pipe.
554
+ try close ( fd: outputPipe [ 1 ] )
542
555
543
556
// Create a thread and start reading the output on it.
544
557
var thread = Thread { [ weak self] in
@@ -551,8 +564,8 @@ public final class Process: ObjectIdentifierProtocol {
551
564
552
565
// Only schedule a thread for stderr if no redirect was requested.
553
566
if !outputRedirection. redirectStderr {
554
- // Close the write end of the stderr pipe.
555
- try close ( fd: & stderrPipe[ 1 ] )
567
+ // Close the local write end of the stderr pipe.
568
+ try close ( fd: stderrPipe [ 1 ] )
556
569
557
570
// Create a thread and start reading the stderr output on it.
558
571
thread = Thread { [ weak self] in
@@ -564,7 +577,8 @@ public final class Process: ObjectIdentifierProtocol {
564
577
self . stderr. thread = thread
565
578
}
566
579
}
567
- #endif // POSIX implementation
580
+ return stdinStream
581
+ #endif // POSIX implementation
568
582
}
569
583
570
584
/// Blocks the calling process until the subprocess finishes execution.
@@ -771,11 +785,15 @@ private func open(pipe: inout [Int32]) throws {
771
785
}
772
786
773
787
/// Close the given fd.
774
- private func close( fd: inout Int32 ) throws {
775
- let rv = TSCLibc . close ( fd)
776
- guard rv == 0 else {
777
- throw SystemError . close ( rv)
788
+ private func close( fd: Int32 ) throws {
789
+ func innerClose( _ fd: inout Int32 ) throws {
790
+ let rv = TSCLibc . close ( fd)
791
+ guard rv == 0 else {
792
+ throw SystemError . close ( rv)
793
+ }
778
794
}
795
+ var innerFd = fd
796
+ try innerClose ( & innerFd)
779
797
}
780
798
781
799
extension Process . Error : CustomStringConvertible {
@@ -836,8 +854,26 @@ extension ProcessResult.Error: CustomStringConvertible {
836
854
}
837
855
}
838
856
839
- extension ProcessResult . Error : CustomNSError {
840
- public var errorUserInfo : [ String : Any ] {
841
- return [ NSLocalizedDescriptionKey: self . description]
857
+ #if os(Windows)
858
+ extension FileHandle : WritableByteStream {
859
+ public var position : Int {
860
+ return Int ( offsetInFile)
861
+ }
862
+
863
+ public func write( _ byte: UInt8 ) {
864
+ write ( Data ( [ byte] ) )
865
+ }
866
+
867
+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
868
+ write ( Data ( bytes) )
869
+ }
870
+
871
+ public func flush( ) {
872
+ synchronizeFile ( )
873
+ }
874
+
875
+ public func close( ) throws {
876
+ closeFile ( )
842
877
}
843
878
}
879
+ #endif
0 commit comments