From 89143b875129566b1a460e052f6158aeb645393f Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Sun, 3 Jan 2021 16:38:29 -0700 Subject: [PATCH] Dispatch Process.run work manually instead of using subscribe(on:) and receive(on:) The previous implementation was doing something weird when it was used during installation in: verifySecurityAssessment(of: installedXcode) .combineLatest(self.verifySigningCertificate(of: installedXcode.path.url)) It looks like it would subscribe after the Process.run future had already sent a value, and so it would never finish. This seems weird, and I'm assuming that I'm misunderstanding something about this behaviour, but dispatching manually seems reasonable and works. --- Xcodes/Backend/Process.swift | 71 +++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/Xcodes/Backend/Process.swift b/Xcodes/Backend/Process.swift index 77a2782..88cdf8c 100644 --- a/Xcodes/Backend/Process.swift +++ b/Xcodes/Backend/Process.swift @@ -14,41 +14,52 @@ extension Process { static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) -> AnyPublisher { Deferred { Future { promise in - let process = Process() - process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() - process.executableURL = executable - process.arguments = arguments - - let (stdout, stderr) = (Pipe(), Pipe()) - process.standardOutput = stdout - process.standardError = stderr - - if let input = input { - let inputPipe = Pipe() - process.standardInput = inputPipe.fileHandleForReading - inputPipe.fileHandleForWriting.write(Data(input.utf8)) - inputPipe.fileHandleForWriting.closeFile() - } - - do { - try process.run() - process.waitUntilExit() - - let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" - let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + DispatchQueue.global().async { + let process = Process() + process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent() + process.executableURL = executable + process.arguments = arguments - guard process.terminationReason == .exit, process.terminationStatus == 0 else { - return promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error))) + let (stdout, stderr) = (Pipe(), Pipe()) + process.standardOutput = stdout + process.standardError = stderr + + if let input = input { + let inputPipe = Pipe() + process.standardInput = inputPipe.fileHandleForReading + inputPipe.fileHandleForWriting.write(Data(input.utf8)) + inputPipe.fileHandleForWriting.closeFile() + } + + do { + print("Process.run \(executable), \(input), \(arguments.joined(separator: " "))") + try process.run() + process.waitUntilExit() + + let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + + dump(process) + print(output) + print(error) + guard process.terminationReason == .exit, process.terminationStatus == 0 else { + DispatchQueue.main.async { + promise(.failure(ProcessExecutionError(process: process, standardOutput: output, standardError: error))) + } + return + } + + DispatchQueue.main.async { + promise(.success((process.terminationStatus, output, error))) + } + } catch { + DispatchQueue.main.async { + promise(.failure(error)) + } } - - promise(.success((process.terminationStatus, output, error))) - } catch { - promise(.failure(error)) } } } - .subscribe(on: DispatchQueue.global()) - .receive(on: DispatchQueue.main) .eraseToAnyPublisher() } }