cmCTestBuildAndTestHandler.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmCTestBuildAndTestHandler.h"
  11. #include "cmSystemTools.h"
  12. #include "cmCTest.h"
  13. #include "cmake.h"
  14. #include "cmGlobalGenerator.h"
  15. #include <cmsys/Process.h>
  16. #include "cmCTestTestHandler.h"
  17. #include "cmCacheManager.h"
  18. //----------------------------------------------------------------------
  19. cmCTestBuildAndTestHandler::cmCTestBuildAndTestHandler()
  20. {
  21. this->BuildTwoConfig = false;
  22. this->BuildNoClean = false;
  23. this->BuildNoCMake = false;
  24. this->Timeout = 0;
  25. }
  26. //----------------------------------------------------------------------
  27. void cmCTestBuildAndTestHandler::Initialize()
  28. {
  29. this->BuildTargets.erase(
  30. this->BuildTargets.begin(), this->BuildTargets.end());
  31. this->Superclass::Initialize();
  32. }
  33. //----------------------------------------------------------------------
  34. const char* cmCTestBuildAndTestHandler::GetOutput()
  35. {
  36. return this->Output.c_str();
  37. }
  38. //----------------------------------------------------------------------
  39. int cmCTestBuildAndTestHandler::ProcessHandler()
  40. {
  41. this->Output = "";
  42. std::string output;
  43. cmSystemTools::ResetErrorOccuredFlag();
  44. int retv = this->RunCMakeAndTest(&this->Output);
  45. cmSystemTools::ResetErrorOccuredFlag();
  46. return retv;
  47. }
  48. //----------------------------------------------------------------------
  49. int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring,
  50. cmOStringStream &out, std::string &cmakeOutString, std::string &cwd,
  51. cmake *cm)
  52. {
  53. unsigned int k;
  54. std::vector<std::string> args;
  55. args.push_back(cmSystemTools::GetCMakeCommand());
  56. args.push_back(this->SourceDir);
  57. if(this->BuildGenerator.size())
  58. {
  59. std::string generator = "-G";
  60. generator += this->BuildGenerator;
  61. args.push_back(generator);
  62. }
  63. if(this->BuildGeneratorToolset.size())
  64. {
  65. std::string toolset = "-T";
  66. toolset += this->BuildGeneratorToolset;
  67. args.push_back(toolset);
  68. }
  69. const char* config = 0;
  70. if ( this->CTest->GetConfigType().size() > 0 )
  71. {
  72. config = this->CTest->GetConfigType().c_str();
  73. }
  74. #ifdef CMAKE_INTDIR
  75. if(!config)
  76. {
  77. config = CMAKE_INTDIR;
  78. }
  79. #endif
  80. if ( config )
  81. {
  82. std::string btype
  83. = "-DCMAKE_BUILD_TYPE:STRING=" + std::string(config);
  84. args.push_back(btype);
  85. }
  86. for(k=0; k < this->BuildOptions.size(); ++k)
  87. {
  88. args.push_back(this->BuildOptions[k]);
  89. }
  90. if (cm->Run(args) != 0)
  91. {
  92. out << "Error: cmake execution failed\n";
  93. out << cmakeOutString << "\n";
  94. // return to the original directory
  95. cmSystemTools::ChangeDirectory(cwd.c_str());
  96. if(outstring)
  97. {
  98. *outstring = out.str();
  99. }
  100. else
  101. {
  102. cmCTestLog(this->CTest, ERROR_MESSAGE, out.str() << std::endl);
  103. }
  104. return 1;
  105. }
  106. // do another config?
  107. if(this->BuildTwoConfig)
  108. {
  109. if (cm->Run(args) != 0)
  110. {
  111. out << "Error: cmake execution failed\n";
  112. out << cmakeOutString << "\n";
  113. // return to the original directory
  114. cmSystemTools::ChangeDirectory(cwd.c_str());
  115. if(outstring)
  116. {
  117. *outstring = out.str();
  118. }
  119. else
  120. {
  121. cmCTestLog(this->CTest, ERROR_MESSAGE, out.str() << std::endl);
  122. }
  123. return 1;
  124. }
  125. }
  126. out << "======== CMake output ======\n";
  127. out << cmakeOutString;
  128. out << "======== End CMake output ======\n";
  129. return 0;
  130. }
  131. //----------------------------------------------------------------------
  132. void CMakeMessageCallback(const char* m, const char*, bool&, void* s)
  133. {
  134. std::string* out = (std::string*)s;
  135. *out += m;
  136. *out += "\n";
  137. }
  138. void CMakeProgressCallback(const char*msg, float , void * s)
  139. {
  140. std::string* out = (std::string*)s;
  141. *out += msg;
  142. *out += "\n";
  143. }
  144. //----------------------------------------------------------------------
  145. void CMakeOutputCallback(const char* m, size_t len, void* s)
  146. {
  147. std::string* out = (std::string*)s;
  148. out->append(m, len);
  149. }
  150. //----------------------------------------------------------------------
  151. class cmCTestBuildAndTestCaptureRAII
  152. {
  153. cmake& CM;
  154. public:
  155. cmCTestBuildAndTestCaptureRAII(cmake& cm, std::string& s): CM(cm)
  156. {
  157. cmSystemTools::SetMessageCallback(CMakeMessageCallback, &s);
  158. cmSystemTools::SetStdoutCallback(CMakeOutputCallback, &s);
  159. cmSystemTools::SetStderrCallback(CMakeOutputCallback, &s);
  160. this->CM.SetProgressCallback(CMakeProgressCallback, &s);
  161. }
  162. ~cmCTestBuildAndTestCaptureRAII()
  163. {
  164. this->CM.SetProgressCallback(0, 0);
  165. cmSystemTools::SetStderrCallback(0, 0);
  166. cmSystemTools::SetStdoutCallback(0, 0);
  167. cmSystemTools::SetMessageCallback(0, 0);
  168. }
  169. };
  170. //----------------------------------------------------------------------
  171. int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
  172. {
  173. // if the generator and make program are not specified then it is an error
  174. if (!this->BuildGenerator.size())
  175. {
  176. if(outstring)
  177. {
  178. *outstring =
  179. "--build-and-test requires that the generator "
  180. "be provided using the --build-generator "
  181. "command line option. ";
  182. }
  183. return 1;
  184. }
  185. cmake cm;
  186. std::string cmakeOutString;
  187. cmCTestBuildAndTestCaptureRAII captureRAII(cm, cmakeOutString);
  188. static_cast<void>(captureRAII);
  189. cmOStringStream out;
  190. if ( this->CTest->GetConfigType().size() == 0 &&
  191. this->ConfigSample.size())
  192. {
  193. // use the config sample to set the ConfigType
  194. std::string fullPath;
  195. std::string resultingConfig;
  196. std::vector<std::string> extraPaths;
  197. std::vector<std::string> failed;
  198. fullPath =
  199. cmCTestTestHandler::FindExecutable(this->CTest,
  200. this->ConfigSample.c_str(),
  201. resultingConfig,
  202. extraPaths,
  203. failed);
  204. if (fullPath.size() && resultingConfig.size())
  205. {
  206. this->CTest->SetConfigType(resultingConfig.c_str());
  207. }
  208. out << "Using config sample with results: "
  209. << fullPath << " and " << resultingConfig << std::endl;
  210. }
  211. // we need to honor the timeout specified, the timeout include cmake, build
  212. // and test time
  213. double clock_start = cmSystemTools::GetTime();
  214. // make sure the binary dir is there
  215. std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
  216. out << "Internal cmake changing into directory: "
  217. << this->BinaryDir << std::endl;
  218. if (!cmSystemTools::FileIsDirectory(this->BinaryDir.c_str()))
  219. {
  220. cmSystemTools::MakeDirectory(this->BinaryDir.c_str());
  221. }
  222. cmSystemTools::ChangeDirectory(this->BinaryDir.c_str());
  223. if(this->BuildNoCMake)
  224. {
  225. // Make the generator available for the Build call below.
  226. cm.SetGlobalGenerator(cm.CreateGlobalGenerator(
  227. this->BuildGenerator));
  228. cm.SetGeneratorToolset(this->BuildGeneratorToolset);
  229. // Load the cache to make CMAKE_MAKE_PROGRAM available.
  230. cm.GetCacheManager()->LoadCache(this->BinaryDir);
  231. }
  232. else
  233. {
  234. // do the cmake step, no timeout here since it is not a sub process
  235. if (this->RunCMake(outstring,out,cmakeOutString,cwd,&cm))
  236. {
  237. return 1;
  238. }
  239. }
  240. // do the build
  241. std::vector<std::string>::iterator tarIt;
  242. if ( this->BuildTargets.size() == 0 )
  243. {
  244. this->BuildTargets.push_back("");
  245. }
  246. for ( tarIt = this->BuildTargets.begin();
  247. tarIt != this->BuildTargets.end(); ++ tarIt )
  248. {
  249. double remainingTime = 0;
  250. if (this->Timeout > 0)
  251. {
  252. remainingTime = this->Timeout - cmSystemTools::GetTime() + clock_start;
  253. if (remainingTime <= 0)
  254. {
  255. if(outstring)
  256. {
  257. *outstring = "--build-and-test timeout exceeded. ";
  258. }
  259. return 1;
  260. }
  261. }
  262. std::string output;
  263. const char* config = 0;
  264. if ( this->CTest->GetConfigType().size() > 0 )
  265. {
  266. config = this->CTest->GetConfigType().c_str();
  267. }
  268. #ifdef CMAKE_INTDIR
  269. if(!config)
  270. {
  271. config = CMAKE_INTDIR;
  272. }
  273. #endif
  274. if(!config)
  275. {
  276. config = "Debug";
  277. }
  278. int retVal = cm.GetGlobalGenerator()->Build(
  279. this->SourceDir, this->BinaryDir,
  280. this->BuildProject, *tarIt,
  281. output, this->BuildMakeProgram,
  282. config,
  283. !this->BuildNoClean,
  284. false, remainingTime);
  285. out << output;
  286. // if the build failed then return
  287. if (retVal)
  288. {
  289. if(outstring)
  290. {
  291. *outstring = out.str();
  292. }
  293. return 1;
  294. }
  295. }
  296. if(outstring)
  297. {
  298. *outstring = out.str();
  299. }
  300. // if no test was specified then we are done
  301. if (!this->TestCommand.size())
  302. {
  303. return 0;
  304. }
  305. // now run the compiled test if we can find it
  306. // store the final location in fullPath
  307. std::string fullPath;
  308. std::string resultingConfig;
  309. std::vector<std::string> extraPaths;
  310. // if this->ExecutableDirectory is set try that as well
  311. if (this->ExecutableDirectory.size())
  312. {
  313. std::string tempPath = this->ExecutableDirectory;
  314. tempPath += "/";
  315. tempPath += this->TestCommand;
  316. extraPaths.push_back(tempPath);
  317. }
  318. std::vector<std::string> failed;
  319. fullPath =
  320. cmCTestTestHandler::FindExecutable(this->CTest,
  321. this->TestCommand.c_str(),
  322. resultingConfig,
  323. extraPaths,
  324. failed);
  325. if(!cmSystemTools::FileExists(fullPath.c_str()))
  326. {
  327. out << "Could not find path to executable, perhaps it was not built: "
  328. << this->TestCommand << "\n";
  329. out << "tried to find it in these places:\n";
  330. out << fullPath.c_str() << "\n";
  331. for(unsigned int i=0; i < failed.size(); ++i)
  332. {
  333. out << failed[i] << "\n";
  334. }
  335. if(outstring)
  336. {
  337. *outstring = out.str();
  338. }
  339. else
  340. {
  341. cmCTestLog(this->CTest, ERROR_MESSAGE, out.str());
  342. }
  343. // return to the original directory
  344. cmSystemTools::ChangeDirectory(cwd.c_str());
  345. return 1;
  346. }
  347. std::vector<const char*> testCommand;
  348. testCommand.push_back(fullPath.c_str());
  349. for(size_t k=0; k < this->TestCommandArgs.size(); ++k)
  350. {
  351. testCommand.push_back(this->TestCommandArgs[k].c_str());
  352. }
  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())
  358. {
  359. out << "Run test in directory: " << this->BuildRunDir << "\n";
  360. cmSystemTools::ChangeDirectory(this->BuildRunDir.c_str());
  361. }
  362. out << "Running test command: \"" << fullPath << "\"";
  363. for(size_t k=0; k < this->TestCommandArgs.size(); ++k)
  364. {
  365. out << " \"" << this->TestCommandArgs[k] << "\"";
  366. }
  367. out << "\n";
  368. // how much time is remaining
  369. double remainingTime = 0;
  370. if (this->Timeout > 0)
  371. {
  372. remainingTime = this->Timeout - cmSystemTools::GetTime() + clock_start;
  373. if (remainingTime <= 0)
  374. {
  375. if(outstring)
  376. {
  377. *outstring = "--build-and-test timeout exceeded. ";
  378. }
  379. return 1;
  380. }
  381. }
  382. int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, 0,
  383. remainingTime, 0);
  384. if(runTestRes != cmsysProcess_State_Exited || retval != 0)
  385. {
  386. out << "Test command failed: " << testCommand[0] << "\n";
  387. retval = 1;
  388. }
  389. out << outs << "\n";
  390. if(outstring)
  391. {
  392. *outstring = out.str();
  393. }
  394. else
  395. {
  396. cmCTestLog(this->CTest, OUTPUT, out.str() << std::endl);
  397. }
  398. return retval;
  399. }
  400. //----------------------------------------------------------------------
  401. int cmCTestBuildAndTestHandler::ProcessCommandLineArguments(
  402. const std::string& currentArg, size_t& idx,
  403. const std::vector<std::string>& allArgs)
  404. {
  405. // --build-and-test options
  406. if(currentArg.find("--build-and-test",0) == 0 && idx < allArgs.size() - 1)
  407. {
  408. if(idx+2 < allArgs.size())
  409. {
  410. idx++;
  411. this->SourceDir = allArgs[idx];
  412. idx++;
  413. this->BinaryDir = allArgs[idx];
  414. // dir must exist before CollapseFullPath is called
  415. cmSystemTools::MakeDirectory(this->BinaryDir.c_str());
  416. this->BinaryDir
  417. = cmSystemTools::CollapseFullPath(this->BinaryDir.c_str());
  418. this->SourceDir
  419. = cmSystemTools::CollapseFullPath(this->SourceDir.c_str());
  420. }
  421. else
  422. {
  423. cmCTestLog(this->CTest, ERROR_MESSAGE,
  424. "--build-and-test must have source and binary dir" << std::endl);
  425. return 0;
  426. }
  427. }
  428. if(currentArg.find("--build-target",0) == 0 && idx < allArgs.size() - 1)
  429. {
  430. idx++;
  431. this->BuildTargets.push_back(allArgs[idx]);
  432. }
  433. if(currentArg.find("--build-nocmake",0) == 0)
  434. {
  435. this->BuildNoCMake = true;
  436. }
  437. if(currentArg.find("--build-run-dir",0) == 0 && idx < allArgs.size() - 1)
  438. {
  439. idx++;
  440. this->BuildRunDir = allArgs[idx];
  441. }
  442. if(currentArg.find("--build-two-config",0) == 0)
  443. {
  444. this->BuildTwoConfig = true;
  445. }
  446. if(currentArg.find("--build-exe-dir",0) == 0 && idx < allArgs.size() - 1)
  447. {
  448. idx++;
  449. this->ExecutableDirectory = allArgs[idx];
  450. }
  451. if(currentArg.find("--test-timeout",0) == 0 && idx < allArgs.size() - 1)
  452. {
  453. idx++;
  454. this->Timeout = atof(allArgs[idx].c_str());
  455. }
  456. if(currentArg == "--build-generator" && idx < allArgs.size() - 1)
  457. {
  458. idx++;
  459. this->BuildGenerator = allArgs[idx];
  460. }
  461. if(currentArg == "--build-generator-toolset" &&
  462. idx < allArgs.size() - 1)
  463. {
  464. idx++;
  465. this->BuildGeneratorToolset = allArgs[idx];
  466. }
  467. if(currentArg.find("--build-project",0) == 0 && idx < allArgs.size() - 1)
  468. {
  469. idx++;
  470. this->BuildProject = allArgs[idx];
  471. }
  472. if(currentArg.find("--build-makeprogram",0) == 0 &&
  473. idx < allArgs.size() - 1)
  474. {
  475. idx++;
  476. this->BuildMakeProgram = allArgs[idx];
  477. }
  478. if(currentArg.find("--build-config-sample",0) == 0 &&
  479. idx < allArgs.size() - 1)
  480. {
  481. idx++;
  482. this->ConfigSample = allArgs[idx];
  483. }
  484. if(currentArg.find("--build-noclean",0) == 0)
  485. {
  486. this->BuildNoClean = true;
  487. }
  488. if(currentArg.find("--build-options",0) == 0)
  489. {
  490. while(idx+1 < allArgs.size() &&
  491. allArgs[idx+1] != "--build-target" &&
  492. allArgs[idx+1] != "--test-command")
  493. {
  494. ++idx;
  495. this->BuildOptions.push_back(allArgs[idx]);
  496. }
  497. }
  498. if(currentArg.find("--test-command",0) == 0 && idx < allArgs.size() - 1)
  499. {
  500. ++idx;
  501. this->TestCommand = allArgs[idx];
  502. while(idx+1 < allArgs.size())
  503. {
  504. ++idx;
  505. this->TestCommandArgs.push_back(allArgs[idx]);
  506. }
  507. }
  508. return 1;
  509. }