summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2009-02-11 20:18:08 (GMT)
committerBrad King <brad.king@kitware.com>2009-02-11 20:18:08 (GMT)
commit7497fd575d0bd12e07a6145715a63ce8330a454f (patch)
treece2b499346edca70b285247755fe03fb62e1941d
parentdbe135f20e81cbe6e92888d86e2bc7e95f0b91dd (diff)
downloadCMake-7497fd575d0bd12e07a6145715a63ce8330a454f.zip
CMake-7497fd575d0bd12e07a6145715a63ce8330a454f.tar.gz
CMake-7497fd575d0bd12e07a6145715a63ce8330a454f.tar.bz2
ENH: Teach CTest dashboard builds to use launchers
This defines a 'UseLaunchers' CTest configuration option. When enabled, CTest skips log scraping from the Build step output. Instead it defines the environment variable CTEST_LAUNCH_LOGS to a log directory during the build. After the build it looks for error-*.xml and warning-*.xml files containing fragments for inclusion in Build.xml and submission. This is useful in conjuction with 'ctest --launch' and the RULE_LAUNCH_* properties to get reliable, highly-granular build failure reports.
-rw-r--r--Source/CTest/cmCTestBuildHandler.cxx235
-rw-r--r--Source/CTest/cmCTestBuildHandler.h12
2 files changed, 245 insertions, 2 deletions
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index 76d9425..c8e2185 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -24,9 +24,11 @@
#include "cmGlobalGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmXMLSafe.h"
+#include "cmFileTimeComparison.h"
//#include <cmsys/RegularExpression.hxx>
#include <cmsys/Process.h>
+#include <cmsys/Directory.hxx>
// used for sleep
#ifdef _WIN32
@@ -185,6 +187,7 @@ cmCTestBuildHandler::cmCTestBuildHandler()
this->LastErrorOrWarning = this->ErrorsAndWarnings.end();
+ this->UseCTestLaunch = false;
}
//----------------------------------------------------------------------
@@ -197,6 +200,8 @@ void cmCTestBuildHandler::Initialize()
this->CustomErrorExceptions.clear();
this->CustomWarningMatches.clear();
this->CustomWarningExceptions.clear();
+ this->ReallyCustomWarningMatches.clear();
+ this->ReallyCustomWarningExceptions.clear();
this->ErrorWarningFileLineRegex.clear();
this->ErrorMatchRegex.clear();
@@ -227,6 +232,8 @@ void cmCTestBuildHandler::Initialize()
this->MaxErrors = 50;
this->MaxWarnings = 50;
+
+ this->UseCTestLaunch = false;
}
//----------------------------------------------------------------------
@@ -246,6 +253,20 @@ void cmCTestBuildHandler::PopulateCustomVectors(cmMakefile *mf)
this->CTest->PopulateCustomInteger(mf,
"CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS",
this->MaxWarnings);
+
+ // Record the user-specified custom warning rules.
+ if(const char* customWarningMatchers =
+ mf->GetDefinition("CTEST_CUSTOM_WARNING_MATCH"))
+ {
+ cmSystemTools::ExpandListArgument(customWarningMatchers,
+ this->ReallyCustomWarningMatches);
+ }
+ if(const char* customWarningExceptions =
+ mf->GetDefinition("CTEST_CUSTOM_WARNING_EXCEPTION"))
+ {
+ cmSystemTools::ExpandListArgument(customWarningExceptions,
+ this->ReallyCustomWarningExceptions);
+ }
}
//----------------------------------------------------------------------
@@ -306,6 +327,10 @@ int cmCTestBuildHandler::ProcessHandler()
return -1;
}
+ std::string const& useLaunchers =
+ this->CTest->GetCTestConfiguration("UseLaunchers");
+ this->UseCTestLaunch = cmSystemTools::IsOn(useLaunchers.c_str());
+
// Create a last build log
cmGeneratedFileStream ofs;
double elapsed_time_start = cmSystemTools::GetTime();
@@ -467,7 +492,14 @@ int cmCTestBuildHandler::ProcessHandler()
return -1;
}
this->GenerateXMLHeader(xofs);
- this->GenerateXMLLogScraped(xofs);
+ if(this->UseCTestLaunch)
+ {
+ this->GenerateXMLLaunched(xofs);
+ }
+ else
+ {
+ this->GenerateXMLLogScraped(xofs);
+ }
this->GenerateXMLFooter(xofs, elapsed_build_time);
// Display message about number of errors and warnings
@@ -497,6 +529,70 @@ void cmCTestBuildHandler::GenerateXMLHeader(std::ostream& os)
}
//----------------------------------------------------------------------------
+class cmCTestBuildHandler::FragmentCompare
+{
+public:
+ FragmentCompare(cmFileTimeComparison* ftc): FTC(ftc) {}
+ bool operator()(std::string const& l, std::string const& r)
+ {
+ // Order files by modification time. If comparison fails, just
+ // use lexicographic order (should not happen in our use case).
+ int result;
+ if(this->FTC->FileTimeCompare(l.c_str(), r.c_str(), &result))
+ {
+ return result < 0;
+ }
+ else
+ {
+ return l < r;
+ }
+ }
+private:
+ cmFileTimeComparison* FTC;
+};
+
+//----------------------------------------------------------------------------
+void cmCTestBuildHandler::GenerateXMLLaunched(std::ostream& os)
+{
+ if(this->CTestLaunchDir.empty())
+ {
+ return;
+ }
+
+ // Sort XML fragments in chronological order.
+ cmFileTimeComparison ftc;
+ FragmentCompare fragmentCompare(&ftc);
+ typedef std::set<cmStdString, FragmentCompare> Fragments;
+ Fragments fragments(fragmentCompare);
+
+ // Identify fragments on disk.
+ cmsys::Directory launchDir;
+ launchDir.Load(this->CTestLaunchDir.c_str());
+ unsigned long n = launchDir.GetNumberOfFiles();
+ for(unsigned long i=0; i < n; ++i)
+ {
+ const char* fname = launchDir.GetFile(i);
+ if(this->IsLaunchedErrorFile(fname))
+ {
+ fragments.insert(this->CTestLaunchDir + "/" + fname);
+ ++this->TotalErrors;
+ }
+ else if(this->IsLaunchedWarningFile(fname))
+ {
+ fragments.insert(this->CTestLaunchDir + "/" + fname);
+ ++this->TotalWarnings;
+ }
+ }
+
+ // Copy the fragments into the final XML file.
+ for(Fragments::const_iterator fi = fragments.begin();
+ fi != fragments.end(); ++fi)
+ {
+ this->GenerateXMLLaunchedFragment(os, fi->c_str());
+ }
+}
+
+//----------------------------------------------------------------------------
void cmCTestBuildHandler::GenerateXMLLogScraped(std::ostream& os)
{
std::vector<cmCTestBuildErrorWarning>& ew = this->ErrorsAndWarnings;
@@ -612,11 +708,135 @@ void cmCTestBuildHandler::GenerateXMLFooter(std::ostream& os,
this->CTest->EndXML(os);
}
+//----------------------------------------------------------------------------
+void cmCTestBuildHandler::GenerateXMLLaunchedFragment(std::ostream& os,
+ const char* fname)
+{
+ std::ifstream fin(fname, std::ios::in | std::ios::binary);
+ std::string line;
+ while(cmSystemTools::GetLineFromStream(fin, line))
+ {
+ os << line << "\n";
+ }
+}
+
+//----------------------------------------------------------------------------
+bool cmCTestBuildHandler::IsLaunchedErrorFile(const char* fname)
+{
+ // error-{hash}.xml
+ return (strncmp(fname, "error-", 6) == 0 &&
+ strcmp(fname+strlen(fname)-4, ".xml") == 0);
+}
+
+//----------------------------------------------------------------------------
+bool cmCTestBuildHandler::IsLaunchedWarningFile(const char* fname)
+{
+ // warning-{hash}.xml
+ return (strncmp(fname, "warning-", 8) == 0 &&
+ strcmp(fname+strlen(fname)-4, ".xml") == 0);
+}
+
//######################################################################
//######################################################################
//######################################################################
//######################################################################
+//----------------------------------------------------------------------------
+class cmCTestBuildHandler::LaunchHelper
+{
+public:
+ LaunchHelper(cmCTestBuildHandler* handler);
+ ~LaunchHelper();
+private:
+ cmCTestBuildHandler* Handler;
+ cmCTest* CTest;
+
+ void WriteScrapeMatchers();
+ void WriteScrapeMatchers(const char* purpose,
+ std::vector<std::string> const& matchers);
+};
+
+//----------------------------------------------------------------------------
+cmCTestBuildHandler::LaunchHelper::LaunchHelper(cmCTestBuildHandler* handler):
+ Handler(handler), CTest(handler->CTest)
+{
+ std::string tag = this->CTest->GetCurrentTag();
+ if(tag.empty())
+ {
+ // This is not for a dashboard submission, so there is no XML.
+ // Skip enabling the launchers.
+ this->Handler->UseCTestLaunch = false;
+ }
+ else
+ {
+ // Compute a directory in which to store launcher fragments.
+ std::string& launchDir = this->Handler->CTestLaunchDir;
+ launchDir = this->CTest->GetBinaryDir();
+ launchDir += "/Testing/";
+ launchDir += tag;
+ launchDir += "/Build";
+
+ // Clean out any existing launcher fragments.
+ cmSystemTools::RemoveADirectory(launchDir.c_str());
+
+ if(this->Handler->UseCTestLaunch)
+ {
+ // Enable launcher fragments.
+ cmSystemTools::MakeDirectory(launchDir.c_str());
+ this->WriteScrapeMatchers();
+ std::string launchEnv = "CTEST_LAUNCH_LOGS=";
+ launchEnv += launchDir;
+ cmSystemTools::PutEnv(launchEnv.c_str());
+ }
+ }
+
+ // If not using launchers, make sure they passthru.
+ if(!this->Handler->UseCTestLaunch)
+ {
+ cmSystemTools::UnsetEnv("CTEST_LAUNCH_LOGS");
+ }
+}
+
+//----------------------------------------------------------------------------
+cmCTestBuildHandler::LaunchHelper::~LaunchHelper()
+{
+ if(this->Handler->UseCTestLaunch)
+ {
+ cmSystemTools::UnsetEnv("CTEST_LAUNCH_LOGS");
+ }
+}
+
+//----------------------------------------------------------------------------
+void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers()
+{
+ this->WriteScrapeMatchers("Warning",
+ this->Handler->ReallyCustomWarningMatches);
+ this->WriteScrapeMatchers("WarningSuppress",
+ this->Handler->ReallyCustomWarningExceptions);
+}
+
+//----------------------------------------------------------------------------
+void
+cmCTestBuildHandler::LaunchHelper
+::WriteScrapeMatchers(const char* purpose,
+ std::vector<std::string> const& matchers)
+{
+ if(matchers.empty())
+ {
+ return;
+ }
+ std::string fname = this->Handler->CTestLaunchDir;
+ fname += "/Custom";
+ fname += purpose;
+ fname += ".txt";
+ cmGeneratedFileStream fout(fname.c_str());
+ for(std::vector<std::string>::const_iterator mi = matchers.begin();
+ mi != matchers.end(); ++mi)
+ {
+ fout << *mi << "\n";
+ }
+}
+
//----------------------------------------------------------------------
int cmCTestBuildHandler::RunMakeCommand(const char* command,
int* retVal, const char* dir, int timeout, std::ofstream& ofs)
@@ -645,6 +865,10 @@ int cmCTestBuildHandler::RunMakeCommand(const char* command,
}
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl);
+ // Optionally use make rule launchers to record errors and warnings.
+ LaunchHelper launchHelper(this);
+ static_cast<void>(launchHelper);
+
// Now create process object
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
@@ -662,7 +886,8 @@ int cmCTestBuildHandler::RunMakeCommand(const char* command,
cmCTestLog(this->CTest, HANDLER_OUTPUT,
" Each symbol represents " << tick_len << " bytes of output."
<< std::endl
- << " '!' represents an error and '*' a warning." << std::endl
+ << (this->UseCTestLaunch? "" :
+ " '!' represents an error and '*' a warning.\n")
<< " " << std::flush);
// Initialize building structures
@@ -939,6 +1164,12 @@ void cmCTestBuildHandler::ProcessBuffer(const char* data, int length,
//----------------------------------------------------------------------
int cmCTestBuildHandler::ProcessSingleLine(const char* data)
{
+ if(this->UseCTestLaunch)
+ {
+ // No log scraping when using launchers.
+ return b_REGULAR_LINE;
+ }
+
cmCTestLog(this->CTest, DEBUG, "Line: [" << data << "]" << std::endl);
std::vector<cmsys::RegularExpression>::iterator it;
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h
index a922977..42a1551 100644
--- a/Source/CTest/cmCTestBuildHandler.h
+++ b/Source/CTest/cmCTestBuildHandler.h
@@ -87,8 +87,12 @@ private:
// generate the XML output
void GenerateXMLHeader(std::ostream& os);
+ void GenerateXMLLaunched(std::ostream& os);
void GenerateXMLLogScraped(std::ostream& os);
void GenerateXMLFooter(std::ostream& os, double elapsed_build_time);
+ void GenerateXMLLaunchedFragment(std::ostream& os, const char* fname);
+ bool IsLaunchedErrorFile(const char* fname);
+ bool IsLaunchedWarningFile(const char* fname);
std::string StartBuild;
std::string EndBuild;
@@ -99,6 +103,8 @@ private:
std::vector<cmStdString> CustomErrorExceptions;
std::vector<cmStdString> CustomWarningMatches;
std::vector<cmStdString> CustomWarningExceptions;
+ std::vector<std::string> ReallyCustomWarningMatches;
+ std::vector<std::string> ReallyCustomWarningExceptions;
std::vector<cmCTestCompileErrorWarningRex> ErrorWarningFileLineRegex;
std::vector<cmsys::RegularExpression> ErrorMatchRegex;
@@ -137,6 +143,12 @@ private:
int MaxErrors;
int MaxWarnings;
+
+ bool UseCTestLaunch;
+ std::string CTestLaunchDir;
+ class LaunchHelper;
+ friend class LaunchHelper;
+ class FragmentCompare;
};
#endif