diff options
Diffstat (limited to 'Source/CTest/cmCTestSubmitHandler.cxx')
-rw-r--r-- | Source/CTest/cmCTestSubmitHandler.cxx | 1177 |
1 files changed, 237 insertions, 940 deletions
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 8d62fa156..2ac5af691 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -2,44 +2,36 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestSubmitHandler.h" +#include <chrono> +#include <cstdio> +#include <cstdlib> +#include <sstream> + #include "cm_curl.h" #include "cm_jsoncpp_reader.h" #include "cm_jsoncpp_value.h" -#include "cmsys/Process.h" -#include <sstream> -#include <stdio.h> -#include <stdlib.h> +#include "cmAlgorithms.h" #include "cmCTest.h" #include "cmCTestCurl.h" #include "cmCTestScriptHandler.h" +#include "cmCryptoHash.h" #include "cmCurl.h" +#include "cmDuration.h" #include "cmGeneratedFileStream.h" -#include "cmProcessOutput.h" #include "cmState.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmThirdParty.h" -#include "cmWorkingDirectory.h" #include "cmXMLParser.h" #include "cmake.h" -#if defined(CTEST_USE_XMLRPC) -#include "cmVersion.h" -#include "cm_sys_stat.h" -#include "cm_xmlrpc.h" -#endif - #define SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT 120 -typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar; +using cmCTestSubmitHandlerVectorOfChar = std::vector<char>; class cmCTestSubmitHandler::ResponseParser : public cmXMLParser { public: - ResponseParser() { this->Status = STATUS_OK; } - ~ResponseParser() CM_OVERRIDE {} - -public: enum StatusType { STATUS_OK, @@ -47,10 +39,11 @@ public: STATUS_ERROR }; - StatusType Status; + StatusType Status = STATUS_OK; std::string Filename; std::string MD5; std::string Message; + std::string BuildID; private: std::vector<char> CurrentValue; @@ -65,17 +58,17 @@ private: } void StartElement(const std::string& /*name*/, - const char** /*atts*/) CM_OVERRIDE + const char** /*atts*/) override { this->CurrentValue.clear(); } - void CharacterDataHandler(const char* data, int length) CM_OVERRIDE + void CharacterDataHandler(const char* data, int length) override { - this->CurrentValue.insert(this->CurrentValue.end(), data, data + length); + cmAppend(this->CurrentValue, data, data + length); } - void EndElement(const std::string& name) CM_OVERRIDE + void EndElement(const std::string& name) override { if (name == "status") { std::string status = cmSystemTools::UpperCase(this->GetCurrentValue()); @@ -92,6 +85,8 @@ private: this->MD5 = this->GetCurrentValue(); } else if (name == "message") { this->Message = this->GetCurrentValue(); + } else if (name == "buildId") { + this->BuildID = this->GetCurrentValue(); } } }; @@ -99,13 +94,10 @@ private: static size_t cmCTestSubmitHandlerWriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) { - int realsize = (int)(size * nmemb); - - cmCTestSubmitHandlerVectorOfChar* vec = - static_cast<cmCTestSubmitHandlerVectorOfChar*>(data); + int realsize = static_cast<int>(size * nmemb); const char* chPtr = static_cast<char*>(ptr); - vec->insert(vec->end(), chPtr, chPtr + realsize); - + cmAppend(*static_cast<cmCTestSubmitHandlerVectorOfChar*>(data), chPtr, + chPtr + realsize); return realsize; } @@ -114,16 +106,12 @@ static size_t cmCTestSubmitHandlerCurlDebugCallback(CURL* /*unused*/, char* chPtr, size_t size, void* data) { - cmCTestSubmitHandlerVectorOfChar* vec = - static_cast<cmCTestSubmitHandlerVectorOfChar*>(data); - vec->insert(vec->end(), chPtr, chPtr + size); - + cmAppend(*static_cast<cmCTestSubmitHandlerVectorOfChar*>(data), chPtr, + chPtr + size); return size; } cmCTestSubmitHandler::cmCTestSubmitHandler() - : HTTPProxy() - , FTPProxy() { this->Initialize(); } @@ -135,167 +123,19 @@ void cmCTestSubmitHandler::Initialize() p = cmCTest::Part(p + 1)) { this->SubmitPart[p] = true; } - this->CDash = false; this->HasWarnings = false; this->HasErrors = false; this->Superclass::Initialize(); - this->HTTPProxy = ""; + this->HTTPProxy.clear(); this->HTTPProxyType = 0; - this->HTTPProxyAuth = ""; - this->FTPProxy = ""; - this->FTPProxyType = 0; - this->LogFile = CM_NULLPTR; + this->HTTPProxyAuth.clear(); + this->LogFile = nullptr; this->Files.clear(); } -bool cmCTestSubmitHandler::SubmitUsingFTP(const std::string& localprefix, - const std::set<std::string>& files, - const std::string& remoteprefix, - const std::string& url) -{ - CURL* curl; - CURLcode res; - FILE* ftpfile; - char error_buffer[1024]; - - /* In windows, this will init the winsock stuff */ - ::curl_global_init(CURL_GLOBAL_ALL); - - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { - /* get a curl handle */ - curl = curl_easy_init(); - if (curl) { - // Using proxy - if (this->FTPProxyType > 0) { - curl_easy_setopt(curl, CURLOPT_PROXY, this->FTPProxy.c_str()); - switch (this->FTPProxyType) { - case 2: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); - break; - case 3: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - break; - default: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } - } - - // enable uploading - ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); - - // if there is little to no activity for too long stop submitting - ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1); - ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, - SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT); - - ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); - - std::string local_file = *file; - if (!cmSystemTools::FileExists(local_file.c_str())) { - local_file = localprefix + "/" + *file; - } - std::string upload_as = - url + "/" + remoteprefix + cmSystemTools::GetFilenameName(*file); - - if (!cmSystemTools::FileExists(local_file.c_str())) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Cannot find file: " << local_file << std::endl); - ::curl_easy_cleanup(curl); - ::curl_global_cleanup(); - return false; - } - unsigned long filelen = cmSystemTools::FileLength(local_file); - - ftpfile = cmsys::SystemTools::Fopen(local_file, "rb"); - *this->LogFile << "\tUpload file: " << local_file << " to " << upload_as - << std::endl; - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - " Upload file: " << local_file << " to " - << upload_as << std::endl, - this->Quiet); - - ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - - // specify target - ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str()); - - // now specify which file to upload - ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile); - - // and give the size of the upload (optional) - ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(filelen)); - - // and give curl the buffer for errors - ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer); - - // specify handler for output - ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - cmCTestSubmitHandlerWriteMemoryCallback); - ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, - cmCTestSubmitHandlerCurlDebugCallback); - - /* we pass our 'chunk' struct to the callback function */ - cmCTestSubmitHandlerVectorOfChar chunk; - cmCTestSubmitHandlerVectorOfChar chunkDebug; - ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk); - ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug); - - // Now run off and do what you've been told! - res = ::curl_easy_perform(curl); - - if (!chunk.empty()) { - cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) - << "]" << std::endl, - this->Quiet); - } - if (!chunkDebug.empty()) { - cmCTestOptionalLog( - this->CTest, DEBUG, "CURL debug output: [" - << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]" - << std::endl, - this->Quiet); - } - - fclose(ftpfile); - if (res) { - cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: " - << local_file << std::endl); - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Error message was: " << error_buffer << std::endl); - *this->LogFile << " Error when uploading file: " << local_file - << std::endl - << " Error message was: " << error_buffer << std::endl - << " Curl output was: "; - // avoid dereference of empty vector - if (!chunk.empty()) { - *this->LogFile << cmCTestLogWrite(&*chunk.begin(), chunk.size()); - cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" - << std::endl); - } - *this->LogFile << std::endl; - ::curl_easy_cleanup(curl); - ::curl_global_cleanup(); - return false; - } - // always cleanup - ::curl_easy_cleanup(curl); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Uploaded: " + local_file << std::endl, - this->Quiet); - } - } - ::curl_global_cleanup(); - return true; -} - -// Uploading files is simpler -bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, - const std::set<std::string>& files, - const std::string& remoteprefix, - const std::string& url) +bool cmCTestSubmitHandler::SubmitUsingHTTP( + const std::string& localprefix, const std::vector<std::string>& files, + const std::string& remoteprefix, const std::string& url) { CURL* curl; CURLcode res; @@ -303,37 +143,31 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, char error_buffer[1024]; // Set Content-Type to satisfy fussy modsecurity rules. struct curl_slist* headers = - ::curl_slist_append(CM_NULLPTR, "Content-Type: text/xml"); + ::curl_slist_append(nullptr, "Content-Type: text/xml"); // Add any additional headers that the user specified. - for (std::vector<std::string>::const_iterator h = this->HttpHeaders.begin(); - h != this->HttpHeaders.end(); ++h) { - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Add HTTP Header: \"" << *h << "\"" << std::endl, + for (std::string const& h : this->HttpHeaders) { + cmCTestOptionalLog(this->CTest, DEBUG, + " Add HTTP Header: \"" << h << "\"" << std::endl, this->Quiet); - headers = ::curl_slist_append(headers, h->c_str()); + headers = ::curl_slist_append(headers, h.c_str()); } /* In windows, this will init the winsock stuff */ ::curl_global_init(CURL_GLOBAL_ALL); - std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod")); std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions")); - std::vector<std::string> args; - cmSystemTools::ExpandListArgument(curlopt, args); + std::vector<std::string> args = cmExpandedList(curlopt); bool verifyPeerOff = false; bool verifyHostOff = false; - for (std::vector<std::string>::iterator i = args.begin(); i != args.end(); - ++i) { - if (*i == "CURLOPT_SSL_VERIFYPEER_OFF") { + for (std::string const& arg : args) { + if (arg == "CURLOPT_SSL_VERIFYPEER_OFF") { verifyPeerOff = true; } - if (*i == "CURLOPT_SSL_VERIFYHOST_OFF") { + if (arg == "CURLOPT_SSL_VERIFYHOST_OFF") { verifyHostOff = true; } } - std::string::size_type kk; - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { + for (std::string const& file : files) { /* get a curl handle */ curl = curl_easy_init(); if (curl) { @@ -388,53 +222,72 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - std::string local_file = *file; - if (!cmSystemTools::FileExists(local_file.c_str())) { - local_file = localprefix + "/" + *file; + std::string local_file = file; + bool initialize_cdash_buildid = false; + if (!cmSystemTools::FileExists(local_file)) { + local_file = cmStrCat(localprefix, "/", file); + // If this file exists within the local Testing directory we assume + // that it will be associated with the current build in CDash. + initialize_cdash_buildid = true; } std::string remote_file = - remoteprefix + cmSystemTools::GetFilenameName(*file); + remoteprefix + cmSystemTools::GetFilenameName(file); *this->LogFile << "\tUpload file: " << local_file << " to " << remote_file << std::endl; - std::string ofile; - for (kk = 0; kk < remote_file.size(); kk++) { - char c = remote_file[kk]; - char hexCh[4] = { 0, 0, 0, 0 }; - hexCh[0] = c; - switch (c) { - case '+': - case '?': - case '/': - case '\\': - case '&': - case ' ': - case '=': - case '%': - sprintf(hexCh, "%%%02X", (int)c); - ofile.append(hexCh); - break; - default: - ofile.append(hexCh); + std::string ofile = cmSystemTools::EncodeURL(remote_file); + std::string upload_as = + cmStrCat(url, ((url.find('?') == std::string::npos) ? '?' : '&'), + "FileName=", ofile); + + if (initialize_cdash_buildid) { + // Provide extra arguments to CDash so that it can initialize and + // return a buildid. + cmCTestCurl ctest_curl(this->CTest); + upload_as += "&build="; + upload_as += + ctest_curl.Escape(this->CTest->GetCTestConfiguration("BuildName")); + upload_as += "&site="; + upload_as += + ctest_curl.Escape(this->CTest->GetCTestConfiguration("Site")); + upload_as += "&stamp="; + upload_as += ctest_curl.Escape(this->CTest->GetCurrentTag()); + upload_as += "-"; + upload_as += ctest_curl.Escape(this->CTest->GetTestModelString()); + cmCTestScriptHandler* ch = this->CTest->GetScriptHandler(); + cmake* cm = ch->GetCMake(); + if (cm) { + const char* subproject = + cm->GetState()->GetGlobalProperty("SubProject"); + if (subproject) { + upload_as += "&subproject="; + upload_as += ctest_curl.Escape(subproject); + } } } - std::string upload_as = url + - ((url.find('?') == std::string::npos) ? '?' : '&') + "FileName=" + - ofile; + + // Generate Done.xml right before it is submitted. + // The reason for this is two-fold: + // 1) It must be generated after some other part has been submitted + // so we have a buildId to refer to in its contents. + // 2) By generating Done.xml here its timestamp will be as late as + // possible. This gives us a more accurate record of how long the + // entire build took to complete. + if (file == "Done.xml") { + this->CTest->GenerateDoneFile(); + } upload_as += "&MD5="; - if (cmSystemTools::IsOn(this->GetOption("InternalTest"))) { + if (cmIsOn(this->GetOption("InternalTest"))) { upload_as += "bad_md5sum"; } else { - char md5[33]; - cmSystemTools::ComputeFileMD5(local_file, md5); - md5[32] = 0; - upload_as += md5; + upload_as += + cmSystemTools::ComputeFileHash(local_file, cmCryptoHash::AlgoMD5); } - if (!cmSystemTools::FileExists(local_file.c_str())) { + if (!cmSystemTools::FileExists(local_file)) { cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot find file: " << local_file << std::endl); ::curl_easy_cleanup(curl); @@ -454,6 +307,10 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, // specify target ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str()); + // CURLAUTH_BASIC is default, and here we allow additional methods, + // including more secure ones + ::curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + // now specify which file to upload ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile); @@ -472,23 +329,25 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, /* we pass our 'chunk' struct to the callback function */ cmCTestSubmitHandlerVectorOfChar chunk; cmCTestSubmitHandlerVectorOfChar chunkDebug; - ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk); - ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug); + ::curl_easy_setopt(curl, CURLOPT_FILE, &chunk); + ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug); // Now run off and do what you've been told! res = ::curl_easy_perform(curl); if (!chunk.empty()) { - cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) + cmCTestOptionalLog(this->CTest, DEBUG, + "CURL output: [" + << cmCTestLogWrite(chunk.data(), chunk.size()) << "]" << std::endl, this->Quiet); this->ParseResponse(chunk); } if (!chunkDebug.empty()) { cmCTestOptionalLog( - this->CTest, DEBUG, "CURL debug output: [" - << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]" + this->CTest, DEBUG, + "CURL debug output: [" + << cmCTestLogWrite(chunkDebug.data(), chunkDebug.size()) << "]" << std::endl, this->Quiet); } @@ -496,30 +355,31 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, // If curl failed for any reason, or checksum fails, wait and retry // if (res != CURLE_OK || this->HasErrors) { - std::string retryDelay = this->GetOption("RetryDelay") == CM_NULLPTR + std::string retryDelay = this->GetOption("RetryDelay") == nullptr ? "" : this->GetOption("RetryDelay"); - std::string retryCount = this->GetOption("RetryCount") == CM_NULLPTR + std::string retryCount = this->GetOption("RetryCount") == nullptr ? "" : this->GetOption("RetryCount"); - int delay = retryDelay == "" - ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryDelay") - .c_str()) - : atoi(retryDelay.c_str()); - int count = retryCount == "" + auto delay = cmDuration( + retryDelay.empty() + ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryDelay") + .c_str()) + : atoi(retryDelay.c_str())); + int count = retryCount.empty() ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryCount") .c_str()) : atoi(retryCount.c_str()); for (int i = 0; i < count; i++) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submit failed, waiting " << delay + " Submit failed, waiting " << delay.count() << " seconds...\n", this->Quiet); - double stop = cmSystemTools::GetTime() + delay; - while (cmSystemTools::GetTime() < stop) { + auto stop = std::chrono::steady_clock::now() + delay; + while (std::chrono::steady_clock::now() < stop) { cmSystemTools::Delay(100); } @@ -539,11 +399,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, res = ::curl_easy_perform(curl); if (!chunk.empty()) { - cmCTestOptionalLog( - this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" - << std::endl, - this->Quiet); + cmCTestOptionalLog(this->CTest, DEBUG, + "CURL output: [" + << cmCTestLogWrite(chunk.data(), chunk.size()) + << "]" << std::endl, + this->Quiet); this->ParseResponse(chunk); } @@ -555,8 +415,9 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, fclose(ftpfile); if (res) { - cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: " - << local_file << std::endl); + cmCTestLog(this->CTest, ERROR_MESSAGE, + " Error when uploading file: " << local_file + << std::endl); cmCTestLog(this->CTest, ERROR_MESSAGE, " Error message was: " << error_buffer << std::endl); *this->LogFile << " Error when uploading file: " << local_file @@ -566,10 +427,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, // avoid deref of begin for zero size array if (!chunk.empty()) { *this->LogFile << " Curl output was: " - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) + << cmCTestLogWrite(chunk.data(), chunk.size()) << std::endl; - cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" + cmCTestLog(this->CTest, ERROR_MESSAGE, + "CURL output: [" + << cmCTestLogWrite(chunk.data(), chunk.size()) << "]" << std::endl); } ::curl_easy_cleanup(curl); @@ -605,6 +467,7 @@ void cmCTestSubmitHandler::ParseResponse( " Submission failed: " << parser.Message << std::endl); return; } + this->CTest->SetBuildID(parser.BuildID); } output = cmSystemTools::UpperCase(output); if (output.find("WARNING") != std::string::npos) { @@ -615,395 +478,12 @@ void cmCTestSubmitHandler::ParseResponse( } if (this->HasWarnings || this->HasErrors) { - cmCTestLog(this->CTest, HANDLER_OUTPUT, " Server Response:\n" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n"); + cmCTestLog(this->CTest, HANDLER_OUTPUT, + " Server Response:\n" + << cmCTestLogWrite(chunk.data(), chunk.size()) << "\n"); } } -bool cmCTestSubmitHandler::TriggerUsingHTTP(const std::set<std::string>& files, - const std::string& remoteprefix, - const std::string& url) -{ - CURL* curl; - char error_buffer[1024]; - - /* In windows, this will init the winsock stuff */ - ::curl_global_init(CURL_GLOBAL_ALL); - - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { - /* get a curl handle */ - curl = curl_easy_init(); - if (curl) { - // Using proxy - if (this->HTTPProxyType > 0) { - curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str()); - switch (this->HTTPProxyType) { - case 2: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); - break; - case 3: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - break; - default: - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - if (!this->HTTPProxyAuth.empty()) { - curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, - this->HTTPProxyAuth.c_str()); - } - } - } - - ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - - // and give curl the buffer for errors - ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer); - - // specify handler for output - ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - cmCTestSubmitHandlerWriteMemoryCallback); - ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, - cmCTestSubmitHandlerCurlDebugCallback); - - /* we pass our 'chunk' struct to the callback function */ - cmCTestSubmitHandlerVectorOfChar chunk; - cmCTestSubmitHandlerVectorOfChar chunkDebug; - ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk); - ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug); - - std::string rfile = remoteprefix + cmSystemTools::GetFilenameName(*file); - std::string ofile; - std::string::iterator kk; - for (kk = rfile.begin(); kk < rfile.end(); ++kk) { - char c = *kk; - char hexCh[4] = { 0, 0, 0, 0 }; - hexCh[0] = c; - switch (c) { - case '+': - case '?': - case '/': - case '\\': - case '&': - case ' ': - case '=': - case '%': - sprintf(hexCh, "%%%02X", (int)c); - ofile.append(hexCh); - break; - default: - ofile.append(hexCh); - } - } - std::string turl = url + - ((url.find('?') == std::string::npos) ? '?' : '&') + "xmlfile=" + - ofile; - *this->LogFile << "Trigger url: " << turl << std::endl; - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - " Trigger url: " << turl << std::endl, this->Quiet); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - curl_easy_setopt(curl, CURLOPT_URL, turl.c_str()); - if (curl_easy_perform(curl)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Error when triggering: " << turl << std::endl); - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Error message was: " << error_buffer << std::endl); - *this->LogFile << "\tTriggering failed with error: " << error_buffer - << std::endl - << " Error message was: " << error_buffer - << std::endl; - if (!chunk.empty()) { - *this->LogFile << " Curl output was: " - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) - << std::endl; - cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" - << std::endl); - } - ::curl_easy_cleanup(curl); - ::curl_global_cleanup(); - return false; - } - - if (!chunk.empty()) { - cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) - << "]" << std::endl, - this->Quiet); - } - if (!chunkDebug.empty()) { - cmCTestOptionalLog( - this->CTest, DEBUG, "CURL debug output: [" - << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]" - << std::endl, - this->Quiet); - } - - // always cleanup - ::curl_easy_cleanup(curl); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl, - this->Quiet); - } - } - ::curl_global_cleanup(); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Dart server triggered..." << std::endl, this->Quiet); - return true; -} - -bool cmCTestSubmitHandler::SubmitUsingSCP(const std::string& scp_command, - const std::string& localprefix, - const std::set<std::string>& files, - const std::string& remoteprefix, - const std::string& url) -{ - if (scp_command.empty() || localprefix.empty() || files.empty() || - remoteprefix.empty() || url.empty()) { - return false; - } - - std::vector<const char*> argv; - argv.push_back(scp_command.c_str()); // Scp command - argv.push_back(scp_command.c_str()); // Dummy string for file - argv.push_back(scp_command.c_str()); // Dummy string for remote url - argv.push_back(CM_NULLPTR); - - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - // cmsysProcess_SetTimeout(cp, timeout); - - int problems = 0; - - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { - int retVal; - - std::string lfname = localprefix; - cmSystemTools::ConvertToUnixSlashes(lfname); - lfname += "/" + *file; - lfname = cmSystemTools::ConvertToOutputPath(lfname.c_str()); - argv[1] = lfname.c_str(); - std::string rfname = url + "/" + remoteprefix + *file; - argv[2] = rfname.c_str(); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Execute \"" - << argv[0] << "\" \"" << argv[1] << "\" \"" << argv[2] - << "\"" << std::endl, - this->Quiet); - *this->LogFile << "Execute \"" << argv[0] << "\" \"" << argv[1] << "\" \"" - << argv[2] << "\"" << std::endl; - - cmsysProcess_SetCommand(cp, &*argv.begin()); - cmsysProcess_Execute(cp); - char* data; - int length; - cmProcessOutput processOutput; - std::string strdata; - - while (cmsysProcess_WaitForData(cp, &data, &length, CM_NULLPTR)) { - processOutput.DecodeText(data, length, strdata); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size()), - this->Quiet); - } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size()), - this->Quiet); - } - - cmsysProcess_WaitForExit(cp, CM_NULLPTR); - - int result = cmsysProcess_GetState(cp); - - if (result == cmsysProcess_State_Exited) { - retVal = cmsysProcess_GetExitValue(cp); - if (retVal != 0) { - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "\tSCP returned: " << retVal << std::endl, - this->Quiet); - *this->LogFile << "\tSCP returned: " << retVal << std::endl; - problems++; - } - } else if (result == cmsysProcess_State_Exception) { - retVal = cmsysProcess_GetExitException(cp); - cmCTestLog(this->CTest, ERROR_MESSAGE, - "\tThere was an exception: " << retVal << std::endl); - *this->LogFile << "\tThere was an exception: " << retVal << std::endl; - problems++; - } else if (result == cmsysProcess_State_Expired) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was a timeout" - << std::endl); - *this->LogFile << "\tThere was a timeout" << std::endl; - problems++; - } else if (result == cmsysProcess_State_Error) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "\tError executing SCP: " - << cmsysProcess_GetErrorString(cp) << std::endl); - *this->LogFile << "\tError executing SCP: " - << cmsysProcess_GetErrorString(cp) << std::endl; - problems++; - } - } - cmsysProcess_Delete(cp); - return problems == 0; -} - -bool cmCTestSubmitHandler::SubmitUsingCP(const std::string& localprefix, - const std::set<std::string>& files, - const std::string& remoteprefix, - const std::string& destination) -{ - if (localprefix.empty() || files.empty() || remoteprefix.empty() || - destination.empty()) { - /* clang-format off */ - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Missing arguments for submit via cp:\n" - << "\tlocalprefix: " << localprefix << "\n" - << "\tNumber of files: " << files.size() << "\n" - << "\tremoteprefix: " << remoteprefix << "\n" - << "\tdestination: " << destination << std::endl); - /* clang-format on */ - return false; - } - - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { - std::string lfname = localprefix; - cmSystemTools::ConvertToUnixSlashes(lfname); - lfname += "/" + *file; - std::string rfname = destination + "/" + remoteprefix + *file; - cmSystemTools::CopyFileAlways(lfname, rfname); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Copy file: " - << lfname << " to " << rfname << std::endl, - this->Quiet); - } - std::string tagDoneFile = destination + "/" + remoteprefix + "DONE"; - cmSystemTools::Touch(tagDoneFile, true); - return true; -} - -#if defined(CTEST_USE_XMLRPC) -bool cmCTestSubmitHandler::SubmitUsingXMLRPC( - const std::string& localprefix, const std::set<std::string>& files, - const std::string& remoteprefix, const std::string& url) -{ - xmlrpc_env env; - char ctestString[] = "CTest"; - std::string ctestVersionString = cmVersion::GetCMakeVersion(); - char* ctestVersion = const_cast<char*>(ctestVersionString.c_str()); - - std::string realURL = url + "/" + remoteprefix + "/Command/"; - - /* Start up our XML-RPC client library. */ - xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, ctestString, ctestVersion); - - /* Initialize our error-handling environment. */ - xmlrpc_env_init(&env); - - /* Call the famous server at UserLand. */ - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Submitting to: " - << realURL << " (" << remoteprefix << ")" << std::endl, - this->Quiet); - cmCTest::SetOfStrings::const_iterator file; - for (file = files.begin(); file != files.end(); ++file) { - xmlrpc_value* result; - - std::string local_file = *file; - if (!cmSystemTools::FileExists(local_file.c_str())) { - local_file = localprefix + "/" + *file; - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submit file: " << local_file << std::endl, - this->Quiet); - struct stat st; - if (::stat(local_file.c_str(), &st)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Cannot find file: " << local_file << std::endl); - return false; - } - - // off_t can be bigger than size_t. fread takes size_t. - // make sure the file is not too big. - if (static_cast<off_t>(static_cast<size_t>(st.st_size)) != - static_cast<off_t>(st.st_size)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, " File too big: " << local_file - << std::endl); - return false; - } - size_t fileSize = static_cast<size_t>(st.st_size); - FILE* fp = cmsys::SystemTools::Fopen(local_file, "rb"); - if (!fp) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Cannot open file: " << local_file << std::endl); - return false; - } - - unsigned char* fileBuffer = new unsigned char[fileSize]; - if (fread(fileBuffer, 1, fileSize, fp) != fileSize) { - delete[] fileBuffer; - fclose(fp); - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Cannot read file: " << local_file << std::endl); - return false; - } - fclose(fp); - - char remoteCommand[] = "Submit.put"; - char* pRealURL = const_cast<char*>(realURL.c_str()); - result = xmlrpc_client_call(&env, pRealURL, remoteCommand, "(6)", - fileBuffer, (xmlrpc_int32)fileSize); - - delete[] fileBuffer; - - if (env.fault_occurred) { - cmCTestLog(this->CTest, ERROR_MESSAGE, " Submission problem: " - << env.fault_string << " (" << env.fault_code << ")" - << std::endl); - xmlrpc_env_clean(&env); - xmlrpc_client_cleanup(); - return false; - } - - /* Dispose of our result value. */ - xmlrpc_DECREF(result); - } - - /* Clean up our error-handling environment. */ - xmlrpc_env_clean(&env); - - /* Shutdown our XML-RPC client library. */ - xmlrpc_client_cleanup(); - return true; -} -#else -bool cmCTestSubmitHandler::SubmitUsingXMLRPC( - std::string const& /*unused*/, std::set<std::string> const& /*unused*/, - std::string const& /*unused*/, std::string const& /*unused*/) -{ - return false; -} -#endif - -void cmCTestSubmitHandler::ConstructCDashURL(std::string& dropMethod, - std::string& url) -{ - dropMethod = this->CTest->GetCTestConfiguration("DropMethod"); - url = dropMethod; - url += "://"; - if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) { - url += this->CTest->GetCTestConfiguration("DropSiteUser"); - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, - this->CTest->GetCTestConfiguration("DropSiteUser").c_str(), this->Quiet); - if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) { - url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword"); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******", this->Quiet); - } - url += "@"; - } - url += this->CTest->GetCTestConfiguration("DropSite") + - this->CTest->GetCTestConfiguration("DropLocation"); -} - int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, std::string const& typeString) { @@ -1012,70 +492,75 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, return -1; } if (!cmSystemTools::FileExists(file)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Upload file not found: '" - << file << "'\n"); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Upload file not found: '" << file << "'\n"); return -1; } cmCTestCurl curl(this->CTest); curl.SetQuiet(this->Quiet); std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions")); - std::vector<std::string> args; - cmSystemTools::ExpandListArgument(curlopt, args); + std::vector<std::string> args = cmExpandedList(curlopt); curl.SetCurlOptions(args); curl.SetTimeOutSeconds(SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT); curl.SetHttpHeaders(this->HttpHeaders); - std::string dropMethod; - std::string url; - this->ConstructCDashURL(dropMethod, url); - std::string::size_type pos = url.find("submit.php?"); - url = url.substr(0, pos + 10); - if (!(dropMethod == "http" || dropMethod == "https")) { + std::string url = this->CTest->GetSubmitURL(); + std::string fields; + std::string::size_type pos = url.find('?'); + if (pos != std::string::npos) { + fields = url.substr(pos + 1); + url = url.substr(0, pos); + } + if (!cmHasLiteralPrefix(url, "http://") && + !cmHasLiteralPrefix(url, "https://")) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Only http and https are supported for CDASH_UPLOAD\n"); return -1; } - bool internalTest = cmSystemTools::IsOn(this->GetOption("InternalTest")); + bool internalTest = cmIsOn(this->GetOption("InternalTest")); // Get RETRY_COUNT and RETRY_DELAY values if they were set. - std::string retryDelayString = this->GetOption("RetryDelay") == CM_NULLPTR + std::string retryDelayString = this->GetOption("RetryDelay") == nullptr ? "" : this->GetOption("RetryDelay"); - std::string retryCountString = this->GetOption("RetryCount") == CM_NULLPTR + std::string retryCountString = this->GetOption("RetryCount") == nullptr ? "" : this->GetOption("RetryCount"); - unsigned long retryDelay = 0; - if (retryDelayString != "") { - if (!cmSystemTools::StringToULong(retryDelayString.c_str(), &retryDelay)) { - cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : " - << retryDelayString << std::endl); + auto retryDelay = std::chrono::seconds(0); + if (!retryDelayString.empty()) { + unsigned long retryDelayValue = 0; + if (!cmStrToULong(retryDelayString, &retryDelayValue)) { + cmCTestLog(this->CTest, WARNING, + "Invalid value for 'RETRY_DELAY' : " << retryDelayString + << std::endl); + } else { + retryDelay = std::chrono::seconds(retryDelayValue); } } unsigned long retryCount = 0; - if (retryCountString != "") { - if (!cmSystemTools::StringToULong(retryCountString.c_str(), &retryCount)) { - cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : " - << retryCountString << std::endl); + if (!retryCountString.empty()) { + if (!cmStrToULong(retryCountString, &retryCount)) { + cmCTestLog(this->CTest, WARNING, + "Invalid value for 'RETRY_DELAY' : " << retryCountString + << std::endl); } } - char md5sum[33]; - md5sum[32] = 0; - cmSystemTools::ComputeFileMD5(file, md5sum); + std::string md5sum = + cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5); // 1. request the buildid and check to see if the file // has already been uploaded // TODO I added support for subproject. You would need to add // a "&subproject=subprojectname" to the first POST. - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->CTest->GetHandler("script")); + cmCTestScriptHandler* ch = this->CTest->GetScriptHandler(); cmake* cm = ch->GetCMake(); const char* subproject = cm->GetState()->GetGlobalProperty("SubProject"); // TODO: Encode values for a URL instead of trusting caller. std::ostringstream str; - str << "project=" - << curl.Escape(this->CTest->GetCTestConfiguration("ProjectName")) << "&"; if (subproject) { str << "subproject=" << curl.Escape(subproject) << "&"; } + auto timeNow = + std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); str << "stamp=" << curl.Escape(this->CTest->GetCurrentTag()) << "-" << curl.Escape(this->CTest->GetTestModelString()) << "&" << "model=" << curl.Escape(this->CTest->GetTestModelString()) << "&" @@ -1083,12 +568,20 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, << curl.Escape(this->CTest->GetCTestConfiguration("BuildName")) << "&" << "site=" << curl.Escape(this->CTest->GetCTestConfiguration("Site")) << "&" + << "group=" << curl.Escape(this->CTest->GetTestModelString()) + << "&" + // For now, we send both "track" and "group" to CDash in case we're + // submitting to an older instance that still expects the prior + // terminology. << "track=" << curl.Escape(this->CTest->GetTestModelString()) << "&" - << "starttime=" << (int)cmSystemTools::GetTime() << "&" - << "endtime=" << (int)cmSystemTools::GetTime() << "&" + << "starttime=" << timeNow << "&" + << "endtime=" << timeNow << "&" << "datafilesmd5[0]=" << md5sum << "&" << "type=" << curl.Escape(typeString); - std::string fields = str.str(); + if (!fields.empty()) { + fields += '&'; + } + fields += str.str(); cmCTestOptionalLog(this->CTest, DEBUG, "fields: " << fields << "\nurl:" << url << "\nfile: " << file << "\n", @@ -1100,12 +593,12 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, // If request failed, wait and retry. for (unsigned long i = 0; i < retryCount; i++) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Request failed, waiting " << retryDelay + " Request failed, waiting " << retryDelay.count() << " seconds...\n", this->Quiet); - double stop = cmSystemTools::GetTime() + static_cast<double>(retryDelay); - while (cmSystemTools::GetTime() < stop) { + auto stop = std::chrono::steady_clock::now() + retryDelay; + while (std::chrono::steady_clock::now() < stop) { cmSystemTools::Delay(100); } @@ -1121,7 +614,8 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, } } if (!internalTest && !requestSucceeded) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "Error in HttpRequest\n" + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Error in HttpRequest\n" << response); return -1; } @@ -1131,7 +625,8 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, Json::Value json; Json::Reader reader; if (!internalTest && !reader.parse(response, json)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string [" + cmCTestLog(this->CTest, ERROR_MESSAGE, + "error parsing json string [" << response << "]\n" << reader.getFormattedErrorMessages() << "\n"); return -1; @@ -1174,12 +669,12 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, // If upload failed, wait and retry. for (unsigned long i = 0; i < retryCount; i++) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Upload failed, waiting " << retryDelay + " Upload failed, waiting " << retryDelay.count() << " seconds...\n", this->Quiet); - double stop = cmSystemTools::GetTime() + static_cast<double>(retryDelay); - while (cmSystemTools::GetTime() < stop) { + auto stop = std::chrono::steady_clock::now() + retryDelay; + while (std::chrono::steady_clock::now() < stop) { cmSystemTools::Delay(100); } @@ -1198,12 +693,14 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, } if (!uploadSucceeded) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "error uploading to CDash. " - << file << " " << url << " " << fstr.str()); + cmCTestLog(this->CTest, ERROR_MESSAGE, + "error uploading to CDash. " << file << " " << url << " " + << fstr.str()); return -1; } if (!reader.parse(response, json)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string [" + cmCTestLog(this->CTest, ERROR_MESSAGE, + "error parsing json string [" << response << "]\n" << reader.getFormattedErrorMessages() << "\n"); return -1; @@ -1221,11 +718,6 @@ int cmCTestSubmitHandler::ProcessHandler() if (cdashUploadFile && cdashUploadType) { return this->HandleCDashUploadFile(cdashUploadFile, cdashUploadType); } - std::string iscdash = this->CTest->GetCTestConfiguration("IsCDash"); - // cdash does not need to trigger so just return true - if (!iscdash.empty()) { - this->CDash = true; - } const std::string& buildDirectory = this->CTest->GetCTestConfiguration("BuildDirectory"); @@ -1263,46 +755,20 @@ int cmCTestSubmitHandler::ProcessHandler() } } - if (getenv("FTP_PROXY")) { - this->FTPProxyType = 1; - this->FTPProxy = getenv("FTP_PROXY"); - if (getenv("FTP_PROXY_PORT")) { - this->FTPProxy += ":"; - this->FTPProxy += getenv("FTP_PROXY_PORT"); - } - if (getenv("FTP_PROXY_TYPE")) { - std::string type = getenv("FTP_PROXY_TYPE"); - // HTTP/SOCKS4/SOCKS5 - if (type == "HTTP") { - this->FTPProxyType = 1; - } else if (type == "SOCKS4") { - this->FTPProxyType = 2; - } else if (type == "SOCKS5") { - this->FTPProxyType = 3; - } - } - } - if (!this->HTTPProxy.empty()) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Use HTTP Proxy: " << this->HTTPProxy << std::endl, this->Quiet); } - if (!this->FTPProxy.empty()) { - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Use FTP Proxy: " << this->FTPProxy << std::endl, - this->Quiet); - } cmGeneratedFileStream ofs; this->StartLogFile("Submit", ofs); - cmCTest::SetOfStrings files; + std::vector<std::string> files; std::string prefix = this->GetSubmitResultsPrefix(); if (!this->Files.empty()) { // Submit the explicitly selected files: - // - files.insert(this->Files.begin(), this->Files.end()); + cmAppend(files, this->Files); } // Add to the list of files to submit from any selected, existing parts: @@ -1324,13 +790,11 @@ int cmCTestSubmitHandler::ProcessHandler() cmCTestOptionalLog(this->CTest, DEBUG, "Globbing for: " << gpath << std::endl, this->Quiet); if (cmSystemTools::SimpleGlob(gpath, gfiles, 1)) { - size_t cc; - for (cc = 0; cc < gfiles.size(); cc++) { - gfiles[cc] = gfiles[cc].substr(glen); + for (std::string& gfile : gfiles) { + gfile = gfile.substr(glen); cmCTestOptionalLog(this->CTest, DEBUG, - "Glob file: " << gfiles[cc] << std::endl, - this->Quiet); - this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfiles[cc].c_str()); + "Glob file: " << gfile << std::endl, this->Quiet); + this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfile.c_str()); } } else { cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem globbing" << std::endl); @@ -1350,234 +814,67 @@ int cmCTestSubmitHandler::ProcessHandler() } // Submit files from this part. - std::vector<std::string> const& pfiles = this->CTest->GetSubmitFiles(p); - files.insert(pfiles.begin(), pfiles.end()); + cmAppend(files, this->CTest->GetSubmitFiles(p)); + } + + // Make sure files are unique, but preserve order. + { + // This endPos intermediate is needed to work around non-conformant C++11 + // standard libraries that have erase(iterator,iterator) instead of + // erase(const_iterator,const_iterator). + size_t endPos = cmRemoveDuplicates(files) - files.cbegin(); + files.erase(files.begin() + endPos, files.end()); + } + + // Submit Done.xml last + if (this->SubmitPart[cmCTest::PartDone]) { + files.emplace_back("Done.xml"); } if (ofs) { ofs << "Upload files:" << std::endl; int cnt = 0; - cmCTest::SetOfStrings::iterator it; - for (it = files.begin(); it != files.end(); ++it) { - ofs << cnt << "\t" << *it << std::endl; + for (std::string const& file : files) { + ofs << cnt << "\t" << file << std::endl; cnt++; } } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Submit files (using " - << this->CTest->GetCTestConfiguration("DropMethod") - << ")" << std::endl, + cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Submit files\n", this->Quiet); - const char* specificTrack = this->CTest->GetSpecificTrack(); - if (specificTrack) { + const char* specificGroup = this->CTest->GetSpecificGroup(); + if (specificGroup) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Send to track: " << specificTrack << std::endl, + " Send to group: " << specificGroup << std::endl, this->Quiet); } this->SetLogFile(&ofs); - std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod")); - - if (dropMethod == "" || dropMethod == "ftp") { - ofs << "Using drop method: FTP" << std::endl; - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Using FTP submit method" << std::endl - << " Drop site: ftp://", - this->Quiet); - std::string url = "ftp://"; - url += cmCTest::MakeURLSafe( - this->CTest->GetCTestConfiguration("DropSiteUser")) + - ":" + cmCTest::MakeURLSafe( - this->CTest->GetCTestConfiguration("DropSitePassword")) + - "@" + this->CTest->GetCTestConfiguration("DropSite") + - cmCTest::MakeURLSafe(this->CTest->GetCTestConfiguration("DropLocation")); - if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) { - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, - this->CTest->GetCTestConfiguration("DropSiteUser").c_str(), - this->Quiet); - if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) { - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******", - this->Quiet); - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet); - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - this->CTest->GetCTestConfiguration("DropSite") - << this->CTest->GetCTestConfiguration("DropLocation") - << std::endl, - this->Quiet); - if (!this->SubmitUsingFTP(buildDirectory + "/Testing/" + - this->CTest->GetCurrentTag(), - files, prefix, url)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when submitting via FTP" << std::endl); - ofs << " Problems when submitting via FTP" << std::endl; - return -1; - } - if (!this->CDash) { - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method" - << std::endl - << " Trigger site: " - << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl, - this->Quiet); - if (!this->TriggerUsingHTTP( - files, prefix, - this->CTest->GetCTestConfiguration("TriggerSite"))) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when triggering via HTTP" << std::endl); - ofs << " Problems when triggering via HTTP" << std::endl; - return -1; - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submission successful" << std::endl, this->Quiet); - ofs << " Submission successful" << std::endl; - return 0; - } - } else if (dropMethod == "http" || dropMethod == "https") { - std::string url = dropMethod; - url += "://"; - ofs << "Using drop method: " << dropMethod << std::endl; - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Using HTTP submit method" << std::endl - << " Drop site:" << url, - this->Quiet); - if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) { - url += this->CTest->GetCTestConfiguration("DropSiteUser"); - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, - this->CTest->GetCTestConfiguration("DropSiteUser").c_str(), - this->Quiet); - if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) { - url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword"); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******", - this->Quiet); - } - url += "@"; - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet); - } - url += this->CTest->GetCTestConfiguration("DropSite") + - this->CTest->GetCTestConfiguration("DropLocation"); - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - this->CTest->GetCTestConfiguration("DropSite") - << this->CTest->GetCTestConfiguration("DropLocation") - << std::endl, - this->Quiet); - if (!this->SubmitUsingHTTP(buildDirectory + "/Testing/" + - this->CTest->GetCurrentTag(), - files, prefix, url)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when submitting via HTTP" << std::endl); - ofs << " Problems when submitting via HTTP" << std::endl; - return -1; - } - if (!this->CDash) { - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method" - << std::endl - << " Trigger site: " - << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl, - this->Quiet); - if (!this->TriggerUsingHTTP( - files, prefix, - this->CTest->GetCTestConfiguration("TriggerSite"))) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when triggering via HTTP" << std::endl); - ofs << " Problems when triggering via HTTP" << std::endl; - return -1; - } - } - if (this->HasErrors) { - cmCTestLog(this->CTest, HANDLER_OUTPUT, " Errors occurred during " - "submission." - << std::endl); - ofs << " Errors occurred during submission. " << std::endl; - } else { - cmCTestOptionalLog( - this->CTest, HANDLER_OUTPUT, " Submission successful" - << (this->HasWarnings ? ", with warnings." : "") << std::endl, - this->Quiet); - ofs << " Submission successful" - << (this->HasWarnings ? ", with warnings." : "") << std::endl; - } - - return 0; - } else if (dropMethod == "xmlrpc") { -#if defined(CTEST_USE_XMLRPC) - ofs << "Using drop method: XML-RPC" << std::endl; - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Using XML-RPC submit method" << std::endl, - this->Quiet); - std::string url = this->CTest->GetCTestConfiguration("DropSite"); - prefix = this->CTest->GetCTestConfiguration("DropLocation"); - if (!this->SubmitUsingXMLRPC(buildDirectory + "/Testing/" + - this->CTest->GetCurrentTag(), - files, prefix, url)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when submitting via XML-RPC" << std::endl); - ofs << " Problems when submitting via XML-RPC" << std::endl; - return -1; - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submission successful" << std::endl, this->Quiet); - ofs << " Submission successful" << std::endl; - return 0; -#else + std::string url = this->CTest->GetSubmitURL(); + cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, + " SubmitURL: " << url << '\n', this->Quiet); + if (!this->SubmitUsingHTTP(buildDirectory + "/Testing/" + + this->CTest->GetCurrentTag(), + files, prefix, url)) { cmCTestLog(this->CTest, ERROR_MESSAGE, - " Submission method \"xmlrpc\" not compiled into CTest!" - << std::endl); + " Problems when submitting via HTTP\n"); + ofs << " Problems when submitting via HTTP\n"; return -1; -#endif - } else if (dropMethod == "scp") { - std::string url; - if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) { - url += this->CTest->GetCTestConfiguration("DropSiteUser") + "@"; - } - url += this->CTest->GetCTestConfiguration("DropSite") + ":" + - this->CTest->GetCTestConfiguration("DropLocation"); - - // change to the build directory so that we can uses a relative path - // on windows since scp dosn't support "c:" a drive in the path - cmWorkingDirectory workdir(buildDirectory); - - if (!this->SubmitUsingSCP(this->CTest->GetCTestConfiguration("ScpCommand"), - "Testing/" + this->CTest->GetCurrentTag(), files, - prefix, url)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when submitting via SCP" << std::endl); - ofs << " Problems when submitting via SCP" << std::endl; - return -1; - } + } + if (this->HasErrors) { + cmCTestLog(this->CTest, HANDLER_OUTPUT, + " Errors occurred during submission.\n"); + ofs << " Errors occurred during submission.\n"; + } else { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submission successful" << std::endl, this->Quiet); - ofs << " Submission successful" << std::endl; - return 0; - } else if (dropMethod == "cp") { - std::string location = this->CTest->GetCTestConfiguration("DropLocation"); - - // change to the build directory so that we can uses a relative path - // on windows since scp dosn't support "c:" a drive in the path - cmWorkingDirectory workdir(buildDirectory); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - " Change directory: " << buildDirectory << std::endl, + " Submission successful" + << (this->HasWarnings ? ", with warnings." : "") + << std::endl, this->Quiet); - - if (!this->SubmitUsingCP("Testing/" + this->CTest->GetCurrentTag(), files, - prefix, location)) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - " Problems when submitting via CP" << std::endl); - ofs << " Problems when submitting via cp" << std::endl; - return -1; - } - cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, - " Submission successful" << std::endl, this->Quiet); - ofs << " Submission successful" << std::endl; - return 0; + ofs << " Submission successful" + << (this->HasWarnings ? ", with warnings." : "") << std::endl; } - cmCTestLog(this->CTest, ERROR_MESSAGE, " Unknown submission method: \"" - << dropMethod << "\"" << std::endl); - return -1; + return 0; } std::string cmCTestSubmitHandler::GetSubmitResultsPrefix() @@ -1600,7 +897,7 @@ void cmCTestSubmitHandler::SelectParts(std::set<cmCTest::Part> const& parts) } } -void cmCTestSubmitHandler::SelectFiles(cmCTest::SetOfStrings const& files) +void cmCTestSubmitHandler::SelectFiles(std::set<std::string> const& files) { this->Files.insert(files.begin(), files.end()); } |