cmCTestSubmitHandler.cxx 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCTestSubmitHandler.h"
  4. #include "cm_curl.h"
  5. #include "cm_jsoncpp_reader.h"
  6. #include "cm_jsoncpp_value.h"
  7. #include "cmsys/Process.h"
  8. #include <sstream>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include "cmCTest.h"
  12. #include "cmCTestCurl.h"
  13. #include "cmCTestScriptHandler.h"
  14. #include "cmCryptoHash.h"
  15. #include "cmCurl.h"
  16. #include "cmGeneratedFileStream.h"
  17. #include "cmProcessOutput.h"
  18. #include "cmState.h"
  19. #include "cmSystemTools.h"
  20. #include "cmThirdParty.h"
  21. #include "cmWorkingDirectory.h"
  22. #include "cmXMLParser.h"
  23. #include "cmake.h"
  24. #if defined(CTEST_USE_XMLRPC)
  25. #include "cmVersion.h"
  26. #include "cm_sys_stat.h"
  27. #include "cm_xmlrpc.h"
  28. #endif
  29. #define SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT 120
  30. typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
  31. class cmCTestSubmitHandler::ResponseParser : public cmXMLParser
  32. {
  33. public:
  34. ResponseParser() { this->Status = STATUS_OK; }
  35. ~ResponseParser() CM_OVERRIDE {}
  36. public:
  37. enum StatusType
  38. {
  39. STATUS_OK,
  40. STATUS_WARNING,
  41. STATUS_ERROR
  42. };
  43. StatusType Status;
  44. std::string Filename;
  45. std::string MD5;
  46. std::string Message;
  47. private:
  48. std::vector<char> CurrentValue;
  49. std::string GetCurrentValue()
  50. {
  51. std::string val;
  52. if (!this->CurrentValue.empty()) {
  53. val.assign(&this->CurrentValue[0], this->CurrentValue.size());
  54. }
  55. return val;
  56. }
  57. void StartElement(const std::string& /*name*/,
  58. const char** /*atts*/) CM_OVERRIDE
  59. {
  60. this->CurrentValue.clear();
  61. }
  62. void CharacterDataHandler(const char* data, int length) CM_OVERRIDE
  63. {
  64. this->CurrentValue.insert(this->CurrentValue.end(), data, data + length);
  65. }
  66. void EndElement(const std::string& name) CM_OVERRIDE
  67. {
  68. if (name == "status") {
  69. std::string status = cmSystemTools::UpperCase(this->GetCurrentValue());
  70. if (status == "OK" || status == "SUCCESS") {
  71. this->Status = STATUS_OK;
  72. } else if (status == "WARNING") {
  73. this->Status = STATUS_WARNING;
  74. } else {
  75. this->Status = STATUS_ERROR;
  76. }
  77. } else if (name == "filename") {
  78. this->Filename = this->GetCurrentValue();
  79. } else if (name == "md5") {
  80. this->MD5 = this->GetCurrentValue();
  81. } else if (name == "message") {
  82. this->Message = this->GetCurrentValue();
  83. }
  84. }
  85. };
  86. static size_t cmCTestSubmitHandlerWriteMemoryCallback(void* ptr, size_t size,
  87. size_t nmemb, void* data)
  88. {
  89. int realsize = static_cast<int>(size * nmemb);
  90. cmCTestSubmitHandlerVectorOfChar* vec =
  91. static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  92. const char* chPtr = static_cast<char*>(ptr);
  93. vec->insert(vec->end(), chPtr, chPtr + realsize);
  94. return realsize;
  95. }
  96. static size_t cmCTestSubmitHandlerCurlDebugCallback(CURL* /*unused*/,
  97. curl_infotype /*unused*/,
  98. char* chPtr, size_t size,
  99. void* data)
  100. {
  101. cmCTestSubmitHandlerVectorOfChar* vec =
  102. static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  103. vec->insert(vec->end(), chPtr, chPtr + size);
  104. return size;
  105. }
  106. cmCTestSubmitHandler::cmCTestSubmitHandler()
  107. : HTTPProxy()
  108. , FTPProxy()
  109. {
  110. this->Initialize();
  111. }
  112. void cmCTestSubmitHandler::Initialize()
  113. {
  114. // We submit all available parts by default.
  115. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  116. p = cmCTest::Part(p + 1)) {
  117. this->SubmitPart[p] = true;
  118. }
  119. this->CDash = false;
  120. this->HasWarnings = false;
  121. this->HasErrors = false;
  122. this->Superclass::Initialize();
  123. this->HTTPProxy = "";
  124. this->HTTPProxyType = 0;
  125. this->HTTPProxyAuth = "";
  126. this->FTPProxy = "";
  127. this->FTPProxyType = 0;
  128. this->LogFile = nullptr;
  129. this->Files.clear();
  130. }
  131. bool cmCTestSubmitHandler::SubmitUsingFTP(const std::string& localprefix,
  132. const std::set<std::string>& files,
  133. const std::string& remoteprefix,
  134. const std::string& url)
  135. {
  136. CURL* curl;
  137. CURLcode res;
  138. FILE* ftpfile;
  139. char error_buffer[1024];
  140. /* In windows, this will init the winsock stuff */
  141. ::curl_global_init(CURL_GLOBAL_ALL);
  142. for (std::string const& file : files) {
  143. /* get a curl handle */
  144. curl = curl_easy_init();
  145. if (curl) {
  146. // Using proxy
  147. if (this->FTPProxyType > 0) {
  148. curl_easy_setopt(curl, CURLOPT_PROXY, this->FTPProxy.c_str());
  149. switch (this->FTPProxyType) {
  150. case 2:
  151. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  152. break;
  153. case 3:
  154. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  155. break;
  156. default:
  157. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  158. }
  159. }
  160. // enable uploading
  161. ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  162. // if there is little to no activity for too long stop submitting
  163. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
  164. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
  165. SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  166. ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  167. std::string local_file = file;
  168. if (!cmSystemTools::FileExists(local_file.c_str())) {
  169. local_file = localprefix + "/" + file;
  170. }
  171. std::string upload_as =
  172. url + "/" + remoteprefix + cmSystemTools::GetFilenameName(file);
  173. if (!cmSystemTools::FileExists(local_file.c_str())) {
  174. cmCTestLog(this->CTest, ERROR_MESSAGE,
  175. " Cannot find file: " << local_file << std::endl);
  176. ::curl_easy_cleanup(curl);
  177. ::curl_global_cleanup();
  178. return false;
  179. }
  180. unsigned long filelen = cmSystemTools::FileLength(local_file);
  181. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  182. *this->LogFile << "\tUpload file: " << local_file << " to " << upload_as
  183. << std::endl;
  184. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  185. " Upload file: " << local_file << " to "
  186. << upload_as << std::endl,
  187. this->Quiet);
  188. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  189. // specify target
  190. ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str());
  191. // now specify which file to upload
  192. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  193. // and give the size of the upload (optional)
  194. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(filelen));
  195. // and give curl the buffer for errors
  196. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  197. // specify handler for output
  198. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  199. cmCTestSubmitHandlerWriteMemoryCallback);
  200. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  201. cmCTestSubmitHandlerCurlDebugCallback);
  202. /* we pass our 'chunk' struct to the callback function */
  203. cmCTestSubmitHandlerVectorOfChar chunk;
  204. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  205. ::curl_easy_setopt(curl, CURLOPT_FILE, &chunk);
  206. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
  207. // Now run off and do what you've been told!
  208. res = ::curl_easy_perform(curl);
  209. if (!chunk.empty()) {
  210. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  211. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  212. << "]" << std::endl,
  213. this->Quiet);
  214. }
  215. if (!chunkDebug.empty()) {
  216. cmCTestOptionalLog(
  217. this->CTest, DEBUG, "CURL debug output: ["
  218. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  219. << std::endl,
  220. this->Quiet);
  221. }
  222. fclose(ftpfile);
  223. if (res) {
  224. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: "
  225. << local_file << std::endl);
  226. cmCTestLog(this->CTest, ERROR_MESSAGE,
  227. " Error message was: " << error_buffer << std::endl);
  228. *this->LogFile << " Error when uploading file: " << local_file
  229. << std::endl
  230. << " Error message was: " << error_buffer << std::endl
  231. << " Curl output was: ";
  232. // avoid dereference of empty vector
  233. if (!chunk.empty()) {
  234. *this->LogFile << cmCTestLogWrite(&*chunk.begin(), chunk.size());
  235. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  236. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  237. << std::endl);
  238. }
  239. *this->LogFile << std::endl;
  240. ::curl_easy_cleanup(curl);
  241. ::curl_global_cleanup();
  242. return false;
  243. }
  244. // always cleanup
  245. ::curl_easy_cleanup(curl);
  246. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  247. " Uploaded: " + local_file << std::endl,
  248. this->Quiet);
  249. }
  250. }
  251. ::curl_global_cleanup();
  252. return true;
  253. }
  254. // Uploading files is simpler
  255. bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix,
  256. const std::set<std::string>& files,
  257. const std::string& remoteprefix,
  258. const std::string& url)
  259. {
  260. CURL* curl;
  261. CURLcode res;
  262. FILE* ftpfile;
  263. char error_buffer[1024];
  264. // Set Content-Type to satisfy fussy modsecurity rules.
  265. struct curl_slist* headers =
  266. ::curl_slist_append(nullptr, "Content-Type: text/xml");
  267. // Add any additional headers that the user specified.
  268. for (std::string const& h : this->HttpHeaders) {
  269. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  270. " Add HTTP Header: \"" << h << "\"" << std::endl,
  271. this->Quiet);
  272. headers = ::curl_slist_append(headers, h.c_str());
  273. }
  274. /* In windows, this will init the winsock stuff */
  275. ::curl_global_init(CURL_GLOBAL_ALL);
  276. std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod"));
  277. std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
  278. std::vector<std::string> args;
  279. cmSystemTools::ExpandListArgument(curlopt, args);
  280. bool verifyPeerOff = false;
  281. bool verifyHostOff = false;
  282. for (std::string const& arg : args) {
  283. if (arg == "CURLOPT_SSL_VERIFYPEER_OFF") {
  284. verifyPeerOff = true;
  285. }
  286. if (arg == "CURLOPT_SSL_VERIFYHOST_OFF") {
  287. verifyHostOff = true;
  288. }
  289. }
  290. for (std::string const& file : files) {
  291. /* get a curl handle */
  292. curl = curl_easy_init();
  293. if (curl) {
  294. cmCurlSetCAInfo(curl);
  295. if (verifyPeerOff) {
  296. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  297. " Set CURLOPT_SSL_VERIFYPEER to off\n",
  298. this->Quiet);
  299. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  300. }
  301. if (verifyHostOff) {
  302. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  303. " Set CURLOPT_SSL_VERIFYHOST to off\n",
  304. this->Quiet);
  305. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
  306. }
  307. // Using proxy
  308. if (this->HTTPProxyType > 0) {
  309. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  310. switch (this->HTTPProxyType) {
  311. case 2:
  312. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  313. break;
  314. case 3:
  315. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  316. break;
  317. default:
  318. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  319. if (!this->HTTPProxyAuth.empty()) {
  320. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  321. this->HTTPProxyAuth.c_str());
  322. }
  323. }
  324. }
  325. if (this->CTest->ShouldUseHTTP10()) {
  326. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  327. }
  328. // enable HTTP ERROR parsing
  329. curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
  330. /* enable uploading */
  331. curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  332. // if there is little to no activity for too long stop submitting
  333. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
  334. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
  335. SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  336. /* HTTP PUT please */
  337. ::curl_easy_setopt(curl, CURLOPT_PUT, 1);
  338. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  339. ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  340. std::string local_file = file;
  341. if (!cmSystemTools::FileExists(local_file.c_str())) {
  342. local_file = localprefix + "/" + file;
  343. }
  344. std::string remote_file =
  345. remoteprefix + cmSystemTools::GetFilenameName(file);
  346. *this->LogFile << "\tUpload file: " << local_file << " to "
  347. << remote_file << std::endl;
  348. std::string ofile;
  349. for (char c : remote_file) {
  350. char hexCh[4] = { 0, 0, 0, 0 };
  351. hexCh[0] = c;
  352. switch (c) {
  353. case '+':
  354. case '?':
  355. case '/':
  356. case '\\':
  357. case '&':
  358. case ' ':
  359. case '=':
  360. case '%':
  361. sprintf(hexCh, "%%%02X", static_cast<int>(c));
  362. ofile.append(hexCh);
  363. break;
  364. default:
  365. ofile.append(hexCh);
  366. }
  367. }
  368. std::string upload_as = url +
  369. ((url.find('?') == std::string::npos) ? '?' : '&') + "FileName=" +
  370. ofile;
  371. upload_as += "&MD5=";
  372. if (cmSystemTools::IsOn(this->GetOption("InternalTest"))) {
  373. upload_as += "bad_md5sum";
  374. } else {
  375. upload_as +=
  376. cmSystemTools::ComputeFileHash(local_file, cmCryptoHash::AlgoMD5);
  377. }
  378. if (!cmSystemTools::FileExists(local_file.c_str())) {
  379. cmCTestLog(this->CTest, ERROR_MESSAGE,
  380. " Cannot find file: " << local_file << std::endl);
  381. ::curl_easy_cleanup(curl);
  382. ::curl_slist_free_all(headers);
  383. ::curl_global_cleanup();
  384. return false;
  385. }
  386. unsigned long filelen = cmSystemTools::FileLength(local_file);
  387. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  388. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  389. " Upload file: " << local_file << " to "
  390. << upload_as << " Size: "
  391. << filelen << std::endl,
  392. this->Quiet);
  393. // specify target
  394. ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str());
  395. // now specify which file to upload
  396. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  397. // and give the size of the upload (optional)
  398. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(filelen));
  399. // and give curl the buffer for errors
  400. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  401. // specify handler for output
  402. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  403. cmCTestSubmitHandlerWriteMemoryCallback);
  404. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  405. cmCTestSubmitHandlerCurlDebugCallback);
  406. /* we pass our 'chunk' struct to the callback function */
  407. cmCTestSubmitHandlerVectorOfChar chunk;
  408. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  409. ::curl_easy_setopt(curl, CURLOPT_FILE, &chunk);
  410. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
  411. // Now run off and do what you've been told!
  412. res = ::curl_easy_perform(curl);
  413. if (!chunk.empty()) {
  414. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  415. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  416. << "]" << std::endl,
  417. this->Quiet);
  418. this->ParseResponse(chunk);
  419. }
  420. if (!chunkDebug.empty()) {
  421. cmCTestOptionalLog(
  422. this->CTest, DEBUG, "CURL debug output: ["
  423. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  424. << std::endl,
  425. this->Quiet);
  426. }
  427. // If curl failed for any reason, or checksum fails, wait and retry
  428. //
  429. if (res != CURLE_OK || this->HasErrors) {
  430. std::string retryDelay = this->GetOption("RetryDelay") == nullptr
  431. ? ""
  432. : this->GetOption("RetryDelay");
  433. std::string retryCount = this->GetOption("RetryCount") == nullptr
  434. ? ""
  435. : this->GetOption("RetryCount");
  436. int delay = retryDelay == ""
  437. ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryDelay")
  438. .c_str())
  439. : atoi(retryDelay.c_str());
  440. int count = retryCount == ""
  441. ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryCount")
  442. .c_str())
  443. : atoi(retryCount.c_str());
  444. for (int i = 0; i < count; i++) {
  445. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  446. " Submit failed, waiting " << delay
  447. << " seconds...\n",
  448. this->Quiet);
  449. double stop = cmSystemTools::GetTime() + delay;
  450. while (cmSystemTools::GetTime() < stop) {
  451. cmSystemTools::Delay(100);
  452. }
  453. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  454. " Retry submission: Attempt "
  455. << (i + 1) << " of " << count << std::endl,
  456. this->Quiet);
  457. ::fclose(ftpfile);
  458. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  459. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  460. chunk.clear();
  461. chunkDebug.clear();
  462. this->HasErrors = false;
  463. res = ::curl_easy_perform(curl);
  464. if (!chunk.empty()) {
  465. cmCTestOptionalLog(
  466. this->CTest, DEBUG, "CURL output: ["
  467. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  468. << std::endl,
  469. this->Quiet);
  470. this->ParseResponse(chunk);
  471. }
  472. if (res == CURLE_OK && !this->HasErrors) {
  473. break;
  474. }
  475. }
  476. }
  477. fclose(ftpfile);
  478. if (res) {
  479. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: "
  480. << local_file << std::endl);
  481. cmCTestLog(this->CTest, ERROR_MESSAGE,
  482. " Error message was: " << error_buffer << std::endl);
  483. *this->LogFile << " Error when uploading file: " << local_file
  484. << std::endl
  485. << " Error message was: " << error_buffer
  486. << std::endl;
  487. // avoid deref of begin for zero size array
  488. if (!chunk.empty()) {
  489. *this->LogFile << " Curl output was: "
  490. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  491. << std::endl;
  492. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  493. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  494. << std::endl);
  495. }
  496. ::curl_easy_cleanup(curl);
  497. ::curl_slist_free_all(headers);
  498. ::curl_global_cleanup();
  499. return false;
  500. }
  501. // always cleanup
  502. ::curl_easy_cleanup(curl);
  503. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  504. " Uploaded: " + local_file << std::endl,
  505. this->Quiet);
  506. }
  507. }
  508. ::curl_slist_free_all(headers);
  509. ::curl_global_cleanup();
  510. return true;
  511. }
  512. void cmCTestSubmitHandler::ParseResponse(
  513. cmCTestSubmitHandlerVectorOfChar chunk)
  514. {
  515. std::string output;
  516. output.append(chunk.begin(), chunk.end());
  517. if (output.find("<cdash") != std::string::npos) {
  518. ResponseParser parser;
  519. parser.Parse(output.c_str());
  520. if (parser.Status != ResponseParser::STATUS_OK) {
  521. this->HasErrors = true;
  522. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  523. " Submission failed: " << parser.Message << std::endl);
  524. return;
  525. }
  526. }
  527. output = cmSystemTools::UpperCase(output);
  528. if (output.find("WARNING") != std::string::npos) {
  529. this->HasWarnings = true;
  530. }
  531. if (output.find("ERROR") != std::string::npos) {
  532. this->HasErrors = true;
  533. }
  534. if (this->HasWarnings || this->HasErrors) {
  535. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Server Response:\n"
  536. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
  537. }
  538. }
  539. bool cmCTestSubmitHandler::TriggerUsingHTTP(const std::set<std::string>& files,
  540. const std::string& remoteprefix,
  541. const std::string& url)
  542. {
  543. CURL* curl;
  544. char error_buffer[1024];
  545. /* In windows, this will init the winsock stuff */
  546. ::curl_global_init(CURL_GLOBAL_ALL);
  547. for (std::string const& file : files) {
  548. /* get a curl handle */
  549. curl = curl_easy_init();
  550. if (curl) {
  551. // Using proxy
  552. if (this->HTTPProxyType > 0) {
  553. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  554. switch (this->HTTPProxyType) {
  555. case 2:
  556. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  557. break;
  558. case 3:
  559. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  560. break;
  561. default:
  562. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  563. if (!this->HTTPProxyAuth.empty()) {
  564. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  565. this->HTTPProxyAuth.c_str());
  566. }
  567. }
  568. }
  569. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  570. // and give curl the buffer for errors
  571. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  572. // specify handler for output
  573. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  574. cmCTestSubmitHandlerWriteMemoryCallback);
  575. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  576. cmCTestSubmitHandlerCurlDebugCallback);
  577. /* we pass our 'chunk' struct to the callback function */
  578. cmCTestSubmitHandlerVectorOfChar chunk;
  579. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  580. ::curl_easy_setopt(curl, CURLOPT_FILE, &chunk);
  581. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
  582. std::string rfile = remoteprefix + cmSystemTools::GetFilenameName(file);
  583. std::string ofile;
  584. for (char c : rfile) {
  585. char hexCh[4] = { 0, 0, 0, 0 };
  586. hexCh[0] = c;
  587. switch (c) {
  588. case '+':
  589. case '?':
  590. case '/':
  591. case '\\':
  592. case '&':
  593. case ' ':
  594. case '=':
  595. case '%':
  596. sprintf(hexCh, "%%%02X", static_cast<int>(c));
  597. ofile.append(hexCh);
  598. break;
  599. default:
  600. ofile.append(hexCh);
  601. }
  602. }
  603. std::string turl = url +
  604. ((url.find('?') == std::string::npos) ? '?' : '&') + "xmlfile=" +
  605. ofile;
  606. *this->LogFile << "Trigger url: " << turl << std::endl;
  607. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  608. " Trigger url: " << turl << std::endl, this->Quiet);
  609. curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  610. curl_easy_setopt(curl, CURLOPT_URL, turl.c_str());
  611. if (curl_easy_perform(curl)) {
  612. cmCTestLog(this->CTest, ERROR_MESSAGE,
  613. " Error when triggering: " << turl << std::endl);
  614. cmCTestLog(this->CTest, ERROR_MESSAGE,
  615. " Error message was: " << error_buffer << std::endl);
  616. *this->LogFile << "\tTriggering failed with error: " << error_buffer
  617. << std::endl
  618. << " Error message was: " << error_buffer
  619. << std::endl;
  620. if (!chunk.empty()) {
  621. *this->LogFile << " Curl output was: "
  622. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  623. << std::endl;
  624. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  625. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  626. << std::endl);
  627. }
  628. ::curl_easy_cleanup(curl);
  629. ::curl_global_cleanup();
  630. return false;
  631. }
  632. if (!chunk.empty()) {
  633. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  634. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  635. << "]" << std::endl,
  636. this->Quiet);
  637. }
  638. if (!chunkDebug.empty()) {
  639. cmCTestOptionalLog(
  640. this->CTest, DEBUG, "CURL debug output: ["
  641. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  642. << std::endl,
  643. this->Quiet);
  644. }
  645. // always cleanup
  646. ::curl_easy_cleanup(curl);
  647. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl,
  648. this->Quiet);
  649. }
  650. }
  651. ::curl_global_cleanup();
  652. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  653. " Dart server triggered..." << std::endl, this->Quiet);
  654. return true;
  655. }
  656. bool cmCTestSubmitHandler::SubmitUsingSCP(const std::string& scp_command,
  657. const std::string& localprefix,
  658. const std::set<std::string>& files,
  659. const std::string& remoteprefix,
  660. const std::string& url)
  661. {
  662. if (scp_command.empty() || localprefix.empty() || files.empty() ||
  663. remoteprefix.empty() || url.empty()) {
  664. return false;
  665. }
  666. std::vector<const char*> argv;
  667. argv.push_back(scp_command.c_str()); // Scp command
  668. argv.push_back(scp_command.c_str()); // Dummy string for file
  669. argv.push_back(scp_command.c_str()); // Dummy string for remote url
  670. argv.push_back(nullptr);
  671. cmsysProcess* cp = cmsysProcess_New();
  672. cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
  673. // cmsysProcess_SetTimeout(cp, timeout);
  674. int problems = 0;
  675. for (std::string const& file : files) {
  676. int retVal;
  677. std::string lfname = localprefix;
  678. cmSystemTools::ConvertToUnixSlashes(lfname);
  679. lfname += "/" + file;
  680. lfname = cmSystemTools::ConvertToOutputPath(lfname.c_str());
  681. argv[1] = lfname.c_str();
  682. std::string rfname = url + "/" + remoteprefix + file;
  683. argv[2] = rfname.c_str();
  684. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Execute \""
  685. << argv[0] << "\" \"" << argv[1] << "\" \"" << argv[2]
  686. << "\"" << std::endl,
  687. this->Quiet);
  688. *this->LogFile << "Execute \"" << argv[0] << "\" \"" << argv[1] << "\" \""
  689. << argv[2] << "\"" << std::endl;
  690. cmsysProcess_SetCommand(cp, &*argv.begin());
  691. cmsysProcess_Execute(cp);
  692. char* data;
  693. int length;
  694. cmProcessOutput processOutput;
  695. std::string strdata;
  696. while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
  697. processOutput.DecodeText(data, length, strdata);
  698. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  699. cmCTestLogWrite(strdata.c_str(), strdata.size()),
  700. this->Quiet);
  701. }
  702. processOutput.DecodeText(std::string(), strdata);
  703. if (!strdata.empty()) {
  704. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  705. cmCTestLogWrite(strdata.c_str(), strdata.size()),
  706. this->Quiet);
  707. }
  708. cmsysProcess_WaitForExit(cp, nullptr);
  709. int result = cmsysProcess_GetState(cp);
  710. if (result == cmsysProcess_State_Exited) {
  711. retVal = cmsysProcess_GetExitValue(cp);
  712. if (retVal != 0) {
  713. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  714. "\tSCP returned: " << retVal << std::endl,
  715. this->Quiet);
  716. *this->LogFile << "\tSCP returned: " << retVal << std::endl;
  717. problems++;
  718. }
  719. } else if (result == cmsysProcess_State_Exception) {
  720. retVal = cmsysProcess_GetExitException(cp);
  721. cmCTestLog(this->CTest, ERROR_MESSAGE,
  722. "\tThere was an exception: " << retVal << std::endl);
  723. *this->LogFile << "\tThere was an exception: " << retVal << std::endl;
  724. problems++;
  725. } else if (result == cmsysProcess_State_Expired) {
  726. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was a timeout"
  727. << std::endl);
  728. *this->LogFile << "\tThere was a timeout" << std::endl;
  729. problems++;
  730. } else if (result == cmsysProcess_State_Error) {
  731. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tError executing SCP: "
  732. << cmsysProcess_GetErrorString(cp) << std::endl);
  733. *this->LogFile << "\tError executing SCP: "
  734. << cmsysProcess_GetErrorString(cp) << std::endl;
  735. problems++;
  736. }
  737. }
  738. cmsysProcess_Delete(cp);
  739. return problems == 0;
  740. }
  741. bool cmCTestSubmitHandler::SubmitUsingCP(const std::string& localprefix,
  742. const std::set<std::string>& files,
  743. const std::string& remoteprefix,
  744. const std::string& destination)
  745. {
  746. if (localprefix.empty() || files.empty() || remoteprefix.empty() ||
  747. destination.empty()) {
  748. /* clang-format off */
  749. cmCTestLog(this->CTest, ERROR_MESSAGE,
  750. "Missing arguments for submit via cp:\n"
  751. << "\tlocalprefix: " << localprefix << "\n"
  752. << "\tNumber of files: " << files.size() << "\n"
  753. << "\tremoteprefix: " << remoteprefix << "\n"
  754. << "\tdestination: " << destination << std::endl);
  755. /* clang-format on */
  756. return false;
  757. }
  758. for (std::string const& file : files) {
  759. std::string lfname = localprefix;
  760. cmSystemTools::ConvertToUnixSlashes(lfname);
  761. lfname += "/" + file;
  762. std::string rfname = destination + "/" + remoteprefix + file;
  763. cmSystemTools::CopyFileAlways(lfname, rfname);
  764. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Copy file: "
  765. << lfname << " to " << rfname << std::endl,
  766. this->Quiet);
  767. }
  768. std::string tagDoneFile = destination + "/" + remoteprefix + "DONE";
  769. cmSystemTools::Touch(tagDoneFile, true);
  770. return true;
  771. }
  772. #if defined(CTEST_USE_XMLRPC)
  773. bool cmCTestSubmitHandler::SubmitUsingXMLRPC(
  774. const std::string& localprefix, const std::set<std::string>& files,
  775. const std::string& remoteprefix, const std::string& url)
  776. {
  777. xmlrpc_env env;
  778. char ctestString[] = "CTest";
  779. std::string ctestVersionString = cmVersion::GetCMakeVersion();
  780. char* ctestVersion = const_cast<char*>(ctestVersionString.c_str());
  781. std::string realURL = url + "/" + remoteprefix + "/Command/";
  782. /* Start up our XML-RPC client library. */
  783. xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, ctestString, ctestVersion);
  784. /* Initialize our error-handling environment. */
  785. xmlrpc_env_init(&env);
  786. /* Call the famous server at UserLand. */
  787. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Submitting to: "
  788. << realURL << " (" << remoteprefix << ")" << std::endl,
  789. this->Quiet);
  790. for (std::string const& file : files) {
  791. xmlrpc_value* result;
  792. std::string local_file = file;
  793. if (!cmSystemTools::FileExists(local_file.c_str())) {
  794. local_file = localprefix + "/" + file;
  795. }
  796. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  797. " Submit file: " << local_file << std::endl,
  798. this->Quiet);
  799. struct stat st;
  800. if (::stat(local_file.c_str(), &st)) {
  801. cmCTestLog(this->CTest, ERROR_MESSAGE,
  802. " Cannot find file: " << local_file << std::endl);
  803. return false;
  804. }
  805. // off_t can be bigger than size_t. fread takes size_t.
  806. // make sure the file is not too big.
  807. if (static_cast<off_t>(static_cast<size_t>(st.st_size)) !=
  808. static_cast<off_t>(st.st_size)) {
  809. cmCTestLog(this->CTest, ERROR_MESSAGE, " File too big: " << local_file
  810. << std::endl);
  811. return false;
  812. }
  813. size_t fileSize = static_cast<size_t>(st.st_size);
  814. FILE* fp = cmsys::SystemTools::Fopen(local_file, "rb");
  815. if (!fp) {
  816. cmCTestLog(this->CTest, ERROR_MESSAGE,
  817. " Cannot open file: " << local_file << std::endl);
  818. return false;
  819. }
  820. unsigned char* fileBuffer = new unsigned char[fileSize];
  821. if (fread(fileBuffer, 1, fileSize, fp) != fileSize) {
  822. delete[] fileBuffer;
  823. fclose(fp);
  824. cmCTestLog(this->CTest, ERROR_MESSAGE,
  825. " Cannot read file: " << local_file << std::endl);
  826. return false;
  827. }
  828. fclose(fp);
  829. char remoteCommand[] = "Submit.put";
  830. char* pRealURL = const_cast<char*>(realURL.c_str());
  831. result =
  832. xmlrpc_client_call(&env, pRealURL, remoteCommand, "(6)", fileBuffer,
  833. static_cast<xmlrpc_int32>(fileSize));
  834. delete[] fileBuffer;
  835. if (env.fault_occurred) {
  836. cmCTestLog(this->CTest, ERROR_MESSAGE, " Submission problem: "
  837. << env.fault_string << " (" << env.fault_code << ")"
  838. << std::endl);
  839. xmlrpc_env_clean(&env);
  840. xmlrpc_client_cleanup();
  841. return false;
  842. }
  843. /* Dispose of our result value. */
  844. xmlrpc_DECREF(result);
  845. }
  846. /* Clean up our error-handling environment. */
  847. xmlrpc_env_clean(&env);
  848. /* Shutdown our XML-RPC client library. */
  849. xmlrpc_client_cleanup();
  850. return true;
  851. }
  852. #else
  853. bool cmCTestSubmitHandler::SubmitUsingXMLRPC(
  854. std::string const& /*unused*/, std::set<std::string> const& /*unused*/,
  855. std::string const& /*unused*/, std::string const& /*unused*/)
  856. {
  857. return false;
  858. }
  859. #endif
  860. void cmCTestSubmitHandler::ConstructCDashURL(std::string& dropMethod,
  861. std::string& url)
  862. {
  863. dropMethod = this->CTest->GetCTestConfiguration("DropMethod");
  864. url = dropMethod;
  865. url += "://";
  866. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  867. url += this->CTest->GetCTestConfiguration("DropSiteUser");
  868. cmCTestOptionalLog(
  869. this->CTest, HANDLER_OUTPUT,
  870. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(), this->Quiet);
  871. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  872. url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
  873. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******", this->Quiet);
  874. }
  875. url += "@";
  876. }
  877. url += this->CTest->GetCTestConfiguration("DropSite") +
  878. this->CTest->GetCTestConfiguration("DropLocation");
  879. }
  880. int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file,
  881. std::string const& typeString)
  882. {
  883. if (file.empty()) {
  884. cmCTestLog(this->CTest, ERROR_MESSAGE, "Upload file not specified\n");
  885. return -1;
  886. }
  887. if (!cmSystemTools::FileExists(file)) {
  888. cmCTestLog(this->CTest, ERROR_MESSAGE, "Upload file not found: '"
  889. << file << "'\n");
  890. return -1;
  891. }
  892. cmCTestCurl curl(this->CTest);
  893. curl.SetQuiet(this->Quiet);
  894. std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
  895. std::vector<std::string> args;
  896. cmSystemTools::ExpandListArgument(curlopt, args);
  897. curl.SetCurlOptions(args);
  898. curl.SetTimeOutSeconds(SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  899. curl.SetHttpHeaders(this->HttpHeaders);
  900. std::string dropMethod;
  901. std::string url;
  902. this->ConstructCDashURL(dropMethod, url);
  903. std::string::size_type pos = url.find("submit.php?");
  904. url = url.substr(0, pos + 10);
  905. if (!(dropMethod == "http" || dropMethod == "https")) {
  906. cmCTestLog(this->CTest, ERROR_MESSAGE,
  907. "Only http and https are supported for CDASH_UPLOAD\n");
  908. return -1;
  909. }
  910. bool internalTest = cmSystemTools::IsOn(this->GetOption("InternalTest"));
  911. // Get RETRY_COUNT and RETRY_DELAY values if they were set.
  912. std::string retryDelayString = this->GetOption("RetryDelay") == nullptr
  913. ? ""
  914. : this->GetOption("RetryDelay");
  915. std::string retryCountString = this->GetOption("RetryCount") == nullptr
  916. ? ""
  917. : this->GetOption("RetryCount");
  918. unsigned long retryDelay = 0;
  919. if (retryDelayString != "") {
  920. if (!cmSystemTools::StringToULong(retryDelayString.c_str(), &retryDelay)) {
  921. cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : "
  922. << retryDelayString << std::endl);
  923. }
  924. }
  925. unsigned long retryCount = 0;
  926. if (retryCountString != "") {
  927. if (!cmSystemTools::StringToULong(retryCountString.c_str(), &retryCount)) {
  928. cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : "
  929. << retryCountString << std::endl);
  930. }
  931. }
  932. std::string md5sum =
  933. cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5);
  934. // 1. request the buildid and check to see if the file
  935. // has already been uploaded
  936. // TODO I added support for subproject. You would need to add
  937. // a "&subproject=subprojectname" to the first POST.
  938. cmCTestScriptHandler* ch =
  939. static_cast<cmCTestScriptHandler*>(this->CTest->GetHandler("script"));
  940. cmake* cm = ch->GetCMake();
  941. const char* subproject = cm->GetState()->GetGlobalProperty("SubProject");
  942. // TODO: Encode values for a URL instead of trusting caller.
  943. std::ostringstream str;
  944. str << "project="
  945. << curl.Escape(this->CTest->GetCTestConfiguration("ProjectName")) << "&";
  946. if (subproject) {
  947. str << "subproject=" << curl.Escape(subproject) << "&";
  948. }
  949. str << "stamp=" << curl.Escape(this->CTest->GetCurrentTag()) << "-"
  950. << curl.Escape(this->CTest->GetTestModelString()) << "&"
  951. << "model=" << curl.Escape(this->CTest->GetTestModelString()) << "&"
  952. << "build="
  953. << curl.Escape(this->CTest->GetCTestConfiguration("BuildName")) << "&"
  954. << "site=" << curl.Escape(this->CTest->GetCTestConfiguration("Site"))
  955. << "&"
  956. << "track=" << curl.Escape(this->CTest->GetTestModelString()) << "&"
  957. << "starttime=" << static_cast<int>(cmSystemTools::GetTime()) << "&"
  958. << "endtime=" << static_cast<int>(cmSystemTools::GetTime()) << "&"
  959. << "datafilesmd5[0]=" << md5sum << "&"
  960. << "type=" << curl.Escape(typeString);
  961. std::string fields = str.str();
  962. cmCTestOptionalLog(this->CTest, DEBUG,
  963. "fields: " << fields << "\nurl:" << url
  964. << "\nfile: " << file << "\n",
  965. this->Quiet);
  966. std::string response;
  967. bool requestSucceeded = curl.HttpRequest(url, fields, response);
  968. if (!internalTest && !requestSucceeded) {
  969. // If request failed, wait and retry.
  970. for (unsigned long i = 0; i < retryCount; i++) {
  971. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  972. " Request failed, waiting " << retryDelay
  973. << " seconds...\n",
  974. this->Quiet);
  975. double stop = cmSystemTools::GetTime() + static_cast<double>(retryDelay);
  976. while (cmSystemTools::GetTime() < stop) {
  977. cmSystemTools::Delay(100);
  978. }
  979. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  980. " Retry request: Attempt "
  981. << (i + 1) << " of " << retryCount << std::endl,
  982. this->Quiet);
  983. requestSucceeded = curl.HttpRequest(url, fields, response);
  984. if (requestSucceeded) {
  985. break;
  986. }
  987. }
  988. }
  989. if (!internalTest && !requestSucceeded) {
  990. cmCTestLog(this->CTest, ERROR_MESSAGE, "Error in HttpRequest\n"
  991. << response);
  992. return -1;
  993. }
  994. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  995. "Request upload response: [" << response << "]\n",
  996. this->Quiet);
  997. Json::Value json;
  998. Json::Reader reader;
  999. if (!internalTest && !reader.parse(response, json)) {
  1000. cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string ["
  1001. << response << "]\n"
  1002. << reader.getFormattedErrorMessages() << "\n");
  1003. return -1;
  1004. }
  1005. if (!internalTest && json["status"].asInt() != 0) {
  1006. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1007. "Bad status returned from CDash: " << json["status"].asInt());
  1008. return -1;
  1009. }
  1010. if (!internalTest) {
  1011. if (json["datafilesmd5"].isArray()) {
  1012. int datares = json["datafilesmd5"][0].asInt();
  1013. if (datares == 1) {
  1014. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1015. "File already exists on CDash, skip upload "
  1016. << file << "\n",
  1017. this->Quiet);
  1018. return 0;
  1019. }
  1020. } else {
  1021. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1022. "bad datafilesmd5 value in response " << response << "\n");
  1023. return -1;
  1024. }
  1025. }
  1026. std::string upload_as = cmSystemTools::GetFilenameName(file);
  1027. std::ostringstream fstr;
  1028. fstr << "type=" << curl.Escape(typeString) << "&"
  1029. << "md5=" << md5sum << "&"
  1030. << "filename=" << curl.Escape(upload_as) << "&"
  1031. << "buildid=" << json["buildid"].asString();
  1032. bool uploadSucceeded = false;
  1033. if (!internalTest) {
  1034. uploadSucceeded = curl.UploadFile(file, url, fstr.str(), response);
  1035. }
  1036. if (!uploadSucceeded) {
  1037. // If upload failed, wait and retry.
  1038. for (unsigned long i = 0; i < retryCount; i++) {
  1039. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1040. " Upload failed, waiting " << retryDelay
  1041. << " seconds...\n",
  1042. this->Quiet);
  1043. double stop = cmSystemTools::GetTime() + static_cast<double>(retryDelay);
  1044. while (cmSystemTools::GetTime() < stop) {
  1045. cmSystemTools::Delay(100);
  1046. }
  1047. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1048. " Retry upload: Attempt "
  1049. << (i + 1) << " of " << retryCount << std::endl,
  1050. this->Quiet);
  1051. if (!internalTest) {
  1052. uploadSucceeded = curl.UploadFile(file, url, fstr.str(), response);
  1053. }
  1054. if (uploadSucceeded) {
  1055. break;
  1056. }
  1057. }
  1058. }
  1059. if (!uploadSucceeded) {
  1060. cmCTestLog(this->CTest, ERROR_MESSAGE, "error uploading to CDash. "
  1061. << file << " " << url << " " << fstr.str());
  1062. return -1;
  1063. }
  1064. if (!reader.parse(response, json)) {
  1065. cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string ["
  1066. << response << "]\n"
  1067. << reader.getFormattedErrorMessages() << "\n");
  1068. return -1;
  1069. }
  1070. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1071. "Upload file response: [" << response << "]\n",
  1072. this->Quiet);
  1073. return 0;
  1074. }
  1075. int cmCTestSubmitHandler::ProcessHandler()
  1076. {
  1077. const char* cdashUploadFile = this->GetOption("CDashUploadFile");
  1078. const char* cdashUploadType = this->GetOption("CDashUploadType");
  1079. if (cdashUploadFile && cdashUploadType) {
  1080. return this->HandleCDashUploadFile(cdashUploadFile, cdashUploadType);
  1081. }
  1082. std::string iscdash = this->CTest->GetCTestConfiguration("IsCDash");
  1083. // cdash does not need to trigger so just return true
  1084. if (!iscdash.empty()) {
  1085. this->CDash = true;
  1086. }
  1087. const std::string& buildDirectory =
  1088. this->CTest->GetCTestConfiguration("BuildDirectory");
  1089. if (buildDirectory.empty()) {
  1090. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1091. "Cannot find BuildDirectory key in the DartConfiguration.tcl"
  1092. << std::endl);
  1093. return -1;
  1094. }
  1095. if (getenv("HTTP_PROXY")) {
  1096. this->HTTPProxyType = 1;
  1097. this->HTTPProxy = getenv("HTTP_PROXY");
  1098. if (getenv("HTTP_PROXY_PORT")) {
  1099. this->HTTPProxy += ":";
  1100. this->HTTPProxy += getenv("HTTP_PROXY_PORT");
  1101. }
  1102. if (getenv("HTTP_PROXY_TYPE")) {
  1103. std::string type = getenv("HTTP_PROXY_TYPE");
  1104. // HTTP/SOCKS4/SOCKS5
  1105. if (type == "HTTP") {
  1106. this->HTTPProxyType = 1;
  1107. } else if (type == "SOCKS4") {
  1108. this->HTTPProxyType = 2;
  1109. } else if (type == "SOCKS5") {
  1110. this->HTTPProxyType = 3;
  1111. }
  1112. }
  1113. if (getenv("HTTP_PROXY_USER")) {
  1114. this->HTTPProxyAuth = getenv("HTTP_PROXY_USER");
  1115. }
  1116. if (getenv("HTTP_PROXY_PASSWD")) {
  1117. this->HTTPProxyAuth += ":";
  1118. this->HTTPProxyAuth += getenv("HTTP_PROXY_PASSWD");
  1119. }
  1120. }
  1121. if (getenv("FTP_PROXY")) {
  1122. this->FTPProxyType = 1;
  1123. this->FTPProxy = getenv("FTP_PROXY");
  1124. if (getenv("FTP_PROXY_PORT")) {
  1125. this->FTPProxy += ":";
  1126. this->FTPProxy += getenv("FTP_PROXY_PORT");
  1127. }
  1128. if (getenv("FTP_PROXY_TYPE")) {
  1129. std::string type = getenv("FTP_PROXY_TYPE");
  1130. // HTTP/SOCKS4/SOCKS5
  1131. if (type == "HTTP") {
  1132. this->FTPProxyType = 1;
  1133. } else if (type == "SOCKS4") {
  1134. this->FTPProxyType = 2;
  1135. } else if (type == "SOCKS5") {
  1136. this->FTPProxyType = 3;
  1137. }
  1138. }
  1139. }
  1140. if (!this->HTTPProxy.empty()) {
  1141. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1142. " Use HTTP Proxy: " << this->HTTPProxy << std::endl,
  1143. this->Quiet);
  1144. }
  1145. if (!this->FTPProxy.empty()) {
  1146. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1147. " Use FTP Proxy: " << this->FTPProxy << std::endl,
  1148. this->Quiet);
  1149. }
  1150. cmGeneratedFileStream ofs;
  1151. this->StartLogFile("Submit", ofs);
  1152. cmCTest::SetOfStrings files;
  1153. std::string prefix = this->GetSubmitResultsPrefix();
  1154. if (!this->Files.empty()) {
  1155. // Submit the explicitly selected files:
  1156. //
  1157. files.insert(this->Files.begin(), this->Files.end());
  1158. }
  1159. // Add to the list of files to submit from any selected, existing parts:
  1160. //
  1161. // TODO:
  1162. // Check if test is enabled
  1163. this->CTest->AddIfExists(cmCTest::PartUpdate, "Update.xml");
  1164. this->CTest->AddIfExists(cmCTest::PartConfigure, "Configure.xml");
  1165. this->CTest->AddIfExists(cmCTest::PartBuild, "Build.xml");
  1166. this->CTest->AddIfExists(cmCTest::PartTest, "Test.xml");
  1167. if (this->CTest->AddIfExists(cmCTest::PartCoverage, "Coverage.xml")) {
  1168. std::vector<std::string> gfiles;
  1169. std::string gpath =
  1170. buildDirectory + "/Testing/" + this->CTest->GetCurrentTag();
  1171. std::string::size_type glen = gpath.size() + 1;
  1172. gpath = gpath + "/CoverageLog*";
  1173. cmCTestOptionalLog(this->CTest, DEBUG,
  1174. "Globbing for: " << gpath << std::endl, this->Quiet);
  1175. if (cmSystemTools::SimpleGlob(gpath, gfiles, 1)) {
  1176. for (std::string& gfile : gfiles) {
  1177. gfile = gfile.substr(glen);
  1178. cmCTestOptionalLog(this->CTest, DEBUG,
  1179. "Glob file: " << gfile << std::endl, this->Quiet);
  1180. this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfile.c_str());
  1181. }
  1182. } else {
  1183. cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem globbing" << std::endl);
  1184. }
  1185. }
  1186. this->CTest->AddIfExists(cmCTest::PartMemCheck, "DynamicAnalysis.xml");
  1187. this->CTest->AddIfExists(cmCTest::PartMemCheck, "Purify.xml");
  1188. this->CTest->AddIfExists(cmCTest::PartNotes, "Notes.xml");
  1189. this->CTest->AddIfExists(cmCTest::PartUpload, "Upload.xml");
  1190. // Query parts for files to submit.
  1191. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  1192. p = cmCTest::Part(p + 1)) {
  1193. // Skip parts we are not submitting.
  1194. if (!this->SubmitPart[p]) {
  1195. continue;
  1196. }
  1197. // Submit files from this part.
  1198. std::vector<std::string> const& pfiles = this->CTest->GetSubmitFiles(p);
  1199. files.insert(pfiles.begin(), pfiles.end());
  1200. }
  1201. if (ofs) {
  1202. ofs << "Upload files:" << std::endl;
  1203. int cnt = 0;
  1204. for (std::string const& file : files) {
  1205. ofs << cnt << "\t" << file << std::endl;
  1206. cnt++;
  1207. }
  1208. }
  1209. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Submit files (using "
  1210. << this->CTest->GetCTestConfiguration("DropMethod")
  1211. << ")" << std::endl,
  1212. this->Quiet);
  1213. const char* specificTrack = this->CTest->GetSpecificTrack();
  1214. if (specificTrack) {
  1215. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1216. " Send to track: " << specificTrack << std::endl,
  1217. this->Quiet);
  1218. }
  1219. this->SetLogFile(&ofs);
  1220. std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod"));
  1221. if (dropMethod == "" || dropMethod == "ftp") {
  1222. ofs << "Using drop method: FTP" << std::endl;
  1223. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1224. " Using FTP submit method" << std::endl
  1225. << " Drop site: ftp://",
  1226. this->Quiet);
  1227. std::string url = "ftp://";
  1228. url += cmCTest::MakeURLSafe(
  1229. this->CTest->GetCTestConfiguration("DropSiteUser")) +
  1230. ":" + cmCTest::MakeURLSafe(
  1231. this->CTest->GetCTestConfiguration("DropSitePassword")) +
  1232. "@" + this->CTest->GetCTestConfiguration("DropSite") +
  1233. cmCTest::MakeURLSafe(this->CTest->GetCTestConfiguration("DropLocation"));
  1234. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1235. cmCTestOptionalLog(
  1236. this->CTest, HANDLER_OUTPUT,
  1237. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(),
  1238. this->Quiet);
  1239. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  1240. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******",
  1241. this->Quiet);
  1242. }
  1243. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet);
  1244. }
  1245. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1246. this->CTest->GetCTestConfiguration("DropSite")
  1247. << this->CTest->GetCTestConfiguration("DropLocation")
  1248. << std::endl,
  1249. this->Quiet);
  1250. if (!this->SubmitUsingFTP(buildDirectory + "/Testing/" +
  1251. this->CTest->GetCurrentTag(),
  1252. files, prefix, url)) {
  1253. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1254. " Problems when submitting via FTP" << std::endl);
  1255. ofs << " Problems when submitting via FTP" << std::endl;
  1256. return -1;
  1257. }
  1258. if (!this->CDash) {
  1259. cmCTestOptionalLog(
  1260. this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  1261. << std::endl
  1262. << " Trigger site: "
  1263. << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl,
  1264. this->Quiet);
  1265. if (!this->TriggerUsingHTTP(
  1266. files, prefix,
  1267. this->CTest->GetCTestConfiguration("TriggerSite"))) {
  1268. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1269. " Problems when triggering via HTTP" << std::endl);
  1270. ofs << " Problems when triggering via HTTP" << std::endl;
  1271. return -1;
  1272. }
  1273. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1274. " Submission successful" << std::endl, this->Quiet);
  1275. ofs << " Submission successful" << std::endl;
  1276. return 0;
  1277. }
  1278. } else if (dropMethod == "http" || dropMethod == "https") {
  1279. std::string url = dropMethod;
  1280. url += "://";
  1281. ofs << "Using drop method: " << dropMethod << std::endl;
  1282. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1283. " Using HTTP submit method" << std::endl
  1284. << " Drop site:" << url,
  1285. this->Quiet);
  1286. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1287. url += this->CTest->GetCTestConfiguration("DropSiteUser");
  1288. cmCTestOptionalLog(
  1289. this->CTest, HANDLER_OUTPUT,
  1290. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(),
  1291. this->Quiet);
  1292. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  1293. url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
  1294. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******",
  1295. this->Quiet);
  1296. }
  1297. url += "@";
  1298. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet);
  1299. }
  1300. url += this->CTest->GetCTestConfiguration("DropSite") +
  1301. this->CTest->GetCTestConfiguration("DropLocation");
  1302. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1303. this->CTest->GetCTestConfiguration("DropSite")
  1304. << this->CTest->GetCTestConfiguration("DropLocation")
  1305. << std::endl,
  1306. this->Quiet);
  1307. if (!this->SubmitUsingHTTP(buildDirectory + "/Testing/" +
  1308. this->CTest->GetCurrentTag(),
  1309. files, prefix, url)) {
  1310. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1311. " Problems when submitting via HTTP" << std::endl);
  1312. ofs << " Problems when submitting via HTTP" << std::endl;
  1313. return -1;
  1314. }
  1315. if (!this->CDash) {
  1316. cmCTestOptionalLog(
  1317. this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  1318. << std::endl
  1319. << " Trigger site: "
  1320. << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl,
  1321. this->Quiet);
  1322. if (!this->TriggerUsingHTTP(
  1323. files, prefix,
  1324. this->CTest->GetCTestConfiguration("TriggerSite"))) {
  1325. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1326. " Problems when triggering via HTTP" << std::endl);
  1327. ofs << " Problems when triggering via HTTP" << std::endl;
  1328. return -1;
  1329. }
  1330. }
  1331. if (this->HasErrors) {
  1332. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Errors occurred during "
  1333. "submission."
  1334. << std::endl);
  1335. ofs << " Errors occurred during submission. " << std::endl;
  1336. } else {
  1337. cmCTestOptionalLog(
  1338. this->CTest, HANDLER_OUTPUT, " Submission successful"
  1339. << (this->HasWarnings ? ", with warnings." : "") << std::endl,
  1340. this->Quiet);
  1341. ofs << " Submission successful"
  1342. << (this->HasWarnings ? ", with warnings." : "") << std::endl;
  1343. }
  1344. return 0;
  1345. } else if (dropMethod == "xmlrpc") {
  1346. #if defined(CTEST_USE_XMLRPC)
  1347. ofs << "Using drop method: XML-RPC" << std::endl;
  1348. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1349. " Using XML-RPC submit method" << std::endl,
  1350. this->Quiet);
  1351. std::string url = this->CTest->GetCTestConfiguration("DropSite");
  1352. prefix = this->CTest->GetCTestConfiguration("DropLocation");
  1353. if (!this->SubmitUsingXMLRPC(buildDirectory + "/Testing/" +
  1354. this->CTest->GetCurrentTag(),
  1355. files, prefix, url)) {
  1356. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1357. " Problems when submitting via XML-RPC" << std::endl);
  1358. ofs << " Problems when submitting via XML-RPC" << std::endl;
  1359. return -1;
  1360. }
  1361. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1362. " Submission successful" << std::endl, this->Quiet);
  1363. ofs << " Submission successful" << std::endl;
  1364. return 0;
  1365. #else
  1366. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1367. " Submission method \"xmlrpc\" not compiled into CTest!"
  1368. << std::endl);
  1369. return -1;
  1370. #endif
  1371. } else if (dropMethod == "scp") {
  1372. std::string url;
  1373. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1374. url += this->CTest->GetCTestConfiguration("DropSiteUser") + "@";
  1375. }
  1376. url += this->CTest->GetCTestConfiguration("DropSite") + ":" +
  1377. this->CTest->GetCTestConfiguration("DropLocation");
  1378. // change to the build directory so that we can uses a relative path
  1379. // on windows since scp dosn't support "c:" a drive in the path
  1380. cmWorkingDirectory workdir(buildDirectory);
  1381. if (!this->SubmitUsingSCP(this->CTest->GetCTestConfiguration("ScpCommand"),
  1382. "Testing/" + this->CTest->GetCurrentTag(), files,
  1383. prefix, url)) {
  1384. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1385. " Problems when submitting via SCP" << std::endl);
  1386. ofs << " Problems when submitting via SCP" << std::endl;
  1387. return -1;
  1388. }
  1389. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1390. " Submission successful" << std::endl, this->Quiet);
  1391. ofs << " Submission successful" << std::endl;
  1392. return 0;
  1393. } else if (dropMethod == "cp") {
  1394. std::string location = this->CTest->GetCTestConfiguration("DropLocation");
  1395. // change to the build directory so that we can uses a relative path
  1396. // on windows since scp dosn't support "c:" a drive in the path
  1397. cmWorkingDirectory workdir(buildDirectory);
  1398. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1399. " Change directory: " << buildDirectory << std::endl,
  1400. this->Quiet);
  1401. if (!this->SubmitUsingCP("Testing/" + this->CTest->GetCurrentTag(), files,
  1402. prefix, location)) {
  1403. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1404. " Problems when submitting via CP" << std::endl);
  1405. ofs << " Problems when submitting via cp" << std::endl;
  1406. return -1;
  1407. }
  1408. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1409. " Submission successful" << std::endl, this->Quiet);
  1410. ofs << " Submission successful" << std::endl;
  1411. return 0;
  1412. }
  1413. cmCTestLog(this->CTest, ERROR_MESSAGE, " Unknown submission method: \""
  1414. << dropMethod << "\"" << std::endl);
  1415. return -1;
  1416. }
  1417. std::string cmCTestSubmitHandler::GetSubmitResultsPrefix()
  1418. {
  1419. std::string buildname =
  1420. cmCTest::SafeBuildIdField(this->CTest->GetCTestConfiguration("BuildName"));
  1421. std::string name = this->CTest->GetCTestConfiguration("Site") + "___" +
  1422. buildname + "___" + this->CTest->GetCurrentTag() + "-" +
  1423. this->CTest->GetTestModelString() + "___XML___";
  1424. return name;
  1425. }
  1426. void cmCTestSubmitHandler::SelectParts(std::set<cmCTest::Part> const& parts)
  1427. {
  1428. // Check whether each part is selected.
  1429. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  1430. p = cmCTest::Part(p + 1)) {
  1431. this->SubmitPart[p] =
  1432. (std::set<cmCTest::Part>::const_iterator(parts.find(p)) != parts.end());
  1433. }
  1434. }
  1435. void cmCTestSubmitHandler::SelectFiles(cmCTest::SetOfStrings const& files)
  1436. {
  1437. this->Files.insert(files.begin(), files.end());
  1438. }