diff options
32 files changed, 957 insertions, 295 deletions
diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index f19b5c0..f86f3c5 100644 --- a/Help/command/add_library.rst +++ b/Help/command/add_library.rst @@ -94,7 +94,8 @@ object library name. For example: will include objlib's object files in a library and an executable along with those compiled from their own sources. Object libraries -may contain only sources (and headers) that compile to object files. +may contain only sources that compile, header files, and other files +that would not affect linking of a normal library (e.g. ``.txt``). They may contain custom commands generating such sources, but not ``PRE_BUILD``, ``PRE_LINK``, or ``POST_BUILD`` commands. Object libraries cannot be imported, exported, installed, or linked. Some native build diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 983bf22..05a7b33 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -344,6 +344,7 @@ Variables for CTest /variable/CTEST_MEMORYCHECK_COMMAND /variable/CTEST_MEMORYCHECK_COMMAND_OPTIONS /variable/CTEST_MEMORYCHECK_SUPPRESSIONS_FILE + /variable/CTEST_MEMORYCHECK_TYPE /variable/CTEST_NIGHTLY_START_TIME /variable/CTEST_P4_CLIENT /variable/CTEST_P4_COMMAND diff --git a/Help/release/dev/allow-OBJECT-library-extra-sources.rst b/Help/release/dev/allow-OBJECT-library-extra-sources.rst new file mode 100644 index 0000000..472fcdc --- /dev/null +++ b/Help/release/dev/allow-OBJECT-library-extra-sources.rst @@ -0,0 +1,6 @@ +allow-OBJECT-library-extra-sources +---------------------------------- + +* :ref:`Object Libraries` may now have extra sources that do not + compile to object files so long as they would not affect linking + of a normal library (e.g. ``.dat`` is okay but not ``.def``). diff --git a/Help/release/dev/export-from-obj-libs.rst b/Help/release/dev/export-from-obj-libs.rst new file mode 100644 index 0000000..6d58288 --- /dev/null +++ b/Help/release/dev/export-from-obj-libs.rst @@ -0,0 +1,5 @@ +export-from-obj-libs +-------------------- + +* The :module:`GenerateExportHeader` module ``generate_export_header`` + function learned to allow use with :ref:`Object Libraries`. diff --git a/Help/release/dev/thread-sanitizer.rst b/Help/release/dev/thread-sanitizer.rst new file mode 100644 index 0000000..f38e8e1 --- /dev/null +++ b/Help/release/dev/thread-sanitizer.rst @@ -0,0 +1,5 @@ +thread-sanitizer +---------------- + +* The :command:`ctest_memcheck` command learned to support + ``ThreadSanitizer``. diff --git a/Help/variable/CTEST_MEMORYCHECK_TYPE.rst b/Help/variable/CTEST_MEMORYCHECK_TYPE.rst new file mode 100644 index 0000000..f7875da --- /dev/null +++ b/Help/variable/CTEST_MEMORYCHECK_TYPE.rst @@ -0,0 +1,6 @@ +CTEST_MEMORYCHECK_TYPE +------------------------- + +Specify the CTest ``MemoryCheckType`` setting +in a :manual:`ctest(1)` dashboard client script. +Valid values are Valgrind, Purify, BoundsChecker, and ThreadSanitizer. diff --git a/Modules/CMakeExpandImportedTargets.cmake b/Modules/CMakeExpandImportedTargets.cmake index 0752e04..b6ab7ef 100644 --- a/Modules/CMakeExpandImportedTargets.cmake +++ b/Modules/CMakeExpandImportedTargets.cmake @@ -71,7 +71,11 @@ function(CMAKE_EXPAND_IMPORTED_TARGETS _RESULT ) set(_CCSR_NEW_REQ_LIBS ) set(_CHECK_FOR_IMPORTED_TARGETS FALSE) foreach(_CURRENT_LIB ${_CCSR_REQ_LIBS}) - get_target_property(_importedConfigs "${_CURRENT_LIB}" IMPORTED_CONFIGURATIONS) + if(TARGET "${_CURRENT_LIB}") + get_target_property(_importedConfigs "${_CURRENT_LIB}" IMPORTED_CONFIGURATIONS) + else() + set(_importedConfigs "") + endif() if (_importedConfigs) # message(STATUS "Detected imported target ${_CURRENT_LIB}") # Ok, so this is an imported target. @@ -123,7 +127,11 @@ function(CMAKE_EXPAND_IMPORTED_TARGETS _RESULT ) # all remaining imported target names (there shouldn't be any left anyway). set(_CCSR_NEW_REQ_LIBS ) foreach(_CURRENT_LIB ${_CCSR_REQ_LIBS}) - get_target_property(_importedConfigs "${_CURRENT_LIB}" IMPORTED_CONFIGURATIONS) + if(TARGET "${_CURRENT_LIB}") + get_target_property(_importedConfigs "${_CURRENT_LIB}" IMPORTED_CONFIGURATIONS) + else() + set(_importedConfigs "") + endif() if (NOT _importedConfigs) list(APPEND _CCSR_NEW_REQ_LIBS "${_CURRENT_LIB}" ) # message(STATUS "final: appending ${_CURRENT_LIB}") diff --git a/Modules/FindSWIG.cmake b/Modules/FindSWIG.cmake index 8bd4048..818d1f2 100644 --- a/Modules/FindSWIG.cmake +++ b/Modules/FindSWIG.cmake @@ -25,6 +25,7 @@ #============================================================================= # Copyright 2004-2009 Kitware, Inc. # Copyright 2011 Mathieu Malaterre <mathieu.malaterre@gmail.com> +# Copyright 2014 Sylvain Joubert <joubert.sy@gmail.com> # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. @@ -36,7 +37,7 @@ # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) -find_program(SWIG_EXECUTABLE NAMES swig2.0 swig) +find_program(SWIG_EXECUTABLE NAMES swig3.0 swig2.0 swig) if(SWIG_EXECUTABLE) execute_process(COMMAND ${SWIG_EXECUTABLE} -swiglib diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 8321e0a..0de45a0 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 0) -set(CMake_VERSION_PATCH 20140707) +set(CMake_VERSION_PATCH 20140714) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestMemCheckCommand.cxx b/Source/CTest/cmCTestMemCheckCommand.cxx index 535c993..939b4dc 100644 --- a/Source/CTest/cmCTestMemCheckCommand.cxx +++ b/Source/CTest/cmCTestMemCheckCommand.cxx @@ -21,6 +21,8 @@ cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler() = this->CTest->GetInitializedHandler("memcheck"); this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, + "MemoryCheckType", "CTEST_MEMORYCHECK_TYPE"); + this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, "MemoryCheckCommand", "CTEST_MEMORYCHECK_COMMAND"); this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile, "MemoryCheckCommandOptions", "CTEST_MEMORYCHECK_COMMAND_OPTIONS"); diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 7b50174..bcf09ad 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/Glob.hxx> #include <cmsys/FStream.hxx> #include "cmMakefile.h" #include "cmXMLSafe.h" @@ -124,60 +125,7 @@ public: #define BOUNDS_CHECKER_MARKER \ "******######*****Begin BOUNDS CHECKER XML******######******" -//---------------------------------------------------------------------- -static const char* cmCTestMemCheckResultStrings[] = { - "ABR", - "ABW", - "ABWL", - "COR", - "EXU", - "FFM", - "FIM", - "FMM", - "FMR", - "FMW", - "FUM", - "IPR", - "IPW", - "MAF", - "MLK", - "MPK", - "NPR", - "ODS", - "PAR", - "PLK", - "UMC", - "UMR", - 0 -}; - -//---------------------------------------------------------------------- -static const char* cmCTestMemCheckResultLongStrings[] = { - "Threading Problem", - "ABW", - "ABWL", - "COR", - "EXU", - "FFM", - "FIM", - "Mismatched deallocation", - "FMR", - "FMW", - "FUM", - "IPR", - "IPW", - "MAF", - "Memory Leak", - "Potential Memory Leak", - "NPR", - "ODS", - "Invalid syscall param", - "PLK", - "Uninitialized Memory Conditional", - "Uninitialized Memory Read", - 0 -}; //---------------------------------------------------------------------- @@ -186,12 +134,14 @@ cmCTestMemCheckHandler::cmCTestMemCheckHandler() this->MemCheck = true; this->CustomMaximumPassedTestOutputSize = 0; this->CustomMaximumFailedTestOutputSize = 0; + this->LogWithPID = false; } //---------------------------------------------------------------------- void cmCTestMemCheckHandler::Initialize() { this->Superclass::Initialize(); + this->LogWithPID = false; this->CustomMaximumPassedTestOutputSize = 0; this->CustomMaximumFailedTestOutputSize = 0; this->MemoryTester = ""; @@ -199,12 +149,6 @@ void cmCTestMemCheckHandler::Initialize() this->MemoryTesterOptions.clear(); this->MemoryTesterStyle = UNKNOWN; this->MemoryTesterOutputFile = ""; - int cc; - for ( cc = 0; cc < NO_MEMORY_FAULT; cc ++ ) - { - this->MemoryTesterGlobalResults[cc] = 0; - } - } //---------------------------------------------------------------------- @@ -249,8 +193,8 @@ void cmCTestMemCheckHandler::GenerateTestCommand( index = stream.str(); for ( pp = 0; pp < this->MemoryTesterDynamicOptions.size(); pp ++ ) { - std::string arg = this->MemoryTesterDynamicOptions[pp]; - std::string::size_type pos = arg.find("??"); + std::string arg = this->MemoryTesterDynamicOptions[pp]; + std::string::size_type pos = arg.find("??"); if (pos != std::string::npos) { arg.replace(pos, 2, index); @@ -260,18 +204,125 @@ void cmCTestMemCheckHandler::GenerateTestCommand( memcheckcommand += arg; memcheckcommand += "\""; } + // Create a copy of the memory tester environment variable. + // This is used for memory testing programs that pass options + // via environment varaibles. + std::string memTesterEnvironmentVariable = + this->MemoryTesterEnvironmentVariable; for ( pp = 0; pp < this->MemoryTesterOptions.size(); pp ++ ) { - args.push_back(this->MemoryTesterOptions[pp]); - memcheckcommand += " \""; - memcheckcommand += this->MemoryTesterOptions[pp]; - memcheckcommand += "\""; + if(memTesterEnvironmentVariable.size()) + { + // If we are using env to pass options, append all the options to + // this string with space separation. + memTesterEnvironmentVariable += " " + this->MemoryTesterOptions[pp]; + } + // for regular options just add them to args and memcheckcommand + // which is just used for display + else + { + args.push_back(this->MemoryTesterOptions[pp]); + memcheckcommand += " \""; + memcheckcommand += this->MemoryTesterOptions[pp]; + memcheckcommand += "\""; + } + } + // if this is an env option type, then add the env string as a single + // argument. + if(memTesterEnvironmentVariable.size()) + { + std::string::size_type pos = memTesterEnvironmentVariable.find("??"); + if (pos != std::string::npos) + { + memTesterEnvironmentVariable.replace(pos, 2, index); + } + memcheckcommand += " " + memTesterEnvironmentVariable; + args.push_back(memTesterEnvironmentVariable); } cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Memory check command: " << memcheckcommand << std::endl); } //---------------------------------------------------------------------- +void cmCTestMemCheckHandler::InitializeResultsVectors() +{ + // fill these members +// cmsys::vector<std::string> ResultStrings; +// cmsys::vector<std::string> ResultStringsLong; +// cmsys::vector<int> GlobalResults; + this->ResultStringsLong.clear(); + this->ResultStrings.clear(); + this->GlobalResults.clear(); + // If we are working with style checkers that dynamically fill + // the results strings then return. + if(this->MemoryTesterStyle > cmCTestMemCheckHandler::BOUNDS_CHECKER) + { + return; + } + + // define the standard set of errors + //---------------------------------------------------------------------- + static const char* cmCTestMemCheckResultStrings[] = { + "ABR", + "ABW", + "ABWL", + "COR", + "EXU", + "FFM", + "FIM", + "FMM", + "FMR", + "FMW", + "FUM", + "IPR", + "IPW", + "MAF", + "MLK", + "MPK", + "NPR", + "ODS", + "PAR", + "PLK", + "UMC", + "UMR", + 0 + }; +//---------------------------------------------------------------------- + static const char* cmCTestMemCheckResultLongStrings[] = { + "Threading Problem", + "ABW", + "ABWL", + "COR", + "EXU", + "FFM", + "FIM", + "Mismatched deallocation", + "FMR", + "FMW", + "FUM", + "IPR", + "IPW", + "MAF", + "Memory Leak", + "Potential Memory Leak", + "NPR", + "ODS", + "Invalid syscall param", + "PLK", + "Uninitialized Memory Conditional", + "Uninitialized Memory Read", + 0 + }; + this->GlobalResults.clear(); + for(int i =0; cmCTestMemCheckResultStrings[i] != 0; ++i) + { + this->ResultStrings.push_back(cmCTestMemCheckResultStrings[i]); + this->ResultStringsLong.push_back(cmCTestMemCheckResultLongStrings[i]); + this->GlobalResults.push_back(0); + } +} + +//---------------------------------------------------------------------- void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf) { this->cmCTestTestHandler::PopulateCustomVectors(mf); @@ -283,6 +334,8 @@ void cmCTestMemCheckHandler::PopulateCustomVectors(cmMakefile *mf) this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_MEMCHECK_IGNORE", this->CustomTestsIgnore); + this->CTest->SetCTestConfigurationFromCMakeVariable( + mf, "CMakeCommand", "CMAKE_COMMAND"); } //---------------------------------------------------------------------- @@ -292,7 +345,6 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) { return; } - this->CTest->StartXML(os, this->AppendXML); os << "<DynamicAnalysis Checker=\""; switch ( this->MemoryTesterStyle ) @@ -306,6 +358,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) case cmCTestMemCheckHandler::BOUNDS_CHECKER: os << "BoundsChecker"; break; + case cmCTestMemCheckHandler::THREAD_SANITIZER: + os << "ThreadSanitizer"; + break; default: os << "Unknown"; } @@ -333,8 +388,7 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) { cmCTestTestResult *result = &this->TestResults[cc]; std::string memcheckstr; - int memcheckresults[cmCTestMemCheckHandler::NO_MEMORY_FAULT]; - int kk; + std::vector<int> memcheckresults(this->ResultStrings.size(), 0); bool res = this->ProcessMemCheckOutput(result->Output, memcheckstr, memcheckresults); if ( res && result->Status == cmCTestMemCheckHandler::COMPLETED ) @@ -345,16 +399,17 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)); this->WriteTestResultHeader(os, result); os << "\t\t<Results>" << std::endl; - for ( kk = 0; cmCTestMemCheckResultLongStrings[kk]; kk ++ ) + for(std::vector<int>::size_type kk = 0; + kk < memcheckresults.size(); ++kk) { if ( memcheckresults[kk] ) { - os << "\t\t\t<Defect type=\"" << cmCTestMemCheckResultLongStrings[kk] + os << "\t\t\t<Defect type=\"" << this->ResultStringsLong[kk] << "\">" << memcheckresults[kk] << "</Defect>" << std::endl; } - this->MemoryTesterGlobalResults[kk] += memcheckresults[kk]; + this->GlobalResults[kk] += memcheckresults[kk]; } std::string logTag; @@ -383,9 +438,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory checking results:" << std::endl); os << "\t<DefectList>" << std::endl; - for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ ) + for ( cc = 0; cc < this->GlobalResults.size(); cc ++ ) { - if ( this->MemoryTesterGlobalResults[cc] ) + if ( this->GlobalResults[cc] ) { #ifdef cerr # undef cerr @@ -393,9 +448,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) std::cerr.width(35); #define cerr no_cerr cmCTestLog(this->CTest, HANDLER_OUTPUT, - cmCTestMemCheckResultLongStrings[cc] << " - " - << this->MemoryTesterGlobalResults[cc] << std::endl); - os << "\t\t<Defect Type=\"" << cmCTestMemCheckResultLongStrings[cc] + this->ResultStringsLong[cc] << " - " + << this->GlobalResults[cc] << std::endl); + os << "\t\t<Defect Type=\"" << this->ResultStringsLong[cc] << "\"/>" << std::endl; } } @@ -410,13 +465,13 @@ void cmCTestMemCheckHandler::GenerateDartOutput(std::ostream& os) os << "</DynamicAnalysis>" << std::endl; this->CTest->EndXML(os); - - } //---------------------------------------------------------------------- bool cmCTestMemCheckHandler::InitializeMemoryChecking() { + this->MemoryTesterEnvironmentVariable = ""; + this->MemoryTester = ""; // Setup the command if ( cmSystemTools::FileExists(this->CTest->GetCTestConfiguration( "MemoryCheckCommand").c_str()) ) @@ -426,7 +481,9 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() std::string testerName = cmSystemTools::GetFilenameName(this->MemoryTester); // determine the checker type - if ( testerName.find("valgrind") != std::string::npos ) + if ( testerName.find("valgrind") != std::string::npos || + this->CTest->GetCTestConfiguration("MemoryCheckType") + == "Valgrind") { this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND; } @@ -464,12 +521,38 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() = this->CTest->GetCTestConfiguration("BoundsCheckerCommand").c_str(); this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER; } - else + if ( this->CTest->GetCTestConfiguration("MemoryCheckType") + == "ThreadSanitizer") + { + this->MemoryTester + = this->CTest->GetCTestConfiguration("CMakeCommand").c_str(); + this->MemoryTesterStyle = cmCTestMemCheckHandler::THREAD_SANITIZER; + this->LogWithPID = true; // even if we give the log file the pid is added + } + // Check the MemoryCheckType + if(this->MemoryTesterStyle == cmCTestMemCheckHandler::UNKNOWN) + { + std::string checkType = + this->CTest->GetCTestConfiguration("MemoryCheckType"); + if(checkType == "Purify") + { + this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY; + } + else if(checkType == "BoundsChecker") + { + this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER; + } + else if(checkType == "Valgrind") + { + this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND; + } + } + if(this->MemoryTester.size() == 0 ) { cmCTestLog(this->CTest, WARNING, - "Memory checker (MemoryCheckCommand) " - "not set, or cannot find the specified program." - << std::endl); + "Memory checker (MemoryCheckCommand) " + "not set, or cannot find the specified program." + << std::endl); return false; } @@ -568,6 +651,20 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->MemoryTesterOptions.push_back("/M"); break; } + case cmCTestMemCheckHandler::THREAD_SANITIZER: + { + // To pass arguments to ThreadSanitizer the environment variable + // TSAN_OPTIONS is used. This is done with the cmake -E env command. + // The MemoryTesterDynamicOptions is setup with the -E env + // Then the MemoryTesterEnvironmentVariable gets the + // TSAN_OPTIONS string with the log_path in it. + this->MemoryTesterDynamicOptions.push_back("-E"); + this->MemoryTesterDynamicOptions.push_back("env"); + std::string outputFile = "TSAN_OPTIONS=log_path=\"" + + this->MemoryTesterOutputFile + "\""; + this->MemoryTesterEnvironmentVariable = outputFile; + break; + } default: cmCTestLog(this->CTest, ERROR_MESSAGE, "Do not understand memory checker: " << this->MemoryTester @@ -575,24 +672,20 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() return false; } - std::vector<std::string>::size_type cc; - for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ ) - { - this->MemoryTesterGlobalResults[cc] = 0; - } + this->InitializeResultsVectors(); + // std::vector<std::string>::size_type cc; + // for ( cc = 0; cmCTestMemCheckResultStrings[cc]; cc ++ ) + // { + // this->MemoryTesterGlobalResults[cc] = 0; + // } return true; } //---------------------------------------------------------------------- -bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str, - std::string& log, int* results) +bool cmCTestMemCheckHandler:: +ProcessMemCheckOutput(const std::string& str, + std::string& log, std::vector<int>& results) { - std::string::size_type cc; - for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ ) - { - results[cc] = 0; - } - if ( this->MemoryTesterStyle == cmCTestMemCheckHandler::VALGRIND ) { return this->ProcessMemCheckValgrindOutput(str, log, results); @@ -602,6 +695,11 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str, return this->ProcessMemCheckPurifyOutput(str, log, results); } else if ( this->MemoryTesterStyle == + cmCTestMemCheckHandler::THREAD_SANITIZER ) + { + return this->ProcessMemCheckThreadSanitizerOutput(str, log, results); + } + else if ( this->MemoryTesterStyle == cmCTestMemCheckHandler::BOUNDS_CHECKER ) { return this->ProcessMemCheckBoundsCheckerOutput(str, log, results); @@ -612,15 +710,68 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str, log.append("None that I know"); log = str; } - - return true; } +std::vector<int>::size_type cmCTestMemCheckHandler::FindOrAddWarning( + const std::string& warning) +{ + for(std::vector<std::string>::size_type i =0; + i < this->ResultStrings.size(); ++i) + { + if(this->ResultStrings[i] == warning) + { + return i; + } + } + this->GlobalResults.push_back(0); // this must stay the same size + this->ResultStrings.push_back(warning); + this->ResultStringsLong.push_back(warning); + return this->ResultStrings.size()-1; +} +//---------------------------------------------------------------------- +bool cmCTestMemCheckHandler::ProcessMemCheckThreadSanitizerOutput( + const std::string& str, std::string& log, + std::vector<int>& result) +{ + cmsys::RegularExpression + sanitizerWarning("WARNING: ThreadSanitizer: (.*) \\(pid=.*\\)"); + int defects = 0; + std::vector<std::string> lines; + cmSystemTools::Split(str.c_str(), lines); + cmOStringStream ostr; + log = ""; + for( std::vector<std::string>::iterator i = lines.begin(); + i != lines.end(); ++i) + { + if(sanitizerWarning.find(*i)) + { + std::string warning = sanitizerWarning.match(1); + std::vector<int>::size_type idx = this->FindOrAddWarning(warning); + if(result.size() == 0 || idx > result.size()-1) + { + result.push_back(1); + } + else + { + result[idx]++; + } + defects++; + ostr << "<b>" << this->ResultStrings[idx] << "</b> "; + } + ostr << cmXMLSafe(*i) << std::endl; + } + log = ostr.str(); + if(defects) + { + return false; + } + return true; +} //---------------------------------------------------------------------- bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( const std::string& str, std::string& log, - int* results) + std::vector<int>& results) { std::vector<std::string> lines; cmSystemTools::Split(str.c_str(), lines); @@ -634,19 +785,19 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( for( std::vector<std::string>::iterator i = lines.begin(); i != lines.end(); ++i) { - int failure = cmCTestMemCheckHandler::NO_MEMORY_FAULT; + std::vector<int>::size_type failure = this->ResultStrings.size(); if ( pfW.find(*i) ) { - int cc; - for ( cc = 0; cc < cmCTestMemCheckHandler::NO_MEMORY_FAULT; cc ++ ) + std::vector<int>::size_type cc; + for ( cc = 0; cc < this->ResultStrings.size(); cc ++ ) { - if ( pfW.match(1) == cmCTestMemCheckResultStrings[cc] ) + if ( pfW.match(1) == this->ResultStrings[cc] ) { failure = cc; break; } } - if ( cc == cmCTestMemCheckHandler::NO_MEMORY_FAULT ) + if ( cc == this->ResultStrings.size() ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown Purify memory fault: " << pfW.match(1) << std::endl); @@ -654,9 +805,9 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( << std::endl; } } - if ( failure != NO_MEMORY_FAULT ) + if ( failure != this->ResultStrings.size() ) { - ostr << "<b>" << cmCTestMemCheckResultStrings[failure] << "</b> "; + ostr << "<b>" << this->ResultStrings[failure] << "</b> "; results[failure] ++; defects ++; } @@ -674,7 +825,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( //---------------------------------------------------------------------- bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( const std::string& str, std::string& log, - int* results) + std::vector<int>& results) { std::vector<std::string> lines; cmSystemTools::Split(str.c_str(), lines); @@ -803,7 +954,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( if ( failure != cmCTestMemCheckHandler::NO_MEMORY_FAULT ) { - ostr << "<b>" << cmCTestMemCheckResultStrings[failure] << "</b> "; + ostr << "<b>" << this->ResultStrings[failure] << "</b> "; results[failure] ++; defects ++; } @@ -855,7 +1006,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( //---------------------------------------------------------------------- bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput( const std::string& str, std::string& log, - int* results) + std::vector<int>& results) { log = ""; double sttime = cmSystemTools::GetTime(); @@ -909,6 +1060,26 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput( return true; } +// PostProcessTest memcheck results +void +cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, + int test) +{ + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "PostProcessTest memcheck results for : " + << res.Name << std::endl); + if(this->MemoryTesterStyle + == cmCTestMemCheckHandler::BOUNDS_CHECKER) + { + this->PostProcessBoundsCheckerTest(res, test); + } + else + { + this->AppendMemTesterOutput(res, test); + } +} + + // This method puts the bounds checker output file into the output // for the test void @@ -951,35 +1122,16 @@ cmCTestMemCheckHandler::PostProcessBoundsCheckerTest(cmCTestTestResult& res, } void -cmCTestMemCheckHandler::PostProcessPurifyTest(cmCTestTestResult& res, - int test) -{ - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "PostProcessPurifyTest for : " - << res.Name << std::endl); - this->AppendMemTesterOutput(res, test); -} - -void -cmCTestMemCheckHandler::PostProcessValgrindTest(cmCTestTestResult& res, - int test) -{ - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "PostProcessValgrindTest for : " - << res.Name << std::endl); - this->AppendMemTesterOutput(res, test); -} - -void cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res, int test) { std::string ofile = this->TestOutputFileName(test); - if ( ofile.empty() ) { return; } + // put ifs in scope so file can be deleted if needed + { cmsys::ifstream ifs(ofile.c_str()); if ( !ifs ) { @@ -993,6 +1145,12 @@ cmCTestMemCheckHandler::AppendMemTesterOutput(cmCTestTestResult& res, res.Output += line; res.Output += "\n"; } + } + if(this->LogWithPID) + { + cmSystemTools::RemoveFile(ofile.c_str()); + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Remove: "<< ofile <<"\n"); + } } std::string @@ -1005,14 +1163,29 @@ cmCTestMemCheckHandler::TestOutputFileName(int test) std::string ofile = this->MemoryTesterOutputFile; std::string::size_type pos = ofile.find("??"); ofile.replace(pos, 2, index); - - if ( !cmSystemTools::FileExists(ofile.c_str()) ) + if(this->LogWithPID) + { + ofile += ".*"; + cmsys::Glob g; + g.FindFiles(ofile); + if(g.GetFiles().size() == 0) + { + std::string log = "Cannot find memory tester output file: " + + ofile; + cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl); + ofile = ""; + } + else + { + ofile = g.GetFiles()[0]; + } + } + else if ( !cmSystemTools::FileExists(ofile.c_str()) ) { std::string log = "Cannot find memory tester output file: " + ofile; cmCTestLog(this->CTest, ERROR_MESSAGE, log.c_str() << std::endl); ofile = ""; } - return ofile; } diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h index 20a38bb..ffe57f6 100644 --- a/Source/CTest/cmCTestMemCheckHandler.h +++ b/Source/CTest/cmCTestMemCheckHandler.h @@ -15,7 +15,10 @@ #include "cmCTestTestHandler.h" +#include "cmStandardIncludes.h" #include "cmListFileCache.h" +#include <vector> +#include <string> class cmMakefile; @@ -45,7 +48,9 @@ private: UNKNOWN = 0, VALGRIND, PURIFY, - BOUNDS_CHECKER + BOUNDS_CHECKER, + // checkers after hear do not use the standard error list + THREAD_SANITIZER }; public: enum { // Memory faults @@ -93,7 +98,17 @@ private: std::vector<std::string> MemoryTesterOptions; int MemoryTesterStyle; std::string MemoryTesterOutputFile; - int MemoryTesterGlobalResults[NO_MEMORY_FAULT]; + std::string MemoryTesterEnvironmentVariable; + // these are used to store the types of errors that can show up + std::vector<std::string> ResultStrings; + std::vector<std::string> ResultStringsLong; + std::vector<int> GlobalResults; + bool LogWithPID; // does log file add pid + + std::vector<int>::size_type FindOrAddWarning(const std::string& warning); + // initialize the ResultStrings and ResultStringsLong for + // this type of checker + void InitializeResultsVectors(); ///! Initialize memory checking subsystem. bool InitializeMemoryChecking(); @@ -110,17 +125,22 @@ private: //string. After running, log holds the output and results hold the //different memmory errors. bool ProcessMemCheckOutput(const std::string& str, - std::string& log, int* results); + std::string& log, std::vector<int>& results); bool ProcessMemCheckValgrindOutput(const std::string& str, - std::string& log, int* results); + std::string& log, + std::vector<int>& results); bool ProcessMemCheckPurifyOutput(const std::string& str, - std::string& log, int* results); + std::string& log, + std::vector<int>& results); + bool ProcessMemCheckThreadSanitizerOutput(const std::string& str, + std::string& log, + std::vector<int>& results); bool ProcessMemCheckBoundsCheckerOutput(const std::string& str, - std::string& log, int* results); + std::string& log, + std::vector<int>& results); - void PostProcessPurifyTest(cmCTestTestResult& res, int test); + void PostProcessTest(cmCTestTestResult& res, int test); void PostProcessBoundsCheckerTest(cmCTestTestResult& res, int test); - void PostProcessValgrindTest(cmCTestTestResult& res, int test); ///! append MemoryTesterOutputFile to the test log void AppendMemTesterOutput(cmCTestTestHandler::cmCTestTestResult& res, diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 385388d..bdd8c02 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -392,20 +392,7 @@ void cmCTestRunTest::MemCheckPostProcess() << this->TestResult.Name << std::endl); cmCTestMemCheckHandler * handler = static_cast<cmCTestMemCheckHandler*> (this->TestHandler); - switch ( handler->MemoryTesterStyle ) - { - case cmCTestMemCheckHandler::VALGRIND: - handler->PostProcessValgrindTest(this->TestResult, this->Index); - break; - case cmCTestMemCheckHandler::PURIFY: - handler->PostProcessPurifyTest(this->TestResult, this->Index); - break; - case cmCTestMemCheckHandler::BOUNDS_CHECKER: - handler->PostProcessBoundsCheckerTest(this->TestResult, this->Index); - break; - default: - break; - } + handler->PostProcessTest(this->TestResult, this->Index); } //---------------------------------------------------------------------- diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index eccb06a..64c5822 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -38,7 +38,8 @@ void reportBadObjLib(std::vector<cmSourceFile*> const& badObjLib, { e << " " << (*i)->GetLocation().GetName() << "\n"; } - e << "but may contain only headers and sources that compile."; + e << "but may contain only sources that compile, header files, and " + "other files that would not affect linking of a normal library."; cm->IssueMessage(cmake::FATAL_ERROR, e.str(), target->GetBacktrace()); } @@ -205,10 +206,6 @@ struct TagVisitor else { DoAccept<IsSameTag<Tag, ExtraSourcesTag>::Result>::Do(this->Data, sf); - if(this->IsObjLib && ext != "txt") - { - this->BadObjLibFiles.push_back(sf); - } } } }; diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx index 8a96289..7667a85 100644 --- a/Source/cmGetSourceFilePropertyCommand.cxx +++ b/Source/cmGetSourceFilePropertyCommand.cxx @@ -29,7 +29,7 @@ bool cmGetSourceFilePropertyCommand // for the location we must create a source file first if (!sf && args[2] == "LOCATION") { - sf = this->Makefile->GetOrCreateSource(file); + sf = this->Makefile->CreateSource(file); } if(sf) { diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 412c998..2218e2f 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -989,7 +989,7 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, // Choose a source file on which to store the custom command. cmSourceFile* file = 0; - if(!main_dependency.empty()) + if(!commandLines.empty() && !main_dependency.empty()) { // The main dependency was specified. Use it unless a different // custom command already used it. @@ -1010,11 +1010,9 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, file = 0; } } - else + else if (!file) { - // The main dependency does not have a custom command or we are - // allowed to replace it. Use it to store the command. - file = this->GetOrCreateSource(main_dependency); + file = this->CreateSource(main_dependency); } } @@ -1041,8 +1039,11 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, } // Create a cmSourceFile for the rule file. - file = this->GetOrCreateSource(outName, true); - file->SetProperty("__CMAKE_RULE", "1"); + if (!file) + { + file = this->CreateSource(outName, true); + file->SetProperty("__CMAKE_RULE", "1"); + } } // Always create the output sources and mark them generated. @@ -1055,16 +1056,16 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs, } } - // Construct a complete list of dependencies. - std::vector<std::string> depends2(depends); - if(!main_dependency.empty()) - { - depends2.push_back(main_dependency); - } - // Attach the custom command to the file. if(file) { + // Construct a complete list of dependencies. + std::vector<std::string> depends2(depends); + if(!main_dependency.empty()) + { + depends2.push_back(main_dependency); + } + cmCustomCommand* cc = new cmCustomCommand(this, outputs, depends2, commandLines, comment, workingDir); @@ -1256,28 +1257,31 @@ cmMakefile::AddUtilityCommand(const std::string& utilityName, } // Store the custom command in the target. - std::string force = this->GetStartOutputDirectory(); - force += cmake::GetCMakeFilesDirectory(); - force += "/"; - force += utilityName; - std::string no_main_dependency = ""; - bool no_replace = false; - this->AddCustomCommandToOutput(force, depends, - no_main_dependency, - commandLines, comment, - workingDirectory, no_replace, - escapeOldStyle); - cmSourceFile* sf = target->AddSourceCMP0049(force); - - // The output is not actually created so mark it symbolic. - if(sf) - { - sf->SetProperty("SYMBOLIC", "1"); - } - else - { - cmSystemTools::Error("Could not get source file entry for ", - force.c_str()); + if (!commandLines.empty() || !depends.empty()) + { + std::string force = this->GetStartOutputDirectory(); + force += cmake::GetCMakeFilesDirectory(); + force += "/"; + force += utilityName; + std::string no_main_dependency = ""; + bool no_replace = false; + this->AddCustomCommandToOutput(force, depends, + no_main_dependency, + commandLines, comment, + workingDirectory, no_replace, + escapeOldStyle); + cmSourceFile* sf = target->AddSourceCMP0049(force); + + // The output is not actually created so mark it symbolic. + if(sf) + { + sf->SetProperty("SYMBOLIC", "1"); + } + else + { + cmSystemTools::Error("Could not get source file entry for ", + force.c_str()); + } } return target; } @@ -3451,6 +3455,19 @@ cmSourceFile* cmMakefile::GetSource(const std::string& sourceName) const } //---------------------------------------------------------------------------- +cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, + bool generated) +{ + cmSourceFile* sf = new cmSourceFile(this, sourceName); + if(generated) + { + sf->SetProperty("GENERATED", "1"); + } + this->SourceFiles.push_back(sf); + return sf; +} + +//---------------------------------------------------------------------------- cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, bool generated) { @@ -3460,13 +3477,7 @@ cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, } else { - cmSourceFile* sf = new cmSourceFile(this, sourceName); - if(generated) - { - sf->SetProperty("GENERATED", "1"); - } - this->SourceFiles.push_back(sf); - return sf; + return this->CreateSource(sourceName, generated); } } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index d5ffd98..3a40c1c 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -560,6 +560,13 @@ public: */ cmSourceFile* GetSource(const std::string& sourceName) const; + /** Create the source file and return it. generated + * indicates if it is a generated file, this is used in determining + * how to create the source file instance e.g. name + */ + cmSourceFile* CreateSource(const std::string& sourceName, + bool generated = false); + /** Get a cmSourceFile pointer for a given source name, if the name is * not found, then create the source file and return it. generated * indicates if it is a generated file, this is used in determining diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index 5e6a226..8ca4360 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -1171,10 +1171,9 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) ADD_EXECUTABLE(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_CXX_TEST_SRCS}) SET_PROPERTY(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY LABELS ${KWSYS_LABELS_EXE}) TARGET_LINK_LIBRARIES(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_NAMESPACE}) - SET(TEST_SYSTEMTOOLS_BIN_FILE - "${CMAKE_CURRENT_SOURCE_DIR}/testSystemTools.bin") - SET(TEST_SYSTEMTOOLS_SRC_FILE - "${CMAKE_CURRENT_SOURCE_DIR}/testSystemTools.cxx") + + SET(TEST_SYSTEMTOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + SET(TEST_SYSTEMTOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") CONFIGURE_FILE( ${PROJECT_SOURCE_DIR}/testSystemTools.h.in ${PROJECT_BINARY_DIR}/testSystemTools.h) diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index d54e607..b305fd7 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -113,15 +113,24 @@ bool Directory::Load(const char* name) #endif char* buf; size_t n = strlen(name); - if ( name[n - 1] == '/' ) + if ( name[n - 1] == '/' || name[n - 1] == '\\' ) { buf = new char[n + 1 + 1]; sprintf(buf, "%s*", name); } else { + // Make sure the slashes in the wildcard suffix are consistent with the + // rest of the path buf = new char[n + 2 + 1]; - sprintf(buf, "%s/*", name); + if ( strchr(name, '\\') ) + { + sprintf(buf, "%s\\*", name); + } + else + { + sprintf(buf, "%s/*", name); + } } struct _wfinddata_t data; // data of current file diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 704cbbc..db94510 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -187,17 +187,34 @@ static inline char *realpath(const char *path, char *resolved_path) } #endif +#ifdef _WIN32 +static time_t windows_filetime_to_posix_time(const FILETIME& ft) +{ + LARGE_INTEGER date; + date.HighPart = ft.dwHighDateTime; + date.LowPart = ft.dwLowDateTime; + + // removes the diff between 1970 and 1601 + date.QuadPart -= ((LONGLONG)(369 * 365 + 89) * 24 * 3600 * 10000000); + + // converts back from 100-nanoseconds to seconds + return date.QuadPart / 10000000; +} +#endif + #if defined(_WIN32) && (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__MINGW32__)) #include <wctype.h> inline int Mkdir(const char* dir) { - return _wmkdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str()); + return _wmkdir( + KWSYS_NAMESPACE::SystemTools::ConvertToWindowsExtendedPath(dir).c_str()); } inline int Rmdir(const char* dir) { - return _wrmdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str()); + return _wrmdir( + KWSYS_NAMESPACE::SystemTools::ConvertToWindowsExtendedPath(dir).c_str()); } inline const char* Getcwd(char* buf, unsigned int len) { @@ -609,7 +626,7 @@ const char* SystemTools::GetExecutableExtension() FILE* SystemTools::Fopen(const char* file, const char* mode) { #ifdef _WIN32 - return _wfopen(Encoding::ToWide(file).c_str(), + return _wfopen(SystemTools::ConvertToWindowsExtendedPath(file).c_str(), Encoding::ToWide(mode).c_str()); #else return fopen(file, mode); @@ -1081,7 +1098,8 @@ bool SystemTools::FileExists(const char* filename) } return access(filename, R_OK) == 0; #elif defined(_WIN32) - return (GetFileAttributesW(Encoding::ToWide(filename).c_str()) + return (GetFileAttributesW( + SystemTools::ConvertToWindowsExtendedPath(filename).c_str()) != INVALID_FILE_ATTRIBUTES); #else return access(filename, R_OK) == 0; @@ -1137,10 +1155,11 @@ bool SystemTools::Touch(const char* filename, bool create) return false; } #if defined(_WIN32) && !defined(__CYGWIN__) - HANDLE h = CreateFileW(Encoding::ToWide(filename).c_str(), - FILE_WRITE_ATTRIBUTES, - FILE_SHARE_WRITE, 0, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, 0); + HANDLE h = CreateFileW( + SystemTools::ConvertToWindowsExtendedPath(filename).c_str(), + FILE_WRITE_ATTRIBUTES, + FILE_SHARE_WRITE, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0); if(!h) { return false; @@ -1242,13 +1261,15 @@ bool SystemTools::FileTimeCompare(const char* f1, const char* f2, // Windows version. Get the modification time from extended file attributes. WIN32_FILE_ATTRIBUTE_DATA f1d; WIN32_FILE_ATTRIBUTE_DATA f2d; - if(!GetFileAttributesExW(Encoding::ToWide(f1).c_str(), - GetFileExInfoStandard, &f1d)) + if(!GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(f1).c_str(), + GetFileExInfoStandard, &f1d)) { return false; } - if(!GetFileAttributesExW(Encoding::ToWide(f2).c_str(), - GetFileExInfoStandard, &f2d)) + if(!GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(f2).c_str(), + GetFileExInfoStandard, &f2d)) { return false; } @@ -1830,6 +1851,71 @@ void SystemTools::ConvertToUnixSlashes(kwsys_stl::string& path) } } +#ifdef _WIN32 +// Convert local paths to UNC style paths +kwsys_stl::wstring +SystemTools::ConvertToWindowsExtendedPath(const kwsys_stl::string &source) +{ + kwsys_stl::wstring wsource = Encoding::ToWide(source); + + // Resolve any relative paths + DWORD wfull_len; + + /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that + * won't return a large enough buffer size if the input is too small */ + wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3; + kwsys_stl::vector<wchar_t> wfull(wfull_len); + GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL); + + /* This should get the correct size without any extra padding from the + * previous size workaround. */ + wfull_len = static_cast<DWORD>(wcslen(&wfull[0])); + + if(wfull_len >= 2 && isalpha(wfull[0]) && wfull[1] == L':') + { /* C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + kwsys_stl::wstring(&wfull[0]); + } + else if(wfull_len >= 2 && wfull[0] == L'\\' && wfull[1] == L'\\') + { /* Starts with \\ */ + if(wfull_len >= 4 && wfull[2] == L'?' && wfull[3] == L'\\') + { /* Starts with \\?\ */ + if(wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && + wfull[6] == L'C' && wfull[7] == L'\\') + { /* \\?\UNC\Foo\bar\FooBar.txt */ + return kwsys_stl::wstring(&wfull[0]); + } + else if(wfull_len >= 6 && isalpha(wfull[4]) && wfull[5] == L':') + { /* \\?\C:\Foo\bar\FooBar.txt */ + return kwsys_stl::wstring(&wfull[0]); + } + else if(wfull_len >= 5) + { /* \\?\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + kwsys_stl::wstring(&wfull[4]); + } + } + else if(wfull_len >= 4 && wfull[2] == L'.' && wfull[3] == L'\\') + { /* Starts with \\.\ a device name */ + if(wfull_len >= 6 && isalpha(wfull[4]) && wfull[5] == L':') + { /* \\.\C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + kwsys_stl::wstring(&wfull[4]); + } + else if(wfull_len >= 5) + { /* \\.\Foo\bar\ Device name is left unchanged */ + return kwsys_stl::wstring(&wfull[0]); + } + } + else if(wfull_len >= 3) + { /* \\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + kwsys_stl::wstring(&wfull[2]); + } + } + + // If this case has been reached, then the path is invalid. Leave it + // unchanged + return Encoding::ToWide(source); +} +#endif + // change // to /, and escape any spaces in the path kwsys_stl::string SystemTools::ConvertToUnixOutputPath(const char* path) { @@ -1960,17 +2046,19 @@ bool SystemTools::FilesDiffer(const char* source, #if defined(_WIN32) WIN32_FILE_ATTRIBUTE_DATA statSource; - if (GetFileAttributesExW(Encoding::ToWide(source).c_str(), - GetFileExInfoStandard, - &statSource) == 0) + if (GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(source).c_str(), + GetFileExInfoStandard, + &statSource) == 0) { return true; } WIN32_FILE_ATTRIBUTE_DATA statDestination; - if (GetFileAttributesExW(Encoding::ToWide(destination).c_str(), - GetFileExInfoStandard, - &statDestination) == 0) + if (GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(destination).c_str(), + GetFileExInfoStandard, + &statDestination) == 0) { return true; } @@ -2191,7 +2279,12 @@ bool SystemTools::CopyADirectory(const char* source, const char* destination, bool always) { Directory dir; +#ifdef _WIN32 + dir.Load(Encoding::ToNarrow( + SystemTools::ConvertToWindowsExtendedPath(source)).c_str()); +#else dir.Load(source); +#endif size_t fileNum; if ( !SystemTools::MakeDirectory(destination) ) { @@ -2234,15 +2327,28 @@ bool SystemTools::CopyADirectory(const char* source, const char* destination, // return size of file; also returns zero if no file exists unsigned long SystemTools::FileLength(const char* filename) { - struct stat fs; - if (stat(filename, &fs) != 0) + unsigned long length = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, &fs) != 0) { - return 0; + /* To support the full 64-bit file size, use fs.nFileSizeHigh + * and fs.nFileSizeLow to construct the 64 bit size + + length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow; + */ + length = static_cast<unsigned long>(fs.nFileSizeLow); } - else +#else + struct stat fs; + if (stat(filename, &fs) == 0) { - return static_cast<unsigned long>(fs.st_size); + length = static_cast<unsigned long>(fs.st_size); } +#endif + return length; } int SystemTools::Strucmp(const char *s1, const char *s2) @@ -2261,29 +2367,47 @@ int SystemTools::Strucmp(const char *s1, const char *s2) // return file's modified time long int SystemTools::ModifiedTime(const char* filename) { - struct stat fs; - if (stat(filename, &fs) != 0) + long int mt = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, + &fs) != 0) { - return 0; + mt = windows_filetime_to_posix_time(fs.ftLastWriteTime); } - else +#else + struct stat fs; + if (stat(filename, &fs) == 0) { - return static_cast<long int>(fs.st_mtime); + mt = static_cast<long int>(fs.st_mtime); } +#endif + return mt; } // return file's creation time long int SystemTools::CreationTime(const char* filename) { - struct stat fs; - if (stat(filename, &fs) != 0) + long int ct = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW( + SystemTools::ConvertToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, + &fs) != 0) { - return 0; + ct = windows_filetime_to_posix_time(fs.ftCreationTime); } - else +#else + struct stat fs; + if (stat(filename, &fs) == 0) { - return fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0; + ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0; } +#endif + return ct; } bool SystemTools::ConvertDateMacroString(const char *str, time_t *tmt) @@ -2406,7 +2530,8 @@ bool SystemTools::RemoveFile(const char* source) SystemTools::SetPermissions(source, S_IWRITE); #endif #ifdef _WIN32 - bool res = _wunlink(Encoding::ToWide(source).c_str()) != 0 ? false : true; + bool res = + _wunlink(SystemTools::ConvertToWindowsExtendedPath(source).c_str()) == 0; #else bool res = unlink(source) != 0 ? false : true; #endif @@ -2435,7 +2560,12 @@ bool SystemTools::RemoveADirectory(const char* source) } Directory dir; +#ifdef _WIN32 + dir.Load(Encoding::ToNarrow( + SystemTools::ConvertToWindowsExtendedPath(source)).c_str()); +#else dir.Load(source); +#endif size_t fileNum; for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { @@ -2849,7 +2979,8 @@ bool SystemTools::FileIsDirectory(const char* name) // Now check the file node type. #if defined( _WIN32 ) - DWORD attr = GetFileAttributesW(Encoding::ToWide(name).c_str()); + DWORD attr = GetFileAttributesW( + SystemTools::ConvertToWindowsExtendedPath(name).c_str()); if (attr != INVALID_FILE_ATTRIBUTES) { return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -4041,7 +4172,7 @@ bool SystemTools::GetShortPath(const kwsys_stl::string& path, kwsys_stl::string& kwsys_stl::wstring wtempPath = Encoding::ToWide(tempPath); kwsys_stl::vector<wchar_t> buffer(wtempPath.size()+1); buffer[0] = 0; - ret = GetShortPathNameW(Encoding::ToWide(tempPath).c_str(), + ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0], static_cast<DWORD>(wtempPath.size())); if(buffer[0] == 0 || ret > wtempPath.size()) @@ -4279,7 +4410,8 @@ bool SystemTools::GetPermissions(const char* file, mode_t& mode) } #if defined(_WIN32) - DWORD attr = GetFileAttributesW(Encoding::ToWide(file).c_str()); + DWORD attr = GetFileAttributesW( + SystemTools::ConvertToWindowsExtendedPath(file).c_str()); if(attr == INVALID_FILE_ATTRIBUTES) { return false; @@ -4331,7 +4463,8 @@ bool SystemTools::SetPermissions(const char* file, mode_t mode) return false; } #ifdef _WIN32 - if ( _wchmod(Encoding::ToWide(file).c_str(), mode) < 0 ) + if ( _wchmod(SystemTools::ConvertToWindowsExtendedPath(file).c_str(), + mode) < 0 ) #else if ( chmod(file, mode) < 0 ) #endif diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index fb55848..b7c7206 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -249,7 +249,18 @@ public: * Replace Windows file system slashes with Unix-style slashes. */ static void ConvertToUnixSlashes(kwsys_stl::string& path); - + +#ifdef _WIN32 + /** + * Convert the path to an extended length path to avoid MAX_PATH length + * limitations on Windows. If the input is a local path the result will be + * prefixed with \\?\; if the input is instead a network path, the result + * will be prefixed with \\?\UNC\. All output will also be converted to + * absolute paths with Windows-style backslashes. + **/ + static kwsys_stl::wstring ConvertToWindowsExtendedPath(const kwsys_stl::string&); +#endif + /** * For windows this calls ConvertToWindowsOutputPath and for unix * it calls ConvertToUnixOutputPath diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx index 1bff707..58adb84 100644 --- a/Source/kwsys/testDynamicLoader.cxx +++ b/Source/kwsys/testDynamicLoader.cxx @@ -108,7 +108,7 @@ int testDynamicLoader(int argc, char *argv[]) // Make sure that inexistent lib is giving correct result res += TestDynamicLoader("azerty_", "foo_bar",0,0,0); // Make sure that random binary file cannot be assimilated as dylib - res += TestDynamicLoader(TEST_SYSTEMTOOLS_BIN_FILE, "wp",0,0,0); + res += TestDynamicLoader(TEST_SYSTEMTOOLS_SOURCE_DIR "/testSystemTools.bin", "wp",0,0,0); #endif #ifdef __linux__ diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index 69825a8..8b21081 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx @@ -98,32 +98,124 @@ static bool CheckEscapeChars(kwsys_stl::string input, static bool CheckFileOperations() { bool res = true; - - if (kwsys::SystemTools::DetectFileType(TEST_SYSTEMTOOLS_BIN_FILE) != + const kwsys_stl::string testBinFile(TEST_SYSTEMTOOLS_SOURCE_DIR + "/testSystemTools.bin"); + const kwsys_stl::string testTxtFile(TEST_SYSTEMTOOLS_SOURCE_DIR + "/testSystemTools.cxx"); + const kwsys_stl::string testNewDir(TEST_SYSTEMTOOLS_BINARY_DIR + "/testSystemToolsNewDir"); + const kwsys_stl::string testNewFile(testNewDir + "/testNewFile.txt"); + + if (kwsys::SystemTools::DetectFileType(testBinFile.c_str()) != kwsys::SystemTools::FileTypeBinary) { kwsys_ios::cerr << "Problem with DetectFileType - failed to detect type of: " - << TEST_SYSTEMTOOLS_BIN_FILE << kwsys_ios::endl; + << testBinFile << kwsys_ios::endl; res = false; } - if (kwsys::SystemTools::DetectFileType(TEST_SYSTEMTOOLS_SRC_FILE) != + if (kwsys::SystemTools::DetectFileType(testTxtFile.c_str()) != kwsys::SystemTools::FileTypeText) { kwsys_ios::cerr << "Problem with DetectFileType - failed to detect type of: " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << testTxtFile << kwsys_ios::endl; res = false; } - - if (kwsys::SystemTools::FileLength(TEST_SYSTEMTOOLS_BIN_FILE) != 766) + + if (kwsys::SystemTools::FileLength(testBinFile.c_str()) != 766) { kwsys_ios::cerr << "Problem with FileLength - incorrect length for: " - << TEST_SYSTEMTOOLS_BIN_FILE << kwsys_ios::endl; - res = false; + << testBinFile << kwsys_ios::endl; + res = false; + } + + if (!kwsys::SystemTools::MakeDirectory(testNewDir.c_str())) + { + kwsys_ios::cerr + << "Problem with MakeDirectory for: " + << testNewDir << kwsys_ios::endl; + res = false; + } + + if (!kwsys::SystemTools::Touch(testNewFile.c_str(), true)) + { + kwsys_ios::cerr + << "Problem with Touch for: " + << testNewFile << kwsys_ios::endl; + res = false; + } + + if (!kwsys::SystemTools::RemoveFile(testNewFile.c_str())) + { + kwsys_ios::cerr + << "Problem with RemoveFile: " + << testNewFile << kwsys_ios::endl; + res = false; + } + + kwsys::SystemTools::Touch(testNewFile.c_str(), true); + if (!kwsys::SystemTools::RemoveADirectory(testNewDir.c_str())) + { + kwsys_ios::cerr + << "Problem with RemoveADirectory for: " + << testNewDir << kwsys_ios::endl; + res = false; + } + +#ifdef KWSYS_TEST_SYSTEMTOOLS_LONG_PATHS + // Perform the same file and directory creation and deletion tests but + // with paths > 256 characters in length. + + const kwsys_stl::string testNewLongDir( + TEST_SYSTEMTOOLS_BINARY_DIR "/" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "01234567890123"); + const kwsys_stl::string testNewLongFile(testNewLongDir + "/" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "0123456789.txt"); + + if (!kwsys::SystemTools::MakeDirectory(testNewLongDir.c_str())) + { + kwsys_ios::cerr + << "Problem with MakeDirectory for: " + << testNewLongDir << kwsys_ios::endl; + res = false; + } + + if (!kwsys::SystemTools::Touch(testNewLongFile.c_str(), true)) + { + kwsys_ios::cerr + << "Problem with Touch for: " + << testNewLongFile << kwsys_ios::endl; + res = false; + } + + if (!kwsys::SystemTools::RemoveFile(testNewLongFile.c_str())) + { + kwsys_ios::cerr + << "Problem with RemoveFile: " + << testNewLongFile << kwsys_ios::endl; + res = false; + } + + kwsys::SystemTools::Touch(testNewLongFile.c_str(), true); + if (!kwsys::SystemTools::RemoveADirectory(testNewLongDir.c_str())) + { + kwsys_ios::cerr + << "Problem with RemoveADirectory for: " + << testNewLongDir << kwsys_ios::endl; + res = false; } +#endif return res; } @@ -138,7 +230,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with CapitalizedWords " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << '"' << test << '"' << kwsys_ios::endl; res = false; } @@ -148,7 +240,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with UnCapitalizedWords " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << '"' << test << '"' << kwsys_ios::endl; res = false; } @@ -158,7 +250,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with AddSpaceBetweenCapitalizedWords " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << '"' << test << '"' << kwsys_ios::endl; res = false; } @@ -168,7 +260,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with AppendStrings " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A\" \" Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres; @@ -179,7 +271,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with AppendStrings " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had\" \" A \" \"Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres; @@ -188,7 +280,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with CountChar " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } @@ -198,7 +290,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with RemoveChars " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres; @@ -209,7 +301,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with RemoveCharsButUpperHex " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres; @@ -221,7 +313,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with ReplaceChars " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres2; @@ -231,7 +323,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with StringStartsWith " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } @@ -240,7 +332,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with StringEndsWith " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } @@ -249,7 +341,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with DuplicateString " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } delete [] cres; @@ -260,7 +352,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with CropString " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; res = false; } @@ -271,16 +363,124 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with Split " - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; - res = false; + << "\"Mary Had A Little Lamb.\"" << kwsys_ios::endl; + res = false; + } + +#ifdef _WIN32 + if (kwsys::SystemTools::ConvertToWindowsExtendedPath + ("L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << kwsys_ios::endl; + res = false; } + if (kwsys::SystemTools::ConvertToWindowsExtendedPath + ("L:/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"L:/Local Mojo/Hex Power Pack/Iffy Voodoo\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath + ("\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath + ("//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("//") != + L"//") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"//\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\") != + L"\\\\.\\") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"\\\\.\\\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X") != + L"\\\\.\\X") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"\\\\.\\X\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X:") != + L"\\\\?\\X:") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"\\\\.\\X:\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("\\\\.\\X:\\") != + L"\\\\?\\X:\\") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"\\\\.\\X:\\\"" + << kwsys_ios::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsExtendedPath("NUL") != + L"\\\\.\\NUL") + { + kwsys_ios::cerr + << "Problem with ConvertToWindowsExtendedPath " + << "\"NUL\"" + << kwsys_ios::endl; + res = false; + } + +#endif + if (kwsys::SystemTools::ConvertToWindowsOutputPath ("L://Local Mojo/Hex Power Pack/Iffy Voodoo") != "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"") { kwsys_ios::cerr << "Problem with ConvertToWindowsOutputPath " + << "\"L://Local Mojo/Hex Power Pack/Iffy Voodoo\"" << kwsys_ios::endl; res = false; } @@ -291,6 +491,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with ConvertToWindowsOutputPath " + << "\"//grayson/Local Mojo/Hex Power Pack/Iffy Voodoo\"" << kwsys_ios::endl; res = false; } @@ -301,6 +502,7 @@ static bool CheckStringOperations() { kwsys_ios::cerr << "Problem with ConvertToUnixOutputPath " + << "\"//Local Mojo/Hex Power Pack/Iffy Voodoo\"" << kwsys_ios::endl; res = false; } @@ -308,14 +510,17 @@ static bool CheckStringOperations() int targc; char **targv; kwsys::SystemTools::ConvertWindowsCommandLineToUnixArguments - ("\"Local Mojo\\Voodoo.asp\" -CastHex \"D:\\My Secret Mojo\\Voodoo.mp3\"", &targc, &targv); + ("\"Local Mojo\\Voodoo.asp\" -CastHex \"D:\\My Secret Mojo\\Voodoo.mp3\"", + &targc, &targv); if (targc != 4 || strcmp(targv[1],"Local Mojo\\Voodoo.asp") || strcmp(targv[2],"-CastHex") || strcmp(targv[3],"D:\\My Secret Mojo\\Voodoo.mp3")) { kwsys_ios::cerr << "Problem with ConvertWindowsCommandLineToUnixArguments" - << TEST_SYSTEMTOOLS_SRC_FILE << kwsys_ios::endl; + << "\'\"Local Mojo\\Voodoo.asp\" " + << "-CastHex \"D:\\My Secret Mojo\\Voodoo.mp3\"\'" + << kwsys_ios::endl; res = false; } for (;targc >=0; --targc) diff --git a/Source/kwsys/testSystemTools.h.in b/Source/kwsys/testSystemTools.h.in index 4b94bb6..66f0f72 100644 --- a/Source/kwsys/testSystemTools.h.in +++ b/Source/kwsys/testSystemTools.h.in @@ -14,7 +14,8 @@ #define EXECUTABLE_OUTPUT_PATH "@CMAKE_CURRENT_BINARY_DIR@" -#define TEST_SYSTEMTOOLS_BIN_FILE "@TEST_SYSTEMTOOLS_BIN_FILE@" -#define TEST_SYSTEMTOOLS_SRC_FILE "@TEST_SYSTEMTOOLS_SRC_FILE@" +#define TEST_SYSTEMTOOLS_SOURCE_DIR "@TEST_SYSTEMTOOLS_SOURCE_DIR@" +#define TEST_SYSTEMTOOLS_BINARY_DIR "@TEST_SYSTEMTOOLS_BINARY_DIR@" +#cmakedefine KWSYS_TEST_SYSTEMTOOLS_LONG_PATHS #endif diff --git a/Tests/CTestTestMemcheck/CMakeLists.txt b/Tests/CTestTestMemcheck/CMakeLists.txt index 8984463..f470835 100644 --- a/Tests/CTestTestMemcheck/CMakeLists.txt +++ b/Tests/CTestTestMemcheck/CMakeLists.txt @@ -103,6 +103,19 @@ unset(CTEST_EXTRA_CONFIG) unset(CTEST_EXTRA_CODE) unset(CMAKELISTS_EXTRA_CODE) +# add ThreadSanitizer test +set(CTEST_EXTRA_CODE +"set(CTEST_MEMORYCHECK_COMMAND_OPTIONS \"report_bugs=1 history_size=5 exitcode=55\") +") + +set(CMAKELISTS_EXTRA_CODE +"add_test(NAME TestSan COMMAND \"${CMAKE_COMMAND}\" +-P \"${CMAKE_CURRENT_SOURCE_DIR}/testThreadSanitizer.cmake\") +") +gen_mc_test_internal(DummyThreadSanitizer "" -DMEMCHECK_TYPE=ThreadSanitizer) +set(CMAKELISTS_EXTRA_CODE ) +set(CTEST_EXTRA_CODE) + gen_mc_test(DummyPurify "\${PSEUDO_PURIFY}") gen_mc_test(DummyValgrind "\${PSEUDO_VALGRIND}") gen_mc_test(DummyBC "\${PSEUDO_BC}") @@ -189,6 +202,11 @@ set_tests_properties(CTestTestMemcheckDummyValgrindTwoTargets PROPERTIES PASS_REGULAR_EXPRESSION "\nMemory check project ${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets\n.*\n *Start 1: RunCMake\n(.*\n)?Memory check command: .* \"--log-file=${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets/Testing/Temporary/MemoryChecker.1.log\" \"-q\".*\n *Start 2: RunCMakeAgain\n(.*\n)?Memory check command: .* \"--log-file=${CTEST_ESCAPED_CMAKE_CURRENT_BINARY_DIR}/DummyValgrindTwoTargets/Testing/Temporary/MemoryChecker.2.log\" \"-q\".*\n") +set_tests_properties(CTestTestMemcheckDummyThreadSanitizer PROPERTIES + PASS_REGULAR_EXPRESSION + ".*Memory checking results:.*data race.* - 1.*data race on vptr .ctor/dtor vs virtual call. - 1.*heap-use-after-free - 1.*thread leak - 1.*destroy of a locked mutex - 1.*double lock of a mutex - 1.*unlock of an unlocked mutex .or by a wrong thread. - 1.*read lock of a write locked mutex - 1.*read unlock of a write locked mutex - 1.*signal-unsafe call inside of a signal - 1.*signal handler spoils errno - 1.*lock-order-inversion .potential deadlock. - 1.*") + + # Xcode 2.x forgets to create the output directory before linking # the individual architectures. if(CMAKE_OSX_ARCHITECTURES AND XCODE AND NOT "${XCODE_VERSION}" MATCHES "^[^12]") diff --git a/Tests/CTestTestMemcheck/test.cmake.in b/Tests/CTestTestMemcheck/test.cmake.in index 471e5a5..87195c5 100644 --- a/Tests/CTestTestMemcheck/test.cmake.in +++ b/Tests/CTestTestMemcheck/test.cmake.in @@ -15,6 +15,7 @@ set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") set(CTEST_MEMORYCHECK_COMMAND "@CHECKER_COMMAND@") +set(CTEST_MEMORYCHECK_TYPE "${MEMCHECK_TYPE}") @CTEST_EXTRA_CODE@ diff --git a/Tests/CTestTestMemcheck/testThreadSanitizer.cmake b/Tests/CTestTestMemcheck/testThreadSanitizer.cmake new file mode 100644 index 0000000..d591931 --- /dev/null +++ b/Tests/CTestTestMemcheck/testThreadSanitizer.cmake @@ -0,0 +1,47 @@ +# this file simulates a program that has been built with thread sanitizer +# options + +message("TSAN_OPTIONS = [$ENV{TSAN_OPTIONS}]") +string(REGEX REPLACE ".*log_path=\"([^\"]*)\".*" "\\1" LOG_FILE "$ENV{TSAN_OPTIONS}") +message("LOG_FILE=[${LOG_FILE}]") + +set(error_types + "data race" + "data race on vptr (ctor/dtor vs virtual call)" + "heap-use-after-free" + "thread leak" + "destroy of a locked mutex" + "double lock of a mutex" + "unlock of an unlocked mutex (or by a wrong thread)" + "read lock of a write locked mutex" + "read unlock of a write locked mutex" + "signal-unsafe call inside of a signal" + "signal handler spoils errno" + "lock-order-inversion (potential deadlock)" + ) + +# clear the log file +file(REMOVE "${LOG_FILE}.2343") + +# create an error of each type of thread santizer +# these names come from tsan_report.cc in llvm +foreach(error_type ${error_types} ) + + file(APPEND "${LOG_FILE}.2343" +"================== +WARNING: ThreadSanitizer: ${error_type} (pid=27978) + Write of size 4 at 0x7fe017ce906c by thread T1: + #0 Thread1 ??:0 (exe+0x000000000bb0) + #1 <null> <null>:0 (libtsan.so.0+0x00000001b279) + + Previous write of size 4 at 0x7fe017ce906c by main thread: + #0 main ??:0 (exe+0x000000000c3c) + + Thread T1 (tid=27979, running) created by main thread at: + #0 <null> <null>:0 (libtsan.so.0+0x00000001ed7b) + #1 main ??:0 (exe+0x000000000c2c) + +SUMMARY: ThreadSanitizer: ${error_type} ??:0 Thread1 +================== +") +endforeach() diff --git a/Tests/ObjectLibrary/A/CMakeLists.txt b/Tests/ObjectLibrary/A/CMakeLists.txt index 55778ea..c24c5ed 100644 --- a/Tests/ObjectLibrary/A/CMakeLists.txt +++ b/Tests/ObjectLibrary/A/CMakeLists.txt @@ -1,18 +1,26 @@ project(ObjectLibraryA C) # Add -fPIC so objects can be used in shared libraries. -# TODO: Need property for this. -if(CMAKE_SHARED_LIBRARY_C_FLAGS AND NOT WATCOM) - set(CMAKE_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS} ${CMAKE_C_FLAGS}") -endif() +set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_definitions(-DA_DEF) add_custom_command( OUTPUT a1.c - DEPENDS a1.c.in + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/a1.c.in COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/a1.c.in ${CMAKE_CURRENT_BINARY_DIR}/a1.c ) -add_library(A OBJECT a1.c a2.c) +# Remove the custom command output to be sure it runs in an +# incremental test. Skip this on VS 6 because it sometimes +# re-runs CMake after the custom command runs. +if(NOT CMAKE_GENERATOR STREQUAL "Visual Studio 6") + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/a.cmake) +endif() +add_custom_command( + OUTPUT a.cmake + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/a.cmake + ) + +add_library(A OBJECT a1.c a2.c a.cmake) target_include_directories(A PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/ObjectLibrary/B/CMakeLists.txt b/Tests/ObjectLibrary/B/CMakeLists.txt index a567f96..2158084 100644 --- a/Tests/ObjectLibrary/B/CMakeLists.txt +++ b/Tests/ObjectLibrary/B/CMakeLists.txt @@ -5,10 +5,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Visual Studio 6") endif() # Add -fPIC so objects can be used in shared libraries. -# TODO: Need property for this. -if(CMAKE_SHARED_LIBRARY_C_FLAGS AND NOT WATCOM) - set(CMAKE_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS} ${CMAKE_C_FLAGS}") -endif() +set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_library(B OBJECT b1.c b2.c) target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt index 75c34d8..e9f553e 100644 --- a/Tests/ObjectLibrary/CMakeLists.txt +++ b/Tests/ObjectLibrary/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(Cshared SHARED c.c $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:Bexport>) add_executable(UseCshared main.c) set_property(TARGET UseCshared PROPERTY COMPILE_DEFINITIONS SHARED_C) target_link_libraries(UseCshared Cshared) +add_custom_command(TARGET UseCshared POST_BUILD COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/A/a.cmake) add_executable(UseCinternal main.c c.c $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:B>) diff --git a/Tests/RunCMake/ObjectLibrary/BadObjSource1-stderr.txt b/Tests/RunCMake/ObjectLibrary/BadObjSource1-stderr.txt index b31225b..a09552b 100644 --- a/Tests/RunCMake/ObjectLibrary/BadObjSource1-stderr.txt +++ b/Tests/RunCMake/ObjectLibrary/BadObjSource1-stderr.txt @@ -3,6 +3,7 @@ CMake Error at BadObjSource1.cmake:1 \(add_library\): bad.def - but may contain only headers and sources that compile. + but may contain only sources that compile, header files, and other files + that would not affect linking of a normal library. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/ObjectLibrary/BadObjSource2-stderr.txt b/Tests/RunCMake/ObjectLibrary/BadObjSource2-stderr.txt index 906cf0b..b91ffd0 100644 --- a/Tests/RunCMake/ObjectLibrary/BadObjSource2-stderr.txt +++ b/Tests/RunCMake/ObjectLibrary/BadObjSource2-stderr.txt @@ -3,6 +3,7 @@ CMake Error at BadObjSource2.cmake:1 \(add_library\): bad.obj - but may contain only headers and sources that compile. + but may contain only sources that compile, header files, and other files + that would not affect linking of a normal library. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) |