]> git.tdb.fi Git - builder.git/blob - source/externaltask.cpp
dce0e06d6157700db2efab0ca32a27d5ba26aceb
[builder.git] / source / externaltask.cpp
1 #include <cstdlib>
2 #include <unistd.h>
3 #include <msp/fs/dir.h>
4 #include <msp/io/console.h>
5 #include <msp/io/file.h>
6 #include <msp/io/print.h>
7 #include <msp/time/timedelta.h>
8 #include "externaltask.h"
9
10 using namespace std;
11 using namespace Msp;
12
13 ExternalTask::ExternalTask(const Arguments &a, const FS::Path &wd):
14         argv(a),
15         work_dir(wd),
16         process(0),
17         exit_code(-1),
18         stdin_action(PASSTHROUGH),
19         stdout_action(PASSTHROUGH),
20         stderr_action(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         if(stdin_action==REDIRECT)
49         {
50                 cmd += " <";
51                 cmd += stdin_file.str();
52         }
53
54         if(stdout_action==REDIRECT)
55         {
56                 cmd += " >";
57                 cmd += stdout_file.str();
58         }
59
60         return cmd;
61 }
62
63 void ExternalTask::start()
64 {
65         IO::File *devnull = 0;
66         IO::File *infile = 0;
67         IO::File *outfile = 0;
68
69         prepare();
70
71         process = new Process;
72
73         if(stdin_action==IGNORE || stdout_action==IGNORE || stderr_action==IGNORE)
74         {
75 #ifdef _WIN32
76                 devnull = new IO::File("nul", IO::M_RDWR);
77 #else
78                 devnull = new IO::File("/dev/null", IO::M_RDWR);
79 #endif
80                 if(stdin_action==IGNORE)
81                         process->redirect_cin(*devnull);
82                 if(stdout_action==IGNORE)
83                         process->redirect_cout(*devnull);
84                 if(stderr_action==IGNORE)
85                         process->redirect_cerr(*devnull);
86         }
87
88         if(stdout_action==REDIRECT)
89         {
90                 outfile = new IO::File((work_dir/stdout_file).str(), IO::M_WRITE);
91                 process->redirect_cout(*outfile);
92         }
93
94         if(stdout_action==CAPTURE || stderr_action==CAPTURE)
95         {
96                 capture_pipe = new IO::Pipe;
97                 if(stdout_action==CAPTURE)
98                         process->redirect_cout(*capture_pipe);
99                 if(stderr_action==CAPTURE)
100                         process->redirect_cerr(*capture_pipe);
101         }
102
103         if(stdin_action==REDIRECT)
104         {
105                 infile = new IO::File((work_dir/stdin_file).str());
106                 process->redirect_cin(*infile);
107         }
108
109         if(!work_dir.empty())
110                 process->set_working_directory(work_dir);
111
112         Process::Arguments args(argv.begin()+1, argv.end());
113         process->execute(argv.front(), args);
114         if(capture_pipe)
115                 capture_pipe->set_mode(IO::M_READ);
116
117         delete devnull;
118         delete infile;
119         delete outfile;
120 }
121
122 Task::Status ExternalTask::check()
123 {
124         return do_wait(false);
125 }
126
127 Task::Status ExternalTask::wait()
128 {
129         return do_wait(true);
130 }
131
132 Task::Status ExternalTask::do_wait(bool block)
133 {
134         while(process)
135         {
136                 if(process->wait(block && !capture_pipe))
137                 {
138                         exit_code = process->get_exit_code();
139                         delete process;
140                         process = 0;
141                 }
142
143                 // Do this after waiting to avoid a race condition
144                 while(capture_pipe && IO::poll(*capture_pipe, IO::P_INPUT, 10*Time::msec))
145                 {
146                         char buf[1024];
147                         unsigned len = capture_pipe->read(buf, sizeof(buf));
148                         if(len)
149                                 output.append(buf, len);
150                         else
151                                 break;
152                 }
153
154                 if(process)
155                 {
156                         if(!block)
157                                 return RUNNING;
158                 }
159                 else
160                         signal_finished.emit(!exit_code);
161         }
162
163         return exit_code ? ERROR : SUCCESS;
164 }
165
166 void ExternalTask::set_stdin(const FS::Path &f)
167 {
168         stdin_action = REDIRECT;
169         stdin_file = f;
170 }
171
172 void ExternalTask::set_stdout(StreamAction a)
173 {
174         if(a==REDIRECT)
175                 throw invalid_argument("ExternalTask::set_stdout");
176         stdout_action = a;
177 }
178
179 void ExternalTask::set_stdout(const FS::Path &f)
180 {
181         stdout_action = REDIRECT;
182         stdout_file = f;
183 }
184
185 void ExternalTask::set_stderr(StreamAction a)
186 {
187         if(a==REDIRECT)
188                 throw invalid_argument("ExternalTask::set_stdout");
189         stderr_action = a;
190 }
191
192 string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd, bool capture_stderr)
193 {
194         ExternalTask task(argv, wd);
195         task.stdin_action = IGNORE;
196         task.set_stdout(CAPTURE);
197         task.set_stderr(capture_stderr ? CAPTURE : IGNORE);
198         task.start();
199         if(task.wait()!=SUCCESS)
200                 throw runtime_error(format("%s failed", argv.front()));
201         return task.get_output();
202 }