]> git.tdb.fi Git - builder.git/blob - source/externaltask.cpp
Rewrite ExternalTask to use Msp::Process
[builder.git] / source / externaltask.cpp
1 #include <cstdlib>
2 #include <unistd.h>
3 #include <sys/wait.h>
4 #include <msp/fs/dir.h>
5 #include <msp/io/console.h>
6 #include <msp/io/file.h>
7 #include <msp/io/print.h>
8 #include <msp/time/units.h>
9 #include "externaltask.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd):
15         argv(a),
16         work_dir(wd),
17         process(0),
18         exit_code(-1),
19         stdout_dest(PASSTHROUGH),
20         stderr_dest(PASSTHROUGH),
21         capture_pipe(0)
22 {
23         if(argv.empty())
24                 throw invalid_argument("ExternalTask::ExternalTask");
25 }
26
27 ExternalTask::~ExternalTask()
28 {
29         delete capture_pipe;
30 }
31
32 string ExternalTask::get_command() const
33 {
34         string cmd;
35         for(vector<string>::const_iterator i=argv.begin(); i!=argv.end(); ++i)
36         {
37                 if(i!=argv.begin())
38                         cmd += ' ';
39
40                 for(string::const_iterator j=i->begin(); j!=i->end(); ++j)
41                 {
42                         if(*j=='"' || *j=='\'' || *j==' ' || *j=='\\' || *j=='&')
43                                 cmd += '\\';
44                         cmd += *j;
45                 }
46         }
47
48         return cmd;
49 }
50
51 void ExternalTask::start()
52 {
53         IO::File *devnull = 0;
54
55         prepare();
56
57         process = new Process;
58
59         if(stdout_dest==IGNORE || stderr_dest==IGNORE)
60         {
61                 devnull = new IO::File("/dev/null", IO::M_WRITE);
62                 if(stdout_dest==IGNORE)
63                         process->redirect_cout(*devnull);
64                 if(stderr_dest==IGNORE)
65                         process->redirect_cerr(*devnull);
66         }
67
68         if(stdout_dest==CAPTURE || stderr_dest==CAPTURE)
69         {
70                 capture_pipe = new IO::Pipe;
71                 if(stdout_dest==CAPTURE)
72                         process->redirect_cout(*capture_pipe);
73                 if(stderr_dest==CAPTURE)
74                         process->redirect_cerr(*capture_pipe);
75         }
76
77         if(!work_dir.empty())
78                 process->set_working_directory(work_dir);
79
80         Process::Arguments args(argv.begin()+1, argv.end());
81         process->execute(argv.front(), args);
82         if(capture_pipe)
83                 capture_pipe->set_mode(IO::M_READ);
84
85         delete devnull;
86 }
87
88 Task::Status ExternalTask::check()
89 {
90         return do_wait(false);
91 }
92
93 Task::Status ExternalTask::wait()
94 {
95         return do_wait(true);
96 }
97
98 Task::Status ExternalTask::do_wait(bool block)
99 {
100         if(process)
101         {
102                 if(process->wait(block))
103                 {
104                         exit_code = process->get_exit_code();
105                         delete process;
106                         process = 0;
107                 }
108
109                 // Do this after waiting to avoid a race condition
110                 while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, Time::zero))
111                 {
112                         char buf[1024];
113                         unsigned len = capture_pipe->read(buf, sizeof(buf));
114                         if(len)
115                                 output.append(buf, len);
116                         else
117                                 break;
118                 }
119
120                 if(process)
121                         return RUNNING;
122                 else
123                         signal_finished.emit(!exit_code);
124         }
125
126         return exit_code ? ERROR : SUCCESS;
127 }
128
129 void ExternalTask::set_stdout(Destination d)
130 {
131         stdout_dest = d;
132 }
133
134 void ExternalTask::set_stderr(Destination d)
135 {
136         stderr_dest = d;
137 }
138
139 string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd)
140 {
141         ExternalTask task(argv, wd);
142         task.set_stdout(CAPTURE);
143         task.set_stderr(IGNORE);
144         task.start();
145         if(task.wait()!=SUCCESS)
146                 throw runtime_error(format("%s failed", argv.front()));
147         return task.get_output();
148 }