瀏覽代碼

ENH: Separate standard output and standard error for problematic commands

Andy Cedilnik 20 年之前
父節點
當前提交
a95a4b000d
共有 4 個文件被更改,包括 200 次插入22 次删除
  1. 36 6
      Source/CTest/cmCTestCoverageHandler.cxx
  2. 32 15
      Source/CTest/cmCTestUpdateHandler.cxx
  3. 107 0
      Source/cmCTest.cxx
  4. 25 1
      Source/cmCTest.h

+ 36 - 6
Source/CTest/cmCTestCoverageHandler.cxx

@@ -155,6 +155,15 @@ int cmCTestCoverageHandler::ProcessHandler()
   std::string binaryDir = m_CTest->GetCTestConfiguration("BuildDirectory");
   std::string gcovCommand = m_CTest->GetCTestConfiguration("CoverageCommand");
 
+  cmGeneratedFileStream ofs;
+  double elapsed_time_start = cmSystemTools::GetTime();
+  if ( !m_CTest->OpenOutputFile("Temporary", "LastCoverage.log", ofs) )
+    {
+    cmCTestLog(m_CTest, ERROR_MESSAGE, "Cannot create LastCoverage.log file" << std::endl);
+    }
+
+  ofs << "Performing coverage: " << elapsed_time_start << std::endl;
+
   cmSystemTools::ConvertToUnixSlashes(sourceDir);
   cmSystemTools::ConvertToUnixSlashes(binaryDir);
 
@@ -165,7 +174,6 @@ int cmCTestCoverageHandler::ProcessHandler()
   std::string gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
 
   cmCTestLog(m_CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
-  double elapsed_time_start = cmSystemTools::GetTime();
 
   std::string coverage_start_time = m_CTest->CurrentTime();
 
@@ -204,19 +212,26 @@ int cmCTestCoverageHandler::ProcessHandler()
     std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir + "\" \"" + *it + "\"";
     cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, command.c_str() << std::endl);
     std::string output = "";
+    std::string errors = "";
     int retVal = 0;
-    int res = cmSystemTools::RunSingleCommand(command.c_str(), &output, 
-      &retVal, tempDir.c_str(),
-      false, 0 /*m_TimeOut*/);
+    ofs << "* Run coverage for: " << fileDir.c_str() << std::endl;
+    ofs << "  Command: " << command.c_str() << std::endl;
+    int res = m_CTest->RunCommand(command.c_str(), &output, &errors,
+      &retVal, tempDir.c_str(), 0 /*m_TimeOut*/);
+
+    ofs << "  Output: " << output.c_str() << std::endl;
+    ofs << "  Errors: " << errors.c_str() << std::endl;
     if ( ! res )
       {
       cmCTestLog(m_CTest, ERROR_MESSAGE, "Problem running coverage on file: " << it->c_str() << std::endl);
+      cmCTestLog(m_CTest, ERROR_MESSAGE, "Command produced error: " << error << std::endl);
       error ++;
       continue;
       }
     if ( retVal != 0 )
       {
       cmCTestLog(m_CTest, ERROR_MESSAGE, "Coverage command returned: " << retVal << " while processing: " << it->c_str() << std::endl);
+      cmCTestLog(m_CTest, ERROR_MESSAGE, "Command produced error: " << error << std::endl);
       }
     std::vector<cmStdString> lines;
     std::vector<cmStdString>::iterator line;
@@ -232,7 +247,8 @@ int cmCTestCoverageHandler::ProcessHandler()
           file.substr(0, sourceDir.size()) == sourceDir &&
           file[sourceDir.size()] == '/' )
           {
-          cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "   produced s: " << file << std::endl);
+          cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "   produced s: " << file.c_str() << std::endl);
+          ofs << "  produced in source dir: " << file.c_str() << std::endl;
           cfile = file;
           }
         // Binary dir?
@@ -240,7 +256,8 @@ int cmCTestCoverageHandler::ProcessHandler()
           file.substr(0, binaryDir.size()) == binaryDir &&
           file[binaryDir.size()] == '/' )
           {
-          cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "   produce b: " << file << std::endl);
+          cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "   produce b: " << file.c_str() << std::endl);
+          ofs << "  produced in binary dir: " << file.c_str() << std::endl;
           cfile = file;
           }
         if ( cfile.empty() )
@@ -249,6 +266,9 @@ int cmCTestCoverageHandler::ProcessHandler()
           cmCTestLog(m_CTest, ERROR_MESSAGE, "File: [" << file << "]" << std::endl);
           cmCTestLog(m_CTest, ERROR_MESSAGE, "s: [" << file.substr(0, sourceDir.size()) << "]" << std::endl);
           cmCTestLog(m_CTest, ERROR_MESSAGE, "b: [" << file.substr(0, binaryDir.size()) << "]" << std::endl);
+          ofs << "  Something went wrong. Cannot find: " << file.c_str()
+            << " in source dir: " << sourceDir.c_str()
+            << " or binary dir: " << binaryDir.c_str() << std::endl;
           }
         }
       else if ( re2.find(line->c_str() ) )
@@ -258,6 +278,7 @@ int cmCTestCoverageHandler::ProcessHandler()
           {
           singleFileCoverageVector* vec = &totalCoverage[cfile];
           cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "   in file: " << fname << std::endl);
+          ofs << "  In file: " << fname << std::endl;
           std::ifstream ifile(fname.c_str());
           if ( ! ifile )
             {
@@ -299,6 +320,7 @@ int cmCTestCoverageHandler::ProcessHandler()
       else
         {
         cmCTestLog(m_CTest, ERROR_MESSAGE, "Unknown line: " << line->c_str() << std::endl);
+        ofs << "  Unknown line: " << line->c_str() << std::endl;
         error ++;
         }
       }
@@ -476,6 +498,14 @@ int cmCTestCoverageHandler::ProcessHandler()
     << std::setprecision(2)
     << (percent_coverage) << "%" << std::endl);
 
+  ofs << "\tCovered LOC:         " << total_tested << std::endl
+    << "\tNot covered LOC:     " << total_untested << std::endl
+    << "\tTotal LOC:           " << total_lines << std::endl
+    << "\tPercentage Coverage: " 
+    << std::setiosflags(std::ios::fixed)
+    << std::setprecision(2)
+    << (percent_coverage) << "%" << std::endl;
+
   cmSystemTools::ChangeDirectory(currentDirectory.c_str());
 
   if ( error )

+ 32 - 15
Source/CTest/cmCTestUpdateHandler.cxx

@@ -308,6 +308,7 @@ int cmCTestUpdateHandler::ProcessHandler()
   int svn_use_status = 0;
 
   std::string goutput;
+  std::string errors;
   int retVal = 0;
   bool res = true;
 
@@ -320,9 +321,13 @@ int cmCTestUpdateHandler::ProcessHandler()
       cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "* Get repository information: " << command.c_str() << std::endl);
     if ( !m_CTest->GetShowOnly() )
       {
-      res = cmSystemTools::RunSingleCommand(command.c_str(), &goutput, 
-        &retVal, sourceDirectory,
-        m_HandlerVerbose, 0 /*m_TimeOut*/);
+      ofs << "* Get repository information" << std::endl;
+      ofs << "  Command: " << command.c_str() << std::endl;
+      res = m_CTest->RunCommand(command.c_str(), &goutput, &errors,
+        &retVal, sourceDirectory, 0 /*m_TimeOut*/);
+
+      ofs << "  Output: " << goutput.c_str() << std::endl;
+      ofs << "  Errors: " << errors.c_str() << std::endl;
       if ( ofs )
         {
         ofs << "--- Update information ---" << std::endl;
@@ -373,22 +378,31 @@ int cmCTestUpdateHandler::ProcessHandler()
     case cmCTestUpdateHandler::e_CVS:
       command = updateCommand + " -z3 update " + updateOptions +
         " " + extra_update_opts;
-      res = cmSystemTools::RunSingleCommand(command.c_str(), &goutput, 
-        &retVal, sourceDirectory,
-        m_HandlerVerbose, 0 /*m_TimeOut*/);
+      ofs << "* Update repository: " << std::endl;
+      ofs << "  Command: " << command.c_str() << std::endl;
+      res = m_CTest->RunCommand(command.c_str(), &goutput, &errors,
+        &retVal, sourceDirectory, 0 /*m_TimeOut*/);
+      ofs << "  Output: " << goutput.c_str() << std::endl;
+      ofs << "  Errors: " << errors.c_str() << std::endl;
       break;
     case cmCTestUpdateHandler::e_SVN:
         {
         std::string partialOutput;
         command = updateCommand + " update " + updateOptions +
           " " + extra_update_opts;
-        bool res1 = cmSystemTools::RunSingleCommand(command.c_str(), &partialOutput, 
-          &retVal, sourceDirectory,
-          m_HandlerVerbose, 0 /*m_TimeOut*/);
+        ofs << "* Update repository: " << std::endl;
+        ofs << "  Command: " << command.c_str() << std::endl;
+        bool res1 = m_CTest->RunCommand(command.c_str(), &partialOutput, &errors,
+          &retVal, sourceDirectory, 0 /*m_TimeOut*/);
+        ofs << "  Output: " << partialOutput.c_str() << std::endl;
+        ofs << "  Errors: " << errors.c_str() << std::endl;
         command = updateCommand + " status";
-        res = cmSystemTools::RunSingleCommand(command.c_str(), &partialOutput, 
-          &retVal, sourceDirectory,
-          m_HandlerVerbose, 0 /*m_TimeOut*/);
+        ofs << "* Status repository: " << std::endl;
+        ofs << "  Command: " << command.c_str() << std::endl;
+        res = m_CTest->RunCommand(command.c_str(), &partialOutput, &errors,
+          &retVal, sourceDirectory, 0 /*m_TimeOut*/);
+        ofs << "  Output: " << partialOutput.c_str() << std::endl;
+        ofs << "  Errors: " << errors.c_str() << std::endl;
         goutput += partialOutput;
         res = res && res1;
         }
@@ -505,9 +519,12 @@ int cmCTestUpdateHandler::ProcessHandler()
           }
         cmCTestLog(m_CTest, DEBUG, "Do log: " << logcommand << std::endl);
         cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "* Get file update information: " << logcommand.c_str() << std::endl);
-        res = cmSystemTools::RunSingleCommand(logcommand.c_str(), &output, 
-          &retVal, sourceDirectory,
-          m_HandlerVerbose, 0 /*m_TimeOut*/);
+        ofs << "* Get log information for file: " << file << std::endl;
+        ofs << "  Command: " << logcommand.c_str() << std::endl;
+        res = m_CTest->RunCommand(logcommand.c_str(), &output, &errors,
+          &retVal, sourceDirectory, 0 /*m_TimeOut*/);
+        ofs << "  Output: " << output.c_str() << std::endl;
+        ofs << "  Errors: " << errors.c_str() << std::endl;
         if ( ofs )
           {
           ofs << output << std::endl;

+ 107 - 0
Source/cmCTest.cxx

@@ -1786,6 +1786,113 @@ bool cmCTest::SetCTestConfigurationFromCMakeVariable(cmMakefile* mf, const char*
   return true;
 }
 
+bool cmCTest::RunCommand(
+  const char* command, 
+  std::string* stdOut,
+  std::string* stdErr,
+  int *retVal, 
+  const char* dir,
+  double timeout)
+{
+  std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
+
+  if(args.size() < 1)
+    {
+    return false;
+    }
+  
+  std::vector<const char*> argv;
+  for(std::vector<cmStdString>::const_iterator a = args.begin();
+      a != args.end(); ++a)
+    {
+    argv.push_back(a->c_str());
+    }
+  argv.push_back(0);
+
+  *stdOut = "";
+  *stdErr = "";
+
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, &*argv.begin());
+  cmsysProcess_SetWorkingDirectory(cp, dir);
+  if(cmSystemTools::GetRunCommandHideConsole())
+    {
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+    }
+  cmsysProcess_SetTimeout(cp, timeout);
+  cmsysProcess_Execute(cp);
+  
+  std::vector<char> tempOutput;
+  std::vector<char> tempError;
+  char* data;
+  int length;
+  int res;
+  bool done = false;
+  while(!done)
+    {
+    res = cmsysProcess_WaitForData(cp, &data, &length, 0);
+    switch ( res )
+      {
+    case cmsysProcess_Pipe_STDOUT:
+      tempOutput.insert(tempOutput.end(), data, data+length);
+      break;
+    case cmsysProcess_Pipe_STDERR:
+      tempError.insert(tempError.end(), data, data+length);
+      break;
+    default:
+      done = true;
+      }
+    if(m_ExtraVerbose)
+      {
+      cmSystemTools::Stdout(data, length);
+      }
+    }
+  
+  cmsysProcess_WaitForExit(cp, 0);
+  stdOut->append(&*tempOutput.begin(), tempOutput.size());
+  stdErr->append(&*tempError.begin(), tempError.size());
+  
+  bool result = true;
+  if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exited)
+    {
+    if ( retVal )
+      {
+      *retVal = cmsysProcess_GetExitValue(cp);
+      }
+    else
+      {
+      if ( cmsysProcess_GetExitValue(cp) !=  0 )
+        {
+        result = false;
+        }
+      }
+    }
+  else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exception)
+    {
+    const char* exception_str = cmsysProcess_GetExceptionString(cp);
+    cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
+    stdErr->append(exception_str, strlen(exception_str));
+    result = false;
+    }
+  else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Error)
+    {
+    const char* error_str = cmsysProcess_GetErrorString(cp);
+    cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
+    stdErr->append(error_str, strlen(error_str));
+    result = false;
+    }
+  else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Expired)
+    {
+    const char* error_str = "Process terminated due to timeout\n";
+    cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
+    stdErr->append(error_str, strlen(error_str));
+    result = false;
+    }
+  
+  cmsysProcess_Delete(cp);
+  return result;
+}
+
 //----------------------------------------------------------------------
 void cmCTest::SetOutputLogFileName(const char* name)
 {

+ 25 - 1
Source/cmCTest.h

@@ -140,7 +140,31 @@ public:
 
   ///! Should we only show what we would do?
   bool GetShowOnly();
-  
+
+   /**
+   * Run a single executable command and put the stdout and stderr 
+   * in output.
+   *
+   * If verbose is false, no user-viewable output from the program
+   * being run will be generated.
+   *
+   * If timeout is specified, the command will be terminated after
+   * timeout expires. Timeout is specified in seconds.
+   *
+   * Argument retVal should be a pointer to the location where the
+   * exit code will be stored. If the retVal is not specified and 
+   * the program exits with a code other than 0, then the this 
+   * function will return false.
+   *
+   * If the command has spaces in the path the caller MUST call
+   * cmSystemTools::ConvertToRunCommandPath on the command before passing
+   * it into this function or it will not work.  The command must be correctly
+   * escaped for this to with spaces.  
+   */
+  bool RunCommand(const char* command,
+    std::string* stdOut, std::string* stdErr,
+    int* retVal = 0, const char* dir = 0, double timeout = 0.0);
+ 
   //! Start CTest XML output file
   void StartXML(std::ostream& ostr);