cmCTestBuildAndTestHandler.cxx 14 KB

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