Skip to content

Commit

Permalink
Revert "[Wrangle] Revert noasync annotation. (#392)"
Browse files Browse the repository at this point in the history
This reverts commit f0e80d1.
  • Loading branch information
jakepetroules committed Feb 8, 2023
1 parent add9e15 commit d5f8c2b
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 6 deletions.
6 changes: 6 additions & 0 deletions Sources/TSCBasic/Await.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
/// should be passed to the async method's completion handler.
/// - Returns: The value wrapped by the async method's result.
/// - Throws: The error wrapped by the async method's result
#if compiler(>=5.8)
@available(*, noasync)
#endif
public func tsc_await<T, ErrorType>(_ body: (@escaping (Result<T, ErrorType>) -> Void) -> Void) throws -> T {
return try tsc_await(body).get()
}

#if compiler(>=5.8)
@available(*, noasync)
#endif
public func tsc_await<T>(_ body: (@escaping (T) -> Void) -> Void) -> T {
let condition = Condition()
var result: T? = nil
Expand Down
135 changes: 131 additions & 4 deletions Sources/TSCBasic/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,20 @@ import Foundation
import TSCLibc
import Dispatch

import _Concurrency

/// Process result data which is available after process termination.
public struct ProcessResult: CustomStringConvertible {
public struct ProcessResult: CustomStringConvertible, Sendable {

public enum Error: Swift.Error {
public enum Error: Swift.Error, Sendable {
/// The output is not a valid UTF8 sequence.
case illegalUTF8Sequence

/// The process had a non zero exit.
case nonZeroExit(ProcessResult)
}

public enum ExitStatus: Equatable {
public enum ExitStatus: Equatable, Sendable {
/// The process was terminated normally with a exit code.
case terminated(code: Int32)
#if os(Windows)
Expand Down Expand Up @@ -125,12 +127,18 @@ public struct ProcessResult: CustomStringConvertible {
}
}

#if swift(<5.6)
extension Process: UnsafeSendable {}
#else
extension Process: @unchecked Sendable {}
#endif

/// Process allows spawning new subprocesses and working with them.
///
/// Note: This class is thread safe.
public final class Process {
/// Errors when attempting to invoke a process
public enum Error: Swift.Error {
public enum Error: Swift.Error, Sendable {
/// The program requested to be executed cannot be found on the existing search paths, or is not executable.
case missingExecutableProgram(program: String)

Expand Down Expand Up @@ -807,7 +815,29 @@ public final class Process {
#endif // POSIX implementation
}

/// Executes the process I/O state machine, returning the result when finished.
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@discardableResult
public func waitUntilExit() async throws -> ProcessResult {
#if compiler(>=5.6)
return try await withCheckedThrowingContinuation { continuation in
waitUntilExit(continuation.resume(with:))
}
#else
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
return try await withCheckedThrowingContinuation { continuation in
waitUntilExit(continuation.resume(with:))
}
} else {
preconditionFailure("Unsupported with Swift 5.5 on this OS version")
}
#endif
}

/// Blocks the calling process until the subprocess finishes execution.
#if compiler(>=5.8)
@available(*, noasync)
#endif
@discardableResult
public func waitUntilExit() throws -> ProcessResult {
let group = DispatchGroup()
Expand Down Expand Up @@ -938,6 +968,88 @@ public final class Process {
}
}

extension Process {
/// Execute a subprocess and returns the result when it finishes execution
///
/// - Parameters:
/// - arguments: The arguments for the subprocess.
/// - environment: The environment to pass to subprocess. By default the current process environment
/// will be inherited.
/// - loggingHandler: Handler for logging messages
@available(macOS 10.15, *)
static public func popen(
arguments: [String],
environment: [String: String] = ProcessEnv.vars,
loggingHandler: LoggingHandler? = .none
) async throws -> ProcessResult {
let process = Process(
arguments: arguments,
environment: environment,
outputRedirection: .collect,
loggingHandler: loggingHandler
)
try process.launch()
return try await process.waitUntilExit()
}

/// Execute a subprocess and returns the result when it finishes execution
///
/// - Parameters:
/// - args: The arguments for the subprocess.
/// - environment: The environment to pass to subprocess. By default the current process environment
/// will be inherited.
/// - loggingHandler: Handler for logging messages
@available(macOS 10.15, *)
static public func popen(
args: String...,
environment: [String: String] = ProcessEnv.vars,
loggingHandler: LoggingHandler? = .none
) async throws -> ProcessResult {
try await popen(arguments: args, environment: environment, loggingHandler: loggingHandler)
}

/// Execute a subprocess and get its (UTF-8) output if it has a non zero exit.
///
/// - Parameters:
/// - arguments: The arguments for the subprocess.
/// - environment: The environment to pass to subprocess. By default the current process environment
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process output (stdout + stderr).
@available(macOS 10.15, *)
@discardableResult
static public func checkNonZeroExit(
arguments: [String],
environment: [String: String] = ProcessEnv.vars,
loggingHandler: LoggingHandler? = .none
) async throws -> String {
let result = try await popen(arguments: arguments, environment: environment, loggingHandler: loggingHandler)
// Throw if there was a non zero termination.
guard result.exitStatus == .terminated(code: 0) else {
throw ProcessResult.Error.nonZeroExit(result)
}
return try result.utf8Output()
}

/// Execute a subprocess and get its (UTF-8) output if it has a non zero exit.
///
/// - Parameters:
/// - args: The arguments for the subprocess.
/// - environment: The environment to pass to subprocess. By default the current process environment
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process output (stdout + stderr).
@available(macOS 10.15, *)
@discardableResult
static public func checkNonZeroExit(
args: String...,
environment: [String: String] = ProcessEnv.vars,
loggingHandler: LoggingHandler? = .none
) async throws -> String {
try await checkNonZeroExit(arguments: args, environment: environment, loggingHandler: loggingHandler)
}
}

extension Process {
/// Execute a subprocess and calls completion block when it finishes execution
///
Expand All @@ -948,6 +1060,9 @@ extension Process {
/// - loggingHandler: Handler for logging messages
/// - queue: Queue to use for callbacks
/// - completion: A completion handler to return the process result
#if compiler(>=5.8)
@available(*, noasync)
#endif
static public func popen(
arguments: [String],
environment: [String: String] = ProcessEnv.vars,
Expand Down Expand Up @@ -982,6 +1097,9 @@ extension Process {
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process result.
#if compiler(>=5.8)
@available(*, noasync)
#endif
@discardableResult
static public func popen(
arguments: [String],
Expand All @@ -1006,6 +1124,9 @@ extension Process {
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process result.
#if compiler(>=5.8)
@available(*, noasync)
#endif
@discardableResult
static public func popen(
args: String...,
Expand All @@ -1023,6 +1144,9 @@ extension Process {
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process output (stdout + stderr).
#if compiler(>=5.8)
@available(*, noasync)
#endif
@discardableResult
static public func checkNonZeroExit(
arguments: [String],
Expand Down Expand Up @@ -1052,6 +1176,9 @@ extension Process {
/// will be inherited.
/// - loggingHandler: Handler for logging messages
/// - Returns: The process output (stdout + stderr).
#if compiler(>=5.8)
@available(*, noasync)
#endif
@discardableResult
static public func checkNonZeroExit(
args: String...,
Expand Down
3 changes: 3 additions & 0 deletions Sources/TSCBasic/ProcessSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ public final class ProcessSet {
/// Terminate all the processes. This method blocks until all processes in the set are terminated.
///
/// A process set cannot be used once it has been asked to terminate.
#if compiler(>=5.8)
@available(*, noasync)
#endif
public func terminate() {
// Mark a process set as cancelled.
serialQueue.sync {
Expand Down
2 changes: 1 addition & 1 deletion Sources/TSCBasic/WritableByteStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ public final class ThreadSafeOutputByteStream: WritableByteStream {
}


#if swift(<5.7)
#if swift(<5.6)
extension ThreadSafeOutputByteStream: UnsafeSendable {}
#else
extension ThreadSafeOutputByteStream: @unchecked Sendable {}
Expand Down
Loading

0 comments on commit d5f8c2b

Please sign in to comment.