X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fexternaltask.cpp;h=643831e993b9b7e8a8d4c2b65f11245c612bba18;hb=c75c89d2425ab0c69e94d45a5d956286838bbc67;hp=c205cbcf1a5c704f92aeed61cda08537c580224d;hpb=f58a6a7389e0c2609c56a184f98731e01ab450a4;p=builder.git diff --git a/source/externaltask.cpp b/source/externaltask.cpp index c205cbc..643831e 100644 --- a/source/externaltask.cpp +++ b/source/externaltask.cpp @@ -2,20 +2,60 @@ #include #include #include +#include +#include #include +#include #include "externaltask.h" using namespace std; using namespace Msp; -ExternalTask::ExternalTask(const vector &argv, const FS::Path &work_dir): +ExternalTask::ExternalTask(const vector &a, const FS::Path &wd): + argv(a), + work_dir(wd), pid(-1), - exit_code(-1) + exit_code(-1), + stdout_dest(PASSTHROUGH), + stderr_dest(PASSTHROUGH), + capture_pipe(0) +{ } + +ExternalTask::~ExternalTask() +{ + delete capture_pipe; +} + +string ExternalTask::get_command() const +{ + string cmd; + for(vector::const_iterator i=argv.begin(); i!=argv.end(); ++i) + { + if(i!=argv.begin()) + cmd += ' '; + + for(string::const_iterator j=i->begin(); j!=i->end(); ++j) + { + if(*j=='"' || *j=='\'' || *j==' ' || *j=='\\' || *j=='&') + cmd += '\\'; + cmd += *j; + } + } + + return cmd; +} + +void ExternalTask::start() { + if(stdout_dest==CAPTURE || stderr_dest==CAPTURE) + capture_pipe = new IO::Pipe; + + prepare(); + if((pid = fork())) { if(pid==-1) - exit_code = 1; + exit_code = 1026; else exit_code = 0; } @@ -26,6 +66,24 @@ ExternalTask::ExternalTask(const vector &argv, const FS::Path &work_dir) cargv[i] = argv[i].c_str(); cargv.back() = 0; + if(stdout_dest==IGNORE || stderr_dest==IGNORE) + { + IO::File devnull("/dev/null", IO::M_WRITE); + if(stdout_dest==IGNORE) + IO::cout.redirect(devnull); + if(stderr_dest==IGNORE) + IO::cerr.redirect(devnull); + } + + if(capture_pipe) + { + if(stdout_dest==CAPTURE) + IO::cout.redirect(*capture_pipe); + if(stderr_dest==CAPTURE) + IO::cerr.redirect(*capture_pipe); + delete capture_pipe; + } + if(!work_dir.empty()) FS::chdir(work_dir); execvp(cargv.front(), const_cast(&cargv.front())); @@ -35,11 +93,21 @@ ExternalTask::ExternalTask(const vector &argv, const FS::Path &work_dir) } Task::Status ExternalTask::check() +{ + return do_wait(false); +} + +Task::Status ExternalTask::wait() +{ + return do_wait(true); +} + +Task::Status ExternalTask::do_wait(bool block) { if(pid>0) { int status; - if(waitpid(pid, &status, WNOHANG)==pid) + if(waitpid(pid, &status, (block ? 0 : WNOHANG))==pid) { if(WIFEXITED(status)) exit_code = WEXITSTATUS(status); @@ -48,12 +116,47 @@ Task::Status ExternalTask::check() else exit_code = 1025; pid = 0; + } - signal_finished.emit(!exit_code); + // Do this after waiting to avoid a race condition + if(capture_pipe) + { + while(IO::poll(*capture_pipe, IO::P_INPUT, Time::zero)) + { + char buf[1024]; + unsigned len = capture_pipe->read(buf, sizeof(buf)); + output.append(buf, len); + if(len0) return RUNNING; + else + signal_finished.emit(!exit_code); } return exit_code ? ERROR : SUCCESS; } + +void ExternalTask::set_stdout(Destination d) +{ + stdout_dest = d; +} + +void ExternalTask::set_stderr(Destination d) +{ + stderr_dest = d; +} + +string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd) +{ + ExternalTask task(argv, wd); + task.set_stdout(CAPTURE); + task.set_stderr(IGNORE); + task.start(); + if(task.wait()!=SUCCESS) + throw runtime_error(format("%s failed", argv.front())); + return task.get_output(); +}