import osproc, streams, strtabs, strutils ## Process execution type HandleProcMsgCB* = proc (outMsg: string, errMsg: string, cmd: string): void proc sendMsg*(h: HandleProcMsgCB, outMsg: string, errMsg: string = "", cmd: string = ""): void = if h != nil: h(outMsg, errMsg, cmd) proc makeProcMsgHandler*(outSink, errSink: File, prefixCmd: bool = true): HandleProcMsgCB = result = proc(outMsg, errMsg: string, cmd: string): void {.closure.} = let prefix = if cmd.len == 0 or not prefixCmd: "" else: cmd & ": " if outMsg.len > 0: outSink.writeLine(prefix & outMsg) if errMsg.len > 0: errSink.writeLine(prefix & errMsg) proc makeProcMsgHandler*(outSink, errSink: Stream, prefixCmd: bool = true): HandleProcMsgCB = result = proc(outMsg, errMsg: string, cmd: string): void {.closure.} = let prefix = if cmd.len == 0 or not prefixCmd: "" else: cmd & ": " if outMsg.len > 0: outSink.writeLine(prefix & outMsg) if errMsg.len > 0: errSink.writeLine(prefix & errMsg) proc combineProcMsgHandlers*(a, b: HandleProcMsgCB): HandleProcMsgCB = if a == nil: result = b elif b == nil: result = a else: result = proc(cmd: string, outMsg, errMsg: string): void = a(cmd, outMsg, errMsg) b(cmd, outMsg, errMsg) proc waitFor*(p: Process, msgCB: HandleProcMsgCB, procCmd: string = ""): int = var pout = outputStream(p) var perr = errorStream(p) var line = newStringOfCap(120) while true: if pout.readLine(line): msgCB.sendMsg(line, "", procCmd) elif perr.readLine(line): msgCB.sendMsg("", line, procCmd) else: result = peekExitCode(p) if result != -1: break close(p) proc exec*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poUsePath}, msgCB: HandleProcMsgCB = nil): int {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = var p = startProcess(command, workingDir, args, env, options) result = waitFor(p, msgCB, command) proc execWithOutput*(command: string, workingDir:string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poUsePath}, msgCB: HandleProcMsgCB = nil): tuple[output, error: string, exitCode: int] = result = ("", "", -1) var outSeq, errSeq: seq[string] outSeq = @[]; errSeq = @[] let outputCollector = combineProcMsgHandlers(msgCB, proc(outMsg, errMsg: string, cmd: string): void {.closure.} = if outMsg.len > 0: outSeq.add(outMsg) if errMsg.len > 0: errSeq.add(errMsg)) result[2] = exec(command, workingDir, args, env, options, outputCollector) result[0] = outSeq.join("\n") result[1] = errSeq.join("\n")