1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestBuildAndTestHandler.cxx,v $
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"
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;
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()
54 cmSystemTools::ResetErrorOccuredFlag();
55 int retv
= this->RunCMakeAndTest(&this->Output
);
56 cmSystemTools::ResetErrorOccuredFlag();
60 //----------------------------------------------------------------------
61 int cmCTestBuildAndTestHandler::RunCMake(std::string
* outstring
,
62 cmOStringStream
&out
, std::string
&cmakeOutString
, std::string
&cwd
,
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();
84 config
= CMAKE_INTDIR
;
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());
107 *outstring
= out
.str();
111 cmCTestLog(this->CTest
, ERROR_MESSAGE
, out
.str() << std::endl
);
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());
126 *outstring
= out
.str();
130 cmCTestLog(this->CTest
, ERROR_MESSAGE
, out
.str() << std::endl
);
135 out
<< "======== CMake output ======\n";
136 out
<< cmakeOutString
;
137 out
<< "======== End CMake output ======\n";
141 //----------------------------------------------------------------------
142 void CMakeMessageCallback(const char* m
, const char*, bool&, void* s
)
144 std::string
* out
= (std::string
*)s
;
149 void CMakeProgressCallback(const char*msg
, float , void * s
)
151 std::string
* out
= (std::string
*)s
;
156 //----------------------------------------------------------------------
157 void CMakeStdoutCallback(const char* m
, int len
, void* s
)
159 std::string
* out
= (std::string
*)s
;
163 //----------------------------------------------------------------------
164 int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string
* outstring
)
167 std::string cmakeOutString
;
168 cmSystemTools::SetErrorCallback(CMakeMessageCallback
, &cmakeOutString
);
169 cmSystemTools::SetStdoutCallback(CMakeStdoutCallback
, &cmakeOutString
);
172 // if the generator and make program are not specified then it is an error
173 if (!this->BuildGenerator
.size() || !this->BuildMakeProgram
.size())
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. ";
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
;
194 cmCTestTestHandler::FindExecutable(this->CTest
,
195 this->ConfigSample
.c_str(),
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
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());
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
))
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;
248 remainingTime
= this->Timeout
- cmSystemTools::GetTime() + clock_start
;
249 if (remainingTime
<= 0)
253 *outstring
= "--build-and-test timeout exceeded. ";
259 const char* config
= 0;
260 if ( this->CTest
->GetConfigType().size() > 0 )
262 config
= this->CTest
->GetConfigType().c_str();
267 config
= CMAKE_INTDIR
;
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(),
280 false, remainingTime
);
282 // if the build failed then return
287 *outstring
= out
.str();
294 *outstring
= out
.str();
297 // if no test was specified then we are done
298 if (!this->TestCommand
.size())
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
;
313 tempPath
+= this->TestCommand
;
314 extraPaths
.push_back(tempPath
);
316 std::vector
<std::string
> failed
;
318 cmCTestTestHandler::FindExecutable(this->CTest
,
319 this->TestCommand
.c_str(),
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";
336 *outstring
= out
.str();
340 cmCTestLog(this->CTest
, ERROR_MESSAGE
, out
.str());
342 // return to the original directory
343 cmSystemTools::ChangeDirectory(cwd
.c_str());
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);
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
] << "\"";
369 // how much time is remaining
370 double remainingTime
= 0;
373 remainingTime
= this->Timeout
- cmSystemTools::GetTime() + clock_start
;
374 if (remainingTime
<= 0)
378 *outstring
= "--build-and-test timeout exceeded. ";
384 int runTestRes
= this->CTest
->RunTest(testCommand
, &outs
, &retval
, 0,
387 if(runTestRes
!= cmsysProcess_State_Exited
|| retval
!= 0)
389 out
<< "Test command failed: " << testCommand
[0] << "\n";
396 *outstring
= out
.str();
400 cmCTestLog(this->CTest
, OUTPUT
, out
.str() << std::endl
);
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())
416 this->SourceDir
= allArgs
[idx
];
418 this->BinaryDir
= allArgs
[idx
];
419 // dir must exist before CollapseFullPath is called
420 cmSystemTools::MakeDirectory(this->BinaryDir
.c_str());
422 = cmSystemTools::CollapseFullPath(this->BinaryDir
.c_str());
424 = cmSystemTools::CollapseFullPath(this->SourceDir
.c_str());
428 cmCTestLog(this->CTest
, ERROR_MESSAGE
,
429 "--build-and-test must have source and binary dir" << std::endl
);
433 if(currentArg
.find("--build-target",0) == 0 && idx
< allArgs
.size() - 1)
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)
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)
454 this->ExecutableDirectory
= allArgs
[idx
];
456 if(currentArg
.find("--test-timeout",0) == 0 && idx
< allArgs
.size() - 1)
459 this->Timeout
= atof(allArgs
[idx
].c_str());
461 if(currentArg
.find("--build-generator",0) == 0 && idx
< allArgs
.size() - 1)
464 this->BuildGenerator
= allArgs
[idx
];
466 if(currentArg
.find("--build-project",0) == 0 && idx
< allArgs
.size() - 1)
469 this->BuildProject
= allArgs
[idx
];
471 if(currentArg
.find("--build-makeprogram",0) == 0 &&
472 idx
< allArgs
.size() - 1)
475 this->BuildMakeProgram
= allArgs
[idx
];
477 if(currentArg
.find("--build-config-sample",0) == 0 &&
478 idx
< allArgs
.size() - 1)
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)
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"))
506 if(currentArg
.find("--test-command",0) == 0 && idx
< allArgs
.size() - 1)
509 this->TestCommand
= allArgs
[idx
];
510 while(idx
+1 < allArgs
.size())
513 this->TestCommandArgs
.push_back(allArgs
[idx
]);