diff --git a/Source/ctest.cxx b/Source/ctest.cxx index 85d5a2113..4a79b5e12 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -18,6 +18,8 @@ #include "cmRegularExpression.h" #include "cmSystemTools.h" +#include + #include #include @@ -42,6 +44,70 @@ static std::string CurrentTime() return ::CleanString(ctime(¤ttime)); } +static const char* cmCTestErrorMatches[] = { + "^[Bb]us [Ee]rror", + "^[Ss]egmentation [Vv]iolation", + "^[Ss]egmentation [Ff]ault", + "([^ :]+):([0-9]+): ([^ \\t])", + "([^:]+): error[ \\t]*[0-9]+[ \\t]*:", + "^Error ([0-9]+):", + "^Error ", + "^\"[^\"]+\", line [0-9]+: [^Ww]", + "^cc[^C]*CC: ERROR File = ([^,]+), Line = ([0-9]+)", + "^ld([^:])*:([ \\t])*ERROR([^:])*:", + "^ild:([ \\t])*\\(undefined symbol\\)", + "([^ :]+) : (error|fatal error|catastrophic error)", + "([^:]+): (Error:|error|undefined reference|multiply defined)", + "([^:]+)\\(([^\\)]+)\\) : (error|fatal error|catastrophic error)", + "^fatal error C[0-9]+:", + ": syntax error ", + "^collect2: ld returned 1 exit status", + "Unsatisfied symbols:", + "Undefined symbols:", + "^Undefined[ \\t]+first referenced", + "^CMake Error:", + ":[ \\t]cannot find", + 0 +}; + +static const char* cmCTestErrorExceptions[] = { + 0 +}; + +static const char* cmCTestWarningMatches[] = { + "([^ :]+):([0-9]+): warning:", + "^cc[^C]*CC: WARNING File = ([^,]+), Line = ([0-9]+)", + "^ld([^:])*:([ \\t])*WARNING([^:])*:", + "([^:]+): warning ([0-9]+):", + "^\"[^\"]+\", line [0-9]+: [Ww]arning", + "([^:]+): warning[ \\t]*[0-9]+[ \\t]*:", + "^Warning ([0-9]+):", + "^Warning ", + "([^ :]+) : warning", + "([^:]+): warning", + 0 +}; + +static const char* cmCTestWarningExceptions[] = { + "/usr/openwin/include/X11/Xlib\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration", + "/usr/openwin/include/X11/Xutil\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration", + "/usr/openwin/include/X11/XResource\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration", + "WARNING 84 :", + "WARNING 47 :", + "makefile:", + "Makefile:", + "warning: Clock skew detected. Your build may be incomplete.", + "/usr/openwin/include/GL/[^:]+:", + "bind_at_load", + "XrmQGetResource", + "IceFlush", + "warning LNK4089: all references to .GDI32.dll. discarded by .OPT:REF", + "warning LNK4089: all references to .USER32.dll. discarded by .OPT:REF", + "ld32: WARNING 85: definition of dataKey in", + 0 +}; + + bool TryExecutable(const char *dir, const char *file, std::string *fullPath, const char *subdir) { @@ -357,8 +423,200 @@ void ctest::BuildDirectory() } // To do: // Add parsing of output for errors and warnings. + + cmCTestBuildErrorWarning cerw; + char* coutput = new char[ output.size() + 1]; + memcpy(coutput, output.c_str(), output.size()); + int cc; + + std::vector lines; + + // Lines are marked: + // 0 - nothing + // 1 - error + // > 1 - warning + std::vector markedLines; + std::istrstream istr(coutput); + while(istr) + { + char buffer[1024]; + std::string line; + while ( istr ) + { + buffer[0] = 0; + istr.getline(buffer, 1023); + buffer[1023] = 0; + line += buffer; + if ( strlen(buffer) < 1023 ) + { + break; + } + } + lines.push_back(line); + markedLines.push_back(0); + //std::cout << "Line [" << line << "]" << std::endl; + } + + // Errors + for ( cc = 0; cmCTestErrorMatches[cc]; cc ++ ) + { + cmRegularExpression re(cmCTestErrorMatches[cc]); + std::vector::size_type kk; + for ( kk = 0; kk < lines.size(); kk ++ ) + { + if ( re.find(lines[kk]) ) + { + markedLines[kk] = 1; + } + } + } + // Warnings + for ( cc = 0; cmCTestWarningMatches[cc]; cc ++ ) + { + cmRegularExpression re(cmCTestWarningMatches[cc]); + std::vector::size_type kk; + for ( kk = 0; kk < lines.size(); kk ++ ) + { + if ( re.find(lines[kk]) ) + { + markedLines[kk] += 2; + } + } + } + for ( cc = 0; cmCTestErrorExceptions[cc]; cc ++ ) + { + cmRegularExpression re(cmCTestErrorExceptions[cc]); + std::vector::size_type kk; + for ( kk =0; kk < markedLines.size(); kk ++ ) + { + if ( markedLines[cc] == 1 ) + { + if ( re.find(lines[kk]) ) + { + markedLines[cc] = 0; + } + } + } + } + for ( cc = 0; cmCTestWarningExceptions[cc]; cc ++ ) + { + cmRegularExpression re(cmCTestWarningExceptions[cc]); + std::vector::size_type kk; + for ( kk =0; kk < markedLines.size(); kk ++ ) + { + if ( markedLines[cc] > 1 ) + { + if ( re.find(lines[kk]) ) + { + markedLines[cc] = 0; + } + } + } + } + std::vector errorsWarnings; + + std::vector::size_type kk; + cmCTestBuildErrorWarning errorwarning; + for ( kk =0; kk < markedLines.size(); kk ++ ) + { + bool found = false; + if ( markedLines[kk] == 1 ) + { + std::cout << "Error: " << lines[kk] << std::endl; + errorwarning.m_Error = true; + found = true; + } + else if ( markedLines[kk] > 1 ) + { + std::cout << "Warning: " << lines[kk] << std::endl; + errorwarning.m_Error = false; + found = true; + } + if ( found ) + { + errorwarning.m_LogLine = kk; + errorwarning.m_Text = lines[kk]; + errorwarning.m_PreContext = ""; + errorwarning.m_PostContext = ""; + std::vector::size_type jj; + for ( jj = kk; + jj > 0 && jj > kk - 6 /* && markedLines[jj] == 0 */; + jj -- ); + for (; jj < kk; jj ++ ) + { + errorwarning.m_PreContext += lines[jj] + "\n"; + } + for ( jj = kk+1; + jj < lines.size() && jj < kk + 6 /* && markedLines[jj] == 0*/; + jj ++ ) + { + errorwarning.m_PostContext += lines[jj] + "\n"; + } + errorsWarnings.push_back(errorwarning); + } + } + /* + this->GenerateDartBuildOutput(std::cout, + errorsWarnings); + */ } + +void ctest::GenerateDartBuildOutput(std::ostream& os, + std::vector ew) +{ + time_t tctime = time(0); + struct tm *lctime = gmtime(&tctime); + char datestring[100]; + sprintf(datestring, "%4d%02d%02d-%d%d", + lctime->tm_year + 1900, + lctime->tm_mon, + lctime->tm_mday, + lctime->tm_hour, + lctime->tm_min); + + os << "\n" + << "\n" + << "\n" + << " " << ::CurrentTime() << "\n" + << " " << m_DartConfiguration["MakeCommand"] + << "" << std::endl; + + std::vector::iterator it; + for ( it = ew.begin(); it != ew.end(); it++ ) + { + cmCTestBuildErrorWarning *cm = &(*it); + os << " <" << (cm->m_Error ? "Error" : "Warning") << ">\n" + << " " << cm->m_LogLine << "\n" + << " " << cm->m_Text << "" << std::endl; + if ( cm->m_SourceFile.size() > 0 ) + { + os << " " << cm->m_SourceFile << "" + << std::endl; + } + if ( cm->m_SourceFileTail.size() > 0 ) + { + os << " " << cm->m_SourceFileTail + << "" << std::endl; + } + if ( cm->m_LineNumber >= 0 ) + { + os << " " << cm->m_LineNumber + << "" << std::endl; + } + os << " " << cm->m_PreContext << "\n" + << " " << cm->m_PostContext << "\n" + << " m_Error ? "Error" : "Warning") << ">" + << std::endl; + } + os << " \n \n" + << " " << ::CurrentTime() << "\n" + << "\n" + << "" << std::endl; +} + void ctest::ProcessDirectory(std::vector &passed, std::vector &failed) { @@ -547,13 +805,12 @@ void ctest::GenerateDartOutput(std::ostream& os) lctime->tm_hour, lctime->tm_min); - os << "Try to create dart output file" << std::endl; os << "\n" << "\n" << "\n" - << " " << ::CurrentTime() << " \n" + << " " << ::CurrentTime() << "\n" << " \n"; tm_TestResultsVector::size_type cc; for ( cc = 0; cc < m_TestResults.size(); cc ++ ) diff --git a/Source/ctest.h b/Source/ctest.h index 23c59b6d7..34f6858b6 100644 --- a/Source/ctest.h +++ b/Source/ctest.h @@ -60,11 +60,6 @@ public: void ProcessDirectory(std::vector &passed, std::vector &failed); - /** - * Generate the Dart compatible output - */ - void GenerateDartOutput(std::ostream& os); - /** * Find the executable for a test */ @@ -115,6 +110,18 @@ private: std::string m_Output; }; + struct cmCTestBuildErrorWarning + { + bool m_Error; + int m_LogLine; + std::string m_Text; + std::string m_SourceFile; + std::string m_SourceFileTail; + int m_LineNumber; + std::string m_PreContext; + std::string m_PostContext; + }; + typedef std::vector tm_TestResultsVector; typedef std::map tm_DartConfigurationMap; @@ -122,5 +129,14 @@ private: std::string m_ToplevelPath; tm_DartConfigurationMap m_DartConfiguration; int m_Tests[LAST_TEST]; + + + /** + * Generate the Dart compatible output + */ + void GenerateDartOutput(std::ostream& os); + void GenerateDartBuildOutput(std::ostream& os, + std::vector); + };