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