99import Foundation
1010
1111
12- typealias ExecutorReturnValue = ( status: Int , standardOutput: TaskPipe , standardError: TaskPipe )
12+ typealias ExecutorReturnValue = ( status: Int , standardOutput: String , standardError: String )
1313
1414class CommandExecutor {
1515
@@ -25,14 +25,21 @@ protocol TaskExecutor {
2525 func execute( _ commandParts: [ String ] ) -> ExecutorReturnValue
2626}
2727
28+ extension TaskExecutor {
29+ func readPipes( stdoutPipe: Pipe , stderrPipe: Pipe ) -> ( stdout: String , stderr: String ) {
30+ let stdout = readPipe ( stdoutPipe) . trimmingCharacters ( in: CharacterSet . whitespacesAndNewlines)
31+ let stderr = readPipe ( stderrPipe) . trimmingCharacters ( in: CharacterSet . whitespacesAndNewlines)
32+
33+ return ( stdout, stderr)
34+ }
35+ }
36+
2837class DryTaskExecutor : TaskExecutor {
2938
3039 func execute( _ commandParts: [ String ] ) -> ExecutorReturnValue {
3140 let command = commandParts. joined ( separator: " " )
3241 PromptSettings . print ( " Executed command ' \( command) ' " )
33- return ( 0 ,
34- Dryipe ( dataToReturn: " " . data ( using: String . Encoding. utf8) !) ,
35- Dryipe ( dataToReturn: " " . data ( using: String . Encoding. utf8) !) )
42+ return ( 0 , " " , " " )
3643 }
3744}
3845
@@ -52,7 +59,8 @@ class ActualTaskExecutor: TaskExecutor {
5259 task. launch ( )
5360 task. waitUntilExit ( )
5461
55- return ( Int ( task. terminationStatus) , stdoutPipe, stderrPipe)
62+ let ( stdout, stderr) = readPipes ( stdoutPipe: stdoutPipe, stderrPipe: stderrPipe)
63+ return ( Int ( task. terminationStatus) , stdout, stderr)
5664 }
5765}
5866
@@ -72,15 +80,71 @@ class InteractiveTaskExecutor: TaskExecutor {
7280 posix_spawn_file_actions_addclose ( & childFDActions, outputPipe [ 0 ] )
7381 posix_spawn_file_actions_addclose ( & childFDActions, outputPipe [ 1 ] )
7482
75-
7683 var pid : pid_t = 0
7784 let result = posix_spawn ( & pid, argv [ 0 ] , & childFDActions, nil , argv + [ nil ] , nil )
7885
79- let emptyPipe = Dryipe ( dataToReturn: " " . data ( using: String . Encoding. utf8) !)
80- return ( Int ( result) , emptyPipe, emptyPipe)
86+ return ( Int ( result) , " " , " " )
8187 }
8288}
8389
90+ class LogTaskExecutor : TaskExecutor {
91+ let logPath : String
92+
93+ init ( logPath: String ) {
94+ self . logPath = logPath
95+ }
96+
97+ func execute( _ commandParts: [ String ] ) -> ExecutorReturnValue {
98+ let argv : [ UnsafeMutablePointer < CChar > ? ] = commandParts. map { $0. withCString ( strdup) }
99+ defer { for case let arg? in argv { free ( arg) } }
100+
101+ var pid : pid_t = 0
102+ var childFDActions : posix_spawn_file_actions_t ? = nil
103+ let outputPipe : Int32 = 69
104+ let outerrPipe : Int32 = 70
105+
106+ posix_spawn_file_actions_init ( & childFDActions)
107+ posix_spawn_file_actions_addopen ( & childFDActions, outputPipe, stdoutLogPath, O_CREAT | O_TRUNC | O_WRONLY, ~ 0 )
108+ posix_spawn_file_actions_addopen ( & childFDActions, outerrPipe, stderrLogPath, O_CREAT | O_TRUNC | O_WRONLY, ~ 0 )
109+ posix_spawn_file_actions_adddup2 ( & childFDActions, outputPipe, 1 )
110+ posix_spawn_file_actions_adddup2 ( & childFDActions, outerrPipe, 2 )
111+
112+ var result = posix_spawn ( & pid, argv [ 0 ] , & childFDActions, nil , argv + [ nil ] , nil )
113+ guard result == 0 else { return ( Int ( 1 ) , " " , " " ) }
114+
115+ waitpid ( pid, & result, 0 )
116+ posix_spawn_file_actions_addclose ( & childFDActions, outputPipe)
117+ posix_spawn_file_actions_addclose ( & childFDActions, outerrPipe)
118+ posix_spawn_file_actions_destroy ( & childFDActions)
119+
120+ let ( stdout, stderr) = read ( outputPath: stdoutLogPath, outerrPath: stderrLogPath)
121+ removeFiles ( stdoutLogPath, stderrLogPath)
122+ write ( atPath: logPath, content: " \( stdout) \n \( stderr) " )
123+
124+ return ( Int ( 0 ) , stdout, stderr)
125+ }
126+
127+ private var stdoutLogPath : String { return " \( logPath) -stdout.log " }
128+ private var stderrLogPath : String { return " \( logPath) -stderr.log " }
129+
130+ private func removeFiles( _ files: String ... ) {
131+ files. forEach { file in
132+ try ? FileManager . default. removeItem ( atPath: file)
133+ }
134+ }
135+
136+ private func read( outputPath: String , outerrPath: String ) -> ( stdout: String , stderr: String ) {
137+ let stdout = String ( data: FileManager . default. contents ( atPath: outputPath) ?? Data ( ) , encoding: . utf8) ?? " "
138+ let stderr = String ( data: FileManager . default. contents ( atPath: outerrPath) ?? Data ( ) , encoding: . utf8) ?? " "
139+
140+ return ( stdout, stderr)
141+ }
142+
143+ private func write( atPath path: String , content: String ) {
144+ FileManager . default. createFile ( atPath: path, contents: content. data ( using: . utf8) , attributes: nil )
145+ }
146+ }
147+
84148class DummyTaskExecutor : TaskExecutor {
85149
86150 var commandsExecuted : [ String ] = [ ]
@@ -99,8 +163,6 @@ class DummyTaskExecutor: TaskExecutor {
99163 let command = commandParts. joined ( separator: " " )
100164 commandsExecuted. append ( command)
101165
102- return ( statusCodeToReturn,
103- Dryipe ( dataToReturn: outputToReturn. data ( using: String . Encoding. utf8) !) ,
104- Dryipe ( dataToReturn: errorToReturn. data ( using: String . Encoding. utf8) !) )
166+ return ( statusCodeToReturn, outputToReturn, errorToReturn)
105167 }
106168}
0 commit comments