Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / CTest / cmCTestBuildAndTestHandler.cxx
blobcf5c01033f444cb9716adfae25b650df89942a57
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestBuildAndTestHandler.cxx,v $
5 Language: C++
6 Date: $Date: 2008-12-18 17:27:59 $
7 Version: $Revision: 1.24 $
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 =========================================================================*/
18 #include "cmCTestBuildAndTestHandler.h"
20 #include "cmSystemTools.h"
21 #include "cmCTest.h"
22 #include "cmake.h"
23 #include "cmGlobalGenerator.h"
24 #include <cmsys/Process.h>
25 #include "cmCTestTestHandler.h"
27 //----------------------------------------------------------------------
28 cmCTestBuildAndTestHandler::cmCTestBuildAndTestHandler()
30 this->BuildTwoConfig = false;
31 this->BuildNoClean = false;
32 this->BuildNoCMake = false;
33 this->Timeout = 0;
36 //----------------------------------------------------------------------
37 void cmCTestBuildAndTestHandler::Initialize()
39 this->BuildTargets.erase(
40 this->BuildTargets.begin(), this->BuildTargets.end());
41 this->Superclass::Initialize();
44 //----------------------------------------------------------------------
45 const char* cmCTestBuildAndTestHandler::GetOutput()
47 return this->Output.c_str();
49 //----------------------------------------------------------------------
50 int cmCTestBuildAndTestHandler::ProcessHandler()
52 this->Output = "";
53 std::string output;
54 cmSystemTools::ResetErrorOccuredFlag();
55 int retv = this->RunCMakeAndTest(&this->Output);
56 cmSystemTools::ResetErrorOccuredFlag();
57 return retv;
60 //----------------------------------------------------------------------
61 int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring,
62 cmOStringStream &out, std::string &cmakeOutString, std::string &cwd,
63 cmake *cm)
65 unsigned int k;
66 std::vector<std::string> args;
67 args.push_back(this->CTest->GetCMakeExecutable());
68 args.push_back(this->SourceDir);
69 if(this->BuildGenerator.size())
71 std::string generator = "-G";
72 generator += this->BuildGenerator;
73 args.push_back(generator);
76 const char* config = 0;
77 if ( this->CTest->GetConfigType().size() > 0 )
79 config = this->CTest->GetConfigType().c_str();
81 #ifdef CMAKE_INTDIR
82 if(!config)
84 config = CMAKE_INTDIR;
86 #endif
88 if ( config )
90 std::string btype
91 = "-DCMAKE_BUILD_TYPE:STRING=" + std::string(config);
92 args.push_back(btype);
95 for(k=0; k < this->BuildOptions.size(); ++k)
97 args.push_back(this->BuildOptions[k]);
99 if (cm->Run(args) != 0)
101 out << "Error: cmake execution failed\n";
102 out << cmakeOutString << "\n";
103 // return to the original directory
104 cmSystemTools::ChangeDirectory(cwd.c_str());
105 if(outstring)
107 *outstring = out.str();
109 else
111 cmCTestLog(this->CTest, ERROR_MESSAGE, out.str() << std::endl);
113 return 1;
115 // do another config?
116 if(this->BuildTwoConfig)
118 if (cm->Run(args) != 0)
120 out << "Error: cmake execution failed\n";
121 out << cmakeOutString << "\n";
122 // return to the original directory
123 cmSystemTools::ChangeDirectory(cwd.c_str());
124 if(outstring)
126 *outstring = out.str();
128 else
130 cmCTestLog(this->CTest, ERROR_MESSAGE, out.str() << std::endl);
132 return 1;
135 out << "======== CMake output ======\n";
136 out << cmakeOutString;
137 out << "======== End CMake output ======\n";
138 return 0;
141 //----------------------------------------------------------------------
142 void CMakeMessageCallback(const char* m, const char*, bool&, void* s)
144 std::string* out = (std::string*)s;
145 *out += m;
146 *out += "\n";
149 void CMakeProgressCallback(const char*msg, float , void * s)
151 std::string* out = (std::string*)s;
152 *out += msg;
153 *out += "\n";
156 //----------------------------------------------------------------------
157 void CMakeStdoutCallback(const char* m, int len, void* s)
159 std::string* out = (std::string*)s;
160 out->append(m, len);
163 //----------------------------------------------------------------------
164 int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
166 unsigned int k;
167 std::string cmakeOutString;
168 cmSystemTools::SetErrorCallback(CMakeMessageCallback, &cmakeOutString);
169 cmSystemTools::SetStdoutCallback(CMakeStdoutCallback, &cmakeOutString);
170 cmOStringStream out;
172 // if the generator and make program are not specified then it is an error
173 if (!this->BuildGenerator.size() || !this->BuildMakeProgram.size())
175 if(outstring)
177 *outstring =
178 "--build-and-test requires that both the generator and makeprogram "
179 "be provided using the --build-generator and --build-makeprogram "
180 "command line options. ";
182 return 1;
185 if ( this->CTest->GetConfigType().size() == 0 &&
186 this->ConfigSample.size())
188 // use the config sample to set the ConfigType
189 std::string fullPath;
190 std::string resultingConfig;
191 std::vector<std::string> extraPaths;
192 std::vector<std::string> failed;
193 fullPath =
194 cmCTestTestHandler::FindExecutable(this->CTest,
195 this->ConfigSample.c_str(),
196 resultingConfig,
197 extraPaths,
198 failed);
199 if (fullPath.size() && resultingConfig.size())
201 this->CTest->SetConfigType(resultingConfig.c_str());
203 out << "Using config sample with results: "
204 << fullPath << " and " << resultingConfig << std::endl;
207 // we need to honor the timeout specified, the timeout include cmake, build
208 // and test time
209 double clock_start = cmSystemTools::GetTime();
211 // make sure the binary dir is there
212 std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
213 out << "Internal cmake changing into directory: "
214 << this->BinaryDir << std::endl;
215 if (!cmSystemTools::FileIsDirectory(this->BinaryDir.c_str()))
217 cmSystemTools::MakeDirectory(this->BinaryDir.c_str());
219 cmSystemTools::ChangeDirectory(this->BinaryDir.c_str());
221 // should we cmake?
222 cmake cm;
223 cm.SetProgressCallback(CMakeProgressCallback, &cmakeOutString);
224 cm.SetGlobalGenerator(cm.CreateGlobalGenerator(
225 this->BuildGenerator.c_str()));
227 if(!this->BuildNoCMake)
229 // do the cmake step, no timeout here since it is not a sub process
230 if (this->RunCMake(outstring,out,cmakeOutString,cwd,&cm))
232 return 1;
236 // do the build
237 std::vector<std::string>::iterator tarIt;
238 if ( this->BuildTargets.size() == 0 )
240 this->BuildTargets.push_back("");
242 for ( tarIt = this->BuildTargets.begin();
243 tarIt != this->BuildTargets.end(); ++ tarIt )
245 double remainingTime = 0;
246 if (this->Timeout)
248 remainingTime = this->Timeout - cmSystemTools::GetTime() + clock_start;
249 if (remainingTime <= 0)
251 if(outstring)
253 *outstring = "--build-and-test timeout exceeded. ";
255 return 1;
258 std::string output;
259 const char* config = 0;
260 if ( this->CTest->GetConfigType().size() > 0 )
262 config = this->CTest->GetConfigType().c_str();
264 #ifdef CMAKE_INTDIR
265 if(!config)
267 config = CMAKE_INTDIR;
269 #endif
270 if(!config)
272 config = "Debug";
274 int retVal = cm.GetGlobalGenerator()->Build(
275 this->SourceDir.c_str(), this->BinaryDir.c_str(),
276 this->BuildProject.c_str(), tarIt->c_str(),
277 &output, this->BuildMakeProgram.c_str(),
278 config,
279 !this->BuildNoClean,
280 false, remainingTime);
281 out << output;
282 // if the build failed then return
283 if (retVal)
285 if(outstring)
287 *outstring = out.str();
289 return 1;
292 if(outstring)
294 *outstring = out.str();
297 // if no test was specified then we are done
298 if (!this->TestCommand.size())
300 return 0;
303 // now run the compiled test if we can find it
304 // store the final location in fullPath
305 std::string fullPath;
306 std::string resultingConfig;
307 std::vector<std::string> extraPaths;
308 // if this->ExecutableDirectory is set try that as well
309 if (this->ExecutableDirectory.size())
311 std::string tempPath = this->ExecutableDirectory;
312 tempPath += "/";
313 tempPath += this->TestCommand;
314 extraPaths.push_back(tempPath);
316 std::vector<std::string> failed;
317 fullPath =
318 cmCTestTestHandler::FindExecutable(this->CTest,
319 this->TestCommand.c_str(),
320 resultingConfig,
321 extraPaths,
322 failed);
324 if(!cmSystemTools::FileExists(fullPath.c_str()))
326 out << "Could not find path to executable, perhaps it was not built: "
327 << this->TestCommand << "\n";
328 out << "tried to find it in these places:\n";
329 out << fullPath.c_str() << "\n";
330 for(unsigned int i=0; i < failed.size(); ++i)
332 out << failed[i] << "\n";
334 if(outstring)
336 *outstring = out.str();
338 else
340 cmCTestLog(this->CTest, ERROR_MESSAGE, out.str());
342 // return to the original directory
343 cmSystemTools::ChangeDirectory(cwd.c_str());
344 return 1;
347 std::vector<const char*> testCommand;
348 testCommand.push_back(fullPath.c_str());
349 for(k=0; k < this->TestCommandArgs.size(); ++k)
351 testCommand.push_back(this->TestCommandArgs[k].c_str());
353 testCommand.push_back(0);
354 std::string outs;
355 int retval = 0;
356 // run the test from the this->BuildRunDir if set
357 if(this->BuildRunDir.size())
359 out << "Run test in directory: " << this->BuildRunDir << "\n";
360 cmSystemTools::ChangeDirectory(this->BuildRunDir.c_str());
362 out << "Running test command: \"" << fullPath << "\"";
363 for(k=0; k < this->TestCommandArgs.size(); ++k)
365 out << " \"" << this->TestCommandArgs[k] << "\"";
367 out << "\n";
369 // how much time is remaining
370 double remainingTime = 0;
371 if (this->Timeout)
373 remainingTime = this->Timeout - cmSystemTools::GetTime() + clock_start;
374 if (remainingTime <= 0)
376 if(outstring)
378 *outstring = "--build-and-test timeout exceeded. ";
380 return 1;
384 int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, 0,
385 remainingTime, 0);
387 if(runTestRes != cmsysProcess_State_Exited || retval != 0)
389 out << "Test command failed: " << testCommand[0] << "\n";
390 retval = 1;
393 out << outs << "\n";
394 if(outstring)
396 *outstring = out.str();
398 else
400 cmCTestLog(this->CTest, OUTPUT, out.str() << std::endl);
402 return retval;
405 //----------------------------------------------------------------------
406 int cmCTestBuildAndTestHandler::ProcessCommandLineArguments(
407 const std::string& currentArg, size_t& idx,
408 const std::vector<std::string>& allArgs)
410 // --build-and-test options
411 if(currentArg.find("--build-and-test",0) == 0 && idx < allArgs.size() - 1)
413 if(idx+2 < allArgs.size())
415 idx++;
416 this->SourceDir = allArgs[idx];
417 idx++;
418 this->BinaryDir = allArgs[idx];
419 // dir must exist before CollapseFullPath is called
420 cmSystemTools::MakeDirectory(this->BinaryDir.c_str());
421 this->BinaryDir
422 = cmSystemTools::CollapseFullPath(this->BinaryDir.c_str());
423 this->SourceDir
424 = cmSystemTools::CollapseFullPath(this->SourceDir.c_str());
426 else
428 cmCTestLog(this->CTest, ERROR_MESSAGE,
429 "--build-and-test must have source and binary dir" << std::endl);
430 return 0;
433 if(currentArg.find("--build-target",0) == 0 && idx < allArgs.size() - 1)
435 idx++;
436 this->BuildTargets.push_back(allArgs[idx]);
438 if(currentArg.find("--build-nocmake",0) == 0)
440 this->BuildNoCMake = true;
442 if(currentArg.find("--build-run-dir",0) == 0 && idx < allArgs.size() - 1)
444 idx++;
445 this->BuildRunDir = allArgs[idx];
447 if(currentArg.find("--build-two-config",0) == 0)
449 this->BuildTwoConfig = true;
451 if(currentArg.find("--build-exe-dir",0) == 0 && idx < allArgs.size() - 1)
453 idx++;
454 this->ExecutableDirectory = allArgs[idx];
456 if(currentArg.find("--test-timeout",0) == 0 && idx < allArgs.size() - 1)
458 idx++;
459 this->Timeout = atof(allArgs[idx].c_str());
461 if(currentArg.find("--build-generator",0) == 0 && idx < allArgs.size() - 1)
463 idx++;
464 this->BuildGenerator = allArgs[idx];
466 if(currentArg.find("--build-project",0) == 0 && idx < allArgs.size() - 1)
468 idx++;
469 this->BuildProject = allArgs[idx];
471 if(currentArg.find("--build-makeprogram",0) == 0 &&
472 idx < allArgs.size() - 1)
474 idx++;
475 this->BuildMakeProgram = allArgs[idx];
477 if(currentArg.find("--build-config-sample",0) == 0 &&
478 idx < allArgs.size() - 1)
480 idx++;
481 this->ConfigSample = allArgs[idx];
483 if(currentArg.find("--build-noclean",0) == 0)
485 this->BuildNoClean = true;
487 if(currentArg.find("--build-options",0) == 0 && idx < allArgs.size() - 1)
489 ++idx;
490 bool done = false;
491 while(idx < allArgs.size() && !done)
493 this->BuildOptions.push_back(allArgs[idx]);
494 if(idx+1 < allArgs.size()
495 && (allArgs[idx+1] == "--build-target" ||
496 allArgs[idx+1] == "--test-command"))
498 done = true;
500 else
502 ++idx;
506 if(currentArg.find("--test-command",0) == 0 && idx < allArgs.size() - 1)
508 ++idx;
509 this->TestCommand = allArgs[idx];
510 while(idx+1 < allArgs.size())
512 ++idx;
513 this->TestCommandArgs.push_back(allArgs[idx]);
516 return 1;