~ubuntu-branches/ubuntu/dapper/cmake/dapper-backports

« back to all changes in this revision

Viewing changes to Source/CTest/cmCTestBuildHandler.cxx

  • Committer: Bazaar Package Importer
  • Author(s): A. Maitland Bottoms
  • Date: 2006-01-08 10:48:14 UTC
  • mfrom: (1.3.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060108104814-jmy1r2fydpohpc1g
Tags: 2.2.3-1
* New upstream release (Closes: #338324)
* support GNU/kFreeBSD in cmake (Closes: #340764)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*=========================================================================
 
2
 
 
3
  Program:   CMake - Cross-Platform Makefile Generator
 
4
  Module:    $RCSfile: cmCTestBuildHandler.cxx,v $
 
5
  Language:  C++
 
6
  Date:      $Date: 2005/11/23 17:33:39 $
 
7
  Version:   $Revision: 1.30.2.4 $
 
8
 
 
9
  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
 
10
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
 
11
 
 
12
     This software is distributed WITHOUT ANY WARRANTY; without even 
 
13
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 
14
     PURPOSE.  See the above copyright notices for more information.
 
15
 
 
16
=========================================================================*/
 
17
 
 
18
#include "cmCTestBuildHandler.h"
 
19
 
 
20
#include "cmCTest.h"
 
21
#include "cmake.h"
 
22
#include "cmMakefile.h"
 
23
#include "cmLocalGenerator.h"
 
24
#include "cmGlobalGenerator.h"
 
25
#include "cmGeneratedFileStream.h"
 
26
 
 
27
//#include <cmsys/RegularExpression.hxx>
 
28
#include <cmsys/Process.h>
 
29
 
 
30
// used for sleep
 
31
#ifdef _WIN32
 
32
#include "windows.h"
 
33
#endif
 
34
 
 
35
#include <stdlib.h> 
 
36
#include <time.h>
 
37
#include <math.h>
 
38
#include <float.h>
 
39
 
 
40
 
 
41
static const char* cmCTestErrorMatches[] = {
 
42
  "^[Bb]us [Ee]rror",
 
43
  "^[Ss]egmentation [Vv]iolation",
 
44
  "^[Ss]egmentation [Ff]ault",
 
45
  "([^ :]+):([0-9]+): ([^ \\t])",
 
46
  "([^:]+): error[ \\t]*[0-9]+[ \\t]*:",
 
47
  "^Error ([0-9]+):",
 
48
  "^Fatal",
 
49
  "^Error: ",
 
50
  "^Error ",
 
51
  "[0-9] ERROR: ",
 
52
  "^\"[^\"]+\", line [0-9]+: [^Ww]",
 
53
  "^cc[^C]*CC: ERROR File = ([^,]+), Line = ([0-9]+)",
 
54
  "^ld([^:])*:([ \\t])*ERROR([^:])*:",
 
55
  "^ild:([ \\t])*\\(undefined symbol\\)",
 
56
  "([^ :]+) : (error|fatal error|catastrophic error)",
 
57
  "([^:]+): (Error:|error|undefined reference|multiply defined)",
 
58
  "([^:]+)\\(([^\\)]+)\\) : (error|fatal error|catastrophic error)",
 
59
  "^fatal error C[0-9]+:",
 
60
  ": syntax error ",
 
61
  "^collect2: ld returned 1 exit status",
 
62
  "Unsatisfied symbols:",
 
63
  "^Unresolved:",
 
64
  "Undefined symbols:",
 
65
  "^Undefined[ \\t]+first referenced",
 
66
  "^CMake Error:",
 
67
  ":[ \\t]cannot find",
 
68
  ":[ \\t]can't find",
 
69
  ": \\*\\*\\* No rule to make target \\`.*\\'.  Stop",
 
70
  ": Invalid loader fixup for symbol",
 
71
  ": Invalid fixups exist",
 
72
  ": Can't find library for",
 
73
  ": internal link edit command failed",
 
74
  ": Unrecognized option \\`.*\\'",
 
75
  "\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\([^W]\\)",
 
76
  "ld: 0706-006 Cannot find or open library file: -l ",
 
77
  "ild: \\(argument error\\) can't find library argument ::",
 
78
  "^could not be found and will not be loaded.",
 
79
  "s:616 string too big",
 
80
  "make: Fatal error: ",
 
81
  "ld: 0711-993 Error occurred while writing to the output file:",
 
82
  "ld: fatal: ",
 
83
  "final link failed:",
 
84
  "make: \\*\\*\\*.*Error",
 
85
  "make\\[.*\\]: \\*\\*\\*.*Error",
 
86
  "\\*\\*\\* Error code",
 
87
  "nternal error:",
 
88
  "Makefile:[0-9]+: \\*\\*\\* .*  Stop\\.",
 
89
  0
 
90
};
 
91
 
 
92
static const char* cmCTestErrorExceptions[] = {
 
93
  "instantiated from ",
 
94
  "candidates are:",
 
95
  ": warning",
 
96
  ": \\(Warning\\)",
 
97
  "makefile:",
 
98
  "Makefile:",
 
99
  ":[ \\t]+Where:",
 
100
  "([^ :]+):([0-9]+): Warning",
 
101
  "------ Build started: .* ------",
 
102
  0
 
103
};
 
104
 
 
105
static const char* cmCTestWarningMatches[] = {
 
106
  "([^ :]+):([0-9]+): warning:",
 
107
  "^cc[^C]*CC: WARNING File = ([^,]+), Line = ([0-9]+)",
 
108
  "^ld([^:])*:([ \\t])*WARNING([^:])*:",
 
109
  "([^:]+): warning ([0-9]+):",
 
110
  "^\"[^\"]+\", line [0-9]+: [Ww]arning",
 
111
  "([^:]+): warning[ \\t]*[0-9]+[ \\t]*:",
 
112
  "^Warning ([0-9]+):",
 
113
  "^Warning ",
 
114
  "WARNING: ",
 
115
  "([^ :]+) : warning",
 
116
  "([^:]+): warning",
 
117
  "\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\(W\\)",
 
118
  "^cxx: Warning:",
 
119
  ".*file: .* has no symbols",
 
120
  "([^ :]+):([0-9]+): Warning",
 
121
  "\\([0-9]*\\): remark #[0-9]*",
 
122
  "\".*\", line [0-9]+: remark\\([0-9]*\\):",
 
123
  "cc-[0-9]* CC: REMARK File = .*, Line = [0-9]*",
 
124
  0
 
125
};
 
126
 
 
127
static const char* cmCTestWarningExceptions[] = {
 
128
  "/usr/openwin/include/X11/Xlib\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration",
 
129
  "/usr/openwin/include/X11/Xutil\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration",
 
130
  "/usr/openwin/include/X11/XResource\\.h:[0-9]+: warning: ANSI C\\+\\+ forbids declaration",
 
131
  "WARNING 84 :",
 
132
  "WARNING 47 :",
 
133
  "makefile:",
 
134
  "Makefile:",
 
135
  "warning:  Clock skew detected.  Your build may be incomplete.",
 
136
  "/usr/openwin/include/GL/[^:]+:",
 
137
  "bind_at_load",
 
138
  "XrmQGetResource",
 
139
  "IceFlush",
 
140
  "warning LNK4089: all references to [^ \\t]+ discarded by .OPT:REF",
 
141
  "ld32: WARNING 85: definition of dataKey in",
 
142
  "cc: warning 422: Unknown option \"\\+b",
 
143
  "_with_warning_C",
 
144
  0
 
145
};
 
146
 
 
147
struct cmCTestBuildCompileErrorWarningRex
 
148
{
 
149
  const char* m_RegularExpressionString;
 
150
  int m_FileIndex;
 
151
  int m_LineIndex;
 
152
};
 
153
 
 
154
static cmCTestBuildCompileErrorWarningRex
 
155
cmCTestWarningErrorFileLine[] = {
 
156
    { "^Warning W[0-9]+ ([a-zA-Z.\\:/0-9_+ ~-]+) ([0-9]+):", 1, 2 },
 
157
    { "^([a-zA-Z./0-9_+ ~-]+):([0-9]+):", 1, 2 },
 
158
    { "^([a-zA-Z.\\:/0-9_+ ~-]+)\\(([0-9]+)\\)", 1, 2 },
 
159
    { "^([a-zA-Z./0-9_+ ~-]+)\\(([0-9]+)\\)", 1, 2 },
 
160
    { "\"([a-zA-Z./0-9_+ ~-]+)\", line ([0-9]+)", 1, 2 },
 
161
    { "File = ([a-zA-Z./0-9_+ ~-]+), Line = ([0-9]+)", 1, 2 },
 
162
    { 0, 0, 0 }
 
163
};
 
164
 
 
165
//----------------------------------------------------------------------
 
166
cmCTestBuildHandler::cmCTestBuildHandler()
 
167
{
 
168
  m_MaxPreContext = 6;
 
169
  m_MaxPostContext = 6;
 
170
 
 
171
  m_MaxErrors = 50;
 
172
  m_MaxWarnings = 50;
 
173
 
 
174
  m_LastErrorOrWarning = m_ErrorsAndWarnings.end();
 
175
 
 
176
}
 
177
 
 
178
//----------------------------------------------------------------------
 
179
void cmCTestBuildHandler::Initialize()
 
180
{
 
181
  this->Superclass::Initialize();
 
182
  m_StartBuild = "";
 
183
  m_EndBuild = "";
 
184
  m_CustomErrorMatches.clear();
 
185
  m_CustomErrorExceptions.clear();
 
186
  m_CustomWarningMatches.clear();
 
187
  m_CustomWarningExceptions.clear();
 
188
  m_ErrorWarningFileLineRegex.clear();
 
189
 
 
190
  m_ErrorMatchRegex.clear();
 
191
  m_ErrorExceptionRegex.clear();
 
192
  m_WarningMatchRegex.clear();
 
193
  m_WarningExceptionRegex.clear();
 
194
  m_BuildProcessingQueue.clear();
 
195
  m_BuildProcessingQueueLocation = m_BuildProcessingQueue.end();
 
196
  m_BuildOutputLogSize = 0;
 
197
  m_CurrentProcessingLine.clear();
 
198
 
 
199
  m_SimplifySourceDir = "";
 
200
  m_SimplifyBuildDir = "";
 
201
  m_OutputLineCounter = 0;
 
202
  m_ErrorsAndWarnings.clear();
 
203
  m_LastErrorOrWarning = m_ErrorsAndWarnings.end();
 
204
  m_PostContextCount = 0;
 
205
  m_MaxPreContext = 6;
 
206
  m_MaxPostContext = 6;
 
207
  m_PreContext.clear();
 
208
 
 
209
  m_TotalErrors = 0;
 
210
  m_TotalWarnings = 0;
 
211
  m_LastTickChar = 0;
 
212
 
 
213
  m_ErrorQuotaReached = false;
 
214
  m_WarningQuotaReached = false;
 
215
 
 
216
  m_MaxErrors = 50;
 
217
  m_MaxWarnings = 50;
 
218
}
 
219
 
 
220
//----------------------------------------------------------------------
 
221
void cmCTestBuildHandler::PopulateCustomVectors(cmMakefile *mf)
 
222
{
 
223
  cmCTest::PopulateCustomVector(mf, "CTEST_CUSTOM_ERROR_MATCH", 
 
224
                                m_CustomErrorMatches);
 
225
  cmCTest::PopulateCustomVector(mf, "CTEST_CUSTOM_ERROR_EXCEPTION", 
 
226
                                m_CustomErrorExceptions);
 
227
  cmCTest::PopulateCustomVector(mf, "CTEST_CUSTOM_WARNING_MATCH", 
 
228
                                m_CustomWarningMatches);
 
229
  cmCTest::PopulateCustomVector(mf, "CTEST_CUSTOM_WARNING_EXCEPTION", 
 
230
                                m_CustomWarningExceptions);
 
231
  cmCTest::PopulateCustomInteger(mf, 
 
232
                             "CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS", 
 
233
                             m_MaxErrors);
 
234
  cmCTest::PopulateCustomInteger(mf, 
 
235
                             "CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS", 
 
236
                             m_MaxWarnings);
 
237
}
 
238
 
 
239
//----------------------------------------------------------------------
 
240
//clearly it would be nice if this were broken up into a few smaller
 
241
//functions and commented...
 
242
int cmCTestBuildHandler::ProcessHandler()
 
243
{
 
244
  cmCTestLog(m_CTest, HANDLER_OUTPUT, "Build project" << std::endl);
 
245
 
 
246
  int entry;
 
247
  for ( entry = 0; cmCTestWarningErrorFileLine[entry].m_RegularExpressionString; ++ entry )
 
248
    {
 
249
    cmCTestBuildHandler::cmCTestCompileErrorWarningRex r;
 
250
    if ( r.m_RegularExpression.compile(
 
251
        cmCTestWarningErrorFileLine[entry].m_RegularExpressionString) )
 
252
      {
 
253
      r.m_FileIndex = cmCTestWarningErrorFileLine[entry].m_FileIndex;
 
254
      r.m_LineIndex = cmCTestWarningErrorFileLine[entry].m_LineIndex;
 
255
      m_ErrorWarningFileLineRegex.push_back(r);
 
256
      }
 
257
    else
 
258
      {
 
259
      cmCTestLog(m_CTest, ERROR_MESSAGE, "Problem Compiling regular expression: "
 
260
       << cmCTestWarningErrorFileLine[entry].m_RegularExpressionString << std::endl);
 
261
      }
 
262
    }
 
263
 
 
264
  // Determine build command and build directory
 
265
  const std::string &makeCommand = m_CTest->GetCTestConfiguration("MakeCommand");
 
266
  if ( makeCommand.size() == 0 )
 
267
    {
 
268
    cmCTestLog(m_CTest, ERROR_MESSAGE, "Cannot find MakeCommand key in the DartConfiguration.tcl" << std::endl);
 
269
    return -1;
 
270
    }
 
271
  const std::string &buildDirectory = m_CTest->GetCTestConfiguration("BuildDirectory");
 
272
  if ( buildDirectory.size() == 0 )
 
273
    {
 
274
    cmCTestLog(m_CTest, ERROR_MESSAGE, "Cannot find BuildDirectory  key in the DartConfiguration.tcl" << std::endl);
 
275
    return -1;
 
276
    }
 
277
 
 
278
  // Create a last build log
 
279
  cmGeneratedFileStream ofs;
 
280
  double elapsed_time_start = cmSystemTools::GetTime();
 
281
  if ( !this->StartLogFile("Build", ofs) )
 
282
    {
 
283
    cmCTestLog(m_CTest, ERROR_MESSAGE, "Cannot create build log file" << std::endl);
 
284
    }
 
285
 
 
286
  // Create lists of regular expression strings for errors, error exceptions,
 
287
  // warnings and warning exceptions.
 
288
  std::vector<cmStdString>::size_type cc;
 
289
  for ( cc = 0; cmCTestErrorMatches[cc]; cc ++ )
 
290
    {
 
291
    m_CustomErrorMatches.push_back(cmCTestErrorMatches[cc]);
 
292
    }
 
293
  for ( cc = 0; cmCTestErrorExceptions[cc]; cc ++ )
 
294
    {
 
295
    m_CustomErrorExceptions.push_back(cmCTestErrorExceptions[cc]);
 
296
    }
 
297
  for ( cc = 0; cmCTestWarningMatches[cc]; cc ++ )
 
298
    {
 
299
    m_CustomWarningMatches.push_back(cmCTestWarningMatches[cc]);
 
300
    }
 
301
  for ( cc = 0; cmCTestWarningExceptions[cc]; cc ++ )
 
302
    {
 
303
    m_CustomWarningExceptions.push_back(cmCTestWarningExceptions[cc]);
 
304
    }
 
305
 
 
306
  // Pre-compile regular expressions objects for all regular expressions
 
307
  std::vector<cmStdString>::iterator it;
 
308
 
 
309
#define cmCTestBuildHandlerPopulateRegexVector(strings, regexes) \
 
310
  regexes.clear(); \
 
311
  for ( it = strings.begin(); it != strings.end(); ++it ) \
 
312
    { \
 
313
    regexes.push_back(it->c_str()); \
 
314
    }
 
315
  cmCTestBuildHandlerPopulateRegexVector(m_CustomErrorMatches, m_ErrorMatchRegex);
 
316
  cmCTestBuildHandlerPopulateRegexVector(m_CustomErrorExceptions, m_ErrorExceptionRegex);
 
317
  cmCTestBuildHandlerPopulateRegexVector(m_CustomWarningMatches, m_WarningMatchRegex);
 
318
  cmCTestBuildHandlerPopulateRegexVector(m_CustomWarningExceptions, m_WarningExceptionRegex);
 
319
 
 
320
 
 
321
  // Determine source and binary tree substitutions to simplify the output.
 
322
  m_SimplifySourceDir = "";
 
323
  m_SimplifyBuildDir = "";
 
324
  if ( m_CTest->GetCTestConfiguration("SourceDirectory").size() > 20 )
 
325
    {
 
326
    std::string srcdir = m_CTest->GetCTestConfiguration("SourceDirectory") + "/";
 
327
    std::string srcdirrep;
 
328
    for ( cc = srcdir.size()-2; cc > 0; cc -- )
 
329
      {
 
330
      if ( srcdir[cc] == '/' )
 
331
        {
 
332
        srcdirrep = srcdir.c_str() + cc;
 
333
        srcdirrep = "/..." + srcdirrep;
 
334
        srcdir = srcdir.substr(0, cc+1);
 
335
        break;
 
336
        }
 
337
      }
 
338
    m_SimplifySourceDir = srcdir;
 
339
    }
 
340
  if ( m_CTest->GetCTestConfiguration("BuildDirectory").size() > 20 )
 
341
    {
 
342
    std::string bindir = m_CTest->GetCTestConfiguration("BuildDirectory") + "/";
 
343
    std::string bindirrep;
 
344
    for ( cc = bindir.size()-2; cc > 0; cc -- )
 
345
      {
 
346
      if ( bindir[cc] == '/' )
 
347
        {
 
348
        bindirrep = bindir.c_str() + cc;
 
349
        bindirrep = "/..." + bindirrep;
 
350
        bindir = bindir.substr(0, cc+1);
 
351
        break;
 
352
        }
 
353
      }
 
354
    m_SimplifyBuildDir = bindir;
 
355
    }
 
356
 
 
357
 
 
358
  // Ok, let's do the build
 
359
  
 
360
  // Remember start build time
 
361
  m_StartBuild = m_CTest->CurrentTime();
 
362
  int retVal = 0;
 
363
  int res = cmsysProcess_State_Exited;
 
364
  if ( !m_CTest->GetShowOnly() )
 
365
    {
 
366
    res = this->RunMakeCommand(makeCommand.c_str(), &retVal, buildDirectory.c_str(), 0, ofs);
 
367
    }
 
368
  else
 
369
    {
 
370
    cmCTestLog(m_CTest, DEBUG, "Build with command: " << makeCommand << std::endl);
 
371
    }
 
372
 
 
373
  // Remember end build time and calculate elapsed time
 
374
  m_EndBuild = m_CTest->CurrentTime();
 
375
  double elapsed_build_time = cmSystemTools::GetTime() - elapsed_time_start;
 
376
  if (res != cmsysProcess_State_Exited || retVal )
 
377
    {
 
378
    cmCTestLog(m_CTest, ERROR_MESSAGE, "Error(s) when building project" << std::endl);
 
379
    }
 
380
 
 
381
  // Cleanups strings in the errors and warnings list.
 
382
  t_ErrorsAndWarningsVector::iterator evit;
 
383
  if ( !m_SimplifySourceDir.empty() )
 
384
    {
 
385
    for ( evit = m_ErrorsAndWarnings.begin(); evit != m_ErrorsAndWarnings.end(); ++ evit )
 
386
      {
 
387
      cmSystemTools::ReplaceString(evit->m_Text, m_SimplifySourceDir.c_str(), "/.../");
 
388
      cmSystemTools::ReplaceString(evit->m_PreContext, m_SimplifySourceDir.c_str(), "/.../");
 
389
      cmSystemTools::ReplaceString(evit->m_PostContext, m_SimplifySourceDir.c_str(), "/.../");
 
390
      }
 
391
    }
 
392
 
 
393
  if ( !m_SimplifyBuildDir.empty() )
 
394
    {
 
395
    for ( evit = m_ErrorsAndWarnings.begin(); evit != m_ErrorsAndWarnings.end(); ++ evit )
 
396
      {
 
397
      cmSystemTools::ReplaceString(evit->m_Text, m_SimplifyBuildDir.c_str(), "/.../");
 
398
      cmSystemTools::ReplaceString(evit->m_PreContext, m_SimplifyBuildDir.c_str(), "/.../");
 
399
      cmSystemTools::ReplaceString(evit->m_PostContext, m_SimplifyBuildDir.c_str(), "/.../");
 
400
      }
 
401
    }
 
402
 
 
403
  // Display message about number of errors and warnings
 
404
  cmCTestLog(m_CTest, HANDLER_OUTPUT, "   " << m_TotalErrors
 
405
    << (m_TotalErrors >= m_MaxErrors ? " or more" : "")
 
406
    << " Compiler errors" << std::endl);
 
407
  cmCTestLog(m_CTest, HANDLER_OUTPUT, "   " << m_TotalWarnings
 
408
    << (m_TotalWarnings >= m_MaxWarnings ? " or more" : "")
 
409
    << " Compiler warnings" << std::endl);
 
410
 
 
411
  // Generate XML output
 
412
  cmGeneratedFileStream xofs;
 
413
  if( !this->StartResultingXML("Build", xofs))
 
414
    {
 
415
    cmCTestLog(m_CTest, ERROR_MESSAGE, "Cannot create build XML file" << std::endl);
 
416
    return -1;
 
417
    }
 
418
  this->GenerateDartBuildOutput(xofs, m_ErrorsAndWarnings, elapsed_build_time);
 
419
  return 0;
 
420
}
 
421
 
 
422
//----------------------------------------------------------------------
 
423
void cmCTestBuildHandler::GenerateDartBuildOutput(
 
424
  std::ostream& os, 
 
425
  std::vector<cmCTestBuildErrorWarning> ew,
 
426
  double elapsed_build_time)
 
427
{
 
428
  m_CTest->StartXML(os);
 
429
  os << "<Build>\n"
 
430
     << "\t<StartDateTime>" << m_StartBuild << "</StartDateTime>\n"
 
431
     << "<BuildCommand>" 
 
432
     << m_CTest->MakeXMLSafe(m_CTest->GetCTestConfiguration("MakeCommand"))
 
433
     << "</BuildCommand>" << std::endl;
 
434
    
 
435
  std::vector<cmCTestBuildErrorWarning>::iterator it;
 
436
  
 
437
  // only report the first 50 warnings and first 50 errors
 
438
  unsigned short numErrorsAllowed = m_MaxErrors;
 
439
  unsigned short numWarningsAllowed = m_MaxWarnings;
 
440
  std::string srcdir = m_CTest->GetCTestConfiguration("SourceDirectory");
 
441
  // make sure the source dir is in the correct case on windows
 
442
  // via a call to collapse full path.
 
443
  srcdir = cmSystemTools::CollapseFullPath(srcdir.c_str());
 
444
  srcdir += "/";
 
445
  for ( it = ew.begin(); 
 
446
        it != ew.end() && (numErrorsAllowed || numWarningsAllowed); it++ )
 
447
    {
 
448
    cmCTestBuildErrorWarning *cm = &(*it);
 
449
    if (cm->m_Error && numErrorsAllowed ||
 
450
        !cm->m_Error && numWarningsAllowed)
 
451
      {
 
452
      if (cm->m_Error)
 
453
        {
 
454
        numErrorsAllowed--;
 
455
        }
 
456
      else
 
457
        {
 
458
        numWarningsAllowed--;
 
459
        }
 
460
      os << "\t<" << (cm->m_Error ? "Error" : "Warning") << ">\n"
 
461
         << "\t\t<BuildLogLine>" << cm->m_LogLine << "</BuildLogLine>\n"
 
462
         << "\t\t<Text>" << m_CTest->MakeXMLSafe(cm->m_Text) 
 
463
         << "\n</Text>" << std::endl;
 
464
      std::vector<cmCTestCompileErrorWarningRex>::iterator rit;
 
465
      for ( rit = m_ErrorWarningFileLineRegex.begin();
 
466
            rit != m_ErrorWarningFileLineRegex.end(); ++ rit )
 
467
        {
 
468
        cmsys::RegularExpression* re = &rit->m_RegularExpression;
 
469
        if ( re->find(cm->m_Text.c_str() ) )
 
470
          {
 
471
          cm->m_SourceFile = re->match(rit->m_FileIndex);
 
472
          // At this point we need to make m_SourceFile relative to 
 
473
          // the source root of the project, so cvs links will work
 
474
          cmSystemTools::ConvertToUnixSlashes(cm->m_SourceFile);
 
475
          if(cm->m_SourceFile.find("/.../") != cm->m_SourceFile.npos)
 
476
            {
 
477
            cmSystemTools::ReplaceString(cm->m_SourceFile, "/.../", "");
 
478
            std::string::size_type p = cm->m_SourceFile.find("/");
 
479
            if(p != cm->m_SourceFile.npos)
 
480
              {
 
481
              cm->m_SourceFile = cm->m_SourceFile.substr(p+1, cm->m_SourceFile.size()-p);
 
482
              }
 
483
            }
 
484
          else
 
485
            {
 
486
            // make sure it is a full path with the correct case
 
487
            cm->m_SourceFile = cmSystemTools::CollapseFullPath(cm->m_SourceFile.c_str());
 
488
            cmSystemTools::ReplaceString(cm->m_SourceFile, srcdir.c_str(), "");
 
489
            }
 
490
          cm->m_LineNumber = atoi(re->match(rit->m_LineIndex).c_str());
 
491
          break;
 
492
          }
 
493
        }
 
494
      if ( cm->m_SourceFile.size() > 0 )
 
495
        {
 
496
        os << "\t\t<SourceFile>" << cm->m_SourceFile << "</SourceFile>" 
 
497
           << std::endl;
 
498
        }
 
499
      if ( cm->m_SourceFileTail.size() > 0 )
 
500
        {
 
501
        os << "\t\t<SourceFileTail>" << cm->m_SourceFileTail 
 
502
           << "</SourceFileTail>" << std::endl;
 
503
        }
 
504
      if ( cm->m_LineNumber >= 0 )
 
505
        {
 
506
        os << "\t\t<SourceLineNumber>" << cm->m_LineNumber 
 
507
           << "</SourceLineNumber>" << std::endl;
 
508
        }
 
509
      os << "\t\t<PreContext>" << m_CTest->MakeXMLSafe(cm->m_PreContext) 
 
510
         << "</PreContext>\n"
 
511
         << "\t\t<PostContext>" << m_CTest->MakeXMLSafe(cm->m_PostContext);
 
512
      // is this the last warning or error, if so notify
 
513
      if (cm->m_Error && !numErrorsAllowed ||
 
514
          !cm->m_Error && !numWarningsAllowed)
 
515
        {
 
516
        os << "\nThe maximum number of reported warnings or errors has been reached!!!\n";
 
517
        }
 
518
      os << "</PostContext>\n"
 
519
         << "\t\t<RepeatCount>0</RepeatCount>\n"
 
520
         << "</" << (cm->m_Error ? "Error" : "Warning") << ">\n\n" 
 
521
         << std::endl;
 
522
      }
 
523
    }
 
524
  os << "\t<Log Encoding=\"base64\" Compression=\"/bin/gzip\">\n\t</Log>\n"
 
525
     << "\t<EndDateTime>" << m_EndBuild << "</EndDateTime>\n"
 
526
     << "<ElapsedMinutes>" << static_cast<int>(elapsed_build_time/6)/10.0
 
527
     << "</ElapsedMinutes>"
 
528
     << "</Build>" << std::endl;
 
529
  m_CTest->EndXML(os);
 
530
}
 
531
 
 
532
//######################################################################
 
533
//######################################################################
 
534
//######################################################################
 
535
//######################################################################
 
536
 
 
537
//----------------------------------------------------------------------
 
538
int cmCTestBuildHandler::RunMakeCommand(const char* command,
 
539
  int* retVal, const char* dir, int timeout, std::ofstream& ofs)
 
540
{
 
541
  // First generate the command and arguments
 
542
  std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
 
543
 
 
544
  if(args.size() < 1)
 
545
    {
 
546
    return false;
 
547
    }
 
548
 
 
549
  std::vector<const char*> argv;
 
550
  for(std::vector<cmStdString>::const_iterator a = args.begin();
 
551
    a != args.end(); ++a)
 
552
    {
 
553
    argv.push_back(a->c_str());
 
554
    }
 
555
  argv.push_back(0);
 
556
 
 
557
  cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "Run command:");
 
558
  std::vector<const char*>::iterator ait;
 
559
  for ( ait = argv.begin(); ait != argv.end() && *ait; ++ ait )
 
560
    {
 
561
    cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, " \"" << *ait << "\"");
 
562
    }
 
563
  cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, std::endl);
 
564
  
 
565
  // Now create process object
 
566
  cmsysProcess* cp = cmsysProcess_New();
 
567
  cmsysProcess_SetCommand(cp, &*argv.begin());
 
568
  cmsysProcess_SetWorkingDirectory(cp, dir);
 
569
  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
 
570
  cmsysProcess_SetTimeout(cp, timeout);
 
571
  cmsysProcess_Execute(cp);
 
572
 
 
573
  // Initialize tick's
 
574
  std::string::size_type tick = 0;
 
575
  const std::string::size_type tick_len = 1024;
 
576
 
 
577
  char* data;
 
578
  int length;
 
579
  cmCTestLog(m_CTest, HANDLER_OUTPUT,
 
580
    "   Each symbol represents " << tick_len << " bytes of output." << std::endl
 
581
    << "   '!' represents an error and '*' a warning." << std::endl
 
582
    << "    " << std::flush);
 
583
 
 
584
  // Initialize building structures
 
585
  m_BuildProcessingQueue.clear();
 
586
  m_OutputLineCounter = 0;
 
587
  m_ErrorsAndWarnings.clear();
 
588
  m_TotalErrors = 0;
 
589
  m_TotalWarnings = 0;
 
590
  m_BuildOutputLogSize = 0;
 
591
  m_LastTickChar = '.';
 
592
  m_WarningQuotaReached = false;
 
593
  m_ErrorQuotaReached = false;
 
594
 
 
595
  // For every chunk of data
 
596
  while(cmsysProcess_WaitForData(cp, &data, &length, 0))
 
597
    {
 
598
    // Replace '\0' with '\n', since '\0' does not really make sense. This is
 
599
    // for Visual Studio output
 
600
    for(int cc =0; cc < length; ++cc)
 
601
      {
 
602
      if(data[cc] == 0)
 
603
        {
 
604
        data[cc] = '\n';
 
605
        }
 
606
      }
 
607
 
 
608
    // Process the chunk of data
 
609
    this->ProcessBuffer(data, length, tick, tick_len, ofs);
 
610
    }
 
611
 
 
612
  this->ProcessBuffer(0, 0, tick, tick_len, ofs);
 
613
  cmCTestLog(m_CTest, OUTPUT, " Size of output: "
 
614
    << int(m_BuildOutputLogSize / 1024.0) << "K" << std::endl);
 
615
 
 
616
  // Properly handle output of the build command
 
617
  cmsysProcess_WaitForExit(cp, 0);
 
618
  int result = cmsysProcess_GetState(cp);
 
619
 
 
620
  if(result == cmsysProcess_State_Exited)
 
621
    {
 
622
    *retVal = cmsysProcess_GetExitValue(cp);
 
623
    cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, "Command exited with the value: " << *retVal << std::endl);
 
624
    }
 
625
  else if(result == cmsysProcess_State_Exception)
 
626
    {
 
627
    *retVal = cmsysProcess_GetExitException(cp);
 
628
    cmCTestLog(m_CTest, WARNING, "There was an exception: " << *retVal << std::endl);
 
629
    }
 
630
  else if(result == cmsysProcess_State_Expired)
 
631
    {
 
632
    cmCTestLog(m_CTest, WARNING, "There was a timeout" << std::endl);
 
633
    } 
 
634
  else if(result == cmsysProcess_State_Error)
 
635
    {
 
636
    // If there was an error running command, report that on the dashboard.
 
637
    cmCTestBuildErrorWarning errorwarning;
 
638
    errorwarning.m_LogLine     = 1;
 
639
    errorwarning.m_Text        = "*** ERROR executing: "; 
 
640
    errorwarning.m_Text        += cmsysProcess_GetErrorString(cp);
 
641
    errorwarning.m_PreContext  = "";
 
642
    errorwarning.m_PostContext = "";
 
643
    errorwarning.m_Error       = true;
 
644
    m_ErrorsAndWarnings.push_back(errorwarning);
 
645
    m_TotalErrors ++;
 
646
    cmCTestLog(m_CTest, ERROR_MESSAGE, "There was an error: " << cmsysProcess_GetErrorString(cp) << std::endl);
 
647
    }
 
648
 
 
649
  cmsysProcess_Delete(cp);
 
650
 
 
651
  return result;
 
652
}
 
653
 
 
654
//######################################################################
 
655
//######################################################################
 
656
//######################################################################
 
657
//######################################################################
 
658
 
 
659
//----------------------------------------------------------------------
 
660
void cmCTestBuildHandler::ProcessBuffer(const char* data, int length, size_t& tick, size_t tick_len, 
 
661
    std::ofstream& ofs)
 
662
{
 
663
#undef cerr
 
664
  const std::string::size_type tick_line_len = 50;
 
665
  const char* ptr;
 
666
  for ( ptr = data; ptr < data+length; ptr ++ )
 
667
    {
 
668
    m_BuildProcessingQueue.push_back(*ptr);
 
669
    }
 
670
  m_BuildOutputLogSize += length;
 
671
 
 
672
  // until there are any lines left in the buffer
 
673
  while ( 1 )
 
674
    {
 
675
    // Find the end of line
 
676
    t_BuildProcessingQueueType::iterator it;
 
677
    for ( it = m_BuildProcessingQueue.begin();
 
678
      it != m_BuildProcessingQueue.end();
 
679
      ++ it )
 
680
      {
 
681
      if ( *it == '\n' )
 
682
        {
 
683
        break;
 
684
        }
 
685
      }
 
686
 
 
687
    // Once certain number of errors or warnings reached, ignore future errors or warnings.
 
688
    if ( m_TotalWarnings >= m_MaxWarnings )
 
689
      {
 
690
      m_WarningQuotaReached = true;
 
691
      }
 
692
    if ( m_TotalErrors >= m_MaxErrors )
 
693
      {
 
694
      m_ErrorQuotaReached = true;
 
695
      }
 
696
 
 
697
    // If the end of line was found
 
698
    if ( it != m_BuildProcessingQueue.end() )
 
699
      {
 
700
      // Create a contiguous array for the line
 
701
      m_CurrentProcessingLine.clear();
 
702
      t_BuildProcessingQueueType::iterator cit;
 
703
      for ( cit = m_BuildProcessingQueue.begin(); cit != it; ++cit )
 
704
        {
 
705
        m_CurrentProcessingLine.push_back(*cit);
 
706
        }
 
707
      m_CurrentProcessingLine.push_back(0);
 
708
      const char* line = &*m_CurrentProcessingLine.begin();
 
709
 
 
710
      // Process the line
 
711
      int lineType = this->ProcessSingleLine(line);
 
712
 
 
713
      // Erase the line from the queue
 
714
      m_BuildProcessingQueue.erase(m_BuildProcessingQueue.begin(), it+1);
 
715
 
 
716
      // Depending on the line type, produce error or warning, or nothing
 
717
      cmCTestBuildErrorWarning errorwarning;
 
718
      bool found = false;
 
719
      switch ( lineType )
 
720
        {
 
721
      case b_WARNING_LINE:
 
722
        m_LastTickChar = '*';
 
723
        errorwarning.m_Error = false;
 
724
        found = true;
 
725
        m_TotalWarnings ++;
 
726
        break;
 
727
      case b_ERROR_LINE:
 
728
        m_LastTickChar = '!';
 
729
        errorwarning.m_Error = true;
 
730
        found = true;
 
731
        m_TotalErrors ++;
 
732
        break;
 
733
        }
 
734
      if ( found )
 
735
        {
 
736
        // This is an error or warning, so generate report
 
737
        errorwarning.m_LogLine     = static_cast<int>(m_OutputLineCounter+1);
 
738
        errorwarning.m_Text        = line;
 
739
        errorwarning.m_PreContext  = "";
 
740
        errorwarning.m_PostContext = "";
 
741
 
 
742
        // Copy pre-context to report
 
743
        std::deque<cmStdString>::iterator pcit;
 
744
        for ( pcit = m_PreContext.begin(); pcit != m_PreContext.end(); ++pcit )
 
745
          {
 
746
          errorwarning.m_PreContext += *pcit + "\n";
 
747
          }
 
748
        m_PreContext.clear();
 
749
 
 
750
        // Store report
 
751
        m_ErrorsAndWarnings.push_back(errorwarning);
 
752
        m_LastErrorOrWarning = m_ErrorsAndWarnings.end()-1;
 
753
        m_PostContextCount = 0;
 
754
        }
 
755
      else
 
756
        {
 
757
        // This is not an error or warning.
 
758
        // So, figure out if this is a post-context line
 
759
        if ( m_LastErrorOrWarning != m_ErrorsAndWarnings.end() && m_PostContextCount < m_MaxPostContext )
 
760
          {
 
761
          m_PostContextCount ++;
 
762
          m_LastErrorOrWarning->m_PostContext += line;
 
763
          if ( m_PostContextCount < m_MaxPostContext )
 
764
            {
 
765
            m_LastErrorOrWarning->m_PostContext += "\n";
 
766
            }
 
767
          }
 
768
        else
 
769
          {
 
770
          // Otherwise store pre-context for the next error
 
771
          m_PreContext.push_back(line);
 
772
          if ( m_PreContext.size() > m_MaxPreContext )
 
773
            {
 
774
            m_PreContext.erase(m_PreContext.begin(), m_PreContext.end()-m_MaxPreContext);
 
775
            }
 
776
          }
 
777
        }
 
778
      m_OutputLineCounter ++;
 
779
      }
 
780
    else
 
781
      {
 
782
      break;
 
783
      }
 
784
    }
 
785
 
 
786
  // Now that the buffer is processed, display missing ticks
 
787
  int tickDisplayed = false;
 
788
  while ( m_BuildOutputLogSize > (tick * tick_len) )
 
789
    {
 
790
    tick ++;
 
791
    cmCTestLog(m_CTest, HANDLER_OUTPUT, m_LastTickChar);
 
792
    tickDisplayed = true;
 
793
    if ( tick % tick_line_len == 0 && tick > 0 )
 
794
      {
 
795
      cmCTestLog(m_CTest, HANDLER_OUTPUT, "  Size: "
 
796
        << int((m_BuildOutputLogSize / 1024.0) + 1) << "K" << std::endl
 
797
        << "    ");
 
798
      }
 
799
    }
 
800
  if ( tickDisplayed )
 
801
    {
 
802
    m_LastTickChar = '.';
 
803
    }
 
804
 
 
805
  // And if this is verbose output, display the content of the chunk
 
806
  cmCTestLog(m_CTest, HANDLER_VERBOSE_OUTPUT, cmCTestLogWrite(data, length));
 
807
 
 
808
  // Always store the chunk to the file
 
809
  ofs << cmCTestLogWrite(data, length);
 
810
}
 
811
 
 
812
//----------------------------------------------------------------------
 
813
int cmCTestBuildHandler::ProcessSingleLine(const char* data)
 
814
{
 
815
  cmCTestLog(m_CTest, DEBUG, "Line: [" << data << "]" << std::endl);
 
816
 
 
817
  std::vector<cmsys::RegularExpression>::iterator it;
 
818
 
 
819
  int warningLine = 0;
 
820
  int errorLine = 0;
 
821
 
 
822
  // Check for regular expressions
 
823
  
 
824
  if ( !m_ErrorQuotaReached )
 
825
    {
 
826
    // Errors 
 
827
    for ( it = m_ErrorMatchRegex.begin(); it != m_ErrorMatchRegex.end(); ++ it )
 
828
      {
 
829
      if ( it->find(data) )
 
830
        {
 
831
        errorLine = 1;
 
832
        cmCTestLog(m_CTest, DEBUG, "  Error Line: " << data << std::endl);
 
833
        break;
 
834
        }
 
835
      }
 
836
    // Error exceptions 
 
837
    for ( it = m_ErrorExceptionRegex.begin(); it != m_ErrorExceptionRegex.end(); ++ it )
 
838
      {
 
839
      if ( it->find(data) )
 
840
        {
 
841
        errorLine = 0;
 
842
        cmCTestLog(m_CTest, DEBUG, "  Not an error Line: " << data << std::endl);
 
843
        break;
 
844
        }
 
845
      }
 
846
    }
 
847
  if ( !m_WarningQuotaReached )
 
848
    {
 
849
    // Warnings
 
850
    for ( it = m_WarningMatchRegex.begin(); it != m_WarningMatchRegex.end(); ++ it )
 
851
      {
 
852
      if ( it->find(data) )
 
853
        {
 
854
        warningLine = 1;
 
855
        cmCTestLog(m_CTest, DEBUG, "  Warning Line: " << data << std::endl);
 
856
        break;
 
857
        }    
 
858
      }
 
859
 
 
860
    // Warning exceptions
 
861
    for ( it = m_WarningExceptionRegex.begin(); it != m_WarningExceptionRegex.end(); ++ it )
 
862
      {
 
863
      if ( it->find(data) )
 
864
        {
 
865
        warningLine = 0;
 
866
        cmCTestLog(m_CTest, DEBUG, "  Not a warning Line: " << data << std::endl);
 
867
        break;
 
868
        }    
 
869
      }
 
870
    }
 
871
  if ( errorLine )
 
872
    {
 
873
    return b_ERROR_LINE;
 
874
    }
 
875
  if ( warningLine )
 
876
    {
 
877
    return b_WARNING_LINE;
 
878
    }
 
879
  return b_REGULAR_LINE;
 
880
}
 
881