diff --git a/Sources/TSCBasic/Process.swift b/Sources/TSCBasic/Process.swift index ed8bf092..b4723fb1 100644 --- a/Sources/TSCBasic/Process.swift +++ b/Sources/TSCBasic/Process.swift @@ -331,11 +331,8 @@ public final class Process: ObjectIdentifierProtocol { } } - /// Launch the subprocess. Returns a WritableByteStream object that can be used to communicate to the process's - /// stdin. If needed, the stream can be closed using the close() API. Otherwise, the stream will be closed - /// automatically. - @discardableResult - public func launch() throws -> WritableByteStream { + /// Launch the subprocess. + public func launch() throws { precondition(arguments.count > 0 && !arguments[0].isEmpty, "Need at least one argument to launch the process.") precondition(!launched, "It is not allowed to launch the same process object again.") @@ -354,15 +351,12 @@ public final class Process: ObjectIdentifierProtocol { throw Process.Error.missingExecutableProgram(program: executable) } - #if os(Windows) + #if os(Windows) _process = Foundation.Process() _process?.arguments = Array(arguments.dropFirst()) // Avoid including the executable URL twice. _process?.executableURL = executablePath.asURL _process?.environment = environment - let stdinPipe = Pipe() - _process?.standardInput = stdinPipe - if outputRedirection.redirectsOutput { let stdoutPipe = Pipe() let stderrPipe = Pipe() @@ -385,8 +379,6 @@ public final class Process: ObjectIdentifierProtocol { } try _process?.run() - - return stdinPipe.fileHandleForWriting #else // Initialize the spawn attributes. #if canImport(Darwin) || os(Android) @@ -461,17 +453,14 @@ public final class Process: ObjectIdentifierProtocol { #endif } - var stdinPipe: [Int32] = [-1, -1] - try open(pipe: &stdinPipe) - - let stdinStream = try LocalFileOutputByteStream(filePointer: fdopen(stdinPipe[1], "wb"), closeOnDeinit: true) - - // Dupe the read portion of the remote to 0. - posix_spawn_file_actions_adddup2(&fileActions, stdinPipe[0], 0) - - // Close the other side's pipe since it was dupped to 0. - posix_spawn_file_actions_addclose(&fileActions, stdinPipe[0]) - posix_spawn_file_actions_addclose(&fileActions, stdinPipe[1]) + // Workaround for https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=89e435f3559c53084498e9baad22172b64429362 + // Change allowing for newer version of glibc + guard let devNull = strdup("/dev/null") else { + throw SystemError.posix_spawn(0, arguments) + } + defer { free(devNull) } + // Open /dev/null as stdin. + posix_spawn_file_actions_addopen(&fileActions, 0, devNull, O_RDONLY, 0) var outputPipe: [Int32] = [-1, -1] var stderrPipe: [Int32] = [-1, -1] @@ -482,7 +471,7 @@ public final class Process: ObjectIdentifierProtocol { // Open the write end of the pipe. posix_spawn_file_actions_adddup2(&fileActions, outputPipe[1], 1) - // Close the other ends of the pipe since they were dupped to 1. + // Close the other ends of the pipe. posix_spawn_file_actions_addclose(&fileActions, outputPipe[0]) posix_spawn_file_actions_addclose(&fileActions, outputPipe[1]) @@ -494,7 +483,7 @@ public final class Process: ObjectIdentifierProtocol { try open(pipe: &stderrPipe) posix_spawn_file_actions_adddup2(&fileActions, stderrPipe[1], 2) - // Close the other ends of the pipe since they were dupped to 2. + // Close the other ends of the pipe. posix_spawn_file_actions_addclose(&fileActions, stderrPipe[0]) posix_spawn_file_actions_addclose(&fileActions, stderrPipe[1]) } @@ -511,14 +500,11 @@ public final class Process: ObjectIdentifierProtocol { throw SystemError.posix_spawn(rv, arguments) } - // Close the local read end of the input pipe. - try close(fd: stdinPipe[0]) - if outputRedirection.redirectsOutput { let outputClosures = outputRedirection.outputClosures - // Close the local write end of the output pipe. - try close(fd: outputPipe[1]) + // Close the write end of the output pipe. + try close(fd: &outputPipe[1]) // Create a thread and start reading the output on it. var thread = Thread { [weak self] in @@ -531,8 +517,8 @@ public final class Process: ObjectIdentifierProtocol { // Only schedule a thread for stderr if no redirect was requested. if !outputRedirection.redirectStderr { - // Close the local write end of the stderr pipe. - try close(fd: stderrPipe[1]) + // Close the write end of the stderr pipe. + try close(fd: &stderrPipe[1]) // Create a thread and start reading the stderr output on it. thread = Thread { [weak self] in @@ -544,8 +530,6 @@ public final class Process: ObjectIdentifierProtocol { self.stderr.thread = thread } } - - return stdinStream #endif // POSIX implementation } @@ -747,15 +731,11 @@ private func open(pipe: inout [Int32]) throws { } /// Close the given fd. -private func close(fd: Int32) throws { - func innerClose(_ fd: inout Int32) throws { - let rv = TSCLibc.close(fd) - guard rv == 0 else { - throw SystemError.close(rv) - } +private func close(fd: inout Int32) throws { + let rv = TSCLibc.close(fd) + guard rv == 0 else { + throw SystemError.close(rv) } - var innerFd = fd - try innerClose(&innerFd) } extension Process.Error: CustomStringConvertible { @@ -808,27 +788,3 @@ extension ProcessResult.Error: CustomStringConvertible { } } } - -#if os(Windows) -extension FileHandle: WritableByteStream { - public var position: Int { - return Int(offsetInFile) - } - - public func write(_ byte: UInt8) { - write(Data([byte])) - } - - public func write(_ bytes: C) where C.Element == UInt8 { - write(Data(bytes)) - } - - public func flush() { - synchronizeFile() - } - - public func close() throws { - closeFile() - } -} -#endif diff --git a/Tests/TSCBasicTests/ProcessTests.swift b/Tests/TSCBasicTests/ProcessTests.swift index 4b08c0b3..bd99be59 100644 --- a/Tests/TSCBasicTests/ProcessTests.swift +++ b/Tests/TSCBasicTests/ProcessTests.swift @@ -188,23 +188,6 @@ class ProcessTests: XCTestCase { XCTAssertEqual(result2, "hello\n") } - func testStdin() throws { - var stdout = [UInt8]() - let process = Process(args: script("in-to-out"), outputRedirection: .stream(stdout: { stdoutBytes in - stdout += stdoutBytes - }, stderr: { _ in })) - let stdinStream = try process.launch() - - stdinStream.write("hello\n") - stdinStream.flush() - - try stdinStream.close() - - try process.waitUntilExit() - - XCTAssertEqual(String(decoding: stdout, as: UTF8.self), "hello\n") - } - func testStdoutStdErr() throws { // A simple script to check that stdout and stderr are captured separatly. do { diff --git a/Tests/TSCBasicTests/processInputs/in-to-out b/Tests/TSCBasicTests/processInputs/in-to-out deleted file mode 100755 index 3b86f0d9..00000000 --- a/Tests/TSCBasicTests/processInputs/in-to-out +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -import sys - -sys.stdout.write(sys.stdin.readline())