@@ -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,7 @@ public final class Process: ObjectIdentifierProtocol {
423
429
}
424
430
425
431
try _process? . run ( )
432
+ return stdinPipe. fileHandleForWriting
426
433
#else
427
434
// Initialize the spawn attributes.
428
435
#if canImport(Darwin) || os(Android)
@@ -497,14 +504,17 @@ public final class Process: ObjectIdentifierProtocol {
497
504
#endif
498
505
}
499
506
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 )
507
+ var stdinPipe : [ Int32 ] = [ - 1 , - 1 ]
508
+ try open ( pipe: & stdinPipe)
509
+
510
+ let stdinStream = try LocalFileOutputByteStream ( filePointer: fdopen ( stdinPipe [ 1 ] , " wb " ) , closeOnDeinit: true )
511
+
512
+ // Dupe the read portion of the remote to 0.
513
+ posix_spawn_file_actions_adddup2 ( & fileActions, stdinPipe [ 0 ] , 0 )
514
+
515
+ // Close the other side's pipe since it was dupped to 0.
516
+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 0 ] )
517
+ posix_spawn_file_actions_addclose ( & fileActions, stdinPipe [ 1 ] )
508
518
509
519
var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
510
520
var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -515,7 +525,7 @@ public final class Process: ObjectIdentifierProtocol {
515
525
// Open the write end of the pipe.
516
526
posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
517
527
518
- // Close the other ends of the pipe.
528
+ // Close the other ends of the pipe since they were dupped to 1 .
519
529
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
520
530
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
521
531
@@ -527,7 +537,7 @@ public final class Process: ObjectIdentifierProtocol {
527
537
try open ( pipe: & stderrPipe)
528
538
posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
529
539
530
- // Close the other ends of the pipe.
540
+ // Close the other ends of the pipe since they were dupped to 2 .
531
541
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
532
542
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
533
543
}
@@ -548,6 +558,9 @@ public final class Process: ObjectIdentifierProtocol {
548
558
throw SystemError . posix_spawn ( rv, arguments)
549
559
}
550
560
561
+ // Close the local read end of the input pipe.
562
+ try close ( fd: stdinPipe [ 0 ] )
563
+
551
564
if !outputRedirection. redirectsOutput {
552
565
// no stdout or stderr in this case
553
566
self . stateLock. withLock {
@@ -556,11 +569,10 @@ public final class Process: ObjectIdentifierProtocol {
556
569
} else {
557
570
var outputResult : ( stdout: Result < [ UInt8 ] , Swift . Error > ? , stderr: Result < [ UInt8 ] , Swift . Error > ? )
558
571
let outputResultLock = Lock ( )
559
-
560
572
let outputClosures = outputRedirection. outputClosures
561
573
562
- // Close the write end of the output pipe.
563
- try close ( fd: & outputPipe[ 1 ] )
574
+ // Close the local write end of the output pipe.
575
+ try close ( fd: outputPipe [ 1 ] )
564
576
565
577
// Create a thread and start reading the output on it.
566
578
let stdoutThread = Thread { [ weak self] in
@@ -585,8 +597,8 @@ public final class Process: ObjectIdentifierProtocol {
585
597
// Only schedule a thread for stderr if no redirect was requested.
586
598
var stderrThread : Thread ? = nil
587
599
if !outputRedirection. redirectStderr {
588
- // Close the write end of the stderr pipe.
589
- try close ( fd: & stderrPipe[ 1 ] )
600
+ // Close the local write end of the stderr pipe.
601
+ try close ( fd: stderrPipe [ 1 ] )
590
602
591
603
// Create a thread and start reading the stderr output on it.
592
604
stderrThread = Thread { [ weak self] in
@@ -619,6 +631,7 @@ public final class Process: ObjectIdentifierProtocol {
619
631
stdoutThread. start ( )
620
632
stderrThread? . start ( )
621
633
}
634
+ return stdinStream
622
635
#endif // POSIX implementation
623
636
}
624
637
@@ -830,11 +843,15 @@ private func open(pipe: inout [Int32]) throws {
830
843
}
831
844
832
845
/// 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)
846
+ private func close( fd: Int32 ) throws {
847
+ func innerClose( _ fd: inout Int32 ) throws {
848
+ let rv = TSCLibc . close ( fd)
849
+ guard rv == 0 else {
850
+ throw SystemError . close ( rv)
851
+ }
837
852
}
853
+ var innerFd = fd
854
+ try innerClose ( & innerFd)
838
855
}
839
856
840
857
extension Process . Error : CustomStringConvertible {
@@ -895,8 +912,26 @@ extension ProcessResult.Error: CustomStringConvertible {
895
912
}
896
913
}
897
914
898
- extension ProcessResult . Error : CustomNSError {
899
- public var errorUserInfo : [ String : Any ] {
900
- return [ NSLocalizedDescriptionKey: self . description]
915
+ #if os(Windows)
916
+ extension FileHandle : WritableByteStream {
917
+ public var position : Int {
918
+ return Int ( offsetInFile)
919
+ }
920
+
921
+ public func write( _ byte: UInt8 ) {
922
+ write ( Data ( [ byte] ) )
923
+ }
924
+
925
+ public func write< C: Collection > ( _ bytes: C ) where C. Element == UInt8 {
926
+ write ( Data ( bytes) )
927
+ }
928
+
929
+ public func flush( ) {
930
+ synchronizeFile ( )
931
+ }
932
+
933
+ public func close( ) throws {
934
+ closeFile ( )
901
935
}
902
936
}
937
+ #endif
0 commit comments