@@ -113,6 +113,9 @@ public struct ProcessResult: CustomStringConvertible {
113
113
}
114
114
}
115
115
116
+ /// Alias to make the type's name closer to its purpose.
117
+ public typealias ProcessStdinStream = OutputByteStream
118
+
116
119
/// Process allows spawning new subprocesses and working with them.
117
120
///
118
121
/// Note: This class is thread safe.
@@ -331,8 +334,10 @@ public final class Process: ObjectIdentifierProtocol {
331
334
}
332
335
}
333
336
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 {
336
341
precondition ( arguments. count > 0 && !arguments[ 0 ] . isEmpty, " Need at least one argument to launch the process. " )
337
342
precondition ( !launched, " It is not allowed to launch the same process object again. " )
338
343
@@ -351,12 +356,15 @@ public final class Process: ObjectIdentifierProtocol {
351
356
throw Process . Error. missingExecutableProgram ( program: executable)
352
357
}
353
358
354
- #if os(Windows)
359
+ #if os(Windows)
355
360
_process = Foundation . Process ( )
356
361
_process? . arguments = Array ( arguments. dropFirst ( ) ) // Avoid including the executable URL twice.
357
362
_process? . executableURL = executablePath. asURL
358
363
_process? . environment = environment
359
364
365
+ let stdinPipe = Pipe ( )
366
+ _process? . standardInput = stdinPipe
367
+
360
368
if outputRedirection. redirectsOutput {
361
369
let stdoutPipe = Pipe ( )
362
370
let stderrPipe = Pipe ( )
@@ -379,6 +387,8 @@ public final class Process: ObjectIdentifierProtocol {
379
387
}
380
388
381
389
try _process? . run ( )
390
+
391
+ return stdinPipe. fileHandleForWriting
382
392
#else
383
393
// Initialize the spawn attributes.
384
394
#if canImport(Darwin) || os(Android)
@@ -453,14 +463,17 @@ public final class Process: ObjectIdentifierProtocol {
453
463
#endif
454
464
}
455
465
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 ] )
464
477
465
478
var outputPipe : [ Int32 ] = [ - 1 , - 1 ]
466
479
var stderrPipe : [ Int32 ] = [ - 1 , - 1 ]
@@ -471,7 +484,7 @@ public final class Process: ObjectIdentifierProtocol {
471
484
// Open the write end of the pipe.
472
485
posix_spawn_file_actions_adddup2 ( & fileActions, outputPipe [ 1 ] , 1 )
473
486
474
- // Close the other ends of the pipe.
487
+ // Close the other ends of the pipe since they were dupped to 1 .
475
488
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 0 ] )
476
489
posix_spawn_file_actions_addclose ( & fileActions, outputPipe [ 1 ] )
477
490
@@ -483,7 +496,7 @@ public final class Process: ObjectIdentifierProtocol {
483
496
try open ( pipe: & stderrPipe)
484
497
posix_spawn_file_actions_adddup2 ( & fileActions, stderrPipe [ 1 ] , 2 )
485
498
486
- // Close the other ends of the pipe.
499
+ // Close the other ends of the pipe since they were dupped to 2 .
487
500
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 0 ] )
488
501
posix_spawn_file_actions_addclose ( & fileActions, stderrPipe [ 1 ] )
489
502
}
@@ -500,11 +513,14 @@ public final class Process: ObjectIdentifierProtocol {
500
513
throw SystemError . posix_spawn ( rv, arguments)
501
514
}
502
515
516
+ // Close the local read end of the input pipe.
517
+ try close ( fd: stdinPipe [ 0 ] )
518
+
503
519
if outputRedirection. redirectsOutput {
504
520
let outputClosures = outputRedirection. outputClosures
505
521
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 ] )
508
524
509
525
// Create a thread and start reading the output on it.
510
526
var thread = Thread { [ weak self] in
@@ -517,8 +533,8 @@ public final class Process: ObjectIdentifierProtocol {
517
533
518
534
// Only schedule a thread for stderr if no redirect was requested.
519
535
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 ] )
522
538
523
539
// Create a thread and start reading the stderr output on it.
524
540
thread = Thread { [ weak self] in
@@ -531,6 +547,8 @@ public final class Process: ObjectIdentifierProtocol {
531
547
}
532
548
}
533
549
#endif // POSIX implementation
550
+
551
+ return stdinStream
534
552
}
535
553
536
554
/// Blocks the calling process until the subprocess finishes execution.
@@ -731,11 +749,15 @@ private func open(pipe: inout [Int32]) throws {
731
749
}
732
750
733
751
/// 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
+ }
738
758
}
759
+ var innerFd = fd
760
+ try innerClose ( & innerFd)
739
761
}
740
762
741
763
extension Process . Error : CustomStringConvertible {
@@ -788,3 +810,23 @@ extension ProcessResult.Error: CustomStringConvertible {
788
810
}
789
811
}
790
812
#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