From 2c9a16e7b9d2007422f6ba13bcf51f2454256790 Mon Sep 17 00:00:00 2001 From: Charles Hu Date: Tue, 2 Sep 2025 11:00:46 -0700 Subject: [PATCH] Introduce FileDescriptorInput.standardInput, FileDescriptorOutput.standardOutput, and FileDescriptorOutput.standardError to easily bind current process' descriptors to child processes --- Sources/Subprocess/Configuration.swift | 7 ++++--- Sources/Subprocess/IO/AsyncIO+Linux.swift | 2 +- Sources/Subprocess/IO/Input.swift | 11 +++++++++++ Sources/Subprocess/IO/Output.swift | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Sources/Subprocess/Configuration.swift b/Sources/Subprocess/Configuration.swift index 40a9585..e1b7f86 100644 --- a/Sources/Subprocess/Configuration.swift +++ b/Sources/Subprocess/Configuration.swift @@ -103,6 +103,10 @@ public struct Configuration: Sendable { var _spawnResult = spawnResultBox!.take()! let execution = _spawnResult.execution + defer { + // Close process file descriptor now we finished monitoring + execution.processIdentifier.close() + } return try await withAsyncTaskCleanupHandler { let inputIO = _spawnResult.inputWriteEnd() @@ -127,9 +131,6 @@ public struct Configuration: Sendable { for: execution.processIdentifier ) - // Close process file descriptor now we finished monitoring - execution.processIdentifier.close() - return ExecutionResult( terminationStatus: terminationStatus, value: try result.get() diff --git a/Sources/Subprocess/IO/AsyncIO+Linux.swift b/Sources/Subprocess/IO/AsyncIO+Linux.swift index 1f87041..cd9c63f 100644 --- a/Sources/Subprocess/IO/AsyncIO+Linux.swift +++ b/Sources/Subprocess/IO/AsyncIO+Linux.swift @@ -288,7 +288,7 @@ final class AsyncIO: Sendable { ) if rc != 0 { _registration.withLock { storage in - storage.removeValue(forKey: fileDescriptor.rawValue) + _ = storage.removeValue(forKey: fileDescriptor.rawValue) } let capturedError = errno diff --git a/Sources/Subprocess/IO/Input.swift b/Sources/Subprocess/IO/Input.swift index 3fdccc3..38688d8 100644 --- a/Sources/Subprocess/IO/Input.swift +++ b/Sources/Subprocess/IO/Input.swift @@ -179,6 +179,17 @@ extension InputProtocol where Self == FileDescriptorInput { closeAfterSpawningProcess: closeAfterSpawningProcess ) } + + /// Create a Subprocess input that reads from the standard input of + /// current process. + /// + /// The file descriptor isn't closed afterwards. + public static var standardInput: Self { + return Self.fileDescriptor( + .standardInput, + closeAfterSpawningProcess: false + ) + } } extension InputProtocol { diff --git a/Sources/Subprocess/IO/Output.swift b/Sources/Subprocess/IO/Output.swift index a7cb372..a51689a 100644 --- a/Sources/Subprocess/IO/Output.swift +++ b/Sources/Subprocess/IO/Output.swift @@ -231,6 +231,28 @@ extension OutputProtocol where Self == FileDescriptorOutput { ) -> Self { return .init(fileDescriptor: fd, closeAfterSpawningProcess: closeAfterSpawningProcess) } + + /// Create a Subprocess output that writes output to the standard output of + /// current process. + /// + /// The file descriptor isn't closed afterwards. + public static var standardOutput: Self { + return Self.fileDescriptor( + .standardOutput, + closeAfterSpawningProcess: false + ) + } + + /// Create a Subprocess output that write output to the standard error of + /// current process. + /// + /// The file descriptor isn't closed afterwards. + public static var standardError: Self { + return Self.fileDescriptor( + .standardError, + closeAfterSpawningProcess: false + ) + } } extension OutputProtocol where Self == StringOutput {