]> git.tdb.fi Git - builder.git/blobdiff - source/externaltask.cpp
Additional MSVC fixes
[builder.git] / source / externaltask.cpp
index c205cbcf1a5c704f92aeed61cda08537c580224d..16d1df096dd4a23eee3f60b2e54a8cf289de9884 100644 (file)
 #include <cstdlib>
-#include <unistd.h>
-#include <sys/wait.h>
 #include <msp/fs/dir.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
 #include <msp/io/print.h>
+#include <msp/time/timedelta.h>
 #include "externaltask.h"
 
 using namespace std;
 using namespace Msp;
 
-ExternalTask::ExternalTask(const vector<string> &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),
+       stdin_action(PASSTHROUGH),
+       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<string>::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_action==REDIRECT)
+       {
+               cmd += " <";
+               cmd += stdin_file.str();
        }
-       else
+
+       if(stdout_action==REDIRECT)
        {
-               vector<const char *> cargv(argv.size()+1);
-               for(unsigned i=0; i<argv.size(); ++i)
-                       cargv[i] = argv[i].c_str();
-               cargv.back() = 0;
+               cmd += " >";
+               cmd += stdout_file.str();
+       }
 
-               if(!work_dir.empty())
-                       FS::chdir(work_dir);
-               execvp(cargv.front(), const_cast<char *const *>(&cargv.front()));
-               IO::print("Couldn't execute %s\n", argv.front());
-               exit(1);
+       return cmd;
+}
+
+void ExternalTask::start()
+{
+       IO::File *devnull = 0;
+       IO::File *infile = 0;
+       IO::File *outfile = 0;
+
+       prepare();
+
+       process = new Process;
+
+       if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE)
+       {
+#ifdef _WIN32
+               devnull = new IO::File("nul", IO::M_RDWR);
+#else
+               devnull = new IO::File("/dev/null", IO::M_RDWR);
+#endif
+               if(stdin_action==IGNORE)
+                       process->redirect_cin(*devnull);
+               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)
+       {
+               capture_pipe = new IO::Pipe;
+               if(stdout_action==CAPTURE)
+                       process->redirect_cout(*capture_pipe);
+               if(stderr_action==CAPTURE)
+                       process->redirect_cerr(*capture_pipe);
+       }
+
+       if(stdin_action==REDIRECT)
+       {
+               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)
+{
+       while(process)
        {
-               int status;
-               if(waitpid(pid, &status, WNOHANG)==pid)
+               if(process->wait(block && !capture_pipe))
+               {
+                       exit_code = process->get_exit_code();
+                       delete process;
+                       process = 0;
+               }
+
+               // Do this after waiting to avoid a race condition
+               while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec))
                {
-                       if(WIFEXITED(status))
-                               exit_code = WEXITSTATUS(status);
-                       else if(WIFSIGNALED(status))
-                               exit_code = 256+WTERMSIG(status);
+                       char buf[1024];
+                       unsigned len = capture_pipe->read(buf, sizeof(buf));
+                       if(len)
+                               output.append(buf, len);
                        else
-                               exit_code = 1025;
-                       pid = 0;
+                               break;
+               }
 
-                       signal_finished.emit(!exit_code);
+               if(process)
+               {
+                       if(!block)
+                               return RUNNING;
                }
                else
-                       return RUNNING;
+                       signal_finished.emit(!exit_code);
        }
 
        return exit_code ? ERROR : SUCCESS;
 }
+
+void ExternalTask::set_stdin(const FS::Path &f)
+{
+       stdin_action = REDIRECT;
+       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, bool capture_stderr)
+{
+       ExternalTask task(argv, wd);
+       task.stdin_action = IGNORE;
+       task.set_stdout(CAPTURE);
+       task.set_stderr(capture_stderr ? CAPTURE : IGNORE);
+       task.start();
+       if(task.wait()!=SUCCESS)
+               throw runtime_error(format("%s failed", argv.front()));
+       return task.get_output();
+}