diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/CTest/cmCTestCoverageHandler.cxx | 34 | ||||
-rw-r--r-- | Source/CTest/cmCTestCoverageHandler.h | 60 | ||||
-rw-r--r-- | Source/CTest/cmCTestMultiProcessHandler.cxx | 21 | ||||
-rw-r--r-- | Source/CTest/cmParsePHPCoverage.cxx | 252 | ||||
-rw-r--r-- | Source/CTest/cmParsePHPCoverage.h | 48 | ||||
-rw-r--r-- | Source/QtDialog/CMakeSetup.cxx | 2 | ||||
-rw-r--r-- | Source/cmFileCommand.cxx | 268 | ||||
-rw-r--r-- | Source/cmFileCommand.h | 10 | ||||
-rw-r--r-- | Source/cmLocalUnixMakefileGenerator3.cxx | 22 | ||||
-rw-r--r-- | Source/kwsys/SystemTools.cxx | 41 | ||||
-rw-r--r-- | Source/kwsys/SystemTools.hxx.in | 23 | ||||
-rw-r--r-- | Source/kwsys/kwsysDateStamp.cmake | 2 |
13 files changed, 651 insertions, 134 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 49cbda7..dc73cec 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -366,6 +366,7 @@ SET(CTEST_SRCS cmCTest.cxx CTest/cmCTestConfigureHandler.cxx CTest/cmCTestCoverageCommand.cxx CTest/cmCTestCoverageHandler.cxx + CTest/cmParsePHPCoverage.cxx CTest/cmCTestEmptyBinaryDirectoryCommand.cxx CTest/cmCTestGenericHandler.cxx CTest/cmCTestHandlerCommand.cxx @@ -502,4 +503,3 @@ IF(APPLE) ENDIF(APPLE) INSTALL_FILES(${CMAKE_DATA_DIR}/include cmCPluginAPI.h) - diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index da5aed0..3235bfd 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -10,6 +10,7 @@ See the License for more information. ============================================================================*/ #include "cmCTestCoverageHandler.h" +#include "cmParsePHPCoverage.h" #include "cmCTest.h" #include "cmake.h" #include "cmMakefile.h" @@ -126,20 +127,6 @@ private: //---------------------------------------------------------------------- -//********************************************************************** -class cmCTestCoverageHandlerContainer -{ -public: - int Error; - std::string SourceDir; - std::string BinaryDir; - typedef std::vector<int> SingleFileCoverageVector; - typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap; - TotalCoverageMap TotalCoverage; - std::ostream* OFS; -}; -//********************************************************************** -//---------------------------------------------------------------------- //---------------------------------------------------------------------- cmCTestCoverageHandler::cmCTestCoverageHandler() @@ -395,6 +382,11 @@ int cmCTestCoverageHandler::ProcessHandler() { return error; } + file_count += this->HandlePHPCoverage(&cont); + if ( file_count < 0 ) + { + return error; + } error = cont.Error; std::set<std::string> uncovered = this->FindUncoveredFiles(&cont); @@ -524,7 +516,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmOStringStream ostr; ostr << "Problem reading source file: " << fullFileName.c_str() - << " line:" << cc; + << " line:" << cc << " out total: " << fcov.size()-1; errorsWhileAccumulating.push_back(ostr.str()); error ++; break; @@ -748,6 +740,18 @@ bool IsFileInDir(const std::string &infile, const std::string &indir) } //---------------------------------------------------------------------- +int cmCTestCoverageHandler::HandlePHPCoverage( + cmCTestCoverageHandlerContainer* cont) +{ + cmParsePHPCoverage cov(*cont, this->CTest); + std::string coverageDir = this->CTest->GetBinaryDir() + "/xdebugCoverage"; + if(cmSystemTools::FileIsDirectory(coverageDir.c_str())) + { + cov.ReadPHPCoverageDirectory(coverageDir.c_str()); + } + return static_cast<int>(cont->TotalCoverage.size()); +} +//---------------------------------------------------------------------- int cmCTestCoverageHandler::HandleGCovCoverage( cmCTestCoverageHandlerContainer* cont) { diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index f0564e8..d3e8503 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -20,8 +20,17 @@ #include <cmsys/RegularExpression.hxx> class cmGeneratedFileStream; -class cmCTestCoverageHandlerContainer; - +class cmCTestCoverageHandlerContainer +{ +public: + int Error; + std::string SourceDir; + std::string BinaryDir; + typedef std::vector<int> SingleFileCoverageVector; + typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap; + TotalCoverageMap TotalCoverage; + std::ostream* OFS; +}; /** \class cmCTestCoverageHandler * \brief A class that handles coverage computaiton for ctest * @@ -59,6 +68,9 @@ private: int HandleGCovCoverage(cmCTestCoverageHandlerContainer* cont); void FindGCovFiles(std::vector<std::string>& files); + //! Handle coverage using xdebug php coverage + int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont); + //! Handle coverage using Bullseye int HandleBullseyeCoverage(cmCTestCoverageHandlerContainer* cont); int RunBullseyeSourceSummary(cmCTestCoverageHandlerContainer* cont); @@ -94,54 +106,10 @@ private: std::set<std::string> FindUncoveredFiles( cmCTestCoverageHandlerContainer* cont); - - struct cmCTestCoverage - { - cmCTestCoverage() - { - this->AbsolutePath = ""; - this->FullPath = ""; - this->Covered = false; - this->Tested = 0; - this->UnTested = 0; - this->Lines.clear(); - this->Show = false; - } - cmCTestCoverage(const cmCTestCoverage& rhs) : - AbsolutePath(rhs.AbsolutePath), - FullPath(rhs.FullPath), - Covered(rhs.Covered), - Tested(rhs.Tested), - UnTested(rhs.UnTested), - Lines(rhs.Lines), - Show(rhs.Show) - { - } - cmCTestCoverage& operator=(const cmCTestCoverage& rhs) - { - this->AbsolutePath = rhs.AbsolutePath; - this->FullPath = rhs.FullPath; - this->Covered = rhs.Covered; - this->Tested = rhs.Tested; - this->UnTested = rhs.UnTested; - this->Lines = rhs.Lines; - this->Show = rhs.Show; - return *this; - } - std::string AbsolutePath; - std::string FullPath; - bool Covered; - int Tested; - int UnTested; - std::vector<int> Lines; - bool Show; - }; - std::vector<cmStdString> CustomCoverageExclude; std::vector<cmsys::RegularExpression> CustomCoverageExcludeRegex; std::vector<cmStdString> ExtraCoverageGlobs; - typedef std::map<std::string, cmCTestCoverage> CoverageMap; // Map from source file to label ids. class LabelSet: public std::set<int> {}; diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index c1ca9ea..8a69780 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -453,15 +453,24 @@ void cmCTestMultiProcessHandler::CreateTestCostList() for(TestMap::iterator i = this->Tests.begin(); i != this->Tests.end(); ++i) { - std::string name = this->Properties[i->first]->Name; - if(std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), - name) != this->LastTestsFailed.end()) + //We only want to schedule them by cost in a parallel situation + if(this->ParallelLevel > 1) { - this->TestCosts[FLT_MAX].insert(i->first); + std::string name = this->Properties[i->first]->Name; + if(std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), + name) != this->LastTestsFailed.end()) + { + this->TestCosts[FLT_MAX].insert(i->first); + } + else + { + this->TestCosts[this->Properties[i->first]->Cost].insert(i->first); + } } - else + else //we ignore their cost { - this->TestCosts[this->Properties[i->first]->Cost].insert(i->first); + this->TestCosts[this->Tests.size() + - this->Properties[i->first]->Index].insert(i->first); } } } diff --git a/Source/CTest/cmParsePHPCoverage.cxx b/Source/CTest/cmParsePHPCoverage.cxx new file mode 100644 index 0000000..32c1ec1 --- /dev/null +++ b/Source/CTest/cmParsePHPCoverage.cxx @@ -0,0 +1,252 @@ +#include "cmStandardIncludes.h" +#include "cmSystemTools.h" +#include "cmParsePHPCoverage.h" +#include <cmsys/Directory.hxx> + +/* + To setup coverage for php. + + - edit php.ini to add auto prepend and append php files from phpunit + auto_prepend_file = + auto_append_file = + - run the tests + - run this program on all the files in c:/tmp + +*/ + +cmParsePHPCoverage::cmParsePHPCoverage(cmCTestCoverageHandlerContainer& cont, + cmCTest* ctest) + :Coverage(cont), CTest(ctest) +{ +} + +bool cmParsePHPCoverage::ReadUntil(std::ifstream& in, char until) +{ + char c = 0; + while(in.get(c) && c != until) + { + } + if(c != until) + { + return false; + } + return true; +} +bool cmParsePHPCoverage::ReadCoverageArray(std::ifstream& in, + cmStdString const& fileName) +{ + cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector + = this->Coverage.TotalCoverage[fileName]; + + char c; + char buf[4]; + in.read(buf, 3); + buf[3] = 0; + if(strcmp(buf, ";a:") != 0) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read start of coverage array, found : " + << buf << "\n"); + return false; + } + int size = 0; + if(!this->ReadInt(in, size)) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read size "); + return false; + } + if(!in.get(c) && c == '{') + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read open {\n"); + return false; + } + for(int i =0; i < size; i++) + { + this->ReadUntil(in, ':'); + int line = 0; + this->ReadInt(in, line); + // ok xdebug may have a bug here + // it seems to be 1 based but often times + // seems to have a 0'th line. + line--; + if(line < 0) + { + line = 0; + } + this->ReadUntil(in, ':'); + int value = 0; + this->ReadInt(in, value); + // make sure the vector is the right size and is + // initialized with -1 for each line + while(coverageVector.size() <= static_cast<size_t>(line) ) + { + coverageVector.push_back(-1); + } + // if value is less than 0, set it to zero + // TODO figure out the difference between + // -1 and -2 in xdebug coverage?? For now + // assume less than 0 is just not covered + // CDash expects -1 for non executable code (like comments) + // and 0 for uncovered code, and a positive value + // for number of times a line was executed + if(value < 0) + { + value = 0; + } + // if unset then set it to value + if(coverageVector[line] == -1) + { + coverageVector[line] = value; + } + // otherwise increment by value + else + { + coverageVector[line] += value; + } + } + return true; +} + +bool cmParsePHPCoverage::ReadInt(std::ifstream& in, int& v) +{ + std::string s; + char c = 0; + while(in.get(c) && c != ':' && c != ';') + { + s += c; + } + v = atoi(s.c_str()); + return true; +} + +bool cmParsePHPCoverage::ReadArraySize(std::ifstream& in, int& size) +{ + char c = 0; + in.get(c); + if(c != 'a') + { + return false; + } + if(in.get(c) && c == ':') + { + if(this->ReadInt(in, size)) + { + return true; + } + } + return false; +} + +bool cmParsePHPCoverage::ReadFileInformation(std::ifstream& in) +{ + char buf[4]; + in.read(buf, 2); + buf[2] = 0; + if(strcmp(buf, "s:") != 0) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read start of file info found: [" << buf << "]\n"); + return false; + } + char c; + int size = 0; + if(this->ReadInt(in, size)) + { + size++; // add one for null termination + char* s = new char[size+1]; + // read open quote + if(in.get(c) && c != '"') + { + return false; + } + // read the string data + in.read(s, size-1); + s[size-1] = 0; + cmStdString fileName = s; + delete [] s; + // read close quote + if(in.get(c) && c != '"') + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read close quote\n" + << "read [" << c << "]\n"); + return false; + } + if(!this->ReadCoverageArray(in, fileName) ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read coverage array for file: " + << fileName << "\n"); + return false; + } + return true; + } + return false; +} + + +bool cmParsePHPCoverage::ReadPHPData(const char* file) +{ + std::ifstream in(file); + if(!in) + { + return false; + } + int size = 0; + this->ReadArraySize(in, size); + char c = 0; + in.get(c); + if(c != '{') + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read open array\n"); + return false; + } + for(int i =0; i < size; i++) + { + if(!this->ReadFileInformation(in)) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Failed to read file #" << i << "\n"); + return false; + } + in.get(c); + if(c != '}') + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "failed to read close array\n"); + return false; + } + } + return true; +} + +bool cmParsePHPCoverage::ReadPHPCoverageDirectory(const char* d) +{ + cmsys::Directory dir; + if(!dir.Load(d)) + { + return false; + } + size_t numf; + unsigned int i; + numf = dir.GetNumberOfFiles(); + for (i = 0; i < numf; i++) + { + std::string file = dir.GetFile(i); + if(file != "." && file != ".." + && !cmSystemTools::FileIsDirectory(file.c_str())) + { + std::string path = d; + path += "/"; + path += file; + if(!this->ReadPHPData(path.c_str())) + { + return false; + } + } + } + return true; +} diff --git a/Source/CTest/cmParsePHPCoverage.h b/Source/CTest/cmParsePHPCoverage.h new file mode 100644 index 0000000..ce5741d --- /dev/null +++ b/Source/CTest/cmParsePHPCoverage.h @@ -0,0 +1,48 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmParsePHPCoverage_h +#define cmParsePHPCoverage_h + +#include "cmStandardIncludes.h" +#include "cmCTestCoverageHandler.h" + +/** \class cmParsePHPCoverage + * \brief Parse xdebug PHP coverage information + * + * This class is used to parse php coverage information produced + * by xdebug. The data is stored as a php dump of the array + * return by xdebug coverage. It is an array of arrays. + */ +class cmParsePHPCoverage +{ +public: + cmParsePHPCoverage(cmCTestCoverageHandlerContainer& cont, + cmCTest* ctest); + bool ReadPHPCoverageDirectory(const char* dir); + void PrintCoverage(); +private: + bool ReadPHPData(const char* file); + bool ReadArraySize(std::ifstream& in, int& size); + bool ReadFileInformation(std::ifstream& in); + bool ReadInt(std::ifstream& in, int& v); + bool ReadCoverageArray(std::ifstream& in, cmStdString const&); + bool ReadUntil(std::ifstream& in, char until); + typedef std::map<int, int> FileLineCoverage; + std::map<cmStdString, FileLineCoverage> FileToCoverage; + std::map<int, int> FileCoverage; + cmCTestCoverageHandlerContainer& Coverage; + cmCTest* CTest; +}; + + +#endif diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index fc61709..28f4697 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -164,7 +164,7 @@ int main(int argc, char** argv) QStringList args = app.arguments(); if(args.count() == 2) { - cmsys_stl::string filePath = cmSystemTools::CollapseFullPath(".."); + cmsys_stl::string filePath = cmSystemTools::CollapseFullPath(args[1].toAscii().data()); cmsys_stl::string buildFilePath = cmSystemTools::CollapseFullPath("CMakeCache.txt", filePath.c_str()); cmsys_stl::string srcFilePath = diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index df9863f..133c1a1 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -1504,7 +1504,7 @@ bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile, this->ReportCopy(toFile, TypeFile, copy); // Copy the file. - if(copy && !cmSystemTools::CopyAFile(fromFile, toFile, true, false)) + if(copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) { cmOStringStream e; e << this->Name << " cannot copy file \"" << fromFile @@ -1516,6 +1516,13 @@ bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile, // Set the file modification time of the destination file. if(copy && !this->Always) { + // Add write permission so we can set the file time. + // Permissions are set unconditionally below anyway. + mode_t perm = 0; + if(cmSystemTools::GetPermissions(toFile, perm)) + { + cmSystemTools::SetPermissions(toFile, perm | mode_owner_write); + } if (!cmSystemTools::CopyFileTime(fromFile, toFile)) { cmOStringStream e; @@ -2440,7 +2447,8 @@ namespace{ fout->write(chPtr, realsize); return realsize; } - + + static size_t cmFileCommandCurlDebugCallback(CURL *, curl_infotype, char *chPtr, size_t size, void *data) @@ -2453,6 +2461,72 @@ namespace{ } + class cURLProgressHelper + { + public: + cURLProgressHelper(cmFileCommand *fc) + { + this->CurrentPercentage = -1; + this->FileCommand = fc; + } + + bool UpdatePercentage(double value, double total, std::string &status) + { + int OldPercentage = this->CurrentPercentage; + + if (0.0 == total) + { + this->CurrentPercentage = 100; + } + else + { + this->CurrentPercentage = static_cast<int>(value/total*100.0 + 0.5); + } + + bool updated = (OldPercentage != this->CurrentPercentage); + + if (updated) + { + cmOStringStream oss; + oss << "[download " << this->CurrentPercentage << "% complete]"; + status = oss.str(); + } + + return updated; + } + + cmFileCommand *GetFileCommand() + { + return this->FileCommand; + } + + private: + int CurrentPercentage; + cmFileCommand *FileCommand; + }; + + + static int + cmFileCommandCurlProgressCallback(void *clientp, + double dltotal, double dlnow, + double ultotal, double ulnow) + { + cURLProgressHelper *helper = + reinterpret_cast<cURLProgressHelper *>(clientp); + + static_cast<void>(ultotal); + static_cast<void>(ulnow); + + std::string status; + if (helper->UpdatePercentage(dlnow, dltotal, status)) + { + cmFileCommand *fc = helper->GetFileCommand(); + cmMakefile *mf = fc->GetMakefile(); + mf->DisplayStatus(status.c_str(), -1); + } + + return 0; + } } #endif @@ -2466,8 +2540,8 @@ namespace { cURLEasyGuard(CURL * easy) : Easy(easy) {} - - ~cURLEasyGuard(void) + + ~cURLEasyGuard(void) { if (this->Easy) { @@ -2488,6 +2562,7 @@ namespace { } #endif + bool cmFileCommand::HandleDownloadCommand(std::vector<std::string> const& args) @@ -2505,9 +2580,13 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> ++i; std::string file = *i; ++i; + long timeout = 0; std::string verboseLog; std::string statusVar; + std::string expectedMD5sum; + bool showProgress = false; + while(i != args.end()) { if(*i == "TIMEOUT") @@ -2546,9 +2625,65 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> } statusVar = *i; } + else if(*i == "EXPECTED_MD5") + { + ++i; + if( i == args.end()) + { + this->SetError("FILE(DOWNLOAD url file EXPECTED_MD5 sum) missing " + "sum value for EXPECTED_MD5."); + return false; + } + expectedMD5sum = cmSystemTools::LowerCase(*i); + } + else if(*i == "SHOW_PROGRESS") + { + showProgress = true; + } ++i; } + // If file exists already, and caller specified an expected md5 sum, + // and the existing file already has the expected md5 sum, then simply + // return. + // + if(cmSystemTools::FileExists(file.c_str()) && + !expectedMD5sum.empty()) + { + char computedMD5[32]; + + if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5)) + { + this->SetError("FILE(DOWNLOAD ) error; cannot compute MD5 sum on " + "pre-existing file"); + return false; + } + + std::string actualMD5sum = cmSystemTools::LowerCase( + std::string(computedMD5, 32)); + + if (expectedMD5sum == actualMD5sum) + { + this->Makefile->DisplayStatus( + "FILE(DOWNLOAD ) returning early: file already exists with " + "expected MD5 sum", -1); + + if(statusVar.size()) + { + cmOStringStream result; + result << (int)0 << ";\"" + "returning early: file already exists with expected MD5 sum\""; + this->Makefile->AddDefinition(statusVar.c_str(), + result.str().c_str()); + } + + return true; + } + } + + // Make sure parent directory exists so we can write to the file + // as we receive downloaded bits from curl... + // std::string dir = cmSystemTools::GetFilenamePath(file.c_str()); if(!cmSystemTools::FileExists(dir.c_str()) && !cmSystemTools::MakeDirectory(dir.c_str())) @@ -2567,6 +2702,7 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> "file for write."); return false; } + ::CURL *curl; ::curl_global_init(CURL_GLOBAL_DEFAULT); curl = ::curl_easy_init(); @@ -2582,28 +2718,31 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); if (res != CURLE_OK) { - std::string errstring = "FILE(DOWNLOAD ) error; cannot set url: "; - errstring += ::curl_easy_strerror(res); + std::string errstring = "FILE(DOWNLOAD ) error; cannot set url: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - cmFileCommandWriteMemoryCallback); + cmFileCommandWriteMemoryCallback); if (res != CURLE_OK) - { - std::string errstring = - "FILE(DOWNLOAD ) error; cannot set write function: "; - errstring += ::curl_easy_strerror(res); + { + std::string errstring = + "FILE(DOWNLOAD ) error; cannot set write function: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, - cmFileCommandCurlDebugCallback); + cmFileCommandCurlDebugCallback); if (res != CURLE_OK) { - std::string errstring = - "FILE(DOWNLOAD ) error; cannot set debug function: "; - errstring += ::curl_easy_strerror(res); + std::string errstring = + "FILE(DOWNLOAD ) error; cannot set debug function: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } @@ -2615,14 +2754,25 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> { std::string errstring = "FILE(DOWNLOAD ) error; cannot set write data: "; errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug); if (res != CURLE_OK) { - std::string errstring = "FILE(DOWNLOAD ) error; cannot set write data: "; + std::string errstring = "FILE(DOWNLOAD ) error; cannot set debug data: "; errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); + return false; + } + + res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + if (res != CURLE_OK) + { + std::string errstring = "FILE(DOWNLOAD ) error; cannot set follow-redirect option: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } @@ -2634,24 +2784,70 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> { std::string errstring = "FILE(DOWNLOAD ) error; cannot set verbose: "; errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } } + if(timeout > 0) { res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout ); if (res != CURLE_OK) { - std::string errstring = "FILE(DOWNLOAD ) error; cannot set verbose: "; + std::string errstring = "FILE(DOWNLOAD ) error; cannot set timeout: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); + return false; + } + } + + // Need the progress helper's scope to last through the duration of + // the curl_easy_perform call... so this object is declared at function + // scope intentionally, rather than inside the "if(showProgress)" + // block... + // + cURLProgressHelper helper(this); + + if(showProgress) + { + res = ::curl_easy_setopt(curl, + CURLOPT_NOPROGRESS, 0); + if (res != CURLE_OK) + { + std::string errstring = "FILE(DOWNLOAD ) error; cannot set noprogress value: "; errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); + return false; + } + + res = ::curl_easy_setopt(curl, + CURLOPT_PROGRESSFUNCTION, cmFileCommandCurlProgressCallback); + if (res != CURLE_OK) + { + std::string errstring = "FILE(DOWNLOAD ) error; cannot set progress function: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); + return false; + } + + res = ::curl_easy_setopt(curl, + CURLOPT_PROGRESSDATA, reinterpret_cast<void*>(&helper)); + if (res != CURLE_OK) + { + std::string errstring = "FILE(DOWNLOAD ) error; cannot set progress data: "; + errstring += ::curl_easy_strerror(res); + this->SetError(errstring.c_str()); return false; } } + res = ::curl_easy_perform(curl); + /* always cleanup */ g_curl.release(); ::curl_easy_cleanup(curl); + if(statusVar.size()) { cmOStringStream result; @@ -2659,7 +2855,44 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> this->Makefile->AddDefinition(statusVar.c_str(), result.str().c_str()); } + ::curl_global_cleanup(); + + // Explicitly flush/close so we can measure the md5 accurately. + // + fout.flush(); + fout.close(); + + // Verify MD5 sum if requested: + // + if (!expectedMD5sum.empty()) + { + char computedMD5[32]; + + if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5)) + { + this->SetError("FILE(DOWNLOAD ) error; cannot compute MD5 sum on " + "downloaded file"); + return false; + } + + std::string actualMD5sum = cmSystemTools::LowerCase( + std::string(computedMD5, 32)); + + if (expectedMD5sum != actualMD5sum) + { + cmOStringStream oss; + oss << "FILE(DOWNLOAD ) error; expected and actual MD5 sums differ" + << std::endl + << " for file: [" << file << "]" << std::endl + << " expected MD5 sum: [" << expectedMD5sum << "]" << std::endl + << " actual MD5 sum: [" << actualMD5sum << "]" << std::endl + ; + this->SetError(oss.str().c_str()); + return false; + } + } + if(chunkDebug.size()) { chunkDebug.push_back(0); @@ -2677,6 +2910,7 @@ cmFileCommand::HandleDownloadCommand(std::vector<std::string> this->Makefile->AddDefinition(verboseLog.c_str(), &*chunkDebug.begin()); } + return true; #else this->SetError("FILE(DOWNLOAD ) " diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index c6da301..e771092 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -80,7 +80,8 @@ public: " file(RELATIVE_PATH variable directory file)\n" " file(TO_CMAKE_PATH path result)\n" " file(TO_NATIVE_PATH path result)\n" - " file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log])\n" + " file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log]\n" + " [EXPECTED_MD5 sum] [SHOW_PROGRESS])\n" "WRITE will write a message into a file called 'filename'. It " "overwrites the file if it already exists, and creates the file " "if it does not exist.\n" @@ -152,7 +153,12 @@ public: "and the second element is a string value for the error. A 0 " "numeric error means no error in the operation. " "If TIMEOUT time is specified, the operation will " - "timeout after time seconds, time should be specified as an integer." + "timeout after time seconds, time should be specified as an integer. " + "If EXPECTED_MD5 sum is specified, the operation will verify that the " + "downloaded file's actual md5 sum matches the expected value. If it " + "does not match, the operation fails with an error. " + "If SHOW_PROGRESS is specified, progress information will be printed " + "as status messages until the operation is complete." "\n" "The file() command also provides COPY and INSTALL signatures:\n" " file(<COPY|INSTALL> files... DESTINATION <dir>\n" diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index fce5a9c..004d19a 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -973,6 +973,24 @@ cmLocalUnixMakefileGenerator3 this->ConfigurationName.c_str()); if (cmd.size()) { + // Use "call " before any invocations of .bat or .cmd files + // invoked as custom commands in the WindowsShell. + // + bool useCall = false; + + if (this->WindowsShell) + { + std::string suffix; + if (cmd.size() > 4) + { + suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size()-4)); + if (suffix == ".bat" || suffix == ".cmd") + { + useCall = true; + } + } + } + cmSystemTools::ReplaceString(cmd, "/./", "/"); // Convert the command to a relative path only if the current // working directory will be the start-output directory. @@ -1044,6 +1062,10 @@ cmLocalUnixMakefileGenerator3 } } } + if (useCall && launcher.empty()) + { + cmd = "call " + cmd; + } commands1.push_back(cmd); } } diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index afc7240..3153235 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -1722,8 +1722,7 @@ kwsys_stl::string SystemTools::ConvertToWindowsOutputPath(const char* path) } bool SystemTools::CopyFileIfDifferent(const char* source, - const char* destination, - bool copyPermissions) + const char* destination) { // special check for a destination that is a directory // FilesDiffer does not handle file to directory compare @@ -1736,8 +1735,7 @@ bool SystemTools::CopyFileIfDifferent(const char* source, new_destination += SystemTools::GetFilenameName(source_name); if(SystemTools::FilesDiffer(source, new_destination.c_str())) { - return SystemTools::CopyFileAlways(source, destination, - copyPermissions); + return SystemTools::CopyFileAlways(source, destination); } else { @@ -1750,7 +1748,7 @@ bool SystemTools::CopyFileIfDifferent(const char* source, // are different if(SystemTools::FilesDiffer(source, destination)) { - return SystemTools::CopyFileAlways(source, destination, copyPermissions); + return SystemTools::CopyFileAlways(source, destination); } // at this point the files must be the same so return true return true; @@ -1836,8 +1834,7 @@ bool SystemTools::FilesDiffer(const char* source, /** * Copy a file named by "source" to the file named by "destination". */ -bool SystemTools::CopyFileAlways(const char* source, const char* destination, - bool copyPermissions) +bool SystemTools::CopyFileAlways(const char* source, const char* destination) { // If files are the same do not copy if ( SystemTools::SameFile(source, destination) ) @@ -1924,23 +1921,11 @@ bool SystemTools::CopyFileAlways(const char* source, const char* destination, fin.close(); fout.close(); - // More checks. - struct stat statSource, statDestination; - statSource.st_size = 12345; - statDestination.st_size = 12345; - if(stat(source, &statSource) != 0) - { - return false; - } - else if(stat(destination, &statDestination) != 0) + if(!fout) { return false; } - else if(statSource.st_size != statDestination.st_size) - { - return false; - } - if ( copyPermissions && perms ) + if ( perms ) { if ( !SystemTools::SetPermissions(destination, perm) ) { @@ -1952,15 +1937,15 @@ bool SystemTools::CopyFileAlways(const char* source, const char* destination, //---------------------------------------------------------------------------- bool SystemTools::CopyAFile(const char* source, const char* destination, - bool always, bool copyPermissions) + bool always) { if(always) { - return SystemTools::CopyFileAlways(source, destination, copyPermissions); + return SystemTools::CopyFileAlways(source, destination); } else { - return SystemTools::CopyFileIfDifferent(source, destination, copyPermissions); + return SystemTools::CopyFileIfDifferent(source, destination); } } @@ -1969,7 +1954,7 @@ bool SystemTools::CopyAFile(const char* source, const char* destination, * "destination". */ bool SystemTools::CopyADirectory(const char* source, const char* destination, - bool always, bool copyPermissions) + bool always) { Directory dir; dir.Load(source); @@ -1993,16 +1978,14 @@ bool SystemTools::CopyADirectory(const char* source, const char* destination, fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum)); if (!SystemTools::CopyADirectory(fullPath.c_str(), fullDestPath.c_str(), - always, - copyPermissions)) + always)) { return false; } } else { - if(!SystemTools::CopyAFile(fullPath.c_str(), destination, always, - copyPermissions)) + if(!SystemTools::CopyAFile(fullPath.c_str(), destination, always)) { return false; } diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index fd35742..ec70320 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -500,14 +500,10 @@ public: /** * Copy the source file to the destination file only - * if the two files differ. If the "copyPermissions" - * argument is true, the permissions of the copy are - * set to be the same as the permissions of the - * original. + * if the two files differ. */ static bool CopyFileIfDifferent(const char* source, - const char* destination, - bool copyPermissions = true); + const char* destination); /** * Compare the contents of two files. Return true if different @@ -520,22 +516,17 @@ public: static bool SameFile(const char* file1, const char* file2); /** - * Copy a file. If the "copyPermissions" argument is true, the - * permissions of the copy are set to be the same as the permissions - * of the original. + * Copy a file. */ - static bool CopyFileAlways(const char* source, const char* destination, - bool copyPermissions = true); + static bool CopyFileAlways(const char* source, const char* destination); /** * Copy a file. If the "always" argument is true the file is always * copied. If it is false, the file is copied only if it is new or - * has changed. If the "copyPermissions" argument is true, the - * permissions of the copy are set to be the same as the permissions - * of the original. + * has changed. */ static bool CopyAFile(const char* source, const char* destination, - bool always = true, bool copyPermissions = true); + bool always = true); /** * Copy content directory to another directory with all files and @@ -544,7 +535,7 @@ public: * are new are copied. */ static bool CopyADirectory(const char* source, const char* destination, - bool always = true, bool copyPermissions = true); + bool always = true); /** * Remove a file diff --git a/Source/kwsys/kwsysDateStamp.cmake b/Source/kwsys/kwsysDateStamp.cmake index 09a6912..26e1627 100644 --- a/Source/kwsys/kwsysDateStamp.cmake +++ b/Source/kwsys/kwsysDateStamp.cmake @@ -18,4 +18,4 @@ SET(KWSYS_DATE_STAMP_YEAR 2010) SET(KWSYS_DATE_STAMP_MONTH 06) # KWSys version date day component. Format is DD. -SET(KWSYS_DATE_STAMP_DAY 03) +SET(KWSYS_DATE_STAMP_DAY 07) |