Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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