Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .spi.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
version: 1
metadata:
authors: Apple Inc.
builder:
configs:
- documentation_targets: [Subprocess]
- swift_version: 6.1
documentation_targets: [Subprocess]
scheme: Subprocess
43 changes: 25 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies: [
.package(url: "https://github.com/swiftlang/swift-subprocess.git", branch: "main")
]
```
Then, adding the `Subprocess` module to your target dependencies:
Then, add the `Subprocess` module to your target dependencies:

```swift
.target(
Expand All @@ -29,27 +29,27 @@ Then, adding the `Subprocess` module to your target dependencies:
`Subprocess` offers two [package traits](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0450-swiftpm-package-traits.md):

- `SubprocessFoundation`: includes a dependency on `Foundation` and adds extensions on Foundation types like `Data`. This trait is enabled by default.
- `SubprocessSpan`: makes Subprocess API, mainly `OutputProtocol`, `RawSpan` based. This trait is enabled whenever `RawSpan` is available and should only be disabled when `RawSpan` is not available.
- `SubprocessSpan`: makes Subprocess's API, mainly `OutputProtocol`, `RawSpan`-based. This trait is enabled whenever `RawSpan` is available and should only be disabled when `RawSpan` is not available.

Please find the API proposal [here](https://github.com/swiftlang/swift-foundation/blob/main/Proposals/0007-swift-subprocess.md).

### Swift Versions

The minimal supported Swift version is **Swift 6.1**.
The minimum supported Swift version is **Swift 6.1**.

To experiment with the `SubprocessSpan` trait, Swift 6.2 is required. Currently, you can download the Swift 6.2 toolchain (`main` development snapshot) [here](https://www.swift.org/install/macos/#development-snapshots).


## Feature Overview

### Run and Asynchonously Collect Output
### Run and Asynchronously Collect Output

The easiest way to spawn a process with `Subprocess` is to simply run it and await its `CollectedResult`:

```swift
import Subprocess

let result = try await run(.name("ls"))
let result = try await run(.name("ls"), output: .string(limit: 4096))

print(result.processIdentifier) // prints 1234
print(result.terminationStatus) // prints exited(0)
Expand All @@ -69,7 +69,7 @@ async let monitorResult = run(
.path("/usr/bin/tail"),
arguments: ["-f", "/path/to/nginx.log"]
) { execution, standardOutput in
for try await line in standardOutput.lines(encoding: UTF8.self) {
for try await line in standardOutput.lines() {
// Parse the log text
if line.contains("500") {
// Oh no, 500 error
Expand All @@ -92,6 +92,7 @@ let result = try await run(
// add `NewKey=NewValue`
environment: .inherit.updating(["NewKey": "NewValue"]),
workingDirectory: "/Users/",
output: .string(limit: 4096)
)
```

Expand Down Expand Up @@ -127,20 +128,20 @@ let result = try await run(.path("/bin/exe"), platformOptions: platformOptions)
### Flexible Input and Output Configurations

By default, `Subprocess`:
- Doesnt send any input to the child processs standard input
- Asks the user how to capture the output
- Ignores the child processs standard error
- Doesn't send any input to the child process's standard input
- Requires you to specify how to capture the output
- Ignores the child process's standard error

You can tailor how `Subprocess` handles the standard input, standard output, and standard error by setting the `input`, `output`, and `error` parameters:

```swift
let content = "Hello Subprocess"

// Send "Hello Subprocess" to the standard input of `cat`
let result = try await run(.name("cat"), input: .string(content, using: UTF8.self))
let result = try await run(.name("cat"), input: .string(content), output: .string(limit: 4096))

// Collect both standard error and standard output as Data
let result = try await run(.name("cat"), output: .data, error: .data)
let result = try await run(.name("cat"), output: .data(limit: 4096), error: .data(limit: 4096))
```

`Subprocess` supports these input options:
Expand All @@ -155,13 +156,13 @@ Use it by setting `.none` for `input`.

This option reads input from a specified `FileDescriptor`. If `closeAfterSpawningProcess` is set to `true`, the subprocess will close the file descriptor after spawning. If `false`, you are responsible for closing it, even if the subprocess fails to spawn.

Use it by setting `.fileDescriptor(closeAfterSpawningProcess:)` for `input`.
Use it by setting `.fileDescriptor(_:closeAfterSpawningProcess:)` for `input`.

#### `StringInput`

This option reads input from a type conforming to `StringProtocol` using the specified encoding.

Use it by setting `.string(using:)` for `input`.
Use it by setting `.string(_:)` or `.string(_:using:)` for `input`.

#### `ArrayInput`

Expand All @@ -179,13 +180,13 @@ Use it by setting `.data` for `input`.

This option reads input from a sequence of `Data`.

Use it by setting `.sequence` for `input`.
Use it by setting `.sequence(_:)` for `input`.

#### `DataAsyncSequenceInput` (available with `SubprocessFoundation` trait)

This option reads input from an async sequence of `Data`.

Use it by setting `.asyncSequence` for `input`.
Use it by setting `.sequence(_:)` for `input`.

---

Expand All @@ -201,19 +202,25 @@ Use it by setting `.discarded` for `output` or `error`.

This option writes output to a specified `FileDescriptor`. You can choose to have the `Subprocess` close the file descriptor after spawning.

Use it by setting `.fileDescriptor(closeAfterSpawningProcess:)` for `output` or `error`.
Use it by setting `.fileDescriptor(_:closeAfterSpawningProcess:)` for `output` or `error`.

#### `StringOutput`

This option collects output as a `String` with the given encoding.

Use it by setting `.string(limit:encoding:)` for `output` or `error`.
Use it by setting `.string(limit:)` or `.string(limit:encoding:)` for `output` or `error`.

#### `BytesOutput`

This option collects output as `[UInt8]`.

Use it by setting`.bytes(limit:)` for `output` or `error`.
Use it by setting `.bytes(limit:)` for `output` or `error`.

#### `DataOutput` (available with `SubprocessFoundation` trait)

This option collects output as `Data`.

Use it by setting `.data(limit:)` for `output` or `error`.


### Cross-platform support
Expand Down
101 changes: 42 additions & 59 deletions Sources/Subprocess/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@

// MARK: - Collected Result

/// Run an executable with given parameters asynchronously and returns
/// a `CollectedResult` containing the output of the child process.
/// Run an executable with given parameters asynchronously and return a
/// collected result that contains the output of the child process.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - input: The input to send to the executable.
/// - output: The method to use for redirecting the standard output.
/// - error: The method to use for redirecting the standard error.
/// - Returns a CollectedResult containing the result of the run.
/// - Returns: a `CollectedResult` containing the result of the run.
public func run<
Input: InputProtocol,
Output: OutputProtocol,
Expand Down Expand Up @@ -59,20 +58,19 @@ public func run<
)
}

/// Run an executable with given parameters asynchronously and returns
/// a `CollectedResult` containing the output of the child process.
#if SubprocessSpan
/// Run an executable with given parameters asynchronously and return a
/// collected result that contains the output of the child process.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - input: span to write to subprocess' standard input.
/// - output: The method to use for redirecting the standard output.
/// - error: The method to use for redirecting the standard error.
/// - Returns a CollectedResult containing the result of the run.
#if SubprocessSpan
/// - Returns: a CollectedResult containing the result of the run.
public func run<
InputElement: BitwiseCopyable,
Output: OutputProtocol,
Expand Down Expand Up @@ -138,21 +136,19 @@ public func run<
// MARK: - Custom Execution Body

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime and stream its standard output.
/// to manage the running subprocess lifetime.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - input: The input to send to the executable.
/// - output: How to manager executable standard output.
/// - error: How to manager executable standard error.
/// - output: How to manage executable standard output.
/// - error: How to manage executable standard error.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - Returns: an `ExecutableResult` type containing the return value of the closure.
public func run<Result, Input: InputProtocol, Output: OutputProtocol, Error: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -198,21 +194,19 @@ public func run<Result, Input: InputProtocol, Output: OutputProtocol, Error: Out
}
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime and stream its standard output.
/// Run an executable with given parameters and a custom closure to manage the
/// running subprocess' lifetime and stream its standard output.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - input: The input to send to the executable.
/// - error: How to manager executable standard error.
/// - error: How to manage executable standard error.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - body: The custom execution body to manually control the running process.
/// - Returns: an `ExecutableResult` type containing the return value of the closure.
public func run<Result, Input: InputProtocol, Error: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -260,21 +254,19 @@ public func run<Result, Input: InputProtocol, Error: OutputProtocol>(
}
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime and stream its standard error.
/// Run an executable with given parameters and a custom closure to manage the
/// running subprocess' lifetime and stream its standard error.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - input: The input to send to the executable.
/// - output: How to manager executable standard output.
/// - isolation: the isolation context to run the body closure.
/// - output: How to manage executable standard output.
/// - isolation: The isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - Returns: an `ExecutableResult` type containing the return value of the closure.
public func run<Result, Input: InputProtocol, Output: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -322,21 +314,18 @@ public func run<Result, Input: InputProtocol, Output: OutputProtocol>(
}
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime, write to its
/// standard input, and stream its standard output.
/// Run an executable with given parameters and a custom closure to manage the
/// running subprocess' lifetime, write to its standard input, and stream its standard output.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - error: How to manager executable standard error.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - error: How to manage executable standard error.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - Returns: An `ExecutableResult` type containing the return value of the closure.
public func run<Result, Error: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -367,21 +356,18 @@ public func run<Result, Error: OutputProtocol>(
}
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime, write to its
/// standard input, and stream its standard error.
/// Run an executable with given parameters and a custom closure to manage the
/// running subprocess' lifetime, write to its standard input, and stream its standard error.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - output: How to manager executable standard output.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - output: How to manage executable standard output.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - Returns: An `ExecutableResult` type containing the return value of the closure.
public func run<Result, Output: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -413,19 +399,17 @@ public func run<Result, Output: OutputProtocol>(
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime, write to its
/// to manage the running subprocess lifetime, write to its
/// standard input, and stream its standard output and standard error.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - platformOptions: The platform-specific options to use when running the executable.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
/// - Returns: an `ExecutableResult` type containing the return value of the closure.
public func run<Result>(
_ executable: Executable,
arguments: Arguments = [],
Expand Down Expand Up @@ -474,7 +458,7 @@ public func run<Result>(
/// - input: The input to send to the executable.
/// - output: The method to use for redirecting the standard output.
/// - error: The method to use for redirecting the standard error.
/// - Returns a CollectedResult containing the result of the run.
/// - Returns: a `CollectedResult` containing the result of the run.
public func run<
Input: InputProtocol,
Output: OutputProtocol,
Expand Down Expand Up @@ -562,9 +546,8 @@ public func run<
/// - isolation: the isolation context to run the body closure.
/// - body: The custom configuration body to manually control
/// the running process, write to its standard input, stream
/// its standard output and standard error.
/// - Returns an executableResult type containing the return value
/// of the closure.
/// the standard output and standard error.
/// - Returns: an `ExecutableResult` type containing the return value of the closure.
public func run<Result>(
_ configuration: Configuration,
isolation: isolated (any Actor)? = #isolation,
Expand Down
Loading