diff options
Diffstat (limited to 'Source/CTest')
45 files changed, 1391 insertions, 526 deletions
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 4fa3c53..0fac136 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -18,6 +18,7 @@ #include "cmGlobalGenerator.h" #include <cmsys/Process.h> #include "cmCTestTestHandler.h" +#include "cmCacheManager.h" //---------------------------------------------------------------------- cmCTestBuildAndTestHandler::cmCTestBuildAndTestHandler() @@ -59,7 +60,7 @@ int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring, { unsigned int k; std::vector<std::string> args; - args.push_back(this->CTest->GetCMakeExecutable()); + args.push_back(cmSystemTools::GetCMakeCommand()); args.push_back(this->SourceDir); if(this->BuildGenerator.size()) { @@ -184,14 +185,14 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) cmOStringStream out; // if the generator and make program are not specified then it is an error - if (!this->BuildGenerator.size() || !this->BuildMakeProgram.size()) + if (!this->BuildGenerator.size()) { if(outstring) { *outstring = - "--build-and-test requires that both the generator and makeprogram " - "be provided using the --build-generator and --build-makeprogram " - "command line options. "; + "--build-and-test requires that the generator " + "be provided using the --build-generator " + "command line option. "; } return 1; } @@ -238,9 +239,13 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) if(this->BuildNoCMake) { + // Make the generator available for the Build call below. cm.SetGlobalGenerator(cm.CreateGlobalGenerator( this->BuildGenerator.c_str())); cm.SetGeneratorToolset(this->BuildGeneratorToolset); + + // Load the cache to make CMAKE_MAKE_PROGRAM available. + cm.GetCacheManager()->LoadCache(this->BinaryDir.c_str()); } else { @@ -508,23 +513,14 @@ int cmCTestBuildAndTestHandler::ProcessCommandLineArguments( { this->BuildNoClean = true; } - if(currentArg.find("--build-options",0) == 0 && idx < allArgs.size() - 1) + if(currentArg.find("--build-options",0) == 0) { - ++idx; - bool done = false; - while(idx < allArgs.size() && !done) + while(idx+1 < allArgs.size() && + allArgs[idx+1] != "--build-target" && + allArgs[idx+1] != "--test-command") { + ++idx; this->BuildOptions.push_back(allArgs[idx]); - if(idx+1 < allArgs.size() - && (allArgs[idx+1] == "--build-target" || - allArgs[idx+1] == "--test-command")) - { - done = true; - } - else - { - ++idx; - } } } if(currentArg.find("--test-command",0) == 0 && idx < allArgs.size() - 1) diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 1f63185..12ff718 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -114,9 +114,6 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() this->Makefile->GetCMakeInstance()->CreateGlobalGenerator( cmakeGeneratorName); } - this->GlobalGenerator->FindMakeProgram(this->Makefile); - const char* cmakeMakeProgram - = this->Makefile->GetDefinition("CMAKE_MAKE_PROGRAM"); if(strlen(cmakeBuildConfiguration) == 0) { const char* config = 0; @@ -133,10 +130,8 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory"); std::string buildCommand = this->GlobalGenerator-> - GenerateBuildCommand(cmakeMakeProgram, - cmakeProjectName, dir.c_str(), - cmakeBuildAdditionalFlags, cmakeBuildTarget, - cmakeBuildConfiguration, true, false); + GenerateCMakeBuildCommand(cmakeBuildTarget, cmakeBuildConfiguration, + cmakeBuildAdditionalFlags, true); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "SetMakeCommand:" << buildCommand.c_str() << "\n"); diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h index cabc39b..08887fe 100644 --- a/Source/CTest/cmCTestBuildCommand.h +++ b/Source/CTest/cmCTestBuildCommand.h @@ -45,34 +45,8 @@ public: */ virtual const char* GetName() const { return "ctest_build";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Build the project."; - } virtual bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus &status); - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_build([BUILD build_dir] [TARGET target] [RETURN_VALUE res]\n" - " [APPEND][NUMBER_ERRORS val] [NUMBER_WARNINGS val])\n" - "Builds the given build directory and stores results in Build.xml. " - "If no BUILD is given, the CTEST_BINARY_DIRECTORY variable is used.\n" - "The TARGET variable can be used to specify a build target. If none " - "is specified, the \"all\" target will be built.\n" - "The RETURN_VALUE option specifies a variable in which to store the " - "return value of the native build tool. " - "The NUMBER_ERRORS and NUMBER_WARNINGS options specify variables in " - "which to store the number of build errors and warnings detected." - "\n" - CTEST_COMMAND_APPEND_OPTION_DOCS; - } cmTypeMacro(cmCTestBuildCommand, cmCTestHandlerCommand); diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 39eeb70..c5deb96 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -24,6 +24,7 @@ //#include <cmsys/RegularExpression.hxx> #include <cmsys/Process.h> #include <cmsys/Directory.hxx> +#include <cmsys/FStream.hxx> // used for sleep #ifdef _WIN32 @@ -751,7 +752,7 @@ void cmCTestBuildHandler::GenerateXMLFooter(std::ostream& os, void cmCTestBuildHandler::GenerateXMLLaunchedFragment(std::ostream& os, const char* fname) { - std::ifstream fin(fname, std::ios::in | std::ios::binary); + cmsys::ifstream fin(fname, std::ios::in | std::ios::binary); std::string line; while(cmSystemTools::GetLineFromStream(fin, line)) { @@ -763,7 +764,7 @@ void cmCTestBuildHandler::GenerateXMLLaunchedFragment(std::ostream& os, bool cmCTestBuildHandler::IsLaunchedErrorFile(const char* fname) { // error-{hash}.xml - return (strncmp(fname, "error-", 6) == 0 && + return (cmHasLiteralPrefix(fname, "error-") && strcmp(fname+strlen(fname)-4, ".xml") == 0); } @@ -771,7 +772,7 @@ bool cmCTestBuildHandler::IsLaunchedErrorFile(const char* fname) bool cmCTestBuildHandler::IsLaunchedWarningFile(const char* fname) { // warning-{hash}.xml - return (strncmp(fname, "warning-", 8) == 0 && + return (cmHasLiteralPrefix(fname, "warning-") && strcmp(fname+strlen(fname)-4, ".xml") == 0); } @@ -885,7 +886,7 @@ cmCTestBuildHandler::LaunchHelper //---------------------------------------------------------------------- int cmCTestBuildHandler::RunMakeCommand(const char* command, - int* retVal, const char* dir, int timeout, std::ofstream& ofs) + int* retVal, const char* dir, int timeout, std::ostream& ofs) { // First generate the command and arguments std::vector<cmStdString> args = cmSystemTools::ParseArguments(command); @@ -1049,7 +1050,7 @@ int cmCTestBuildHandler::RunMakeCommand(const char* command, //---------------------------------------------------------------------- void cmCTestBuildHandler::ProcessBuffer(const char* data, int length, - size_t& tick, size_t tick_len, std::ofstream& ofs, + size_t& tick, size_t tick_len, std::ostream& ofs, t_BuildProcessingQueueType* queue) { const std::string::size_type tick_line_len = 50; diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index 439efd6..ff7cfd6 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -54,7 +54,7 @@ private: // and retVal is return value or exception. int RunMakeCommand(const char* command, int* retVal, const char* dir, int timeout, - std::ofstream& ofs); + std::ostream& ofs); enum { b_REGULAR_LINE, @@ -113,7 +113,7 @@ private: typedef std::deque<char> t_BuildProcessingQueueType; void ProcessBuffer(const char* data, int length, size_t& tick, - size_t tick_len, std::ofstream& ofs, t_BuildProcessingQueueType* queue); + size_t tick_len, std::ostream& ofs, t_BuildProcessingQueueType* queue); int ProcessSingleLine(const char* data); t_BuildProcessingQueueType BuildProcessingQueue; diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index 7269507..17dbb55 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -16,6 +16,7 @@ #include "cmXMLSafe.h" #include <cmsys/RegularExpression.hxx> +#include <cmsys/FStream.hxx> //---------------------------------------------------------------------------- cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log) @@ -231,7 +232,7 @@ std::string cmCTestCVS::ComputeBranchFlag(std::string const& dir) // Lookup the branch in the tag file, if any. std::string tagLine; - std::ifstream tagStream(tagFile.c_str()); + cmsys::ifstream tagStream(tagFile.c_str()); if(tagStream && cmSystemTools::GetLineFromStream(tagStream, tagLine) && tagLine.size() > 1 && tagLine[0] == 'T') { diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index db33cb6..5eed409 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -86,7 +86,7 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() } std::string cmakeConfigureCommand = "\""; - cmakeConfigureCommand += this->CTest->GetCMakeExecutable(); + cmakeConfigureCommand += cmSystemTools::GetCMakeCommand(); cmakeConfigureCommand += "\""; std::vector<std::string>::const_iterator it; diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h index b343fc1..b592c5a 100644 --- a/Source/CTest/cmCTestConfigureCommand.h +++ b/Source/CTest/cmCTestConfigureCommand.h @@ -40,34 +40,6 @@ public: */ virtual const char* GetName() const { return "ctest_configure";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Configure the project build tree."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_configure([BUILD build_dir] [SOURCE source_dir] [APPEND]\n" - " [OPTIONS options] [RETURN_VALUE res])\n" - "Configures the given build directory and stores results in " - "Configure.xml. " - "If no BUILD is given, the CTEST_BINARY_DIRECTORY variable is used. " - "If no SOURCE is given, the CTEST_SOURCE_DIRECTORY variable is used. " - "The OPTIONS argument specifies command line arguments to pass to " - "the configuration tool. " - "The RETURN_VALUE option specifies a variable in which to store the " - "return value of the native build tool." - "\n" - CTEST_COMMAND_APPEND_OPTION_DOCS; - } - cmTypeMacro(cmCTestConfigureCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 2fe762c..11bb411 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -41,32 +41,6 @@ public: */ virtual const char* GetName() const { return "ctest_coverage";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Collect coverage tool results."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_coverage([BUILD build_dir] [RETURN_VALUE res] [APPEND]\n" - " [LABELS label1 [label2 [...]]])\n" - "Perform the coverage of the given build directory and stores results " - "in Coverage.xml. The second argument is a variable that will hold " - "value." - "\n" - "The LABELS option filters the coverage report to include only " - "source files labeled with at least one of the labels specified." - "\n" - CTEST_COMMAND_APPEND_OPTION_DOCS; - } - cmTypeMacro(cmCTestCoverageCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 20aded2..3c65c55 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -11,6 +11,7 @@ ============================================================================*/ #include "cmCTestCoverageHandler.h" #include "cmParsePHPCoverage.h" +#include "cmParsePythonCoverage.h" #include "cmParseGTMCoverage.h" #include "cmParseCacheCoverage.h" #include "cmCTest.h" @@ -25,6 +26,7 @@ #include <cmsys/Glob.hxx> #include <cmsys/stl/iterator> #include <cmsys/stl/algorithm> +#include <cmsys/FStream.hxx> #include <stdlib.h> #include <math.h> @@ -141,6 +143,7 @@ void cmCTestCoverageHandler::Initialize() this->Superclass::Initialize(); this->CustomCoverageExclude.clear(); this->SourceLabels.clear(); + this->TargetDirs.clear(); this->LabelIdMap.clear(); this->Labels.clear(); this->LabelFilter.clear(); @@ -392,6 +395,13 @@ int cmCTestCoverageHandler::ProcessHandler() { return error; } + file_count += this->HandlePythonCoverage(&cont); + error = cont.Error; + if ( file_count < 0 ) + { + return error; + } + file_count += this->HandleMumpsCoverage(&cont); error = cont.Error; if ( file_count < 0 ) @@ -502,7 +512,7 @@ int cmCTestCoverageHandler::ProcessHandler() << "\" FullPath=\"" << cmXMLSafe(shortFileName) << "\">\n" << "\t\t<Report>" << std::endl; - std::ifstream ifs(fullFileName.c_str()); + cmsys::ifstream ifs(fullFileName.c_str()); if ( !ifs) { cmOStringStream ostr; @@ -591,7 +601,7 @@ int cmCTestCoverageHandler::ProcessHandler() << "\" FullPath=\"" << cmXMLSafe(*i) << "\">\n" << "\t\t<Report>" << std::endl; - std::ifstream ifs(fullPath.c_str()); + cmsys::ifstream ifs(fullPath.c_str()); if (!ifs) { cmOStringStream ostr; @@ -761,6 +771,32 @@ int cmCTestCoverageHandler::HandlePHPCoverage( } return static_cast<int>(cont->TotalCoverage.size()); } + +//---------------------------------------------------------------------- +int cmCTestCoverageHandler::HandlePythonCoverage( + cmCTestCoverageHandlerContainer* cont) +{ + cmParsePythonCoverage cov(*cont, this->CTest); + + // Assume the coverage.xml is in the source directory + std::string coverageXMLFile = this->CTest->GetBinaryDir() + "/coverage.xml"; + + if(cmSystemTools::FileExists(coverageXMLFile.c_str())) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Parsing coverage.py XML file: " << coverageXMLFile + << std::endl); + cov.ReadCoverageXML(coverageXMLFile.c_str()); + } + else + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Cannot find coverage.py XML file: " << coverageXMLFile + << std::endl); + } + return static_cast<int>(cont->TotalCoverage.size()); +} + //---------------------------------------------------------------------- int cmCTestCoverageHandler::HandleMumpsCoverage( cmCTestCoverageHandlerContainer* cont) @@ -1123,7 +1159,7 @@ int cmCTestCoverageHandler::HandleGCovCoverage( cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in gcovFile: " << gcovFile << std::endl); - std::ifstream ifile(gcovFile.c_str()); + cmsys::ifstream ifile(gcovFile.c_str()); if ( ! ifile ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: " @@ -1335,7 +1371,7 @@ int cmCTestCoverageHandler::HandleTracePyCoverage( = &cont->TotalCoverage[actualSourceFile]; cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: " << fileIt->c_str() << std::endl); - std::ifstream ifile(fileIt->c_str()); + cmsys::ifstream ifile(fileIt->c_str()); if ( ! ifile ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: " @@ -1495,7 +1531,7 @@ int cmCTestCoverageHandler::RunBullseyeCoverageBranch( "covbr output in " << outputFile << std::endl); // open the output file - std::ifstream fin(outputFile.c_str()); + cmsys::ifstream fin(outputFile.c_str()); if(!fin) { cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -1708,7 +1744,7 @@ int cmCTestCoverageHandler::RunBullseyeSourceSummary( std::vector<std::string> coveredFiles; std::vector<std::string> coveredFilesFullPath; // Read and parse the summary output file - std::ifstream fin(outputFile.c_str()); + cmsys::ifstream fin(outputFile.c_str()); if(!fin) { cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -1977,7 +2013,7 @@ void cmCTestCoverageHandler::LoadLabels() fileList += "/TargetDirectories.txt"; cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " target directory list [" << fileList << "]\n"); - std::ifstream finList(fileList.c_str()); + cmsys::ifstream finList(fileList.c_str()); std::string line; while(cmSystemTools::GetLineFromStream(finList, line)) { @@ -1991,7 +2027,7 @@ void cmCTestCoverageHandler::LoadLabels(const char* dir) LabelSet& dirLabels = this->TargetDirs[dir]; std::string fname = dir; fname += "/Labels.txt"; - std::ifstream fin(fname.c_str()); + cmsys::ifstream fin(fname.c_str()); if(!fin) { return; @@ -2045,7 +2081,7 @@ void cmCTestCoverageHandler::LoadLabels(const char* dir) } //---------------------------------------------------------------------- -void cmCTestCoverageHandler::WriteXMLLabels(std::ofstream& os, +void cmCTestCoverageHandler::WriteXMLLabels(std::ostream& os, std::string const& source) { LabelMapType::const_iterator li = this->SourceLabels.find(source); diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index 92b0b22..660f501 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -70,6 +70,10 @@ private: //! Handle coverage using xdebug php coverage int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont); + + //! Handle coverage for Python with coverage.py + int HandlePythonCoverage(cmCTestCoverageHandlerContainer* cont); + //! Handle coverage for mumps int HandleMumpsCoverage(cmCTestCoverageHandlerContainer* cont); @@ -128,7 +132,7 @@ private: // Label reading and writing methods. void LoadLabels(); void LoadLabels(const char* dir); - void WriteXMLLabels(std::ofstream& os, std::string const& source); + void WriteXMLLabels(std::ostream& os, std::string const& source); // Label-based filtering. std::set<int> LabelFilter; diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h index a763fe9..07e59a4 100644 --- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h +++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h @@ -50,26 +50,6 @@ public: */ virtual const char* GetName() const { return "ctest_empty_binary_directory";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "empties the binary directory"; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_empty_binary_directory( directory )\n" - "Removes a binary directory. This command will perform some checks " - "prior to deleting the directory in an attempt to avoid malicious " - "or accidental directory deletion."; - } - cmTypeMacro(cmCTestEmptyBinaryDirectoryCommand, cmCTestCommand); }; diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index 5b34491..0e0e797 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -18,6 +18,7 @@ #include <cmsys/RegularExpression.hxx> #include <cmsys/ios/sstream> #include <cmsys/Process.h> +#include <cmsys/FStream.hxx> #include <sys/types.h> #include <time.h> @@ -200,7 +201,7 @@ bool cmCTestGIT::UpdateByFetchAndReset() std::string sha1; { std::string fetch_head = this->FindGitDir() + "/FETCH_HEAD"; - std::ifstream fin(fetch_head.c_str(), std::ios::in | std::ios::binary); + cmsys::ifstream fin(fetch_head.c_str(), std::ios::in | std::ios::binary); if(!fin) { this->Log << "Unable to open " << fetch_head << "\n"; @@ -536,11 +537,11 @@ private: void DoHeaderLine() { // Look for header fields that we need. - if(strncmp(this->Line.c_str(), "commit ", 7) == 0) + if(cmHasLiteralPrefix(this->Line.c_str(), "commit ")) { this->Rev.Rev = this->Line.c_str()+7; } - else if(strncmp(this->Line.c_str(), "author ", 7) == 0) + else if(cmHasLiteralPrefix(this->Line.c_str(), "author ")) { Person author; this->ParsePerson(this->Line.c_str()+7, author); @@ -548,7 +549,7 @@ private: this->Rev.EMail = author.EMail; this->Rev.Date = this->FormatDateTime(author); } - else if(strncmp(this->Line.c_str(), "committer ", 10) == 0) + else if(cmHasLiteralPrefix(this->Line.c_str(), "committer ")) { Person committer; this->ParsePerson(this->Line.c_str()+10, committer); diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 9831d02..7d9c034 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -19,6 +19,7 @@ #include <cmsys/MD5.h> #include <cmsys/Process.h> #include <cmsys/RegularExpression.hxx> +#include <cmsys/FStream.hxx> //---------------------------------------------------------------------------- cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) @@ -64,7 +65,8 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) DoingTargetName, DoingTargetType, DoingBuildDir, - DoingCount }; + DoingCount, + DoingFilterPrefix }; Doing doing = DoingNone; int arg0 = 0; for(int i=1; !arg0 && i < argc; ++i) @@ -98,6 +100,10 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) { doing = DoingBuildDir; } + else if(strcmp(arg, "--filter-prefix") == 0) + { + doing = DoingFilterPrefix; + } else if(doing == DoingOutput) { this->OptionOutput = arg; @@ -132,6 +138,11 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) this->OptionBuildDir = arg; doing = DoingNone; } + else if(doing == DoingFilterPrefix) + { + this->OptionFilterPrefix = arg; + doing = DoingNone; + } } // Extract the real command line. @@ -161,7 +172,7 @@ void cmCTestLaunch::HandleRealArg(const char* arg) // Expand response file arguments. if(arg[0] == '@' && cmSystemTools::FileExists(arg+1)) { - std::ifstream fin(arg+1); + cmsys::ifstream fin(arg+1); std::string line; while(cmSystemTools::GetLineFromStream(fin, line)) { @@ -231,8 +242,8 @@ void cmCTestLaunch::RunChild() cmsysProcess* cp = this->Process; cmsysProcess_SetCommand(cp, this->RealArgV); - std::ofstream fout; - std::ofstream ferr; + cmsys::ofstream fout; + cmsys::ofstream ferr; if(this->Passthru) { // In passthru mode we just share the output pipes. @@ -320,7 +331,7 @@ void cmCTestLaunch::LoadLabels() cmSystemTools::ConvertToUnixSlashes(source); // Load the labels file. - std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); if(!fin) { return; } bool inTarget = true; bool inSource = false; @@ -569,12 +580,19 @@ void cmCTestLaunch::WriteXMLLabels(std::ostream& fxml) void cmCTestLaunch::DumpFileToXML(std::ostream& fxml, std::string const& fname) { - std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; const char* sep = ""; + while(cmSystemTools::GetLineFromStream(fin, line)) { + if(OptionFilterPrefix.size() && cmSystemTools::StringStartsWith( + line.c_str(), OptionFilterPrefix.c_str())) + { + continue; + } + fxml << sep << cmXMLSafe(line).Quotes(false); sep = "\n"; } @@ -635,7 +653,7 @@ cmCTestLaunch fname += "Custom"; fname += purpose; fname += ".txt"; - std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; cmsys::RegularExpression rex; while(cmSystemTools::GetLineFromStream(fin, line)) @@ -654,7 +672,7 @@ bool cmCTestLaunch::ScrapeLog(std::string const& fname) // Look for log file lines matching warning expressions but not // suppression expressions. - std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; while(cmSystemTools::GetLineFromStream(fin, line)) { diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index 7457e83..a86a9df 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -45,6 +45,7 @@ private: std::string OptionTargetName; std::string OptionTargetType; std::string OptionBuildDir; + std::string OptionFilterPrefix; bool ParseArguments(int argc, const char* const* argv); // The real command line appearing after launcher arguments. diff --git a/Source/CTest/cmCTestMemCheckCommand.h b/Source/CTest/cmCTestMemCheckCommand.h index 6db47ae..b50170d 100644 --- a/Source/CTest/cmCTestMemCheckCommand.h +++ b/Source/CTest/cmCTestMemCheckCommand.h @@ -43,40 +43,6 @@ public: */ virtual const char* GetName() const { return "ctest_memcheck";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Run tests with a dynamic analysis tool."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_memcheck([BUILD build_dir] [RETURN_VALUE res] [APPEND]\n" - " [START start number] [END end number]\n" - " [STRIDE stride number] [EXCLUDE exclude regex ]\n" - " [INCLUDE include regex] \n" - " [EXCLUDE_LABEL exclude regex] \n" - " [INCLUDE_LABEL label regex] \n" - " [PARALLEL_LEVEL level] )\n" - "Tests the given build directory and stores results in MemCheck.xml. " - "The second argument is a variable that will hold value. Optionally, " - "you can specify the starting test number START, the ending test number " - "END, the number of tests to skip between each test STRIDE, a regular " - "expression for tests to run INCLUDE, or a regular expression for tests " - "not to run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular " - "expressions for tests to be included or excluded by the test " - "property LABEL. PARALLEL_LEVEL should be set to a positive number " - "representing the number of tests to be run in parallel." - "\n" - CTEST_COMMAND_APPEND_OPTION_DOCS; - } - cmTypeMacro(cmCTestMemCheckCommand, cmCTestTestCommand); protected: diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 3ae2ac6..fdce04d 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -18,6 +18,7 @@ #include <cmsys/Process.h> #include <cmsys/RegularExpression.hxx> #include <cmsys/Base64.h> +#include <cmsys/FStream.hxx> #include "cmMakefile.h" #include "cmXMLSafe.h" @@ -929,7 +930,7 @@ cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(cmCTestTestResult& res, } // put a scope around this to close ifs so the file can be removed { - std::ifstream ifs(ofile.c_str()); + cmsys::ifstream ifs(ofile.c_str()); if ( !ifs ) { std::string log = "Cannot read memory tester output file: " + ofile; @@ -984,7 +985,7 @@ cmCTestMemCheckHandler::appendMemTesterOutput(cmCTestTestResult& res, { return; } - std::ifstream ifs(ofile.c_str()); + cmsys::ifstream ifs(ofile.c_str()); if ( !ifs ) { std::string log = "Cannot read memory tester output file: " + ofile; diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 76ddeea..ddd1707 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -17,6 +17,7 @@ #include <stdlib.h> #include <stack> #include <float.h> +#include <cmsys/FStream.hxx> class TestComparator { @@ -41,6 +42,7 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() this->Completed = 0; this->RunningCount = 0; this->StopTimePassed = false; + this->HasCycles = false; } cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() @@ -65,6 +67,11 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests, if(!this->CTest->GetShowOnly()) { this->ReadCostData(); + this->HasCycles = !this->CheckCycles(); + if(this->HasCycles) + { + return; + } this->CreateTestCostList(); } } @@ -79,7 +86,7 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level) void cmCTestMultiProcessHandler::RunTests() { this->CheckResume(); - if(!this->CheckCycles()) + if(this->HasCycles) { return; } @@ -133,6 +140,13 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) } else { + + for(TestMap::iterator j = this->Tests.begin(); + j != this->Tests.end(); ++j) + { + j->second.erase(test); + } + this->UnlockResources(test); this->Completed++; this->TestFinishMap[test] = true; @@ -205,37 +219,8 @@ bool cmCTestMultiProcessHandler::StartTest(int test) } } - // copy the depend tests locally because when - // a test is finished it will be removed from the depend list - // and we don't want to be iterating a list while removing from it - TestSet depends = this->Tests[test]; - size_t totalDepends = depends.size(); - if(totalDepends) - { - for(TestSet::const_iterator i = depends.begin(); - i != depends.end(); ++i) - { - // if the test is not already running then start it - if(!this->TestRunningMap[*i]) - { - // this test might be finished, but since - // this is a copy of the depend map we might - // still have it - if(!this->TestFinishMap[*i]) - { - // only start one test in this function - return this->StartTest(*i); - } - else - { - // the depend has been and finished - totalDepends--; - } - } - } - } // if there are no depends left then run this test - if(totalDepends == 0) + if(this->Tests[test].empty()) { this->StartTestProcess(test); return true; @@ -262,25 +247,17 @@ void cmCTestMultiProcessHandler::StartNextTests() TestList copy = this->SortedTests; for(TestList::iterator test = copy.begin(); test != copy.end(); ++test) { - //in case this test has already been started due to dependency - if(this->TestRunningMap[*test] || this->TestFinishMap[*test]) - { - continue; - } size_t processors = GetProcessorsUsed(*test); - if(processors > numToStart) - { - return; - } - if(this->StartTest(*test)) + + if(processors <= numToStart && this->StartTest(*test)) { - if(this->StopTimePassed) - { - return; - } - numToStart -= processors; + if(this->StopTimePassed) + { + return; + } + numToStart -= processors; } - if(numToStart == 0) + else if(numToStart == 0) { return; } @@ -349,7 +326,7 @@ void cmCTestMultiProcessHandler::UpdateCostData() if(cmSystemTools::FileExists(fname.c_str())) { - std::ifstream fin; + cmsys::ifstream fin; fin.open(fname.c_str()); std::string line; @@ -408,7 +385,7 @@ void cmCTestMultiProcessHandler::ReadCostData() if(cmSystemTools::FileExists(fname.c_str(), true)) { - std::ifstream fin; + cmsys::ifstream fin; fin.open(fname.c_str()); std::string line; while(std::getline(fin, line)) @@ -472,24 +449,160 @@ int cmCTestMultiProcessHandler::SearchByName(std::string name) //--------------------------------------------------------- void cmCTestMultiProcessHandler::CreateTestCostList() { - for(TestMap::iterator i = this->Tests.begin(); - i != this->Tests.end(); ++i) + if(this->ParallelLevel > 1) { - SortedTests.push_back(i->first); + CreateParallelTestCostList(); + } + else + { + CreateSerialTestCostList(); + } +} - //If the test failed last time, it should be run first, so max the cost. - //Only do this for parallel runs; in non-parallel runs, avoid clobbering - //the test's explicitly set cost. - if(this->ParallelLevel > 1 && - std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), +//--------------------------------------------------------- +void cmCTestMultiProcessHandler::CreateParallelTestCostList() +{ + TestSet alreadySortedTests; + + std::list<TestSet> priorityStack; + priorityStack.push_back(TestSet()); + TestSet &topLevel = priorityStack.back(); + + // In parallel test runs add previously failed tests to the front + // of the cost list and queue other tests for further sorting + for(TestMap::const_iterator i = this->Tests.begin(); + i != this->Tests.end(); ++i) + { + if(std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), this->Properties[i->first]->Name) != this->LastTestsFailed.end()) { - this->Properties[i->first]->Cost = FLT_MAX; + //If the test failed last time, it should be run first. + this->SortedTests.push_back(i->first); + alreadySortedTests.insert(i->first); + } + else + { + topLevel.insert(i->first); } } + // In parallel test runs repeatedly move dependencies of the tests on + // the current dependency level to the next level until no + // further dependencies exist. + while(priorityStack.back().size()) + { + TestSet &previousSet = priorityStack.back(); + priorityStack.push_back(TestSet()); + TestSet ¤tSet = priorityStack.back(); + + for(TestSet::const_iterator i = previousSet.begin(); + i != previousSet.end(); ++i) + { + TestSet const& dependencies = this->Tests[*i]; + for(TestSet::const_iterator j = dependencies.begin(); + j != dependencies.end(); ++j) + { + currentSet.insert(*j); + } + } + + for(TestSet::const_iterator i = currentSet.begin(); + i != currentSet.end(); ++i) + { + previousSet.erase(*i); + } + } + + // Remove the empty dependency level + priorityStack.pop_back(); + + // Reverse iterate over the different dependency levels (deepest first). + // Sort tests within each level by COST and append them to the cost list. + for(std::list<TestSet>::reverse_iterator i = priorityStack.rbegin(); + i != priorityStack.rend(); ++i) + { + TestSet const& currentSet = *i; + TestComparator comp(this); + + TestList sortedCopy; + + for(TestSet::const_iterator j = currentSet.begin(); + j != currentSet.end(); ++j) + { + sortedCopy.push_back(*j); + } + + std::stable_sort(sortedCopy.begin(), sortedCopy.end(), comp); + + for(TestList::const_iterator j = sortedCopy.begin(); + j != sortedCopy.end(); ++j) + { + if(alreadySortedTests.find(*j) == alreadySortedTests.end()) + { + this->SortedTests.push_back(*j); + alreadySortedTests.insert(*j); + } + } + } +} + +//--------------------------------------------------------- +void cmCTestMultiProcessHandler::GetAllTestDependencies( + int test, TestList& dependencies) +{ + TestSet const& dependencySet = this->Tests[test]; + for(TestSet::const_iterator i = dependencySet.begin(); + i != dependencySet.end(); ++i) + { + GetAllTestDependencies(*i, dependencies); + dependencies.push_back(*i); + } +} + +//--------------------------------------------------------- +void cmCTestMultiProcessHandler::CreateSerialTestCostList() +{ + TestList presortedList; + + for(TestMap::iterator i = this->Tests.begin(); + i != this->Tests.end(); ++i) + { + presortedList.push_back(i->first); + } + TestComparator comp(this); - std::stable_sort(SortedTests.begin(), SortedTests.end(), comp); + std::stable_sort(presortedList.begin(), presortedList.end(), comp); + + TestSet alreadySortedTests; + + for(TestList::const_iterator i = presortedList.begin(); + i != presortedList.end(); ++i) + { + int test = *i; + + if(alreadySortedTests.find(test) != alreadySortedTests.end()) + { + continue; + } + + TestList dependencies; + GetAllTestDependencies(test, dependencies); + + for(TestList::const_iterator j = dependencies.begin(); + j != dependencies.end(); ++j) + { + int testDependency = *j; + + if(alreadySortedTests.find(testDependency) == alreadySortedTests.end()) + { + alreadySortedTests.insert(testDependency); + this->SortedTests.push_back(testDependency); + } + } + + alreadySortedTests.insert(test); + this->SortedTests.push_back(test); + } } //--------------------------------------------------------- @@ -609,7 +722,7 @@ void cmCTestMultiProcessHandler::CheckResume() << "----------------------------------------------------------" << std::endl; - std::ifstream fin; + cmsys::ifstream fin; fin.open(fname.c_str()); std::string line; while(std::getline(fin, line)) diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index cd21d91..1b53ec7 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -72,6 +72,12 @@ protected: int SearchByName(std::string name); void CreateTestCostList(); + + void GetAllTestDependencies(int test, TestList& dependencies); + void CreateSerialTestCostList(); + + void CreateParallelTestCostList(); + // Removes the checkpoint file void MarkFinished(); void EraseTest(int index); @@ -111,6 +117,7 @@ protected: std::set<cmCTestRunTest*> RunningTests; // current running tests cmCTestTestHandler * TestHandler; cmCTest* CTest; + bool HasCycles; }; #endif diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx new file mode 100644 index 0000000..0058721 --- /dev/null +++ b/Source/CTest/cmCTestP4.cxx @@ -0,0 +1,559 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2013 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. +============================================================================*/ +#include "cmCTestP4.h" + +#include "cmCTest.h" +#include "cmSystemTools.h" +#include "cmXMLSafe.h" + +#include <cmsys/RegularExpression.hxx> +#include <cmsys/ios/sstream> +#include <cmsys/Process.h> + +#include <sys/types.h> +#include <time.h> +#include <ctype.h> + +//---------------------------------------------------------------------------- +cmCTestP4::cmCTestP4(cmCTest* ct, std::ostream& log): + cmCTestGlobalVC(ct, log) +{ + this->PriorRev = this->Unknown; +} + +//---------------------------------------------------------------------------- +cmCTestP4::~cmCTestP4() +{ +} + +//---------------------------------------------------------------------------- +class cmCTestP4::IdentifyParser: public cmCTestVC::LineParser +{ +public: + IdentifyParser(cmCTestP4* p4, const char* prefix, + std::string& rev): Rev(rev) + { + this->SetLog(&p4->Log, prefix); + this->RegexIdentify.compile("^Change ([0-9]+) on"); + } +private: + std::string& Rev; + cmsys::RegularExpression RegexIdentify; + + bool ProcessLine() + { + if(this->RegexIdentify.find(this->Line)) + { + this->Rev = this->RegexIdentify.match(1); + return false; + } + return true; + } +}; + +//---------------------------------------------------------------------------- +class cmCTestP4::ChangesParser: public cmCTestVC::LineParser +{ +public: + ChangesParser(cmCTestP4* p4, const char* prefix) : P4(p4) + { + this->SetLog(&P4->Log, prefix); + this->RegexIdentify.compile("^Change ([0-9]+) on"); + } +private: + cmsys::RegularExpression RegexIdentify; + cmCTestP4* P4; + + bool ProcessLine() + { + if(this->RegexIdentify.find(this->Line)) + { + P4->ChangeLists.push_back(this->RegexIdentify.match(1)); + } + return true; + } +}; + +//---------------------------------------------------------------------------- +class cmCTestP4::UserParser: public cmCTestVC::LineParser +{ +public: + UserParser(cmCTestP4* p4, const char* prefix) : P4(p4) + { + this->SetLog(&P4->Log, prefix); + this->RegexUser.compile("^(.+) <(.*)> \\((.*)\\) accessed (.*)$"); + } +private: + cmsys::RegularExpression RegexUser; + cmCTestP4* P4; + + bool ProcessLine() + { + if(this->RegexUser.find(this->Line)) + { + User NewUser; + + NewUser.UserName = this->RegexUser.match(1); + NewUser.EMail = this->RegexUser.match(2); + NewUser.Name = this->RegexUser.match(3); + NewUser.AccessTime = this->RegexUser.match(4); + P4->Users[this->RegexUser.match(1)] = NewUser; + + return false; + } + return true; + } +}; + +//---------------------------------------------------------------------------- +/* Diff format: +==== //depot/file#rev - /absolute/path/to/file ==== +(diff data) +==== //depot/file2#rev - /absolute/path/to/file2 ==== +(diff data) +==== //depot/file3#rev - /absolute/path/to/file3 ==== +==== //depot/file4#rev - /absolute/path/to/file4 ==== +(diff data) +*/ +class cmCTestP4::DiffParser: public cmCTestVC::LineParser +{ +public: + DiffParser(cmCTestP4* p4, const char* prefix) + : P4(p4), AlreadyNotified(false) + { + this->SetLog(&P4->Log, prefix); + this->RegexDiff.compile("^==== (.*)#[0-9]+ - (.*)"); + } +private: + cmCTestP4* P4; + bool AlreadyNotified; + std::string CurrentPath; + cmsys::RegularExpression RegexDiff; + + bool ProcessLine() + { + if(!this->Line.empty() && this->Line[0] == '=' + && this->RegexDiff.find(this->Line)) + { + CurrentPath = this->RegexDiff.match(1); + AlreadyNotified = false; + } + else + { + if(!AlreadyNotified) + { + P4->DoModification(PathModified, CurrentPath); + AlreadyNotified = true; + } + } + return true; + } +}; + +//---------------------------------------------------------------------------- +cmCTestP4::User cmCTestP4::GetUserData(const std::string& username) +{ + std::map<std::string, cmCTestP4::User>::const_iterator it = + Users.find(username); + + if(it == Users.end()) + { + std::vector<char const*> p4_users; + SetP4Options(p4_users); + p4_users.push_back("users"); + p4_users.push_back("-m"); + p4_users.push_back("1"); + p4_users.push_back(username.c_str()); + p4_users.push_back(0); + + UserParser out(this, "users-out> "); + OutputLogger err(this->Log, "users-err> "); + RunChild(&p4_users[0], &out, &err); + + // The user should now be added to the map. Search again. + it = Users.find(username); + if(it == Users.end()) + { + return cmCTestP4::User(); + } + } + + return it->second; +} + +//---------------------------------------------------------------------------- +/* Commit format: + +Change 1111111 by user@client on 2013/09/26 11:50:36 + + text + text + +Affected files ... + +... //path/to/file#rev edit +... //path/to/file#rev add +... //path/to/file#rev delete +... //path/to/file#rev integrate +*/ +class cmCTestP4::DescribeParser: public cmCTestVC::LineParser +{ +public: + DescribeParser(cmCTestP4* p4, const char* prefix): + LineParser('\n', false), P4(p4), Section(SectionHeader) + { + this->SetLog(&P4->Log, prefix); + this->RegexHeader.compile("^Change ([0-9]+) by (.+)@(.+) on (.*)$"); + this->RegexDiff.compile("^\\.\\.\\. (.*)#[0-9]+ ([^ ]+)$"); + } +private: + cmsys::RegularExpression RegexHeader; + cmsys::RegularExpression RegexDiff; + cmCTestP4* P4; + + typedef cmCTestP4::Revision Revision; + typedef cmCTestP4::Change Change; + std::vector<Change> Changes; + enum SectionType { SectionHeader, SectionBody, SectionDiffHeader, + SectionDiff, SectionCount }; + SectionType Section; + Revision Rev; + + virtual bool ProcessLine() + { + if(this->Line.empty()) + { + this->NextSection(); + } + else + { + switch(this->Section) + { + case SectionHeader: this->DoHeaderLine(); break; + case SectionBody: this->DoBodyLine(); break; + case SectionDiffHeader: break; // nothing to do + case SectionDiff: this->DoDiffLine(); break; + case SectionCount: break; // never happens + } + } + return true; + } + + void NextSection() + { + if(this->Section == SectionDiff) + { + this->P4->DoRevision(this->Rev, this->Changes); + this->Rev = Revision(); + } + + this->Section = SectionType((this->Section+1) % SectionCount); + } + + void DoHeaderLine() + { + if(this->RegexHeader.find(this->Line)) + { + this->Rev.Rev = this->RegexHeader.match(1); + this->Rev.Date = this->RegexHeader.match(4); + + cmCTestP4::User user = P4->GetUserData(this->RegexHeader.match(2)); + this->Rev.Author = user.Name; + this->Rev.EMail = user.EMail; + + this->Rev.Committer = this->Rev.Author; + this->Rev.CommitterEMail = this->Rev.EMail; + this->Rev.CommitDate = this->Rev.Date; + } + } + + void DoBodyLine() + { + if(this->Line[0] == '\t') + { + this->Rev.Log += this->Line.substr(1); + } + this->Rev.Log += "\n"; + } + + void DoDiffLine() + { + if(this->RegexDiff.find(this->Line)) + { + Change change; + std::string Path = this->RegexDiff.match(1); + if(Path.length() > 2 && Path[0] == '/' && Path[1] == '/') + { + size_t found = Path.find('/', 2); + if(found != std::string::npos) + { + Path = Path.substr(found + 1); + } + } + + change.Path = Path; + std::string action = this->RegexDiff.match(2); + + if(action == "add") + { + change.Action = 'A'; + } + else if(action == "delete") + { + change.Action = 'D'; + } + else if(action == "edit" || action == "integrate") + { + change.Action = 'M'; + } + + Changes.push_back(change); + } + } +}; + +//---------------------------------------------------------------------------- +void cmCTestP4::SetP4Options(std::vector<char const*> &CommandOptions) +{ + if(P4Options.size() == 0) + { + const char* p4 = this->CommandLineTool.c_str(); + P4Options.push_back(p4); + + //The CTEST_P4_CLIENT variable sets the P4 client used when issuing + //Perforce commands, if it's different from the default one. + std::string client = this->CTest->GetCTestConfiguration("P4Client"); + if(!client.empty()) + { + P4Options.push_back("-c"); + P4Options.push_back(client); + } + + //Set the message language to be English, in case the P4 admin + //has localized them + P4Options.push_back("-L"); + P4Options.push_back("en"); + + //The CTEST_P4_OPTIONS variable adds additional Perforce command line + //options before the main command + std::string opts = this->CTest->GetCTestConfiguration("P4Options"); + std::vector<cmStdString> args = + cmSystemTools::ParseArguments(opts.c_str()); + + for(std::vector<cmStdString>::const_iterator ai = args.begin(); + ai != args.end(); ++ai) + { + P4Options.push_back(ai->c_str()); + } + } + + CommandOptions.clear(); + for(std::vector<std::string>::iterator i = P4Options.begin(); + i != P4Options.end(); ++i) + { + CommandOptions.push_back(i->c_str()); + } +} + +//---------------------------------------------------------------------------- +std::string cmCTestP4::GetWorkingRevision() +{ + std::vector<char const*> p4_identify; + SetP4Options(p4_identify); + + p4_identify.push_back("changes"); + p4_identify.push_back("-m"); + p4_identify.push_back("1"); + p4_identify.push_back("-t"); + + std::string source = this->SourceDirectory + "/...#have"; + p4_identify.push_back(source.c_str()); + p4_identify.push_back(0); + + std::string rev; + IdentifyParser out(this, "rev-out> ", rev); + OutputLogger err(this->Log, "rev-err> "); + + RunChild(&p4_identify[0], &out, &err); + + if(rev.empty()) + { + return "0"; + } + else + { + return rev; + } +} + +//---------------------------------------------------------------------------- +void cmCTestP4::NoteOldRevision() +{ + this->OldRevision = this->GetWorkingRevision(); + + cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: " + << this->OldRevision << "\n"); + this->PriorRev.Rev = this->OldRevision; +} + +//---------------------------------------------------------------------------- +void cmCTestP4::NoteNewRevision() +{ + this->NewRevision = this->GetWorkingRevision(); + + cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: " + << this->NewRevision << "\n"); +} + +//---------------------------------------------------------------------------- +void cmCTestP4::LoadRevisions() +{ + std::vector<char const*> p4_changes; + SetP4Options(p4_changes); + + // Use 'p4 changes ...@old,new' to get a list of changelists + std::string range = this->SourceDirectory + "/..."; + + if(this->OldRevision != "0") + { + range.append("@").append(this->OldRevision); + } + + if(this->NewRevision != "0") + { + if(this->OldRevision != "0") + { + range.append(",").append(this->NewRevision); + } + else + { + range.append("@").append(this->NewRevision); + } + } + + p4_changes.push_back("changes"); + p4_changes.push_back(range.c_str()); + p4_changes.push_back(0); + + ChangesParser out(this, "changes-out> "); + OutputLogger err(this->Log, "changes-err> "); + + ChangeLists.clear(); + this->RunChild(&p4_changes[0], &out, &err); + + if(ChangeLists.size() == 0) + return; + + //p4 describe -s ...@1111111,2222222 + std::vector<char const*> p4_describe; + for(std::vector<std::string>::reverse_iterator i = ChangeLists.rbegin(); + i != ChangeLists.rend(); ++i) + { + SetP4Options(p4_describe); + p4_describe.push_back("describe"); + p4_describe.push_back("-s"); + p4_describe.push_back(i->c_str()); + p4_describe.push_back(0); + + DescribeParser outDescribe(this, "describe-out> "); + OutputLogger errDescribe(this->Log, "describe-err> "); + this->RunChild(&p4_describe[0], &outDescribe, &errDescribe); + } +} + +//---------------------------------------------------------------------------- +void cmCTestP4::LoadModifications() +{ + std::vector<char const*> p4_diff; + SetP4Options(p4_diff); + + p4_diff.push_back("diff"); + + //Ideally we would use -Od but not all clients support it + p4_diff.push_back("-dn"); + std::string source = this->SourceDirectory + "/..."; + p4_diff.push_back(source.c_str()); + p4_diff.push_back(0); + + DiffParser out(this, "diff-out> "); + OutputLogger err(this->Log, "diff-err> "); + this->RunChild(&p4_diff[0], &out, &err); +} + +//---------------------------------------------------------------------------- +bool cmCTestP4::UpdateCustom(const std::string& custom) +{ + std::vector<std::string> p4_custom_command; + cmSystemTools::ExpandListArgument(custom, p4_custom_command, true); + + std::vector<char const*> p4_custom; + for(std::vector<std::string>::const_iterator + i = p4_custom_command.begin(); i != p4_custom_command.end(); ++i) + { + p4_custom.push_back(i->c_str()); + } + p4_custom.push_back(0); + + OutputLogger custom_out(this->Log, "custom-out> "); + OutputLogger custom_err(this->Log, "custom-err> "); + + return this->RunUpdateCommand(&p4_custom[0], &custom_out, &custom_err); +} + +//---------------------------------------------------------------------------- +bool cmCTestP4::UpdateImpl() +{ + std::string custom = this->CTest->GetCTestConfiguration("P4UpdateCustom"); + if(!custom.empty()) + { + return this->UpdateCustom(custom); + } + + std::vector<char const*> p4_sync; + SetP4Options(p4_sync); + + p4_sync.push_back("sync"); + + // Get user-specified update options. + std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions"); + if(opts.empty()) + { + opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); + } + std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str()); + for(std::vector<cmStdString>::const_iterator ai = args.begin(); + ai != args.end(); ++ai) + { + p4_sync.push_back(ai->c_str()); + } + + std::string source = this->SourceDirectory + "/..."; + + // Specify the start time for nightly testing. + if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) + { + std::string date = this->GetNightlyTime(); + //CTest reports the date as YYYY-MM-DD, Perforce needs it as YYYY/MM/DD + std::replace(date.begin(), date.end(), '-', '/'); + + //Revision specification: /...@"YYYY/MM/DD HH:MM:SS" + source.append("@\"").append(date).append("\""); + } + + p4_sync.push_back(source.c_str()); + p4_sync.push_back(0); + + OutputLogger out(this->Log, "sync-out> "); + OutputLogger err(this->Log, "sync-err> "); + + return this->RunUpdateCommand(&p4_sync[0], &out, &err); +} diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h new file mode 100644 index 0000000..7a53475 --- /dev/null +++ b/Source/CTest/cmCTestP4.h @@ -0,0 +1,71 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2013 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 cmCTestP4_h +#define cmCTestP4_h + +#include "cmCTestGlobalVC.h" +#include <vector> +#include <map> + +/** \class cmCTestP4 + * \brief Interaction with the Perforce command-line tool + * + */ +class cmCTestP4: public cmCTestGlobalVC +{ +public: + /** Construct with a CTest instance and update log stream. */ + cmCTestP4(cmCTest* ctest, std::ostream& log); + + virtual ~cmCTestP4(); + +private: + std::vector<std::string> ChangeLists; + + struct User + { + std::string UserName; + std::string Name; + std::string EMail; + std::string AccessTime; + + User(): UserName(), Name(), EMail(), AccessTime() {} + }; + std::map<std::string, User> Users; + std::vector<std::string> P4Options; + + User GetUserData(const std::string& username); + void SetP4Options(std::vector<char const*> &options); + + std::string GetWorkingRevision(); + virtual void NoteOldRevision(); + virtual void NoteNewRevision(); + virtual bool UpdateImpl(); + bool UpdateCustom(const std::string& custom); + + void LoadRevisions(); + void LoadModifications(); + + // Parsing helper classes. + class IdentifyParser; + class ChangesParser; + class UserParser; + class DescribeParser; + class DiffParser; + friend class IdentifyParser; + friend class ChangesParser; + friend class UserParser; + friend class DescribeParser; + friend class DiffParser; +}; + +#endif diff --git a/Source/CTest/cmCTestReadCustomFilesCommand.h b/Source/CTest/cmCTestReadCustomFilesCommand.h index b984c84..9c0af81 100644 --- a/Source/CTest/cmCTestReadCustomFilesCommand.h +++ b/Source/CTest/cmCTestReadCustomFilesCommand.h @@ -48,25 +48,6 @@ public: */ virtual const char* GetName() const { return "ctest_read_custom_files";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "read CTestCustom files."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_read_custom_files( directory ... )\n" - "Read all the CTestCustom.ctest or CTestCustom.cmake files from " - "the given directory."; - } - cmTypeMacro(cmCTestReadCustomFilesCommand, cmCTestCommand); }; diff --git a/Source/CTest/cmCTestRunScriptCommand.h b/Source/CTest/cmCTestRunScriptCommand.h index 05e7899..f34bd13 100644 --- a/Source/CTest/cmCTestRunScriptCommand.h +++ b/Source/CTest/cmCTestRunScriptCommand.h @@ -49,30 +49,6 @@ public: */ virtual const char* GetName() const { return "ctest_run_script";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "runs a ctest -S script"; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_run_script([NEW_PROCESS] script_file_name script_file_name1 \n" - " script_file_name2 ... [RETURN_VALUE var])\n" - "Runs a script or scripts much like if it was run from ctest -S. " - "If no argument is provided then the current script is run using " - "the current settings of the variables. If NEW_PROCESS is specified " - "then each script will be run in a separate process." - "If RETURN_VALUE is specified the return value of the last script " - "run will be put into var."; - } - cmTypeMacro(cmCTestRunScriptCommand, cmCTestCommand); }; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 0e2fa41..cdf90b9 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -206,7 +206,13 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) bool success = !forceFail && (retVal == 0 || this->TestProperties->RequiredRegularExpressions.size()); - if((success && !this->TestProperties->WillFail) + if(this->TestProperties->SkipReturnCode >= 0 + && this->TestProperties->SkipReturnCode == retVal) + { + this->TestResult.Status = cmCTestTestHandler::NOT_RUN; + cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped "); + } + else if((success && !this->TestProperties->WillFail) || (!success && this->TestProperties->WillFail)) { this->TestResult.Status = cmCTestTestHandler::COMPLETED; diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 8643cb3..00a0a09 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -22,6 +22,7 @@ //#include <cmsys/RegularExpression.hxx> #include <cmsys/Process.h> +#include <cmsys/Directory.hxx> // used for sleep #ifdef _WIN32 @@ -221,13 +222,13 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) // execute the script passing in the arguments to the script as well as the // arguments from this invocation of cmake std::vector<const char*> argv; - argv.push_back(this->CTest->GetCTestExecutable()); + argv.push_back(cmSystemTools::GetCTestCommand().c_str()); argv.push_back("-SR"); argv.push_back(total_script_arg.c_str()); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Executable for CTest is: " << - this->CTest->GetCTestExecutable() << "\n"); + cmSystemTools::GetCTestCommand() << "\n"); // now pass through all the other arguments std::vector<cmStdString> &initArgs = @@ -361,12 +362,6 @@ void cmCTestScriptHandler::CreateCMake() this->AddCTestCommand(new cmCTestUploadCommand); } -void cmCTestScriptHandler::GetCommandDocumentation( - std::vector<cmDocumentationEntry>& v) const -{ - this->CMake->GetCommandDocumentation(v); -} - //---------------------------------------------------------------------- // this sets up some variables for the script to use, creates the required // cmake instance and generators, and then reads in the script @@ -402,9 +397,9 @@ int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg) this->Makefile->AddDefinition("CTEST_SCRIPT_NAME", cmSystemTools::GetFilenameName(script).c_str()); this->Makefile->AddDefinition("CTEST_EXECUTABLE_NAME", - this->CTest->GetCTestExecutable()); + cmSystemTools::GetCTestCommand().c_str()); this->Makefile->AddDefinition("CMAKE_EXECUTABLE_NAME", - this->CTest->GetCMakeExecutable()); + cmSystemTools::GetCMakeCommand().c_str()); this->Makefile->AddDefinition("CTEST_RUN_CURRENT_SCRIPT", true); this->UpdateElapsedTime(); @@ -1062,15 +1057,71 @@ bool cmCTestScriptHandler::EmptyBinaryDirectory(const char *sname) return false; } + // consider non existing target directory a success + if(!cmSystemTools::FileExists(sname)) + { + return true; + } + // try to avoid deleting directories that we shouldn't std::string check = sname; check += "/CMakeCache.txt"; - if(cmSystemTools::FileExists(check.c_str()) && - !cmSystemTools::RemoveADirectory(sname)) + + if(!cmSystemTools::FileExists(check.c_str())) { return false; } - return true; + + for(int i = 0; i < 5; ++i) + { + if(TryToRemoveBinaryDirectoryOnce(sname)) + { + return true; + } + cmSystemTools::Delay(100); + } + + return false; +} + +//------------------------------------------------------------------------- +bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce( + const std::string& directoryPath) +{ + cmsys::Directory directory; + directory.Load(directoryPath.c_str()); + + for(unsigned long i = 0; i < directory.GetNumberOfFiles(); ++i) + { + std::string path = directory.GetFile(i); + + if(path == "." || path == ".." || path == "CMakeCache.txt") + { + continue; + } + + std::string fullPath = directoryPath + std::string("/") + path; + + bool isDirectory = cmSystemTools::FileIsDirectory(fullPath.c_str()) && + !cmSystemTools::FileIsSymlink(fullPath.c_str()); + + if(isDirectory) + { + if(!cmSystemTools::RemoveADirectory(fullPath.c_str())) + { + return false; + } + } + else + { + if(!cmSystemTools::RemoveFile(fullPath.c_str())) + { + return false; + } + } + } + + return cmSystemTools::RemoveADirectory(directoryPath.c_str()); } //------------------------------------------------------------------------- diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h index 9d852ca..44e9dd0 100644 --- a/Source/CTest/cmCTestScriptHandler.h +++ b/Source/CTest/cmCTestScriptHandler.h @@ -110,7 +110,6 @@ public: void Initialize(); void CreateCMake(); - void GetCommandDocumentation(std::vector<cmDocumentationEntry>& v) const; cmake* GetCMake() { return this->CMake;} private: // reads in a script @@ -136,6 +135,9 @@ private: // Add ctest command void AddCTestCommand(cmCTestCommand* command); + // Try to remove the binary directory once + static bool TryToRemoveBinaryDirectoryOnce(const std::string& directoryPath); + std::vector<cmStdString> ConfigurationScripts; std::vector<bool> ScriptProcessScope; diff --git a/Source/CTest/cmCTestSleepCommand.h b/Source/CTest/cmCTestSleepCommand.h index 0f51ddf..c6baf1c 100644 --- a/Source/CTest/cmCTestSleepCommand.h +++ b/Source/CTest/cmCTestSleepCommand.h @@ -49,26 +49,6 @@ public: */ virtual const char* GetName() const { return "ctest_sleep";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "sleeps for some amount of time"; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_sleep(<seconds>)\n" - "Sleep for given number of seconds.\n" - " ctest_sleep(<time1> <duration> <time2>)\n" - "Sleep for t=(time1 + duration - time2) seconds if t > 0."; - } - cmTypeMacro(cmCTestSleepCommand, cmCTestCommand); }; diff --git a/Source/CTest/cmCTestStartCommand.h b/Source/CTest/cmCTestStartCommand.h index 6be4770..e5535c1 100644 --- a/Source/CTest/cmCTestStartCommand.h +++ b/Source/CTest/cmCTestStartCommand.h @@ -57,30 +57,6 @@ public: */ virtual const char* GetName() const { return "ctest_start";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Starts the testing for a given model"; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_start(Model [TRACK <track>] [APPEND] [source [binary]])\n" - "Starts the testing for a given model. The command should be called " - "after the binary directory is initialized. If the 'source' and " - "'binary' directory are not specified, it reads the " - "CTEST_SOURCE_DIRECTORY and CTEST_BINARY_DIRECTORY. If the track is " - "specified, the submissions will go to the specified track. " - "If APPEND is used, the existing TAG is used rather than " - "creating a new one based on the current time stamp."; - } - cmTypeMacro(cmCTestStartCommand, cmCTestCommand); private: diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index 53ee875..64c6cae 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -50,44 +50,6 @@ public: */ virtual const char* GetName() const { return "ctest_submit";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Submit results to a dashboard server."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_submit([PARTS ...] [FILES ...] [RETRY_COUNT count] " - " [RETRY_DELAY delay][RETURN_VALUE res])\n" - "By default all available parts are submitted if no PARTS or FILES " - "are specified. " - "The PARTS option lists a subset of parts to be submitted. " - "Valid part names are:\n" - " Start = nothing\n" - " Update = ctest_update results, in Update.xml\n" - " Configure = ctest_configure results, in Configure.xml\n" - " Build = ctest_build results, in Build.xml\n" - " Test = ctest_test results, in Test.xml\n" - " Coverage = ctest_coverage results, in Coverage.xml\n" - " MemCheck = ctest_memcheck results, in DynamicAnalysis.xml\n" - " Notes = Files listed by CTEST_NOTES_FILES, in Notes.xml\n" - " ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES\n" - " Submit = nothing\n" - "The FILES option explicitly lists specific files to be submitted. " - "Each individual file must exist at the time of the call.\n" - "The RETRY_DELAY option specifies how long in seconds to wait after " - "a timed-out submission before attempting to re-submit.\n" - "The RETRY_COUNT option specifies how many times to retry a timed-out " - "submission.\n"; - } - cmTypeMacro(cmCTestSubmitCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 941d348..139f515 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -235,7 +235,7 @@ bool cmCTestSubmitHandler::SubmitUsingFTP(const cmStdString& localprefix, return false; } - ftpfile = ::fopen(local_file.c_str(), "rb"); + ftpfile = cmsys::SystemTools::Fopen(local_file.c_str(), "rb"); *this->LogFile << "\tUpload file: " << local_file.c_str() << " to " << upload_as.c_str() << std::endl; cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Upload file: " @@ -476,7 +476,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix, return false; } - ftpfile = ::fopen(local_file.c_str(), "rb"); + ftpfile = cmsys::SystemTools::Fopen(local_file.c_str(), "rb"); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Upload file: " << local_file.c_str() << " to " << upload_as.c_str() << " Size: " << st.st_size << std::endl); @@ -566,7 +566,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix, << count << std::endl); ::fclose(ftpfile); - ftpfile = ::fopen(local_file.c_str(), "rb"); + ftpfile = cmsys::SystemTools::Fopen(local_file.c_str(), "rb"); ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile); chunk.clear(); @@ -998,7 +998,7 @@ bool cmCTestSubmitHandler::SubmitUsingXMLRPC(const cmStdString& localprefix, return false; } size_t fileSize = static_cast<size_t>(st.st_size); - FILE* fp = fopen(local_file.c_str(), "rb"); + FILE* fp = cmsys::SystemTools::Fopen(local_file.c_str(), "rb"); if ( !fp ) { cmCTestLog(this->CTest, ERROR_MESSAGE, " Cannot open file: " diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h index 130cb69..451ac99 100644 --- a/Source/CTest/cmCTestTestCommand.h +++ b/Source/CTest/cmCTestTestCommand.h @@ -41,45 +41,6 @@ public: */ virtual const char* GetName() const { return "ctest_test";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Run tests in the project build tree."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_test([BUILD build_dir] [APPEND]\n" - " [START start number] [END end number]\n" - " [STRIDE stride number] [EXCLUDE exclude regex ]\n" - " [INCLUDE include regex] [RETURN_VALUE res] \n" - " [EXCLUDE_LABEL exclude regex] \n" - " [INCLUDE_LABEL label regex] \n" - " [PARALLEL_LEVEL level] \n" - " [SCHEDULE_RANDOM on] \n" - " [STOP_TIME time of day]) \n" - "Tests the given build directory and stores results in Test.xml. The " - "second argument is a variable that will hold value. Optionally, " - "you can specify the starting test number START, the ending test number " - "END, the number of tests to skip between each test STRIDE, a regular " - "expression for tests to run INCLUDE, or a regular expression for tests " - "to not run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular " - "expression for test to be included or excluded by the test " - "property LABEL. PARALLEL_LEVEL should be set to a positive number " - "representing the number of tests to be run in parallel. " - "SCHEDULE_RANDOM will launch tests in a random order, and is " - "typically used to detect implicit test dependencies. STOP_TIME is the " - "time of day at which the tests should all stop running." - "\n" - CTEST_COMMAND_APPEND_OPTION_DOCS; - } - cmTypeMacro(cmCTestTestCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 497774d..3a04b33 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -20,6 +20,8 @@ #include <cmsys/Process.h> #include <cmsys/RegularExpression.hxx> #include <cmsys/Base64.h> +#include <cmsys/Directory.hxx> +#include <cmsys/FStream.hxx> #include "cmMakefile.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" @@ -60,10 +62,6 @@ public: */ virtual const char* GetName() const { return "subdirs";} - // Unused methods - virtual const char* GetTerseDocumentation() const { return ""; } - virtual const char* GetFullDocumentation() const { return ""; } - cmTypeMacro(cmCTestSubdirCommand, cmCommand); cmCTestTestHandler* TestHandler; @@ -161,10 +159,6 @@ public: */ virtual const char* GetName() const { return "add_subdirectory";} - // Unused methods - virtual const char* GetTerseDocumentation() const { return ""; } - virtual const char* GetFullDocumentation() const { return ""; } - cmTypeMacro(cmCTestAddSubdirectoryCommand, cmCommand); cmCTestTestHandler* TestHandler; @@ -249,11 +243,7 @@ public: /** * The name of the command as specified in CMakeList.txt. */ - virtual const char* GetName() const { return "ADD_TEST";} - - // Unused methods - virtual const char* GetTerseDocumentation() const { return ""; } - virtual const char* GetFullDocumentation() const { return ""; } + virtual const char* GetName() const { return "add_test";} cmTypeMacro(cmCTestAddTestCommand, cmCommand); @@ -297,11 +287,7 @@ public: /** * The name of the command as specified in CMakeList.txt. */ - virtual const char* GetName() const { return "SET_TESTS_PROPERTIES";} - - // Unused methods - virtual const char* GetTerseDocumentation() const { return ""; } - virtual const char* GetFullDocumentation() const { return ""; } + virtual const char* GetName() const { return "set_tests_properties";} cmTypeMacro(cmCTestSetTestsPropertiesCommand, cmCommand); @@ -537,6 +523,7 @@ int cmCTestTestHandler::ProcessHandler() this->UseExcludeRegExp(); this->SetExcludeRegExp(val); } + this->SetRerunFailed(cmSystemTools::IsOn(this->GetOption("RerunFailed"))); this->TestResults.clear(); @@ -819,6 +806,13 @@ void cmCTestTestHandler::ComputeTestList() { this->TestList.clear(); // clear list of test this->GetListOfTests(); + + if (this->RerunFailed) + { + this->ComputeTestListForRerunFailed(); + return; + } + cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size(); // how many tests are in based on RegExp? int inREcnt = 0; @@ -881,9 +875,47 @@ void cmCTestTestHandler::ComputeTestList() this->TotalNumberOfTests = this->TestList.size(); // Set the TestList to the final list of all test this->TestList = finalList; + + this->UpdateMaxTestNameWidth(); +} + +void cmCTestTestHandler::ComputeTestListForRerunFailed() +{ + this->ExpandTestsToRunInformationForRerunFailed(); + + cmCTestTestHandler::ListOfTests::iterator it; + ListOfTests finalList; + int cnt = 0; + for ( it = this->TestList.begin(); it != this->TestList.end(); it ++ ) + { + cnt ++; + + // if this test is not in our list of tests to run, then skip it. + if ((this->TestsToRun.size() && + std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt) + == this->TestsToRun.end())) + { + continue; + } + + it->Index = cnt; + finalList.push_back(*it); + } + + // Save the total number of tests before exclusions + this->TotalNumberOfTests = this->TestList.size(); + + // Set the TestList to the list of failed tests to rerun + this->TestList = finalList; + + this->UpdateMaxTestNameWidth(); +} + +void cmCTestTestHandler::UpdateMaxTestNameWidth() +{ std::string::size_type max = this->CTest->GetMaxTestNameWidth(); - for (it = this->TestList.begin(); - it != this->TestList.end(); it ++ ) + for ( cmCTestTestHandler::ListOfTests::iterator it = this->TestList.begin(); + it != this->TestList.end(); it ++ ) { cmCTestTestProperties& p = *it; if(max < p.Name.size()) @@ -900,7 +932,7 @@ void cmCTestTestHandler::ComputeTestList() bool cmCTestTestHandler::GetValue(const char* tag, int& value, - std::ifstream& fin) + std::istream& fin) { std::string line; bool ret = true; @@ -922,7 +954,7 @@ bool cmCTestTestHandler::GetValue(const char* tag, bool cmCTestTestHandler::GetValue(const char* tag, double& value, - std::ifstream& fin) + std::istream& fin) { std::string line; cmSystemTools::GetLineFromStream(fin, line); @@ -944,7 +976,7 @@ bool cmCTestTestHandler::GetValue(const char* tag, bool cmCTestTestHandler::GetValue(const char* tag, bool& value, - std::ifstream& fin) + std::istream& fin) { std::string line; cmSystemTools::GetLineFromStream(fin, line); @@ -976,7 +1008,7 @@ bool cmCTestTestHandler::GetValue(const char* tag, bool cmCTestTestHandler::GetValue(const char* tag, size_t& value, - std::ifstream& fin) + std::istream& fin) { std::string line; cmSystemTools::GetLineFromStream(fin, line); @@ -998,7 +1030,7 @@ bool cmCTestTestHandler::GetValue(const char* tag, bool cmCTestTestHandler::GetValue(const char* tag, std::string& value, - std::ifstream& fin) + std::istream& fin) { std::string line; cmSystemTools::GetLineFromStream(fin, line); @@ -1708,6 +1740,91 @@ void cmCTestTestHandler::ExpandTestsToRunInformation(size_t numTests) this->TestsToRun.erase(new_end, this->TestsToRun.end()); } +void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed() +{ + + std::string dirName = this->CTest->GetBinaryDir() + "/Testing/Temporary"; + + cmsys::Directory directory; + if (directory.Load(dirName.c_str()) == 0) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to read the contents of " + << dirName << std::endl); + return; + } + + int numFiles = static_cast<int> + (cmsys::Directory::GetNumberOfFilesInDirectory(dirName.c_str())); + std::string pattern = "LastTestsFailed"; + std::string logName = ""; + + for (int i = 0; i < numFiles; ++i) + { + std::string fileName = directory.GetFile(i); + // bcc crashes if we attempt a normal substring comparison, + // hence the following workaround + std::string fileNameSubstring = fileName.substr(0, pattern.length()); + if (fileNameSubstring.compare(pattern) != 0) + { + continue; + } + if (logName == "") + { + logName = fileName; + } + else + { + // if multiple matching logs were found we use the most recently + // modified one. + int res; + cmSystemTools::FileTimeCompare(logName.c_str(), fileName.c_str(), &res); + if (res == -1) + { + logName = fileName; + } + } + } + + std::string lastTestsFailedLog = this->CTest->GetBinaryDir() + + "/Testing/Temporary/" + logName; + + if ( !cmSystemTools::FileExists(lastTestsFailedLog.c_str()) ) + { + if ( !this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels() ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, lastTestsFailedLog + << " does not exist!" << std::endl); + } + return; + } + + // parse the list of tests to rerun from LastTestsFailed.log + cmsys::ifstream ifs(lastTestsFailedLog.c_str()); + if ( ifs ) + { + std::string line; + std::string::size_type pos; + while ( cmSystemTools::GetLineFromStream(ifs, line) ) + { + pos = line.find(':', 0); + if (pos == line.npos) + { + continue; + } + + int val = atoi(line.substr(0, pos).c_str()); + this->TestsToRun.push_back(val); + } + ifs.close(); + } + else if ( !this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels() ) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem reading file: " + << lastTestsFailedLog.c_str() << + " while generating list of previously failed tests." << std::endl); + } +} + //---------------------------------------------------------------------- // Just for convenience #define SPACE_REGEX "[ \t\r\n]" @@ -1848,7 +1965,7 @@ std::string cmCTestTestHandler::GenerateRegressionImages( } else { - std::ifstream ifs(filename.c_str(), std::ios::in + cmsys::ifstream ifs(filename.c_str(), std::ios::in #ifdef _WIN32 | std::ios::binary #endif @@ -1938,7 +2055,7 @@ void cmCTestTestHandler::SetTestsToRunInformation(const char* in) // string if(cmSystemTools::FileExists(in)) { - std::ifstream fin(in); + cmsys::ifstream fin(in); unsigned long filelen = cmSystemTools::FileLength(in); char* buff = new char[filelen+1]; fin.getline(buff, filelen); @@ -2112,6 +2229,14 @@ bool cmCTestTestHandler::SetTestsProperties( rtit->Processors = 1; } } + if ( key == "SKIP_RETURN_CODE" ) + { + rtit->SkipReturnCode = atoi(val.c_str()); + if(rtit->SkipReturnCode < 0 || rtit->SkipReturnCode > 255) + { + rtit->SkipReturnCode = -1; + } + } if ( key == "DEPENDS" ) { std::vector<std::string> lval; @@ -2247,6 +2372,7 @@ bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args) test.ExplicitTimeout = false; test.Cost = 0; test.Processors = 1; + test.SkipReturnCode = -1; test.PreviousRuns = 0; if (this->UseIncludeRegExpFlag && !this->IncludeTestsRegularExpression.find(testname.c_str())) diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 93b793b..63f9c93 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -44,6 +44,12 @@ public: void SetUseUnion(bool val) { this->UseUnion = val; } /** + * Set whether or not CTest should only execute the tests that failed + * on the previous run. By default this is false. + */ + void SetRerunFailed(bool val) { this->RerunFailed = val; } + + /** * This method is called when reading CTest custom file */ void PopulateCustomVectors(cmMakefile *mf); @@ -103,6 +109,8 @@ public: int Index; //Requested number of process slots int Processors; + // return code of test which will mark test as "not run" + int SkipReturnCode; std::vector<std::string> Environment; std::vector<std::string> Labels; std::set<std::string> LockedResources; @@ -213,21 +221,27 @@ private: // based on union regex and -I stuff void ComputeTestList(); + // compute the lists of tests that will actually run + // based on LastTestFailed.log + void ComputeTestListForRerunFailed(); + + void UpdateMaxTestNameWidth(); + bool GetValue(const char* tag, std::string& value, - std::ifstream& fin); + std::istream& fin); bool GetValue(const char* tag, int& value, - std::ifstream& fin); + std::istream& fin); bool GetValue(const char* tag, size_t& value, - std::ifstream& fin); + std::istream& fin); bool GetValue(const char* tag, bool& value, - std::ifstream& fin); + std::istream& fin); bool GetValue(const char* tag, double& value, - std::ifstream& fin); + std::istream& fin); /** * Find the executable for a test */ @@ -235,6 +249,7 @@ private: const char* GetTestStatus(int status); void ExpandTestsToRunInformation(size_t numPossibleTests); + void ExpandTestsToRunInformationForRerunFailed(); std::vector<cmStdString> CustomPreTest; std::vector<cmStdString> CustomPostTest; @@ -268,6 +283,8 @@ private: cmsys::RegularExpression DartStuff; std::ostream* LogFile; + + bool RerunFailed; }; #endif diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 2ca9f6c..5408a8a 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -59,6 +59,14 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() "HGCommand", "CTEST_HG_COMMAND"); this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, "HGUpdateOptions", "CTEST_HG_UPDATE_OPTIONS"); + this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, + "P4Command", "CTEST_P4_COMMAND"); + this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, + "P4UpdateOptions", "CTEST_P4_UPDATE_OPTIONS"); + this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, + "P4Client", "CTEST_P4_CLIENT"); + this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, + "P4Options", "CTEST_P4_OPTIONS"); cmCTestGenericHandler* handler = this->CTest->GetInitializedHandler("update"); diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h index c578fff..a785bd8 100644 --- a/Source/CTest/cmCTestUpdateCommand.h +++ b/Source/CTest/cmCTestUpdateCommand.h @@ -41,28 +41,6 @@ public: */ virtual const char* GetName() const { return "ctest_update";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Update the work tree from version control."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_update([SOURCE source] [RETURN_VALUE res])\n" - "Updates the given source directory and stores results in Update.xml. " - "If no SOURCE is given, the CTEST_SOURCE_DIRECTORY variable is used. " - "The RETURN_VALUE option specifies a variable in which to store the " - "result, which is the number of files updated or -1 on error." - ; - } - cmTypeMacro(cmCTestUpdateCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx index 9eae3f3..11474ec 100644 --- a/Source/CTest/cmCTestUpdateHandler.cxx +++ b/Source/CTest/cmCTestUpdateHandler.cxx @@ -28,6 +28,7 @@ #include "cmCTestBZR.h" #include "cmCTestGIT.h" #include "cmCTestHG.h" +#include "cmCTestP4.h" #include <cmsys/auto_ptr.hxx> @@ -51,7 +52,8 @@ static const char* cmCTestUpdateHandlerUpdateStrings[] = "SVN", "BZR", "GIT", - "HG" + "HG", + "P4" }; static const char* cmCTestUpdateHandlerUpdateToString(int type) @@ -146,6 +148,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type) { return cmCTestUpdateHandler::e_HG; } + if ( stype.find("p4") != std::string::npos ) + { + return cmCTestUpdateHandler::e_P4; + } } else { @@ -172,6 +178,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type) { return cmCTestUpdateHandler::e_HG; } + if ( stype.find("p4") != std::string::npos ) + { + return cmCTestUpdateHandler::e_P4; + } } return cmCTestUpdateHandler::e_UNKNOWN; } @@ -223,6 +233,7 @@ int cmCTestUpdateHandler::ProcessHandler() case e_BZR: vc.reset(new cmCTestBZR(this->CTest, ofs)); break; case e_GIT: vc.reset(new cmCTestGIT(this->CTest, ofs)); break; case e_HG: vc.reset(new cmCTestHG(this->CTest, ofs)); break; + case e_P4: vc.reset(new cmCTestP4(this->CTest, ofs)); break; default: vc.reset(new cmCTestVC(this->CTest, ofs)); break; } vc->SetCommandLineTool(this->UpdateCommand); @@ -350,6 +361,18 @@ int cmCTestUpdateHandler::DetectVCS(const char* dir) { return cmCTestUpdateHandler::e_HG; } + sourceDirectory = dir; + sourceDirectory += "/.p4"; + if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) + { + return cmCTestUpdateHandler::e_P4; + } + sourceDirectory = dir; + sourceDirectory += "/.p4config"; + if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) + { + return cmCTestUpdateHandler::e_P4; + } return cmCTestUpdateHandler::e_UNKNOWN; } @@ -380,6 +403,7 @@ bool cmCTestUpdateHandler::SelectVCS() case e_BZR: key = "BZRCommand"; break; case e_GIT: key = "GITCommand"; break; case e_HG: key = "HGCommand"; break; + case e_P4: key = "P4Command"; break; default: break; } if (key) diff --git a/Source/CTest/cmCTestUpdateHandler.h b/Source/CTest/cmCTestUpdateHandler.h index 55ec974..954c024 100644 --- a/Source/CTest/cmCTestUpdateHandler.h +++ b/Source/CTest/cmCTestUpdateHandler.h @@ -44,6 +44,7 @@ public: e_BZR, e_GIT, e_HG, + e_P4, e_LAST }; diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index 62f379f..e867fb6 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -45,25 +45,6 @@ public: */ virtual const char* GetName() const { return "ctest_upload";} - /** - * Succinct documentation. - */ - virtual const char* GetTerseDocumentation() const - { - return "Upload files to a dashboard server."; - } - - /** - * More documentation. - */ - virtual const char* GetFullDocumentation() const - { - return - " ctest_upload(FILES ...)\n" - "Pass a list of files to be sent along with the build results to " - "the dashboard server.\n"; - } - cmTypeMacro(cmCTestUploadCommand, cmCTestHandlerCommand); protected: diff --git a/Source/CTest/cmParseCacheCoverage.cxx b/Source/CTest/cmParseCacheCoverage.cxx index 137f344..85e07ae 100644 --- a/Source/CTest/cmParseCacheCoverage.cxx +++ b/Source/CTest/cmParseCacheCoverage.cxx @@ -5,6 +5,7 @@ #include "cmParseCacheCoverage.h" #include <cmsys/Directory.hxx> #include <cmsys/Glob.hxx> +#include <cmsys/FStream.hxx> cmParseCacheCoverage::cmParseCacheCoverage( @@ -106,7 +107,7 @@ bool cmParseCacheCoverage::SplitString(std::vector<std::string>& args, bool cmParseCacheCoverage::ReadCMCovFile(const char* file) { - std::ifstream in(file); + cmsys::ifstream in(file); if(!in) { cmCTestLog(this->CTest, ERROR_MESSAGE, diff --git a/Source/CTest/cmParseGTMCoverage.cxx b/Source/CTest/cmParseGTMCoverage.cxx index 6b4adb4..528d0db 100644 --- a/Source/CTest/cmParseGTMCoverage.cxx +++ b/Source/CTest/cmParseGTMCoverage.cxx @@ -5,6 +5,7 @@ #include "cmParseGTMCoverage.h" #include <cmsys/Directory.hxx> #include <cmsys/Glob.hxx> +#include <cmsys/FStream.hxx> cmParseGTMCoverage::cmParseGTMCoverage(cmCTestCoverageHandlerContainer& cont, @@ -48,7 +49,7 @@ bool cmParseGTMCoverage::LoadCoverageData(const char* d) bool cmParseGTMCoverage::ReadMCovFile(const char* file) { - std::ifstream in(file); + cmsys::ifstream in(file); if(!in) { return false; @@ -127,7 +128,7 @@ bool cmParseGTMCoverage::FindFunctionInMumpsFile(std::string const& filepath, std::string const& function, int& lineoffset) { - std::ifstream in(filepath.c_str()); + cmsys::ifstream in(filepath.c_str()); if(!in) { return false; diff --git a/Source/CTest/cmParseMumpsCoverage.cxx b/Source/CTest/cmParseMumpsCoverage.cxx index 37e8bd0..6226feb 100644 --- a/Source/CTest/cmParseMumpsCoverage.cxx +++ b/Source/CTest/cmParseMumpsCoverage.cxx @@ -5,6 +5,7 @@ #include "cmParseGTMCoverage.h" #include <cmsys/Directory.hxx> #include <cmsys/Glob.hxx> +#include <cmsys/FStream.hxx> cmParseMumpsCoverage::cmParseMumpsCoverage( @@ -23,7 +24,7 @@ bool cmParseMumpsCoverage::ReadCoverageFile(const char* file) // Read the gtm_coverage.mcov file, that has two lines of data: // packages:/full/path/to/Vista/Packages // coverage_dir:/full/path/to/dir/with/*.mcov - std::ifstream in(file); + cmsys::ifstream in(file); if(!in) { return false; @@ -61,7 +62,7 @@ bool cmParseMumpsCoverage::ReadCoverageFile(const char* file) void cmParseMumpsCoverage::InitializeMumpsFile(std::string& file) { // initialize the coverage information for a given mumps file - std::ifstream in(file.c_str()); + cmsys::ifstream in(file.c_str()); if(!in) { return; diff --git a/Source/CTest/cmParsePHPCoverage.cxx b/Source/CTest/cmParsePHPCoverage.cxx index 593b2d1..1c26c1c 100644 --- a/Source/CTest/cmParsePHPCoverage.cxx +++ b/Source/CTest/cmParsePHPCoverage.cxx @@ -2,6 +2,7 @@ #include "cmSystemTools.h" #include "cmParsePHPCoverage.h" #include <cmsys/Directory.hxx> +#include <cmsys/FStream.hxx> /* To setup coverage for php. @@ -20,7 +21,7 @@ cmParsePHPCoverage::cmParsePHPCoverage(cmCTestCoverageHandlerContainer& cont, { } -bool cmParsePHPCoverage::ReadUntil(std::ifstream& in, char until) +bool cmParsePHPCoverage::ReadUntil(std::istream& in, char until) { char c = 0; while(in.get(c) && c != until) @@ -32,7 +33,7 @@ bool cmParsePHPCoverage::ReadUntil(std::ifstream& in, char until) } return true; } -bool cmParsePHPCoverage::ReadCoverageArray(std::ifstream& in, +bool cmParsePHPCoverage::ReadCoverageArray(std::istream& in, cmStdString const& fileName) { cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector @@ -109,7 +110,7 @@ bool cmParsePHPCoverage::ReadCoverageArray(std::ifstream& in, return true; } -bool cmParsePHPCoverage::ReadInt(std::ifstream& in, int& v) +bool cmParsePHPCoverage::ReadInt(std::istream& in, int& v) { std::string s; char c = 0; @@ -121,7 +122,7 @@ bool cmParsePHPCoverage::ReadInt(std::ifstream& in, int& v) return true; } -bool cmParsePHPCoverage::ReadArraySize(std::ifstream& in, int& size) +bool cmParsePHPCoverage::ReadArraySize(std::istream& in, int& size) { char c = 0; in.get(c); @@ -139,7 +140,7 @@ bool cmParsePHPCoverage::ReadArraySize(std::ifstream& in, int& size) return false; } -bool cmParsePHPCoverage::ReadFileInformation(std::ifstream& in) +bool cmParsePHPCoverage::ReadFileInformation(std::istream& in) { char buf[4]; in.read(buf, 2); @@ -190,7 +191,7 @@ bool cmParsePHPCoverage::ReadFileInformation(std::ifstream& in) bool cmParsePHPCoverage::ReadPHPData(const char* file) { - std::ifstream in(file); + cmsys::ifstream in(file); if(!in) { return false; diff --git a/Source/CTest/cmParsePHPCoverage.h b/Source/CTest/cmParsePHPCoverage.h index d50a83c..035a093 100644 --- a/Source/CTest/cmParsePHPCoverage.h +++ b/Source/CTest/cmParsePHPCoverage.h @@ -32,11 +32,11 @@ public: 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); + bool ReadArraySize(std::istream& in, int& size); + bool ReadFileInformation(std::istream& in); + bool ReadInt(std::istream& in, int& v); + bool ReadCoverageArray(std::istream& in, cmStdString const&); + bool ReadUntil(std::istream& in, char until); cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; diff --git a/Source/CTest/cmParsePythonCoverage.cxx b/Source/CTest/cmParsePythonCoverage.cxx new file mode 100644 index 0000000..38a770a --- /dev/null +++ b/Source/CTest/cmParsePythonCoverage.cxx @@ -0,0 +1,113 @@ +#include "cmStandardIncludes.h" +#include "cmSystemTools.h" +#include "cmXMLParser.h" +#include "cmParsePythonCoverage.h" +#include <cmsys/Directory.hxx> +#include <cmsys/FStream.hxx> + +//---------------------------------------------------------------------------- +class cmParsePythonCoverage::XMLParser: public cmXMLParser +{ +public: + XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont) + : CTest(ctest), Coverage(cont) + { + } + + virtual ~XMLParser() + { + } + +protected: + + virtual void StartElement(const char* name, const char** atts) + { + if(strcmp(name, "class") == 0) + { + int tagCount = 0; + while(true) + { + if(strcmp(atts[tagCount], "filename") == 0) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: " + << atts[tagCount+1] << std::endl); + this->CurFileName = this->Coverage.SourceDir + "/" + + atts[tagCount+1]; + FileLinesType& curFileLines = + this->Coverage.TotalCoverage[this->CurFileName]; + cmsys::ifstream fin(this->CurFileName.c_str()); + if(!fin) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Python Coverage: Error opening " << this->CurFileName + << std::endl); + this->Coverage.Error++; + break; + } + + std::string line; + curFileLines.push_back(-1); + while(cmSystemTools::GetLineFromStream(fin, line)) + { + curFileLines.push_back(-1); + } + + break; + } + ++tagCount; + } + } + else if(strcmp(name, "line") == 0) + { + int tagCount = 0; + int curNumber = -1; + int curHits = -1; + while(true) + { + if(strcmp(atts[tagCount], "hits") == 0) + { + curHits = atoi(atts[tagCount+1]); + } + else if(strcmp(atts[tagCount], "number") == 0) + { + curNumber = atoi(atts[tagCount+1]); + } + + if(curHits > -1 && curNumber > -1) + { + FileLinesType& curFileLines = + this->Coverage.TotalCoverage[this->CurFileName]; + curFileLines[curNumber] = curHits; + break; + } + ++tagCount; + } + } + } + + virtual void EndElement(const char*) {} + +private: + + typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector + FileLinesType; + cmCTest* CTest; + cmCTestCoverageHandlerContainer& Coverage; + std::string CurFileName; + +}; + + +cmParsePythonCoverage::cmParsePythonCoverage( + cmCTestCoverageHandlerContainer& cont, + cmCTest* ctest) + :Coverage(cont), CTest(ctest) +{ +} + +bool cmParsePythonCoverage::ReadCoverageXML(const char* xmlFile) +{ + cmParsePythonCoverage::XMLParser parser(this->CTest, this->Coverage); + parser.ParseFile(xmlFile); + return true; +} diff --git a/Source/CTest/cmParsePythonCoverage.h b/Source/CTest/cmParsePythonCoverage.h new file mode 100644 index 0000000..668c7f9 --- /dev/null +++ b/Source/CTest/cmParsePythonCoverage.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 cmParsePythonCoverage_h +#define cmParsePythonCoverage_h + +#include "cmStandardIncludes.h" +#include "cmCTestCoverageHandler.h" + +/** \class cmParsePythonCoverage + * \brief Parse coverage.py Python coverage information + * + * This class is used to parse the output of the coverage.py tool that + * is currently maintained by Ned Batchelder. That tool has a command + * that produces xml output in the format typically output by the common + * Java-based Cobertura coverage application. This helper class parses + * that XML file to fill the coverage-handler container. + */ +class cmParsePythonCoverage +{ +public: + + //! Create the coverage parser by passing in the coverage handler + //! container and the cmCTest object + cmParsePythonCoverage(cmCTestCoverageHandlerContainer& cont, + cmCTest* ctest); + + //! Read the XML produced by running `coverage xml` + bool ReadCoverageXML(const char* xmlFile); + +private: + + class XMLParser; + cmCTestCoverageHandlerContainer& Coverage; + cmCTest* CTest; + std::string CurFileName; +}; + +#endif |