packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmExecuteProcessCommand.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmExecuteProcessCommand.h"
13 #include "cmSystemTools.h"
14
15 #include <cmsys/Process.h>
16
17 #include <ctype.h> /* isspace */
18
19 static bool cmExecuteProcessCommandIsWhitespace(char c)
20 {
21   return (isspace((int)c) || c == '\n' || c == '\r');
22 }
23
24 void cmExecuteProcessCommandFixText(std::vector<char>& output,
25                                     bool strip_trailing_whitespace);
26 void cmExecuteProcessCommandAppend(std::vector<char>& output,
27                                    const char* data, int length);
28
29 // cmExecuteProcessCommand
30 bool cmExecuteProcessCommand
31 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
32 {
33   if(args.size() < 1 )
34     {
35     this->SetError("called with incorrect number of arguments");
36     return false;
37     }
38   std::vector< std::vector<const char*> > cmds;
39   std::string arguments;
40   bool doing_command = false;
41   size_t command_index = 0;
42   bool output_quiet = false;
43   bool error_quiet = false;
44   bool output_strip_trailing_whitespace = false;
45   bool error_strip_trailing_whitespace = false;
46   std::string timeout_string;
47   std::string input_file;
48   std::string output_file;
49   std::string error_file;
50   std::string output_variable;
51   std::string error_variable;
52   std::string result_variable;
53   std::string working_directory;
54   for(size_t i=0; i < args.size(); ++i)
55     {
56     if(args[i] == "COMMAND")
57       {
58       doing_command = true;
59       command_index = cmds.size();
60       cmds.push_back(std::vector<const char*>());
61       }
62     else if(args[i] == "OUTPUT_VARIABLE")
63       {
64       doing_command = false;
65       if(++i < args.size())
66         {
67         output_variable = args[i];
68         }
69       else
70         {
71         this->SetError(" called with no value for OUTPUT_VARIABLE.");
72         return false;
73         }
74       }
75     else if(args[i] == "ERROR_VARIABLE")
76       {
77       doing_command = false;
78       if(++i < args.size())
79         {
80         error_variable = args[i];
81         }
82       else
83         {
84         this->SetError(" called with no value for ERROR_VARIABLE.");
85         return false;
86         }
87       }
88     else if(args[i] == "RESULT_VARIABLE")
89       {
90       doing_command = false;
91       if(++i < args.size())
92         {
93         result_variable = args[i];
94         }
95       else
96         {
97         this->SetError(" called with no value for RESULT_VARIABLE.");
98         return false;
99         }
100       }
101     else if(args[i] == "WORKING_DIRECTORY")
102       {
103       doing_command = false;
104       if(++i < args.size())
105         {
106         working_directory = args[i];
107         }
108       else
109         {
110         this->SetError(" called with no value for WORKING_DIRECTORY.");
111         return false;
112         }
113       }
114     else if(args[i] == "INPUT_FILE")
115       {
116       doing_command = false;
117       if(++i < args.size())
118         {
119         input_file = args[i];
120         }
121       else
122         {
123         this->SetError(" called with no value for INPUT_FILE.");
124         return false;
125         }
126       }
127     else if(args[i] == "OUTPUT_FILE")
128       {
129       doing_command = false;
130       if(++i < args.size())
131         {
132         output_file = args[i];
133         }
134       else
135         {
136         this->SetError(" called with no value for OUTPUT_FILE.");
137         return false;
138         }
139       }
140     else if(args[i] == "ERROR_FILE")
141       {
142       doing_command = false;
143       if(++i < args.size())
144         {
145         error_file = args[i];
146         }
147       else
148         {
149         this->SetError(" called with no value for ERROR_FILE.");
150         return false;
151         }
152       }
153     else if(args[i] == "TIMEOUT")
154       {
155       doing_command = false;
156       if(++i < args.size())
157         {
158         timeout_string = args[i];
159         }
160       else
161         {
162         this->SetError(" called with no value for TIMEOUT.");
163         return false;
164         }
165       }
166     else if(args[i] == "OUTPUT_QUIET")
167       {
168       doing_command = false;
169       output_quiet = true;
170       }
171     else if(args[i] == "ERROR_QUIET")
172       {
173       doing_command = false;
174       error_quiet = true;
175       }
176     else if(args[i] == "OUTPUT_STRIP_TRAILING_WHITESPACE")
177       {
178       doing_command = false;
179       output_strip_trailing_whitespace = true;
180       }
181     else if(args[i] == "ERROR_STRIP_TRAILING_WHITESPACE")
182       {
183       doing_command = false;
184       error_strip_trailing_whitespace = true;
185       }
186     else if(doing_command)
187       {
188       cmds[command_index].push_back(args[i].c_str());
189       }
190     else
191       {
192       cmOStringStream e;
193       e << " given unknown argument \"" << args[i] << "\".";
194       this->SetError(e.str().c_str());
195       return false;
196       }
197     }
198
199   if ( !this->Makefile->CanIWriteThisFile(output_file.c_str()) )
200     {
201     std::string e = "attempted to output into a file: " + output_file
202       + " into a source directory.";
203     this->SetError(e.c_str());
204     cmSystemTools::SetFatalErrorOccured();
205     return false;
206     }
207
208   // Check for commands given.
209   if(cmds.empty())
210     {
211     this->SetError(" called with no COMMAND argument.");
212     return false;
213     }
214   for(unsigned int i=0; i < cmds.size(); ++i)
215     {
216     if(cmds[i].empty())
217       {
218       this->SetError(" given COMMAND argument with no value.");
219       return false;
220       }
221     else
222       {
223       // Add the null terminating pointer to the command argument list.
224       cmds[i].push_back(0);
225       }
226     }
227
228   // Parse the timeout string.
229   double timeout = -1;
230   if(!timeout_string.empty())
231     {
232     if(sscanf(timeout_string.c_str(), "%lg", &timeout) != 1)
233       {
234       this->SetError(" called with TIMEOUT value that could not be parsed.");
235       return false;
236       }
237     }
238
239   // Create a process instance.
240   cmsysProcess* cp = cmsysProcess_New();
241
242   // Set the command sequence.
243   for(unsigned int i=0; i < cmds.size(); ++i)
244     {
245     cmsysProcess_AddCommand(cp, &*cmds[i].begin());
246     }
247
248   // Set the process working directory.
249   if(!working_directory.empty())
250     {
251     cmsysProcess_SetWorkingDirectory(cp, working_directory.c_str());
252     }
253
254   // Always hide the process window.
255   cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
256
257   // Check the output variables.
258   bool merge_output = (output_variable == error_variable);
259   if(error_variable.empty() && !error_quiet)
260     {
261     cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
262     }
263   if(!input_file.empty())
264     {
265     cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, input_file.c_str());
266     }
267   if(!output_file.empty())
268     {
269     cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
270                              output_file.c_str());
271     }
272   if(!error_file.empty())
273     {
274     cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
275                              error_file.c_str());
276     }
277
278   // Set the timeout if any.
279   if(timeout >= 0)
280     {
281     cmsysProcess_SetTimeout(cp, timeout);
282     }
283
284   // Start the process.
285   cmsysProcess_Execute(cp);
286
287   // Read the process output.
288   std::vector<char> tempOutput;
289   std::vector<char> tempError;
290   int length;
291   char* data;
292   int p;
293   while((p = cmsysProcess_WaitForData(cp, &data, &length, 0), p))
294     {
295     // Put the output in the right place.
296     if((p == cmsysProcess_Pipe_STDOUT && !output_quiet) ||
297        (p == cmsysProcess_Pipe_STDERR && !error_quiet && merge_output))
298       {
299       if(output_variable.empty())
300         {
301         cmSystemTools::Stdout(data, length);
302         }
303       else
304         {
305         cmExecuteProcessCommandAppend(tempOutput, data, length);
306         }
307       }
308     else if(p == cmsysProcess_Pipe_STDERR && !error_quiet)
309       {
310       if(!error_variable.empty())
311         {
312         cmExecuteProcessCommandAppend(tempError, data, length);
313         }
314       }
315     }
316
317   // All output has been read.  Wait for the process to exit.
318   cmsysProcess_WaitForExit(cp, 0);
319
320   // Fix the text in the output strings.
321   cmExecuteProcessCommandFixText(tempOutput,
322                                  output_strip_trailing_whitespace);
323   cmExecuteProcessCommandFixText(tempError,
324                                  error_strip_trailing_whitespace);
325
326   // Store the output obtained.
327   if(!output_variable.empty() && tempOutput.size())
328     {
329     this->Makefile->AddDefinition(output_variable.c_str(),
330                                   &*tempOutput.begin());
331     }
332   if(!merge_output && !error_variable.empty() && tempError.size())
333     {
334     this->Makefile->AddDefinition(error_variable.c_str(),
335                                   &*tempError.begin());
336     }
337
338   // Store the result of running the process.
339   if(!result_variable.empty())
340     {
341     switch(cmsysProcess_GetState(cp))
342       {
343       case cmsysProcess_State_Exited:
344         {
345         int v = cmsysProcess_GetExitValue(cp);
346         char buf[100];
347         sprintf(buf, "%d", v);
348         this->Makefile->AddDefinition(result_variable.c_str(), buf);
349         }
350         break;
351       case cmsysProcess_State_Exception:
352         this->Makefile->AddDefinition(result_variable.c_str(),
353                                   cmsysProcess_GetExceptionString(cp));
354         break;
355       case cmsysProcess_State_Error:
356         this->Makefile->AddDefinition(result_variable.c_str(),
357                                   cmsysProcess_GetErrorString(cp));
358         break;
359       case cmsysProcess_State_Expired:
360         this->Makefile->AddDefinition(result_variable.c_str(),
361                                   "Process terminated due to timeout");
362         break;
363       }
364     }
365
366   // Delete the process instance.
367   cmsysProcess_Delete(cp);
368
369   return true;
370 }
371
372 //----------------------------------------------------------------------------
373 void cmExecuteProcessCommandFixText(std::vector<char>& output,
374                                     bool strip_trailing_whitespace)
375 {
376   // Remove \0 characters and the \r part of \r\n pairs.
377   unsigned int in_index = 0;
378   unsigned int out_index = 0;
379   while(in_index < output.size())
380     {
381     char c = output[in_index++];
382     if((c != '\r' || !(in_index < output.size() && output[in_index] == '\n'))
383        && c != '\0')
384       {
385       output[out_index++] = c;
386       }
387     }
388
389   // Remove trailing whitespace if requested.
390   if(strip_trailing_whitespace)
391     {
392     while(out_index > 0 &&
393           cmExecuteProcessCommandIsWhitespace(output[out_index-1]))
394       {
395       --out_index;
396       }
397     }
398
399   // Shrink the vector to the size needed.
400   output.resize(out_index);
401
402   // Put a terminator on the text string.
403   output.push_back('\0');
404 }
405
406 //----------------------------------------------------------------------------
407 void cmExecuteProcessCommandAppend(std::vector<char>& output,
408                                    const char* data, int length)
409 {
410 #if defined(__APPLE__)
411   // HACK on Apple to work around bug with inserting at the
412   // end of an empty vector.  This resulted in random failures
413   // that were hard to reproduce.
414   if(output.empty() && length > 0)
415     {
416     output.push_back(data[0]);
417     ++data;
418     --length;
419     }
420 #endif
421   output.insert(output.end(), data, data+length);
422 }