X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Fexternaltask.cpp;h=257d1318fb22a42d31a5e2ed04e95424cb0a80ae;hb=4b075b4a7ed921be62740c302edeebcd8b06ca29;hp=24716662804528fcdae50421511379fd576b6d57;hpb=338eefb513953ae55e8e3614c009c242ba8ad74e;p=builder.git diff --git a/source/externaltask.cpp b/source/externaltask.cpp index 2471666..257d131 100644 --- a/source/externaltask.cpp +++ b/source/externaltask.cpp @@ -1,58 +1,191 @@ #include +#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): - pid(-1), - exit_code(-1) +ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd): + argv(a), + work_dir(wd), + process(0), + exit_code(-1), + stdout_action(PASSTHROUGH), + stderr_action(PASSTHROUGH), + capture_pipe(0) { - if((pid = fork())) + if(argv.empty()) + throw invalid_argument("ExternalTask::ExternalTask"); +} + +ExternalTask::~ExternalTask() +{ + delete capture_pipe; +} + +string ExternalTask::get_command() const +{ + string cmd; + for(vector::const_iterator i=argv.begin(); i!=argv.end(); ++i) { - if(pid==-1) - exit_code = 1; - else - exit_code = 0; + 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; + } + } + + if(!stdin_file.empty()) + { + cmd += " <"; + cmd += stdin_file.str(); + } + + if(stdout_action==REDIRECT) + { + cmd += " >"; + cmd += stdout_file.str(); } - else + + return cmd; +} + +void ExternalTask::start() +{ + IO::File *devnull = 0; + IO::File *infile = 0; + IO::File *outfile = 0; + + prepare(); + + process = new Process; + + if(stdout_action==IGNORE || stderr_action==IGNORE) + { + devnull = new IO::File("/dev/null", IO::M_WRITE); + if(stdout_action==IGNORE) + process->redirect_cout(*devnull); + if(stderr_action==IGNORE) + process->redirect_cerr(*devnull); + } + + if(stdout_action==REDIRECT) + { + outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE); + process->redirect_cout(*outfile); + } + + if(stdout_action==CAPTURE || stderr_action==CAPTURE) { - vector cargv(argv.size()+1); - for(unsigned i=0; iredirect_cout(*capture_pipe); + if(stderr_action==CAPTURE) + process->redirect_cerr(*capture_pipe); + } - if(!work_dir.empty()) - FS::chdir(work_dir); - execvp(cargv.front(), const_cast(&cargv.front())); - IO::print("Couldn't execute %s\n", argv.front()); - exit(1); + if(!stdin_file.empty()) + { + infile = new IO::File((work_dir/stdin_file).str()); + process->redirect_cin(*infile); } + + if(!work_dir.empty()) + process->set_working_directory(work_dir); + + Process::Arguments args(argv.begin()+1, argv.end()); + process->execute(argv.front(), args); + if(capture_pipe) + capture_pipe->set_mode(IO::M_READ); + + delete devnull; + delete infile; + delete outfile; } Task::Status ExternalTask::check() { - if(pid>0) + return do_wait(false); +} + +Task::Status ExternalTask::wait() +{ + return do_wait(true); +} + +Task::Status ExternalTask::do_wait(bool block) +{ + if(process) { - int status; - if(waitpid(pid, &status, WNOHANG)==pid) + if(process->wait(block)) { - if(WIFEXITED(status)) - exit_code = WEXITSTATUS(status); - else if(WIFSIGNALED(status)) - exit_code = 256+WTERMSIG(status); - else - exit_code = 1025; - pid = 0; + exit_code = process->get_exit_code(); + delete process; + process = 0; + } - signal_finished.emit(!exit_code); + // Do this after waiting to avoid a race condition + while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, Time::zero)) + { + char buf[1024]; + unsigned len = capture_pipe->read(buf, sizeof(buf)); + if(len) + output.append(buf, len); + else + break; } - else + + if(process) return RUNNING; + else + signal_finished.emit(!exit_code); } return exit_code ? ERROR : SUCCESS; } + +void ExternalTask::set_stdin(const FS::Path &f) +{ + stdin_file = f; +} + +void ExternalTask::set_stdout(StreamAction a) +{ + if(a==REDIRECT) + throw invalid_argument("ExternalTask::set_stdout"); + stdout_action = a; +} + +void ExternalTask::set_stdout(const FS::Path &f) +{ + stdout_action = REDIRECT; + stdout_file = f; +} + +void ExternalTask::set_stderr(StreamAction a) +{ + if(a==REDIRECT) + throw invalid_argument("ExternalTask::set_stdout"); + stderr_action = a; +} + +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(); +}