Skip to content

Commit

Permalink
Update package
Browse files Browse the repository at this point in the history
  • Loading branch information
vmanot committed Oct 27, 2024
1 parent 08c92d6 commit dbe09e5
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 312 deletions.
153 changes: 153 additions & 0 deletions Sources/Merge/Intermodular/Extensions/Foundation/Process++.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ import Foundation
import Swift
import System

extension Process {
fileprivate static var _sigintSource: DispatchSourceSignal? = nil
fileprivate static var _sigintSourceProcesses: [Process] = []

fileprivate func _installSigintIfNeeded() {
guard
Self._sigintSource == nil
else {
return
}

signal(SIGINT, SIG_IGN)

Self._sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
Self._sigintSource?.setEventHandler {
Self._sigintSourceProcesses.forEach {
$0.terminate()
}

exit(-1)
}

Self._sigintSource?.resume()
}
}

extension Process {
func pipeStandardOutput<S: Subject>(
on queue: DispatchQueue,
Expand Down Expand Up @@ -42,4 +68,131 @@ extension Process {
}
}

extension Process {
public static func run(
command: String,
arguments: [String]
) async throws -> _ProcessResult {
let process = Process()

process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = [command] + arguments

return try await process._runAsynchronously()
}

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
package func _runRedirectingAllOutput(
to sink: Process.StandardOutputSink
) throws {
_installSigintIfNeeded()

try self.redirectAllOutput(to: sink)
try self.run()

self.waitUntilExit()

if let terminationError = terminationError {
throw terminationError
}
}

public func _runSynchronously() throws -> _ProcessResult {
_installSigintIfNeeded()

let stdout = _UnsafeStandardOutputOrErrorPipeBuffer(id: .stdout)
let stderr = _UnsafeStandardOutputOrErrorPipeBuffer(id: .stderr)

self.standardOutput = stdout.pipe
self.standardError = stderr.pipe

try self.run()

self.waitUntilExit()

return _ProcessResult(
process: self,
stdout: try stdout.closeReturningData(),
stderr: try stderr.closeReturningData(),
terminationError: terminationError
)
}

public func _runAsynchronously() async throws -> _ProcessResult {
_installSigintIfNeeded()

let stdout = _UnsafeAsyncStandardOutputOrErrorPipeBuffer(id: .stdout)
let stderr = _UnsafeAsyncStandardOutputOrErrorPipeBuffer(id: .stderr)

self.standardOutput = stdout.pipe
self.standardError = stderr.pipe

let isRunning = _OSUnfairLocked<Bool>(wrappedValue: false)

return try await withTaskCancellationHandler {
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<_ProcessResult, Error>) in
self.terminationHandler = { process in
_Concurrency.Task {
do {
continuation.resume(
returning: _ProcessResult(
process: self,
stdout: try await stdout.closeReturningData(),
stderr: try await stderr.closeReturningData(),
terminationError: process.terminationError
)
)
} catch {
assertionFailure(error)
}
}
}

do {
try self.run()

isRunning.wrappedValue = true
} catch {
continuation.resume(throwing: error)
}
}
} onCancel: {
if isRunning.wrappedValue {
self.terminate()
}
}
}

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
package func _runAsynchronouslyRedirectingAllOutput(
to sink: Process.StandardOutputSink
) async throws {
_installSigintIfNeeded()

try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
do {
try self.redirectAllOutput(to: sink)
} catch {
continuation.resume(throwing: error)

return
}

self.terminationHandler = { process in
if let terminationError = process.terminationError {
continuation.resume(throwing: terminationError)
} else {
continuation.resume()
}
}

do {
try self.run()
} catch {
continuation.resume(throwing: error)
}
}
}
}

#endif
5 changes: 4 additions & 1 deletion Sources/Merge/Intermodular/Helpers/Swift/EnvSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ internal func validateArgumentsForEnv(_ arguments: [String]) {
///
/// If that *is* why it failed, it's considered a programmer error and the
/// program will be aborted.
internal func makeErrorCheckerForEnv<Failure>(_ arguments: [String], conversion: @escaping (ProcessExitFailure) -> Failure) -> (ProcessExitFailure) -> Failure {
internal func makeErrorCheckerForEnv<Failure>(
_ arguments: [String],
conversion: @escaping (ProcessExitFailure) -> Failure
) -> (ProcessExitFailure) -> Failure {
let command = arguments.first!
return { error in
let commandNotFoundByEnvStatus: CInt = 127
Expand Down
101 changes: 0 additions & 101 deletions Sources/Merge/Intermodular/Helpers/Swift/Lines.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ extension ProcessPublisher where Failure == Error {
///
/// Failures produced by this publisher will be passed through the resulting
/// publisher, terminating its process.
public func directPipe(_ arguments: [String]) -> ProcessPublisher<Failure> {
public func directPipe(
_ arguments: [String]
) -> ProcessPublisher<Failure> {
validateArgumentsForEnv(arguments)
return ProcessPublisher(envExecutableURL, arguments: arguments, input: self.autoconnect().eraseToAnyPublisher(), directInput: self.prepareForDirectPipe(), errorHandler: makeErrorCheckerForEnv(arguments) { $0 })
}
Expand All @@ -100,7 +102,10 @@ extension ProcessPublisher where Failure == Error {
///
/// Failures produced by this publisher will be passed through the resulting
/// publisher, terminating its process.
public func directPipe(_ command: URL, arguments: [String] = []) -> ProcessPublisher<Failure> {
public func directPipe(
_ command: URL,
arguments: [String] = []
) -> ProcessPublisher<Failure> {
return ProcessPublisher(command, arguments: arguments, input: self.autoconnect().eraseToAnyPublisher(), directInput: self.prepareForDirectPipe(), errorHandler: { $0 })
}
}
Expand Down
Loading

0 comments on commit dbe09e5

Please sign in to comment.