1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmExecuteProcessCommand.cxx,v $
6 Date: $Date: 2009-09-11 12:18:00 $
7 Version: $Revision: 1.11 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmExecuteProcessCommand.h"
18 #include "cmSystemTools.h"
20 #include <cmsys/Process.h>
22 #include <ctype.h> /* isspace */
24 static bool cmExecuteProcessCommandIsWhitespace(char c
)
26 return (isspace((int)c
) || c
== '\n' || c
== '\r');
29 void cmExecuteProcessCommandFixText(std::vector
<char>& output
,
30 bool strip_trailing_whitespace
);
31 void cmExecuteProcessCommandAppend(std::vector
<char>& output
,
32 const char* data
, int length
);
34 // cmExecuteProcessCommand
35 bool cmExecuteProcessCommand
36 ::InitialPass(std::vector
<std::string
> const& args
, cmExecutionStatus
&)
40 this->SetError("called with incorrect number of arguments");
43 std::vector
< std::vector
<const char*> > cmds
;
44 std::string arguments
;
45 bool doing_command
= false;
46 size_t command_index
= 0;
47 bool output_quiet
= false;
48 bool error_quiet
= false;
49 bool output_strip_trailing_whitespace
= false;
50 bool error_strip_trailing_whitespace
= false;
51 std::string timeout_string
;
52 std::string input_file
;
53 std::string output_file
;
54 std::string error_file
;
55 std::string output_variable
;
56 std::string error_variable
;
57 std::string result_variable
;
58 std::string working_directory
;
59 for(size_t i
=0; i
< args
.size(); ++i
)
61 if(args
[i
] == "COMMAND")
64 command_index
= cmds
.size();
65 cmds
.push_back(std::vector
<const char*>());
67 else if(args
[i
] == "OUTPUT_VARIABLE")
69 doing_command
= false;
72 output_variable
= args
[i
];
76 this->SetError(" called with no value for OUTPUT_VARIABLE.");
80 else if(args
[i
] == "ERROR_VARIABLE")
82 doing_command
= false;
85 error_variable
= args
[i
];
89 this->SetError(" called with no value for ERROR_VARIABLE.");
93 else if(args
[i
] == "RESULT_VARIABLE")
95 doing_command
= false;
98 result_variable
= args
[i
];
102 this->SetError(" called with no value for RESULT_VARIABLE.");
106 else if(args
[i
] == "WORKING_DIRECTORY")
108 doing_command
= false;
109 if(++i
< args
.size())
111 working_directory
= args
[i
];
115 this->SetError(" called with no value for WORKING_DIRECTORY.");
119 else if(args
[i
] == "INPUT_FILE")
121 doing_command
= false;
122 if(++i
< args
.size())
124 input_file
= args
[i
];
128 this->SetError(" called with no value for INPUT_FILE.");
132 else if(args
[i
] == "OUTPUT_FILE")
134 doing_command
= false;
135 if(++i
< args
.size())
137 output_file
= args
[i
];
141 this->SetError(" called with no value for OUTPUT_FILE.");
145 else if(args
[i
] == "ERROR_FILE")
147 doing_command
= false;
148 if(++i
< args
.size())
150 error_file
= args
[i
];
154 this->SetError(" called with no value for ERROR_FILE.");
158 else if(args
[i
] == "TIMEOUT")
160 doing_command
= false;
161 if(++i
< args
.size())
163 timeout_string
= args
[i
];
167 this->SetError(" called with no value for TIMEOUT.");
171 else if(args
[i
] == "OUTPUT_QUIET")
173 doing_command
= false;
176 else if(args
[i
] == "ERROR_QUIET")
178 doing_command
= false;
181 else if(args
[i
] == "OUTPUT_STRIP_TRAILING_WHITESPACE")
183 doing_command
= false;
184 output_strip_trailing_whitespace
= true;
186 else if(args
[i
] == "ERROR_STRIP_TRAILING_WHITESPACE")
188 doing_command
= false;
189 error_strip_trailing_whitespace
= true;
191 else if(doing_command
)
193 cmds
[command_index
].push_back(args
[i
].c_str());
198 e
<< " given unknown argument \"" << args
[i
] << "\".";
199 this->SetError(e
.str().c_str());
204 if ( !this->Makefile
->CanIWriteThisFile(output_file
.c_str()) )
206 std::string e
= "attempted to output into a file: " + output_file
207 + " into a source directory.";
208 this->SetError(e
.c_str());
209 cmSystemTools::SetFatalErrorOccured();
213 // Check for commands given.
216 this->SetError(" called with no COMMAND argument.");
219 for(unsigned int i
=0; i
< cmds
.size(); ++i
)
223 this->SetError(" given COMMAND argument with no value.");
228 // Add the null terminating pointer to the command argument list.
229 cmds
[i
].push_back(0);
233 // Parse the timeout string.
235 if(!timeout_string
.empty())
237 if(sscanf(timeout_string
.c_str(), "%lg", &timeout
) != 1)
239 this->SetError(" called with TIMEOUT value that could not be parsed.");
244 // Create a process instance.
245 cmsysProcess
* cp
= cmsysProcess_New();
247 // Set the command sequence.
248 for(unsigned int i
=0; i
< cmds
.size(); ++i
)
250 cmsysProcess_AddCommand(cp
, &*cmds
[i
].begin());
253 // Set the process working directory.
254 if(!working_directory
.empty())
256 cmsysProcess_SetWorkingDirectory(cp
, working_directory
.c_str());
259 // Always hide the process window.
260 cmsysProcess_SetOption(cp
, cmsysProcess_Option_HideWindow
, 1);
262 // Check the output variables.
263 bool merge_output
= (output_variable
== error_variable
);
264 if(error_variable
.empty() && !error_quiet
)
266 cmsysProcess_SetPipeShared(cp
, cmsysProcess_Pipe_STDERR
, 1);
268 if(!input_file
.empty())
270 cmsysProcess_SetPipeFile(cp
, cmsysProcess_Pipe_STDIN
, input_file
.c_str());
272 if(!output_file
.empty())
274 cmsysProcess_SetPipeFile(cp
, cmsysProcess_Pipe_STDOUT
,
275 output_file
.c_str());
277 if(!error_file
.empty())
279 cmsysProcess_SetPipeFile(cp
, cmsysProcess_Pipe_STDERR
,
283 // Set the timeout if any.
286 cmsysProcess_SetTimeout(cp
, timeout
);
289 // Start the process.
290 cmsysProcess_Execute(cp
);
292 // Read the process output.
293 std::vector
<char> tempOutput
;
294 std::vector
<char> tempError
;
298 while((p
= cmsysProcess_WaitForData(cp
, &data
, &length
, 0), p
))
300 // Put the output in the right place.
301 if((p
== cmsysProcess_Pipe_STDOUT
&& !output_quiet
) ||
302 (p
== cmsysProcess_Pipe_STDERR
&& !error_quiet
&& merge_output
))
304 if(output_variable
.empty())
306 cmSystemTools::Stdout(data
, length
);
310 cmExecuteProcessCommandAppend(tempOutput
, data
, length
);
313 else if(p
== cmsysProcess_Pipe_STDERR
&& !error_quiet
)
315 if(!error_variable
.empty())
317 cmExecuteProcessCommandAppend(tempError
, data
, length
);
322 // All output has been read. Wait for the process to exit.
323 cmsysProcess_WaitForExit(cp
, 0);
325 // Fix the text in the output strings.
326 cmExecuteProcessCommandFixText(tempOutput
,
327 output_strip_trailing_whitespace
);
328 cmExecuteProcessCommandFixText(tempError
,
329 error_strip_trailing_whitespace
);
331 // Store the output obtained.
332 if(!output_variable
.empty() && tempOutput
.size())
334 this->Makefile
->AddDefinition(output_variable
.c_str(),
335 &*tempOutput
.begin());
337 if(!merge_output
&& !error_variable
.empty() && tempError
.size())
339 this->Makefile
->AddDefinition(error_variable
.c_str(),
340 &*tempError
.begin());
343 // Store the result of running the process.
344 if(!result_variable
.empty())
346 switch(cmsysProcess_GetState(cp
))
348 case cmsysProcess_State_Exited
:
350 int v
= cmsysProcess_GetExitValue(cp
);
352 sprintf(buf
, "%d", v
);
353 this->Makefile
->AddDefinition(result_variable
.c_str(), buf
);
356 case cmsysProcess_State_Exception
:
357 this->Makefile
->AddDefinition(result_variable
.c_str(),
358 cmsysProcess_GetExceptionString(cp
));
360 case cmsysProcess_State_Error
:
361 this->Makefile
->AddDefinition(result_variable
.c_str(),
362 cmsysProcess_GetErrorString(cp
));
364 case cmsysProcess_State_Expired
:
365 this->Makefile
->AddDefinition(result_variable
.c_str(),
366 "Process terminated due to timeout");
371 // Delete the process instance.
372 cmsysProcess_Delete(cp
);
377 //----------------------------------------------------------------------------
378 void cmExecuteProcessCommandFixText(std::vector
<char>& output
,
379 bool strip_trailing_whitespace
)
381 // Remove \0 characters and the \r part of \r\n pairs.
382 unsigned int in_index
= 0;
383 unsigned int out_index
= 0;
384 while(in_index
< output
.size())
386 char c
= output
[in_index
++];
387 if((c
!= '\r' || !(in_index
< output
.size() && output
[in_index
] == '\n'))
390 output
[out_index
++] = c
;
394 // Remove trailing whitespace if requested.
395 if(strip_trailing_whitespace
)
397 while(out_index
> 0 &&
398 cmExecuteProcessCommandIsWhitespace(output
[out_index
-1]))
404 // Shrink the vector to the size needed.
405 output
.resize(out_index
);
407 // Put a terminator on the text string.
408 output
.push_back('\0');
411 //----------------------------------------------------------------------------
412 void cmExecuteProcessCommandAppend(std::vector
<char>& output
,
413 const char* data
, int length
)
415 #if defined(__APPLE__)
416 // HACK on Apple to work around bug with inserting at the
417 // end of an empty vector. This resulted in random failures
418 // that were hard to reproduce.
419 if(output
.empty() && length
> 0)
421 output
.push_back(data
[0]);
426 output
.insert(output
.end(), data
, data
+length
);