@@ -331,8 +331,11 @@ public final class Process: ObjectIdentifierProtocol {
331
331
}
332
332
}
333
333
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 {
336
339
precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
337
340
precondition ( !launched, " It is not allowed to launch the same process object again. " )
338
341
@@ -351,12 +354,15 @@ public final class Process: ObjectIdentifierProtocol {
351
354
throw Process . Error. missingExecutableProgram ( program: executable)
352
355
}
353
356
354
- #if os(Windows)
357
+ #if os(Windows)
355
358
_process = Foundation . Process ( )
356
359
_process? . arguments = Array ( arguments. dropFirst ( ) ) // Avoid including the executable URL twice.
357
360
_process? . executableURL = executablePath. asURL
358
361
_process? . environment = environment
359
362
363
+ let stdinPipe = Pipe ( )
364
+ _process? . standardInput = stdinPipe
365
+
360
366
if outputRedirection. redirectsOutput {
361
367
let stdoutPipe = Pipe ( )
362
368
let stderrPipe = Pipe ( )
@@ -379,6 +385,8 @@ public final class Process: ObjectIdentifierProtocol {
379
385
}
380
386
381
387
try _process? . run ( )
388
+
389
+ return stdinPipe. fileHandleForWriting
382
390
#else
383
391
// Initialize the spawn attributes.
384
392
#if canImport(Darwin) || os(Android)
@@ -453,14 +461,17 @@ public final class Process: ObjectIdentifierProtocol {
453
461
#endif
454
462
}
455
463
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 ] )
464
475
465
476
var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
466
477
var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -471,7 +482,7 @@ public final class Process: ObjectIdentifierProtocol {
471
482
// Open the write end of the pipe.
472
483
posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
473
484
474
- // Close the other ends of the pipe.
485
+ // Close the other ends of the pipe since they were dupped to 1 .
475
486
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
476
487
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
477
488
@@ -483,7 +494,7 @@ public final class Process: ObjectIdentifierProtocol {
483
494
try open ( pipe: & stderrPipe)
484
495
posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
485
496
486
- // Close the other ends of the pipe.
497
+ // Close the other ends of the pipe since they were dupped to 2 .
487
498
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
488
499
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
489
500
}
@@ -500,11 +511,14 @@ public final class Process: ObjectIdentifierProtocol {
500
511
throw SystemError . posix_spawn ( rv, arguments)
501
512
}
502
513
514
+ // Close the local read end of the input pipe.
515
+ try close ( fd: stdinPipe [ 0 ] )
516
+
503
517
if outputRedirection. redirectsOutput {
504
518
let outputClosures = outputRedirection. outputClosures
505
519
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 ] )
508
522
509
523
// Create a thread and start reading the output on it.
510
524
var thread = Thread { [ weak self] in
@@ -517,8 +531,8 @@ public final class Process: ObjectIdentifierProtocol {
517
531
518
532
// Only schedule a thread for stderr if no redirect was requested.
519
533
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 ] )
522
536
523
537
// Create a thread and start reading the stderr output on it.
524
538
thread = Thread { [ weak self] in
@@ -530,6 +544,8 @@ public final class Process: ObjectIdentifierProtocol {
530
544
self . stderr. thread = thread
531
545
}
532
546
}
547
+
548
+ return stdinStream
533
549
#endif // POSIX implementation
534
550
}
535
551
@@ -731,11 +747,15 @@ private func open(pipe: inout [Int32]) throws {
731
747
}
732
748
733
749
/// 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
+ }
738
756
}
757
+ var innerFd = fd
758
+ try innerClose ( & innerFd)
739
759
}
740
760
741
761
extension Process . Error : CustomStringConvertible {
@@ -788,3 +808,27 @@ extension ProcessResult.Error: CustomStringConvertible {
788
808
}
789
809
}
790
810
}
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
+ closeFile ( )
832
+ }
833
+ }
834
+ #endif
0 commit comments