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