|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994
//
// File: buildexe.c
//
// Contents: Functions related to spawning processes and processing
// their output, using pipes and multiple threads.
//
// History: 22-May-89 SteveWo Created
// ... see SLM logs
// 26-Jul-94 LyleC Cleanup/Add Pass0 Support
//
//----------------------------------------------------------------------------
#include "build.h"
#include <fcntl.h>
//+---------------------------------------------------------------------------
//
// Global Data
//
//----------------------------------------------------------------------------
#define DEFAULT_LPS (fStatusTree? 5000 : 50)
#define LastRow(pts) ((USHORT) ((pts)->cRowTotal - 1))
#define LastCol(pts) ((USHORT) ((pts)->cColTotal - 1))
typedef struct _PARALLEL_CHILD { PTHREADSTATE ThreadState; HANDLE Event; CHAR ExecuteProgramCmdLine[1024]; } PARALLEL_CHILD, *PPARALLEL_CHILD;
ULONG_PTR StartCompileTime;
DWORD OldConsoleMode; DWORD NewConsoleMode;
HANDLE *WorkerThreads; HANDLE *WorkerEvents; ULONG NumberProcesses; ULONG ThreadsStarted;
BOOLEAN fConsoleInitialized = FALSE; BYTE ScreenCell[2]; BYTE StatusCell[2];
#define STATE_UNKNOWN 0
#define STATE_COMPILING 1
#define STATE_ASSEMBLING 2
#define STATE_LIBING 3
#define STATE_LINKING 4
#define STATE_C_PREPROC 5
#define STATE_S_PREPROC 6
#define STATE_PRECOMP 7
#define STATE_MKTYPLIB 8
#define STATE_MIDL 9
#define STATE_MC 10
#define STATE_STATUS 11
#define STATE_BINPLACE 12
#define STATE_VSTOOL 13
#define STATE_ASN 14
#define STATE_PACKING 15
#define STATE_BATCHCOMPILE 16
#define STATE_BSCMAKING 17
#define STATE_CTCOMPILING 18
#define STATE_AUTODOCING 19
#define STATE_DOCCHECKING 20
#define STATE_POSTBUILD 21
#define FLAGS_CXX_FILE 0x0001
#define FLAGS_WARNINGS_ARE_ERRORS 0x0002
LPSTR States[] = { "Unknown", // 0
"Compiling", // 1
"Assembling", // 2
"Building Library", // 3
"Linking Executable", // 4
"Preprocessing", // 5
"Assembling", // 6
"Precompiling", // 7
"Building Type Library", // 8
"Running MIDL on", // 9
"Compiling message file", // 10
"Build Status Line", // 11
"Binplacing", // 12
"Processing", // 13
"Running ASN Compiler on", // 14
"Packing Theme", // 15
"Compiling", // 16
"Building Browse File", // 17
"CTC Compiling", // 18
"Generating Documentation", // 19
"Checking Doc Comments", // 20
"PostBuild" //21
};
//----------------------------------------------------------------------------
//
// Function prototypes
//
//----------------------------------------------------------------------------
VOID GetScreenSize(THREADSTATE *ThreadState);
VOID GetCursorPosition(USHORT *pRow, USHORT *pCol, USHORT *pRowTop);
VOID SetCursorPosition(USHORT Row, USHORT Col);
VOID WriteConsoleCells( LPSTR String, USHORT StringLength, USHORT Row, USHORT Col, BYTE *Attribute);
VOID MoveRectangleUp ( USHORT Top, USHORT Left, USHORT Bottom, USHORT Right, USHORT NumRow, BYTE *FillCell);
VOID ReadConsoleCells( BYTE *pScreenCell, USHORT cb, USHORT Row, USHORT Column);
VOID ClearRows( PTHREADSTATE ThreadState, USHORT Top, USHORT NumRows, PBYTE Cell );
LPSTR IsolateFirstToken( LPSTR *pp, CHAR delim );
LPSTR IsolateLastToken( LPSTR p, CHAR delim );
DWORD ParallelChildStart( PPARALLEL_CHILD Data );
DWORD PipeSpawnClose ( FILE *pstream );
FILE * PipeSpawn ( const CHAR *cmdstring );
BOOL DetermineChildState( PTHREADSTATE ThreadState, LPSTR p );
void PrintChildState( PTHREADSTATE ThreadState, LPSTR p, PFILEREC FileDB );
BOOL CoffFilter( PTHREADSTATE ThreadState, LPSTR p );
//+---------------------------------------------------------------------------
//
// Function: RestoreConsoleMode
//
//----------------------------------------------------------------------------
VOID RestoreConsoleMode(VOID) { SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), OldConsoleMode); NewConsoleMode = OldConsoleMode; }
//+---------------------------------------------------------------------------
//
// Function: IsolateFirstToken
//
// Synopsis: Returns the first token in a string.
//
// Arguments: [pp] -- String to parse
// [delim] -- Token delimiter
//
// Returns: Pointer to first token
//
// Notes: Leading spaces are ignored.
//
//----------------------------------------------------------------------------
LPSTR IsolateFirstToken( LPSTR *pp, CHAR delim ) { LPSTR p, Result;
p = *pp; while (*p <= ' ') { if (!*p) { *pp = p; return ( "" ); } else p++; }
Result = p; while (*p) { if (*p == delim) { *p++ = '\0'; break; } else { p++; } } *pp = p; if (*Result == '\0') // don't overrun the buffer
return ( Result );
if (*Result == '.' && Result[1] == '\\') { return ( Result+2 ); } else { return ( Result ); } }
//+---------------------------------------------------------------------------
//
// Function: IsolateLastToken
//
// Synopsis: Return the last token in a string.
//
// Arguments: [p] -- String to parse
// [delim] -- Token delimiter
//
// Returns: Pointer to last token
//
// Notes: Trailing spaces are skipped.
//
//----------------------------------------------------------------------------
LPSTR IsolateLastToken( LPSTR p, CHAR delim ) { LPSTR Start;
Start = p; while (*p) { p++; }
while (--p > Start) { if (*p <= ' ' || *p == ':') { *p = '\0'; } else break; }
while (p > Start) { if (*--p == delim) { p++; break; } }
if (*p == '.' && p[1] == '\\') { return ( p+2 ); } else { return ( p ); } }
//+---------------------------------------------------------------------------
//
// Function: TestPrefix
//
// Synopsis: Returns TRUE if [Prefix] is the first part of [pp]
//
//----------------------------------------------------------------------------
BOOL TestPrefix( LPSTR *pp, LPSTR Prefix ) { LPSTR p = *pp; UINT cb;
if (!_strnicmp( p, Prefix, cb = strlen( Prefix ) )) { *pp = p + cb; return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: TestPrefixPath
//
// Synopsis: Returns TRUE if [Prefix] is the first part of [pp]
// If the firstpart of [pp] (excluding whitespace) contains
// backslashes, then only the right-most component is used
//
//----------------------------------------------------------------------------
BOOL TestPrefixPath( LPSTR *pp, LPSTR Prefix ) { LPSTR p = *pp; UINT cb; LPSTR PathString; INT PathStringLength ; LPSTR LastComp ;
cb = strlen( Prefix );
if (_strnicmp( p, Prefix, cb ) == 0 ) { *pp = p + cb; return ( TRUE ); } else { PathString = strchr( p, ' ' );
if ( PathString ) { PathStringLength = (INT) (PathString - p) ;
*PathString = '\0';
LastComp = strrchr( p, '\\' );
*PathString = ' ';
// Do we have backslashes (ie: a full path name to the tool name)?
if ( LastComp ) {
// Advance past the path.
p = LastComp + 1;
if ( _strnicmp( p, Prefix, cb ) == 0 ) { *pp = p + cb ; return ( TRUE ); } } }
return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: Substr
//
//----------------------------------------------------------------------------
BOOL Substr( LPSTR s, LPSTR p ) { LPSTR x;
while (*p) { x = s; while (*p++ == *x) { if (*x == '\0') { return ( TRUE ); } x++; } if (*x == '\0') { return ( TRUE ); } } return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: WriteTTY
//
// Synopsis: Writes the given string to the output device.
//
// Arguments: [ThreadState] -- Struct containing info about the output dev.
// [p] -- String to display
// [fStatusOutput] -- If TRUE then put on the status line.
//
//----------------------------------------------------------------------------
VOID WriteTTY (THREADSTATE *ThreadState, LPSTR p, BOOL fStatusOutput) { USHORT SaveRow; USHORT SaveCol; USHORT SaveRowTop; USHORT cb, cbT; PBYTE Attribute; BOOL ForceNewline;
if (fSuppressOutput) return;
//
// If we're not writing to the screen then don't do anything fancy, just
// output the string.
//
if (!fStatus || !ThreadState->IsStdErrTty) { while (TRUE) { int cch;
cch = strcspn(p, "\r"); if (cch != 0) { fwrite(p, 1, cch, stderr); p += cch; } if (*p == '\0') { break; } if (p[1] != '\n') { fwrite(p, 1, 1, stderr); } p++; } fflush(stderr); return; }
assert(ThreadState->cColTotal != 0); assert(ThreadState->cRowTotal != 0);
//
// Scroll as necessary
//
GetCursorPosition(&SaveRow, &SaveCol, &SaveRowTop);
// During processing, there might be N threads that are displaying
// messages and a single thread displaying directory-level
// linking and building messages. We need to make sure there's room for
// the single thread's message as well as ours. Since that single
// thread displays one line at a time (including CRLF) we must make sure
// that his display (as well as ours) doesn't inadvertantly scroll
// the status line at the top. We do this by guaranteeing that there is
// a blank line at the end.
// We are synchronized with the single top-level thread
// at a higher level than this routine via TTYCriticalSection. We
// are, thus, assured that we control the cursor completely.
// Stay off the LastRow
if (SaveRow == LastRow(ThreadState)) { USHORT RowTop = 2;
if (fStatus) { RowTop += SaveRowTop + (USHORT) NumberProcesses + 1; }
MoveRectangleUp ( RowTop, // Top
0, // Left
LastRow(ThreadState), // Bottom
LastCol(ThreadState), // Right
2, // NumRow
ScreenCell); // FillCell
SaveRow -= 2; SetCursorPosition(SaveRow, SaveCol); }
//
// Different color for the status line.
//
if (fStatusOutput) { Attribute = &StatusCell[1]; } else { Attribute = &ScreenCell[1]; } cb = (USHORT) strlen(p);
//
// Write out the string.
//
while (cb > 0) { ForceNewline = FALSE;
if (cb > 1) { if (p[cb - 1] == '\n' && p[cb - 2] == '\r') { cb -= 2; ForceNewline = TRUE; } }
if (cb >= ThreadState->cColTotal - SaveCol) { cbT = ThreadState->cColTotal - SaveCol; if (fFullErrors) ForceNewline = TRUE; } else { cbT = cb; }
WriteConsoleCells(p, cbT, SaveRow, SaveCol, Attribute); SetCursorPosition(SaveRow, SaveCol);
if (ForceNewline) { SaveCol = 0; SaveRow++; } else { SaveCol += cbT; }
if (!fFullErrors) { break; }
if (cb > cbT) { // we have more to go... do a newline
// If we're back at the beginning of the bottom line
if (SaveRow == LastRow(ThreadState)) { USHORT RowTop = 1;
if (fStatus) { RowTop += SaveRowTop + (USHORT) NumberProcesses + 1; }
// move window up one line (leaving two lines blank at bottom)
MoveRectangleUp ( RowTop, // Top
0, // Left
LastRow(ThreadState), // Bottom
LastCol(ThreadState), // Right
1, // NumRow
ScreenCell); // FillCell
SaveRow--; } SetCursorPosition(SaveRow, SaveCol); }
cb -= cbT; p += cbT; }
SetCursorPosition(SaveRow, SaveCol); }
//+---------------------------------------------------------------------------
//
// Function: WriteTTYLoggingErrors
//
// Synopsis: Writes a message to the appropriate log file and also the
// screen if specified.
//
// Arguments: [Warning] -- TRUE if the message is a warning
// [ThreadState] -- Info about output device
// [p] -- String
//
//----------------------------------------------------------------------------
VOID WriteTTYLoggingErrors( BOOL Warning, PTHREADSTATE ThreadState, LPSTR p ) { UINT cb; cb = strlen( p );
// ignore empty strings
if (0 == cb) return;
if (fErrorLog) { fwrite( p, 1, cb, Warning ? WrnFile : ErrFile ); } if (fShowWarningsOnScreen && Warning) { WriteTTY(ThreadState, p, FALSE); return; } if (!fErrorLog || !Warning) { WriteTTY(ThreadState, p, FALSE); }
if (!Warning && fErrorBaseline && !bBaselineFailure) { // don't check for a new failure if there is already one
if (NULL == pvBaselineContent || NULL == memfind(pvBaselineContent, cbBaselineContentSize, p, cb)) { // if there is no baseline file, or if the error is not found, we have new failure
bBaselineFailure = TRUE; } } }
//+---------------------------------------------------------------------------
//
// Function: RuntimeErrorFilter
//
// Synopsis: Filters output from the compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// (compiling, linking, etc...)
// [p] -- Message we're trying to parse.
// [FileName] -- [out] Filename in message
// [LineNumber] -- [out] Line number in message
// [Message] -- [out] Message number (for post processing)
// [Warning] -- [out] TRUE if message is a warning.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 26-Jul-94 sbonev Created
//
// Notes:
//
// This routine filters strings that are not standard tool output.
// Any unexpected error checking should go here
//
//----------------------------------------------------------------------------
BOOL RuntimeErrorFilter( PTHREADSTATE ThreadState, LPSTR p, LPSTR *FileName, LPSTR *LineNumber, LPSTR *Message, BOOL *Warning ) { if (strstr(p, "Exception occured:")) { *FileName = NULL; *LineNumber = NULL; *Message = p; *Warning = FALSE;
return TRUE; }
return FALSE; }
//+---------------------------------------------------------------------------
//
// Function: MsCompilerFilter
//
// Synopsis: Filters output from the compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// (compiling, linking, etc...)
// [p] -- Message we're trying to parse.
// [FileName] -- [out] Filename in message
// [LineNumber] -- [out] Line number in message
// [Message] -- [out] Message number (for post processing)
// [Warning] -- [out] TRUE if message is a warning.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 26-Jul-94 LyleC Created
//
// Notes:
//
// This routine filters strings in the MS compiler format. That is:
//
// {toolname} : {number}: {text}
//
// where:
//
// toolname If possible, the container and specific module that has
// the error. For instance, the compiler uses
// filename(linenum), the linker uses library(objname), etc.
// If unable to provide a container, use the tool name.
// number A number, prefixed with some tool identifier (C for
// compiler, LNK for linker, LIB for librarian, N for nmake,
// etc).
// test The descriptive text of the message/error.
//
// Accepted String formats are:
//
// container(module): error/warning NUM ...
// container(module) : error/warning NUM ...
// container (module): error/warning NUM ...
// container (module) : error/warning NUM ...
//
//----------------------------------------------------------------------------
BOOL MsCompilerFilter( PTHREADSTATE ThreadState, LPSTR p, LPSTR *FileName, LPSTR *LineNumber, LPSTR *Message, BOOL *Warning ) { LPSTR p1; BOOL fCommandLineWarning;
*Message = NULL;
p1 = p;
if (strstr(p, "see declaration of")) goto notRecognized;
if (strstr(p, "see previous definition of")) goto notRecognized;
if (strstr(p, "while compiling class-template member function")) goto notRecognized;
if (strstr(p, "see reference to function template instantiation")) goto notRecognized;
if (strstr(p, "Compiler error (")) { *Message = p; *Warning = FALSE; if ((p1 = strstr( p, "source=" ))) *LineNumber = p1+7; else *LineNumber = "1"; *FileName = ThreadState->ChildCurrentFile; return TRUE; }
if (!strncmp(p, "fatal error ", strlen("fatal error "))) { *Message = p; *Warning = FALSE; *LineNumber = "1"; *FileName = ThreadState->ChildCurrentFile; return TRUE; } else if (!strncmp(p, "error ", strlen("error "))) { // Takes care of some C# error messages.
*Message = p+strlen("error "); *Warning = FALSE; *LineNumber = "0"; *FileName = ThreadState->ChildCurrentFile; return TRUE; }
// First look for the " : " or "): " sequence.
while (*p1) { if ((p1[0] == ')') && (p1[1] == ' ')) p1++;
if ((p1[0] == ' ') || (p1[0] == ')')) { if (p1[1] == ':') { if (p1[2] == ' ') { *Message = p1 + 3; *p1 = '\0';
break; } else break; // No sense going any further
} else if ((p1[0] == ' ') && (p1[1] == '(')) p1++; else break; // No sense going any further
} else p1++; }
if (*Message != NULL) { // then figure out if this is an error or warning.
*Warning = TRUE; // Assume the best.
fCommandLineWarning = FALSE;
if (TestPrefix( Message, "error " ) || TestPrefix( Message, "fatal error " ) || TestPrefix( Message, "command line error " ) || TestPrefix( Message, "Compiler error " )) { *Warning = FALSE; } else if (TestPrefix( Message, "warning " )) { *Warning = TRUE; } else if (TestPrefix( Message, "command line warning " )) { // Command-line warnings don't count when considering whether
// warnings should be errors (under /WX).
*Warning = TRUE; fCommandLineWarning = TRUE; } else if (TestPrefix( Message, "LNK6" )) { // Linker notes should be ignored.
return (FALSE); }
if (!fCommandLineWarning && (ThreadState->ChildFlags & FLAGS_WARNINGS_ARE_ERRORS) != 0) { if (Substr( "X0000", *Message )) { *Warning = TRUE; // Special case this one. Never an error
} else { *Warning = FALSE; // Warnings treated as errors for this compile
} }
// Set the container name and look for the module paren's
*FileName = p; *LineNumber = NULL;
p1 = p;
while (*p1) { if (*p1 == '(' && p1[1] != ')') { *p1 = '\0'; p1++; *LineNumber = p1; while (*p1) { if (*p1 == ')') { *p1 = '\0'; break; } p1++; }
break; }
p1++; }
return (TRUE); }
notRecognized: return RuntimeErrorFilter(ThreadState, p, FileName, LineNumber, Message, Warning);
}
//+---------------------------------------------------------------------------
//
// Function: FormatMsErrorMessage
//
// Synopsis: Take the information obtained from MsCompilerFilter,
// reconstruct the error message, and print it to the screen.
//
//----------------------------------------------------------------------------
VOID FormatMsErrorMessage( PTHREADSTATE ThreadState, LPSTR FileName, LPSTR LineNumber, LPSTR Message, BOOL Warning ) { char *DirectoryToUse; SIZE_T BufferUsed, BufferLen; LPSTR pszBuffer;
if (fColorConsole) { if (Warning) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_WARNING); else SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_ERROR); }
if (ThreadState->ChildState == STATE_LIBING) { if (Warning) { RunningTotals.NumberLibraryWarnings++; ThreadState->BuildMetrics.NumberLibraryWarnings++; } else { RunningTotals.NumberLibraryErrors++; ThreadState->BuildMetrics.NumberLibraryErrors++; } }
else if ((ThreadState->ChildState == STATE_LINKING) || (ThreadState->ChildState == STATE_BINPLACE)) { if (Warning) { RunningTotals.NumberLinkWarnings++; ThreadState->BuildMetrics.NumberLinkWarnings++; } else { RunningTotals.NumberLinkErrors++; ThreadState->BuildMetrics.NumberLinkErrors++; } } else { if (Warning) { RunningTotals.NumberCompileWarnings++; ThreadState->BuildMetrics.NumberCompileWarnings++; } else { RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileErrors++; if (ThreadState->CompileDirDB) { ThreadState->CompileDirDB->DirFlags |= DIRDB_COMPILEERRORS; } } }
// start filling up the buffer for the XML log file;
// however, we are going to use the same buffer even if no XML log is requested
// safe to use - it is protected by the TTY critical section
ZeroMemory(szXMLBuffer, sizeof(szXMLBuffer)); pszBuffer = szXMLBuffer; BufferUsed = 0; BufferLen = sizeof(szXMLBuffer) - 1;
if (fParallel && !fNoThreadIndex ) { BufferUsed = _snprintf(pszBuffer, BufferLen, "%d>", ThreadState->ThreadIndex); pszBuffer += BufferUsed; BufferLen -= BufferUsed; }
if (FileName) { DirectoryToUse = ThreadState->ChildCurrentDirectory;
if (TestPrefix( &FileName, CurrentDirectory )) { DirectoryToUse = CurrentDirectory; if (*FileName == '\\') { FileName++; } }
if (TestPrefix( &FileName, ThreadState->ChildCurrentDirectory )) { DirectoryToUse = ThreadState->ChildCurrentDirectory; if (*FileName == '\\') { FileName++; } }
// check for some special cases when the FileName is actually the tool name
// and should not be prepended with path
if (ThreadState->FilterProc == CoffFilter && _stricmp(FileName, "link") == 0 || ThreadState->FilterProc == CoffFilter && _stricmp(FileName, "lib") == 0) { BufferUsed = strlen(strncat(pszBuffer, FileName, BufferLen)); } else { BufferUsed = strlen(strncat(pszBuffer, FormatPathName( DirectoryToUse, FileName), BufferLen)); } pszBuffer += BufferUsed; BufferLen -= BufferUsed; }
if (LineNumber && strlen(LineNumber) > 0) { BufferUsed = _snprintf(pszBuffer, BufferLen, "(%s)%s", LineNumber, FileName && strlen(FileName) ? " : " : ""); pszBuffer += BufferUsed; BufferLen -= BufferUsed; }
_snprintf(pszBuffer, BufferLen, "%s %s", Warning ? "warning" : "error", Message);
if (Warning) { ThreadState->BuildMetrics.NumberActWarnings++; } else { ThreadState->BuildMetrics.NumberActErrors++; }
WriteTTYLoggingErrors( Warning, ThreadState, szXMLBuffer); WriteTTYLoggingErrors( Warning, ThreadState, "\r\n" );
if (fXMLOutput || fXMLFragment) { XMLThreadWrite(ThreadState, "<%s MESSAGE=\"%s\"/>", Warning ? "WARNING" : "ERROR", XMLEncodeBuiltInEntities(szXMLBuffer, sizeof(szXMLBuffer))); }
if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), DefaultConsoleAttributes); }
//+---------------------------------------------------------------------------
//
// Function: PassThrough
//
// Synopsis: Keep track of and print the given message without any
// filtering.
//
// Arguments: [ThreadState] --
// [p] -- Message
// [Warning] -- TRUE if warning
//
// Returns: FALSE
//
//----------------------------------------------------------------------------
BOOL PassThrough( PTHREADSTATE ThreadState, LPSTR p, BOOL Warning ) { if (fColorConsole) { if (Warning) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_WARNING); else SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_ERROR); }
if (ThreadState->ChildState == STATE_VSTOOL) { if (Warning) { RunningTotals.NumberVSToolWarnings++; ThreadState->BuildMetrics.NumberVSToolWarnings++; } else { RunningTotals.NumberVSToolErrors++; ThreadState->BuildMetrics.NumberVSToolErrors++; } } else if (ThreadState->ChildState == STATE_LIBING) { if (Warning) { RunningTotals.NumberLibraryWarnings++; ThreadState->BuildMetrics.NumberLibraryWarnings++; } else { RunningTotals.NumberLibraryErrors++; ThreadState->BuildMetrics.NumberLibraryErrors++; } } else if (ThreadState->ChildState == STATE_LINKING) { if (Warning) { RunningTotals.NumberLinkWarnings++; ThreadState->BuildMetrics.NumberLinkWarnings++; } else { RunningTotals.NumberLinkErrors++; ThreadState->BuildMetrics.NumberLinkErrors++; } } else if (ThreadState->ChildState == STATE_BINPLACE) { if (Warning) { RunningTotals.NumberBinplaceWarnings++; ThreadState->BuildMetrics.NumberBinplaceWarnings++; } else { RunningTotals.NumberBinplaceErrors++; ThreadState->BuildMetrics.NumberBinplaceErrors++; } } else { if (Warning) { RunningTotals.NumberCompileWarnings++; ThreadState->BuildMetrics.NumberCompileWarnings++; } else { RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileErrors++; if (ThreadState->CompileDirDB) { ThreadState->CompileDirDB->DirFlags |= DIRDB_COMPILEERRORS; } } }
// safe to use. it is protected by the TTY critical section
ZeroMemory(szXMLBuffer, sizeof(szXMLBuffer));
if (fParallel && !fNoThreadIndex) { _snprintf(szXMLBuffer, sizeof(szXMLBuffer)-1, "%d>%s", ThreadState->ThreadIndex, p); } else { strncpy(szXMLBuffer, p, sizeof(szXMLBuffer)-1); }
if (Warning) { ThreadState->BuildMetrics.NumberActWarnings++; } else { ThreadState->BuildMetrics.NumberActErrors++; }
WriteTTYLoggingErrors( Warning, ThreadState, szXMLBuffer ); WriteTTYLoggingErrors( Warning, ThreadState, "\r\n" );
if (fXMLOutput || fXMLFragment) { XMLThreadWrite(ThreadState, "<%s MESSAGE=\"%s\"/>", Warning ? "WARNING" : "ERROR", XMLEncodeBuiltInEntities(szXMLBuffer, sizeof(szXMLBuffer))); }
if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), DefaultConsoleAttributes);
return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: PassThroughFilter
//
// Synopsis: Straight pass-through filter for compiler messages
//
//----------------------------------------------------------------------------
BOOL PassThroughFilter( PTHREADSTATE ThreadState, LPSTR p ) { return PassThrough( ThreadState, p, FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: NMakeFilter
//
// Synopsis: Filters output from NMAKE so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the build
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 10-Jun-99 patbr Created
//
//----------------------------------------------------------------------------
BOOL NMakeFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning); return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: BisonFilter
//
// Synopsis: Filters output from the bison compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// (compiling, linking, etc...)
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 04-Oct-99 TomSe Created
//
// Notes:
//
// This routine filters strings in the bison compiler format. That is:
//
// Accepted String formats are:
//
// ("myfile.y", line 3) error: unknown character: #
// "myfile.y", line 83: no input grammar
// vapi.y contains 1 useless nonterminal and 1 useless rule
//
//----------------------------------------------------------------------------
BOOL BisonFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName = NULL; LPSTR LineNumber = NULL; LPSTR Message = NULL; BOOL Warning = TRUE;
// First colon marks beginnning of message.
LPSTR p1 = strchr(p,':');
if (p1) { Message = p1 + 1; *p1 = '\0';
// Get filename, line number.
p1 = p;
do { Warning = FALSE;
// Skip (.
if ( '(' == *p1 ) { p1++; }
// Skip over leading quote in filename.
if ( '"' != *p1 ) { // Unexpected format.
break; } p1++;
FileName = p1;
// Look for trailing quote in filename.
p1 = strchr( p1, '"'); if (NULL==p1) { // Unexpected format.
FileName = NULL; break; }
*p1 = '\0';
p1++;
if (0 !=strncmp( p1, ", line ", 7)) { // Unexpected format.
FileName = NULL; break; } p1 += 7;
LineNumber = p1;
while (isdigit(*p1)) { p1++; }
*p1 = '\0'; } while (0); } else { // Take whole string as message if no colon is found.
Message = p; }
if (NULL==FileName) { FileName = ThreadState->ChildCurrentFile; }
FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning );
// This was a warning or error.
return TRUE ; }
//+---------------------------------------------------------------------------
//
// Function: C510Filter
//
// Synopsis: Compiler filter which strips out unwanted warnings.
//
// Arguments: [ThreadState] --
// [p] --
//
//----------------------------------------------------------------------------
BOOL C510Filter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning; LPSTR t; PFILEREC FileDB;
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning ) ) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else {
// If we're compiling, then the compiler spits out various bit of info,
// namely:
// 1. filename alone on a line (.c, .cpp, .cxx)
// 2. "Generating Code..." when the back-end is invoked
// 3. "Compiling..." when the front-end is invoked again
if (ThreadState->ChildState == STATE_COMPILING) {
if (0 == strcmp(p, "Generating Code...")) {
strcpy( ThreadState->ChildCurrentFile, "Generating Code..." ); PrintChildState(ThreadState, p, NULL); return FALSE; }
t = strrchr(p, '.'); if (t != NULL && (0 == strcmp(t, ".cxx") || 0 == strcmp(t, ".cpp") || 0 == strcmp(t, ".c"))) {
strcpy( ThreadState->ChildCurrentFile, IsolateLastToken(p, ' ')); // strcpy(ThreadState->ChildCurrentFile, p);
if (strstr(ThreadState->ChildCurrentFile, ".cxx") || strstr(ThreadState->ChildCurrentFile, ".cpp")) { ThreadState->ChildFlags |= FLAGS_CXX_FILE; } else { ThreadState->ChildFlags &= ~FLAGS_CXX_FILE; }
FileDB = NULL; if (ThreadState->CompileDirDB) { RunningTotals.NumberCompiles++; ThreadState->BuildMetrics.NumberCompileWarnings++; CopyString( // fixup path string
ThreadState->ChildCurrentFile, ThreadState->ChildCurrentFile, TRUE);
if (!fQuicky) { FileDB = FindSourceFileDB( ThreadState->CompileDirDB, ThreadState->ChildCurrentFile, NULL); } }
PrintChildState(ThreadState, p, FileDB); return FALSE; } }
return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: MSBCFilter
//
// Synopsis: Filters output from the Basic compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 08-Dec-09 marioch Created
//
//----------------------------------------------------------------------------
BOOL MSBCFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (!strncmp(p, "BC Compiler error", 17)) { FormatMsErrorMessage( ThreadState, ThreadState->ChildCurrentFile, NULL, p, FALSE ); return TRUE; }
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { char *pErrorMsg; if (NULL != (pErrorMsg = strstr(p, "error BC"))) { FormatMsErrorMessage( ThreadState, ThreadState->ChildCurrentFile, NULL, pErrorMsg+6, FALSE ); return TRUE; } return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: MSJVCFilter
//
// Synopsis: Filters output from the JVC compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 24-Mar-99 patbr Created
//
//----------------------------------------------------------------------------
BOOL MSJVCFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (!strncmp(p, "fatal error J", 13) || !strncmp(p, "error J", 7)) { if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_ERROR); WriteTTYLoggingErrors( FALSE, ThreadState, p ); WriteTTYLoggingErrors( FALSE, ThreadState, "\r\n" ); if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), DefaultConsoleAttributes); RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileErrors++; return TRUE; }
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: MSCoolFilter
//
// Synopsis: Filters output from the COOL compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 2-Jun-99 patbr Created
//
//----------------------------------------------------------------------------
BOOL MSCoolFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (!strncmp(p, "fatal error SC", 14) || !strncmp(p, "error SC", 8)) { if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), COLOR_ERROR); WriteTTYLoggingErrors( FALSE, ThreadState, p ); WriteTTYLoggingErrors( FALSE, ThreadState, "\r\n" ); if (fColorConsole) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), DefaultConsoleAttributes); RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileErrors++; return TRUE; }
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: MSCSharpFilter
//
// Synopsis: Filters output from the CSharp compiler so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 6-Nov-00 sbonev Copy of MSCoolFilter
//
//----------------------------------------------------------------------------
BOOL MSCSharpFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (!strncmp(p, "fatal error CS", 14) || !strncmp(p, "error CS", 8)) { Message = p + strlen("error "); FileName = "."; LineNumber = NULL; Warning = FALSE; FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileErrors++; return TRUE; }
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
BOOL MSPostBuildFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning; LPSTR p1; FileName = NULL; Message = p +10; LineNumber = NULL; Warning = FALSE;
if (strstr (p,"ERROR:")) FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); RunningTotals.NumberCompileErrors++; ThreadState->BuildMetrics.NumberCompileWarnings++; return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: ResGenFilter
//
// Synopsis: Filters output from the .NET Resource Generator so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 22-Mar-01 sbonev
//
//----------------------------------------------------------------------------
BOOL ResGenFilter( PTHREADSTATE ThreadState, LPSTR p ) { if (!strncmp(p, "ResGen: Error: ", 15)) { LPSTR LineNumber = _strdup(p); LPSTR pch = LineNumber; LPCSTR Line = strstr(p, ". Line "); LPCSTR Pos = strstr(p, ", position ");
if (!LineNumber) { return FALSE; }
LineNumber[0] = 0;
// put line,pos info if available
if (NULL != Line) { Line += 7; while (isdigit(*Line)) *pch++ = *Line++;
if (NULL != Pos) { Pos += 11; *pch++ = ','; while (isdigit(*Pos)) *pch++ = *Pos++; } }
FormatMsErrorMessage( ThreadState, ThreadState->ChildCurrentFile, LineNumber, strlen(ThreadState->ChildCurrentFile) > 0 ? p + 15 : p, // display full message if there is no filename
FALSE);
free(LineNumber); return TRUE; }
return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: CScriptFilter
//
// Synopsis: Filters output from Windows Script Host so we know what's happening
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 22-Mar-01 sbonev
//
//----------------------------------------------------------------------------
BOOL CScriptFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (NULL != strstr(p, "Microsoft JScript runtime error:") || NULL != strstr(p, "Microsoft JScript compilation error:") || NULL != strstr(p, "Microsoft VBScript runtime error:") || NULL != strstr(p, "Microsoft VBScript compilation error:")) {
// just display the message
PassThrough( ThreadState, p, FALSE ); return TRUE;
}
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning)) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: ToolNotFoundFilter
//
// Synopsis: Filters output from the build looking for "name not recognized"
//
// Arguments: [ThreadState] -- State of thread watching the compiler
// [p] -- Message we're trying to parse.
//
// Returns: TRUE - Message is an error or warning
// FALSE - Message is not an error or a warning
//
// History: 10-Dec-98 patbr Created
//
//----------------------------------------------------------------------------
BOOL ToolNotFoundFilter( PTHREADSTATE ThreadState, LPSTR p ) { if ((!strncmp(p, "The name specified is not recognized", 36)) || (!strncmp(p, "internal or external command", 28))) { FormatMsErrorMessage( ThreadState, ThreadState->ChildCurrentFile, NULL, p, FALSE ); return TRUE; }
return (FALSE); }
//+---------------------------------------------------------------------------
//
// Function: MSToolFilter
//
//----------------------------------------------------------------------------
BOOL MSToolFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning ) ) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
BOOL LinkFilter( PTHREADSTATE ThreadState, LPSTR p );
//+---------------------------------------------------------------------------
//
// Function: LinkFilter1
//
//----------------------------------------------------------------------------
BOOL LinkFilter1( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR p1; char buffer[ 256 ];
if (p[ strlen( p ) - 1 ] == ':') { return ( LinkFilter( ThreadState, p ) ); }
p1 = p; while (*p1) { if (*p1 == '(') { *p1++ = 0; if (*p1 == '.' && p1[1] == '\\') { p1 += 2; } FileName = p1; while (*p1) { if (*p1 == ')') { *p1++ = 0; strcpy( buffer, "L2029: Unresolved external reference to " ); strncat( buffer, ThreadState->UndefinedId, sizeof(buffer) - strlen("L2029: Unresolved external reference to ")); FormatMsErrorMessage( ThreadState, FileName, "1", buffer, FALSE ); return ( TRUE ); } else { p1++; } } } else { p1++; } }
return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: LinkFilter
//
//----------------------------------------------------------------------------
BOOL LinkFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName = NULL; LPSTR LineNumber; LPSTR Message; BOOL Warning; LPSTR p1;
p1 = p; while (*p1) { if (*p1 == ':') { if (p1[-1] == ']') { return ( FALSE ); }
if (p1[-1] == ' ' && p1[1] == ' ') { if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning ) ) {
if (!Warning || !(_strnicmp(Message, "L4021", 5) || _strnicmp(Message, "L4038", 5) || _strnicmp(Message, "L4046", 5))) { if (LineNumber) FileName = LineNumber; if (FileName && FileName[0] == '.' && FileName[1] == '\\') { FileName += 2; } FormatMsErrorMessage( ThreadState, FileName, "1", Message, FALSE ); return ( TRUE ); } }
FormatMsErrorMessage( ThreadState, FileName, "1", Message, TRUE );
return ( TRUE ); }
if (p1[-1] == ')') { p1 -= 11; if (p1 > p && !strcmp( p1, " in file(s):" )) { strcpy( ThreadState->UndefinedId, IsolateFirstToken( &p, ' ' ) ); ThreadState->FilterProc = LinkFilter1; return ( TRUE ); } }
return ( FALSE ); } else { p1++; } }
return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: CoffFilter
//
//----------------------------------------------------------------------------
BOOL CoffFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning;
if (MsCompilerFilter( ThreadState, p, &FileName, &LineNumber, &Message, &Warning ) ) { FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } else { return ( FALSE ); } }
//+---------------------------------------------------------------------------
//
// Function: ClRiscFilter
//
// Synopsis: Risc compiler filter
//
// Note: It may be possible to remove this filter.
//
//----------------------------------------------------------------------------
BOOL ClRiscFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FileName; LPSTR LineNumber; LPSTR Message; BOOL Warning; LPSTR q;
if (TestPrefix( &p, "cfe: " )) { if (strncmp(p, "Error: ", strlen("Error: ")) == 0) { p += strlen("Error: "); Warning = FALSE;
} else if (strncmp(p, "Warning: ", strlen("Warning: ")) == 0) { p += strlen("Warning: "); Warning = TRUE; } else { return (FALSE); }
q = p; if (p = strstr( p, ".\\\\" )) { p += 3; } else { p = q; }
FileName = p; while (*p > ' ') { if (*p == ',' || (*p == ':' && *(p+1) == ' ')) { *p++ = '\0'; break; }
p++; }
if (*p != ' ') { return ( FALSE ); }
*p++ = '\0';
if (strcmp(p, "line ") == 0) { p += strlen("line ");
}
LineNumber = p; while (*p != '\0' && *p != ':') { p++; }
if (*p != ':') { return ( FALSE ); }
*p++ = '\0'; if (*p == ' ') { Message = p+1; ThreadState->LinesToIgnore = 2;
FormatMsErrorMessage( ThreadState, FileName, LineNumber, Message, Warning ); return ( TRUE ); } } //
// If we did not recognize the cfe compiler, pass it to the MS compiler
// message filter
//
return ( C510Filter( ThreadState, p ) ); }
//+---------------------------------------------------------------------------
//
// Function: MSXSLFilter
//
// Synopsis: MSXSL filter
//
//----------------------------------------------------------------------------
BOOL MSXSLFilter( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR FirstLine; LPSTR FileName; LPSTR LineNumber; LPSTR ColumnNumber; LPSTR Message;
if (strncmp(p, "Error occurred while ", strlen("Error occurred while ")) == 0) { FirstLine = p; p = ThreadState->ChildOutput->_ptr + 2;
if (strncmp(p, "Code: ", strlen("Code: ")) == 0) { p = strchr(p, '\r') + 2; }
if (strncmp(p, "URL: ", strlen("URL: ")) == 0) { p += strlen("URL: "); while (*p == ' ') { p++; } p += strlen("file:///"); FileName = p; while (*p != '\r') { if (*p == '/') { *p = '\\'; } p++; } *p = '\0'; p += 2;
if (strncmp(p, "Line: ", strlen("Line: ")) == 0) { p += strlen("Line: "); while (*p == ' ') { p++; } LineNumber = p; p = strchr(p, '\r'); *p = '\0'; p += 2; } if (strncmp(p, "Column: ", strlen("Column: ")) == 0) { p += strlen("Column: "); while (*p == ' ') { p++; } ColumnNumber = p; p = strchr(p, '\r'); *p = '\0'; p += 2; strcat (LineNumber, ", "); strcat (LineNumber, ColumnNumber); } } else { FileName = strchr(FirstLine, '\''); if (FileName) { FileName++; *strchr(FileName, '\'') = '\0'; } LineNumber = NULL; }
while (*p == '\n' || *p == '\r' || *p == ' ') { p++; } Message = p; while (*p != '\n' && *p != '\r') { p++; } *p = '\0';
FormatMsErrorMessage (ThreadState, FileName, LineNumber, Message, FALSE);
} else { return (FALSE); }
return ( TRUE ); }
//+---------------------------------------------------------------------------
//
// Function: MgClientFilter
//
//----------------------------------------------------------------------------
BOOL MgClientFilter( PTHREADSTATE ThreadState, LPSTR p ) { return ( PassThrough( ThreadState, p, TRUE ) ); }
BOOL fAlreadyUnknown = FALSE;
//+---------------------------------------------------------------------------
//
// Function: DetermineChildState
//
// Synopsis: Parse the message given by the compiler (or whatever) and try
// to figure out what it's doing.
//
// Arguments: [ThreadState] -- Current thread state
// [p] -- New message string
//
// Returns: TRUE if we figured it out, FALSE if we didn't recognize
// anything.
//
//----------------------------------------------------------------------------
BOOL DetermineChildState( PTHREADSTATE ThreadState, LPSTR p ) { PFILEREC FileDB; char CheckFileName[300]; LPSTR pCheckFileName; LPSTR FileName; BOOL fPrintChildState = TRUE;
//
// ************ Determine what state the child process is in.
// (Compiling, linking, running MIDL, etc.)
//
if ( TestPrefixPath( &p, "rc ") || TestPrefixPath( &p, "rc.exe ")) { if (*p == ':') return FALSE; // This is a warning/error string
if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } ThreadState->FilterProc = MSToolFilter; ThreadState->ChildState = STATE_COMPILING; ThreadState->ChildFlags = 0; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "rc16 ") ) { if (*p == ':') return FALSE; // This is a warning/error string
if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } ThreadState->FilterProc = MSToolFilter; ThreadState->ChildState = STATE_COMPILING; ThreadState->ChildFlags = 0; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if ( TestPrefixPath( &p, "cl " ) || TestPrefixPath( &p, "cl.exe " ) || TestPrefixPath( &p, "clarm " ) || TestPrefixPath( &p, "clarm.exe " ) || TestPrefixPath( &p, "covc " ) || TestPrefixPath( &p, "covc.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = C510Filter; ThreadState->ChildFlags = 0; if ( strstr( p, "/WX" ) != NULL || strstr( p, "-WX" ) != NULL) { ThreadState->ChildFlags |= FLAGS_WARNINGS_ARE_ERRORS; } if ((strstr( p, "/EP " ) != NULL) || (strstr( p, "/E " ) != NULL) || (strstr( p, "/P " ) != NULL) || (strstr( p, "-EP " ) != NULL) || (strstr( p, "-E " ) != NULL) || (strstr( p, "-P " ) != NULL) ) { if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
strcpy( ThreadState->ChildCurrentFile,IsolateLastToken( p, ' ' ) ); if ( strstr( p, ".s" ) != NULL ) ThreadState->ChildState = STATE_S_PREPROC; else ThreadState->ChildState = STATE_C_PREPROC; } else if ( (pch = strstr( p, "/Yc" )) != NULL ) { size_t namelen = strcspn( pch+3, " \t" ); if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
ThreadState->ChildState = STATE_PRECOMP; strncpy( ThreadState->ChildCurrentFile, pch + 3, namelen); ThreadState->ChildCurrentFile[namelen] = '\0'; } else { if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, "" ); fPrintChildState = FALSE; } } else if ( TestPrefixPath( &p, "csc " ) || TestPrefixPath( &p, "csc.exe " ) ) { ThreadState->ChildState = STATE_LINKING; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSCSharpFilter; strcpy(ThreadState->ChildCurrentFile, IsolateLastToken(p, ' ')); } else if ( TestPrefixPath( &p, "cl16 " )) { if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = C510Filter; ThreadState->ChildFlags = 0; if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' )); } else
if (TestPrefixPath( &p, "bc " ) || TestPrefixPath( &p, "bc.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
if (IsolateFirstToken( &p, '-' )) { if (*p == '\0') return FALSE; // This is a warning/error string
while (*p != 'i') { if (!IsolateFirstToken( &p, '-' )) { break; } }
p++; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ));
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSBCFilter;
ThreadState->ChildState = STATE_COMPILING; } } else
if (TestPrefixPath( &p, "vbc " ) || TestPrefixPath( &p, "vbc.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSBCFilter;
ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "jvc " ) || TestPrefixPath( &p, "jvc.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSJVCFilter;
if (((pch = strstr( p, "*.java" )) != NULL ) || (((pch = strstr( p, ".java" )) != NULL ) && ((pch2 = strstr( pch+1, ".java" )) != NULL ))) { ThreadState->ChildState = STATE_BATCHCOMPILE; // batch compiles will be counted by progress output
if (getenv("JVC_TERSE") != NULL) strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, '\\' ) ); else return FALSE; } else { ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } } else
if (TestPrefixPath( &p, "coolc " ) || TestPrefixPath( &p, "coolc.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSCoolFilter;
if (((pch = strstr( p, "*.cool" )) != NULL ) || (((pch = strstr( p, ".cool" )) != NULL ) && ((pch2 = strstr( pch+1, ".cool" )) != NULL ))) { ThreadState->ChildState = STATE_BATCHCOMPILE; // batch compiles will be counted by progress output
return FALSE; } else { ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } } else
if (TestPrefixPath( &p, "resgen " ) || TestPrefixPath( &p, "resgen.exe " ) || TestPrefixPath( &p, "ResGen: Error:" )) { //
// resgen usage:
// ResGen inputFile.ext [outputFile.ext]
// no wildcards
if (*(p-1) == ':') { // this is an error string
if (ThreadState->FilterProc != ResGenFilter) { // switch the filter proc if we didn't know that ResGen was running
ThreadState->FilterProc = ResGenFilter; strcpy( ThreadState->ChildCurrentFile, "" ); } return FALSE; }
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = ResGenFilter;
ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ) ); } else
if (TestPrefixPath( &p, "cscript " ) || TestPrefixPath( &p, "cscript.exe " )) { //
// cscript usage:
// CScript [option...] scriptname.extension [option...] [arguments...]
// options are prefixed with / or -
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->ChildState = STATE_VSTOOL; ThreadState->FilterProc = CScriptFilter; strcpy( ThreadState->ChildCurrentFile, "" ); // don't care about the name; it would be displayed on error
} else
if (TestPrefixPath( &p, "docchecker " ) || TestPrefixPath( &p, "docchecker.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_DOCCHECKING; ThreadState->ChildTarget = "all platforms"; strcpy( ThreadState->ChildCurrentFile, "" ); } else
if (TestPrefixPath( &p, "scc " ) || TestPrefixPath( &p, "scc.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSToolFilter;
if (((pch = strstr( p, "*.sc" )) != NULL ) || (((pch = strstr( p, ".sc" )) != NULL ) && ((pch2 = strstr( pch+1, ".sc" )) != NULL ))) { ThreadState->ChildState = STATE_BATCHCOMPILE; // batch compiles will be counted by progress output
return FALSE; } else { ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } } else
if (TestPrefixPath( &p, "wfctosafec " ) || TestPrefixPath( &p, "wfctosafec.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSToolFilter;
if (((pch = strstr( p, "*.sc" )) != NULL ) || (((pch = strstr( p, ".sc" )) != NULL ) && ((pch2 = strstr( pch+1, ".sc" )) != NULL ))) { ThreadState->ChildState = STATE_BATCHCOMPILE; // batch compiles will be counted by progress output
return FALSE; } else { ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } } else
if (TestPrefixPath( &p, "ml " ) || TestPrefixPath( &p, "ml.exe " ) || TestPrefix( &p, "ml64 " ) || TestPrefix( &p, "ml64.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildState = STATE_ASSEMBLING; ThreadState->ChildFlags = 0; if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "masm ") || TestPrefixPath( &p, "masm.exe ") || TestPrefixPath( &p, "armasm ") || TestPrefixPath( &p, "armasm.exe ") || TestPrefixPath( &p, "masm386 ") || TestPrefixPath( &p, "masm386.exe ")) { if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildState = STATE_ASSEMBLING; ThreadState->ChildFlags = 0; if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
if (strstr(p, ",")) { strcpy( ThreadState->ChildCurrentFile, IsolateLastToken(IsolateFirstToken(&p,','), ' ')); } else { strcpy( ThreadState->ChildCurrentFile, IsolateLastToken(IsolateFirstToken(&p,';'), ' ')); }
} else
if (TestPrefixPath( &p, "lib " ) || TestPrefixPath( &p, "lib.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; } if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } ThreadState->FilterProc = CoffFilter; ThreadState->ChildFlags = 0; if (TestPrefix( &p, "-out:" )) { ThreadState->LinesToIgnore = 1; ThreadState->ChildState = STATE_LIBING; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ) ); } else if (TestPrefix( &p, "-def:" )) { ThreadState->LinesToIgnore = 1; ThreadState->ChildState = STATE_LIBING; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ) ); if (TestPrefix( &p, "-out:" )) { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ) ); } } else { return FALSE; } } else
if (TestPrefixPath( &p, "implib " ) || TestPrefixPath( &p, "implib.exe " ) || TestPrefixPath( &p, "lib16 " ) || TestPrefixPath( &p, "lib16.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; } if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_LIBING; if (strstr(p, ";")) { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ';' )); } else { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' )); } } else
if (TestPrefixPath( &p, "link " ) || TestPrefixPath( &p, "link.exe " ) || TestPrefixPath( &p, "covlink ") || TestPrefixPath( &p, "covlink.exe ")) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; } if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; } ThreadState->FilterProc = CoffFilter; ThreadState->ChildFlags = 0; if (TestPrefix( &p, "-out:" )) { ThreadState->LinesToIgnore = 2; ThreadState->ChildState = STATE_LINKING; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' ) ); } } else
if (TestPrefixPath( &p, "link16" ) ) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; } if (strstr( p, "amd64") || strstr( p, "AMD64")) { ThreadState->ChildTarget = Amd64TargetMachine.Description; } else if (strstr( p, "i386") || strstr( p, "I386")) { ThreadState->ChildTarget = i386TargetMachine.Description; } else if (strstr( p, "ia64") || strstr( p, "IA64")) { ThreadState->ChildTarget = ia64TargetMachine.Description; } else if (strstr( p, "arm") || strstr( p, "ARM")) { ThreadState->ChildTarget = ARMTargetMachine.Description; } else { ThreadState->ChildTarget = "unknown target"; }
ThreadState->FilterProc = LinkFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_LINKING; p = IsolateLastToken(p, ' '); if (strstr(p, ";")) { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ';' )); } else { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ',' )); }
} else
if ( TestPrefixPath( &p, "bscmake " ) || TestPrefixPath( &p, "bscmake.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_BSCMAKING; ThreadState->ChildTarget = "all platforms"; if ( (pch = strstr( p, "/o" )) != NULL ) { size_t namelen; pch2 = pch + 3; if ( *pch2 == '"' ) pch2++; namelen = strcspn( pch2, " \t\"" ); strncpy( ThreadState->ChildCurrentFile, pch2, namelen ); ThreadState->ChildCurrentFile[namelen] = '\0'; } } else
if (TestPrefixPath( &p, "icl ")) { while (*p == ' ') { p++; } ThreadState->ChildState = STATE_COMPILING; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = ia64TargetMachine.Description; ThreadState->FilterProc = C510Filter;
strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "mktyplib " ) || TestPrefixPath( &p, "mktyplib.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_MKTYPLIB; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = C510Filter;
strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefix( &p, "MC: Compiling " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_MC; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = C510Filter;
strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "midl " ) || TestPrefixPath( &p, "midl.exe " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_MIDL; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = C510Filter;
strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else
if (TestPrefixPath( &p, "asn1 " )) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_ASN; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = C510Filter;
strcpy(ThreadState->ChildCurrentFile, IsolateLastToken(p, ' ')); } else
if (TestPrefix( &p, "Build_Status " )) { while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_STATUS; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = ""; ThreadState->FilterProc = C510Filter;
strcpy( ThreadState->ChildCurrentFile, "" ); }
else if (TestPrefixPath( &p, "binplace " )) { if (*p == ':') return FALSE; // This is a warning/error string
RunningTotals.NumberBinplaces++; ThreadState->BuildMetrics.NumberBinplaces++;
while (*p == ' ') { p++; }
// If this is a standard link/binplace step, don't tell the
// user what's going on, just pass any errors/warnings to
// the output. If this is a straight binplace, list the state.
if (ThreadState->ChildState == STATE_LINKING) { ThreadState->ChildState = STATE_BINPLACE; ThreadState->ChildFlags = 0; ThreadState->FilterProc = MSToolFilter; return TRUE; } else { ThreadState->ChildState = STATE_BINPLACE; ThreadState->ChildFlags = 0; ThreadState->FilterProc = MSToolFilter; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } }
else if (TestPrefixPath( &p, "ctc " ) || TestPrefixPath( &p, "ctc.exe " )) { size_t namelen; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_CTCOMPILING; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSToolFilter; while (*p == '-') { p = p + strcspn( p, " \t" ); while (*p == ' ') p++; } namelen = strcspn( p, " \t" ); strncpy( ThreadState->ChildCurrentFile, p, namelen ); ThreadState->ChildCurrentFile[namelen] = '\0'; }
else if (TestPrefixPath( &p, "idheader " )) { size_t namelen; if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_VSTOOL; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSToolFilter; namelen = strcspn( p, " \t" ); strncpy( ThreadState->ChildCurrentFile, p, namelen ); ThreadState->ChildCurrentFile[namelen] = '\0'; }
else if (TestPrefixPath( &p, "bison ")) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_VSTOOL; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSToolFilter; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); } else if ((TestPrefix( &p, "packthem " )) || (TestPrefix( &p, "..\\packthem " ))) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') p++;
ThreadState->ChildTarget = i386TargetMachine.Description;
ThreadState->FilterProc = CoffFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_PACKING;
if (TestPrefix( &p, "-o" )) { strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' )); } }
else if (TestPrefixPath( &p, "gnu_bison ")) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->ChildState = STATE_VSTOOL; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = BisonFilter; strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, ' ' ) ); }
else if ( TestPrefixPath( &p, "vsautodoc " ) || TestPrefixPath( &p, "vsautodoc.exe " )) { LPSTR pch, pch2; if (*p == ':') return FALSE; // This is a warning/error string
ThreadState->FilterProc = MSToolFilter; ThreadState->ChildFlags = 0; ThreadState->ChildState = STATE_AUTODOCING; ThreadState->ChildTarget = "all platforms"; if ( (pch = strstr( p, "/o" )) != NULL ) { size_t namelen; pch2 = pch + 3; if ( *pch2 == '"' ) pch2++; namelen = strcspn( pch2, " \t\"" ); strncpy( ThreadState->ChildCurrentFile, pch2, namelen ); ThreadState->ChildCurrentFile[namelen] = '\0'; } }
else if (TestPrefix( &p, "msxsl " ) ) { if (*p == ':') return FALSE; // This is a warning/error string
while (*p == ' ') { p++; }
ThreadState->FilterProc = MSXSLFilter; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->ChildState = STATE_COMPILING; strcpy( ThreadState->ChildCurrentFile, IsolateFirstToken( &p, ' ' )); } else if (TestPrefix( &p, "POSTBUILD:" ) ) { ThreadState->ChildState = STATE_POSTBUILD; ThreadState->ChildFlags = 0; ThreadState->ChildTarget = "all platforms"; ThreadState->FilterProc = MSPostBuildFilter; return FALSE; } else if (ThreadState->ChildState == STATE_BATCHCOMPILE) { if (strstr( p, "integral type to pointer") || strstr( p, "this conversion is possible") || strstr( p, "void cannot be converted") || strstr( p, "Compiling...") || strstr( p, "Generating Code...") || strstr( p, "Parsing ") || strstr( p, "Loading class:") || strstr( p, "Generating file ")) return FALSE; // This is a warning/error/info string
if (strstr( p, ".c") && !strchr( p, ' ') && !strchr( p, ':')) strcpy( ThreadState->ChildCurrentFile, p ); // C/C++ compile
else if (strstr( p, ".java") && strstr( p, "Compiling ")) { if (getenv("JVC_TERSE") != NULL) { RunningTotals.NumberCompiles++; ThreadState->BuildMetrics.NumberCompiles++; return FALSE; } else { strcpy( ThreadState->ChildCurrentFile, IsolateLastToken( p, '\\' ) ); // Java compile
} } else return FALSE; }
else { return FALSE; }
//
// ***************** Set the Thread State according to what we determined.
//
FileName = ThreadState->ChildCurrentFile;
// make sure directories match to trailing backslash
strcpy(CheckFileName, FileName); pCheckFileName = CheckFileName;
if (TestPrefix( &pCheckFileName, CurrentDirectory )) { if (*pCheckFileName == '\\') { FileName += (pCheckFileName - CheckFileName) + 1; }
if (TestPrefix( &pCheckFileName, ThreadState->ChildCurrentDirectory )) { if (*pCheckFileName == '\\') { FileName += (pCheckFileName - CheckFileName) + 1; } }
strcpy( ThreadState->ChildCurrentFile, FileName ); }
FileDB = NULL;
if (ThreadState->ChildState == STATE_LIBING) { RunningTotals.NumberLibraries++; ThreadState->BuildMetrics.NumberLibraries++; } else if (ThreadState->ChildState == STATE_LINKING) { RunningTotals.NumberLinks++; ThreadState->BuildMetrics.NumberLinks++; } else if (ThreadState->ChildState == STATE_BSCMAKING) { RunningTotals.NumberBSCMakes++; ThreadState->BuildMetrics.NumberBSCMakes++; } else if ((ThreadState->ChildState == STATE_STATUS) || // Don't need to do anything here - binplace count already handled above
(ThreadState->ChildState == STATE_BINPLACE) || (ThreadState->ChildState == STATE_UNKNOWN)) { ; // Do nothing.
} else { if (ThreadState->CompileDirDB) { RunningTotals.NumberCompiles++; ThreadState->BuildMetrics.NumberCompiles++; CopyString(ThreadState->ChildCurrentFile, ThreadState->ChildCurrentFile, TRUE);
if (!fQuicky) { FileDB = FindSourceFileDB( ThreadState->CompileDirDB, ThreadState->ChildCurrentFile, NULL); } } }
if (strstr(ThreadState->ChildCurrentFile, ".cxx") || strstr(ThreadState->ChildCurrentFile, ".cpp")) { ThreadState->ChildFlags |= FLAGS_CXX_FILE; }
if (fPrintChildState) PrintChildState(ThreadState, p, FileDB); return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: PrintChildState
//
// Synopsis:
//
// Arguments: [ThreadState] -- Current thread state
//
// Returns: TRUE if we figured it out, FALSE if we didn't recognize
// anything.
//
//----------------------------------------------------------------------------
void PrintChildState( PTHREADSTATE ThreadState, LPSTR p, PFILEREC FileDB ) { USHORT SaveCol; USHORT SaveRow; USHORT SaveRowTop; BOOL fStatusOutput = FALSE; char buffer[ DB_MAX_PATH_LENGTH ]; LONG FilesLeft; LONG LinesLeft; ULONG LinesPerSecond; ULONG SecondsLeft; ULONG PercentDone;
//
// *********************** Print the thread state to the screen
//
if (ThreadState->IsStdErrTty) { GetScreenSize(ThreadState); assert(ThreadState->cColTotal != 0); assert(ThreadState->cRowTotal != 0);
if (fStatus) { GetCursorPosition(&SaveRow, &SaveCol, &SaveRowTop);
// Clear row for process message
ClearRows (ThreadState, (USHORT) (SaveRowTop + ThreadState->ThreadIndex - 1), 1, StatusCell);
// Clear row for status message
ClearRows (ThreadState, (USHORT) (SaveRowTop + NumberProcesses), 1, StatusCell);
// Make sure there's still some room at the bottom
if (SaveRow == LastRow(ThreadState)) { USHORT RowTop = 1 + SaveRowTop + (USHORT) NumberProcesses + 1;
MoveRectangleUp ( RowTop, // Top
0, // Left
LastRow(ThreadState), // Bottom
LastCol(ThreadState), // Right
1, // NumRow
ScreenCell); // FillCell
SaveRow--; }
SetCursorPosition( (USHORT) (SaveRowTop + ThreadState->ThreadIndex - 1), 0); fStatusOutput = TRUE; } }
if (szBuildTag) { sprintf(buffer, "%s: ", szBuildTag); WriteTTY(ThreadState, buffer, fStatusOutput); }
if (fParallel && !fNoThreadIndex) { sprintf(buffer, "%d>", ThreadState->ThreadIndex); WriteTTY(ThreadState, buffer, fStatusOutput); }
if (ThreadState->ChildState == STATE_UNKNOWN) { if (!fAlreadyUnknown) { WriteTTY( ThreadState, "Processing Unknown item(s)...\r\n", fStatusOutput); fAlreadyUnknown = TRUE; } } else if (ThreadState->ChildState == STATE_STATUS) { WriteTTY(ThreadState, p, fStatusOutput); WriteTTY(ThreadState, "\r\n", fStatusOutput); } else { fAlreadyUnknown = FALSE; WriteTTY(ThreadState, States[ThreadState->ChildState], fStatusOutput); WriteTTY(ThreadState, " - ", fStatusOutput); WriteTTY(ThreadState, FormatPathName(ThreadState->ChildCurrentDirectory, ThreadState->ChildCurrentFile), fStatusOutput); WriteTTY(ThreadState, " for ", fStatusOutput); WriteTTY(ThreadState, ThreadState->ChildTarget, fStatusOutput); WriteTTY(ThreadState, "\r\n", fStatusOutput);
if (fXMLOutput || fXMLFragment) { XMLThreadInitBuffer(ThreadState); if (PXMLThreadStates[ThreadState->XMLThreadIndex]->fXMLInAction) { // check for action errors or warnings
if (ThreadState->BuildMetrics.NumberActErrors || ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer, "<ACTIONSUMMARY "); if (ThreadState->BuildMetrics.NumberActErrors) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " ERRORS=\"%d\"", ThreadState->BuildMetrics.NumberActErrors); } if (ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " WARNINGS=\"%d\"", ThreadState->BuildMetrics.NumberActWarnings); } strcat(szXMLBuffer, "/>"); XMLThreadWrite(ThreadState, szXMLBuffer); } XMLThreadCloseTag(ThreadState, "ACTION"); PXMLThreadStates[ThreadState->XMLThreadIndex]->fXMLInAction = FALSE; } XMLThreadOpenTag(ThreadState, "ACTION", "TYPE=\"%s\" FILE=\"%s\" TARGET=\"%s\"", States[ThreadState->ChildState], ThreadState->ChildCurrentFile, ThreadState->ChildTarget); PXMLThreadStates[ThreadState->XMLThreadIndex]->fXMLInAction = TRUE; } ThreadState->BuildMetrics.NumberDirActions++; ThreadState->BuildMetrics.NumberActErrors = 0; ThreadState->BuildMetrics.NumberActWarnings = 0; }
if (StartCompileTime) { ElapsedCompileTime += (ULONG)(time(NULL) - StartCompileTime); }
if (FileDB != NULL) { StartCompileTime = time(NULL); } else { StartCompileTime = 0L; }
//
// ****************** Update the status line
//
if (ThreadState->IsStdErrTty) { assert(ThreadState->cColTotal != 0); assert(ThreadState->cRowTotal != 0);
if (fStatus) { if (FileDB != NULL) { FilesLeft = TotalFilesToCompile - TotalFilesCompiled; if (FilesLeft < 0) { FilesLeft = 0; } LinesLeft = TotalLinesToCompile - TotalLinesCompiled; if (LinesLeft < 0) { LinesLeft = 0; PercentDone = 99; } else if (TotalLinesToCompile != 0) { if (TotalLinesCompiled > 20000000L) { int TLC = TotalLinesCompiled / 100; int TLTC = TotalLinesToCompile / 100;
PercentDone = (TLC * 100L)/TLTC; } else PercentDone = (TotalLinesCompiled * 100L)/TotalLinesToCompile; } else { PercentDone = 0; }
if (ElapsedCompileTime != 0) { LinesPerSecond = TotalLinesCompiled / ElapsedCompileTime; } else { LinesPerSecond = 0; }
if (LinesPerSecond != 0) { SecondsLeft = LinesLeft / LinesPerSecond; } else { SecondsLeft = LinesLeft / DEFAULT_LPS; }
sprintf( buffer, "%2d%% done. %4ld %sLPS Time Left:%s Files: %d %sLines: %s\r\n", PercentDone, LinesPerSecond, fStatusTree? "T" : "", FormatTime(SecondsLeft), FilesLeft, fStatusTree? "Total " : "", FormatNumber(LinesLeft));
SetCursorPosition((USHORT) (SaveRowTop + NumberProcesses), 0);
WriteTTY(ThreadState, buffer, fStatusOutput); }
SetCursorPosition(SaveRow, SaveCol); } }
//
// ***************** Keep track of how many files have been compiled.
//
if (ThreadState->ChildState == STATE_COMPILING || ThreadState->ChildState == STATE_ASSEMBLING || ThreadState->ChildState == STATE_MKTYPLIB || ThreadState->ChildState == STATE_MIDL || ThreadState->ChildState == STATE_ASN || (FileDB != NULL && ThreadState->ChildState == STATE_PRECOMP)) { TotalFilesCompiled++; } if (FileDB != NULL) { TotalLinesCompiled += FileDB->TotalSourceLines; } }
//+---------------------------------------------------------------------------
//
// Function: ProcessLine
//
// Synopsis: Watch the lines coming from the thread for special strings.
//
//----------------------------------------------------------------------------
BOOL ProcessLine( PTHREADSTATE ThreadState, LPSTR p ) { LPSTR p1;
while (*p <= ' ') { if (!*p) { return ( FALSE ); } else { p++; } }
p1 = p; while (*p1) { if (*p1 == '\r') break; else p1++; } *p1 = '\0';
p1 = p; if (TestPrefix( &p1, "Stop." )) { return ( TRUE ); }
// Stop multithread access to shared:
// database
// window
// compilation stats
EnterCriticalSection(&TTYCriticalSection);
if (TestPrefix( &p1, "nmake :" )) { PassThrough( ThreadState, p, FALSE ); } else if (TestPrefix( &p1, "BUILDMSG: " )) { if (TestPrefix(&p1, "Warning")) { PassThrough(ThreadState, p, TRUE); } else { WriteTTY(ThreadState, p, TRUE); WriteTTY(ThreadState, "\r\n", TRUE); } } else if (ThreadState->LinesToIgnore) { ThreadState->LinesToIgnore--; } else { if ( !DetermineChildState( ThreadState, p ) ) { if (!ToolNotFoundFilter( ThreadState, p )) { if (ThreadState->FilterProc != NULL) { (*ThreadState->FilterProc)( ThreadState, p ); } } } }
if (fXMLVerboseOutput) { XMLThreadWrite(ThreadState, "<RAW MESSAGE=\"%s\"/>\r\n", XMLEncodeBuiltInEntitiesCopy(p, szXMLBuffer)); }
LeaveCriticalSection(&TTYCriticalSection);
return ( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: FilterThread
//
// Synopsis: Capture the output of the thread and process it.
//
//----------------------------------------------------------------------------
VOID FilterThread( PTHREADSTATE ThreadState ) { UINT CountBytesRead; LPSTR StartPointer = NULL; LPSTR EndPointer; LPSTR NewPointer; ULONG BufSize = 512; UINT uThreadIdLen = 0;
AllocMem(BufSize, &StartPointer, MT_THREADFILTER); while (TRUE) { EndPointer = StartPointer; do { if (BufSize - (EndPointer-StartPointer) < 512) { AllocMem(BufSize*2, &NewPointer, MT_THREADFILTER); RtlCopyMemory( NewPointer, StartPointer, EndPointer - StartPointer + 1); // copy null byte, too
EndPointer += NewPointer - StartPointer; FreeMem(&StartPointer, MT_THREADFILTER); StartPointer = NewPointer; BufSize *= 2; } if (!fgets(EndPointer, 512, ThreadState->ChildOutput)) { if (errno != 0) BuildError("Pipe read failed - errno = %d\r\n", errno); FreeMem(&StartPointer, MT_THREADFILTER); return; } CountBytesRead = strlen(EndPointer); EndPointer = EndPointer + CountBytesRead; } while (CountBytesRead == 511 && EndPointer[-1] != '\n');
CountBytesRead = (UINT)(EndPointer - StartPointer); if (fErrorLog && CountBytesRead) { if (fParallel && !fNoThreadIndex) { char buffer[50]; sprintf(buffer, "%d>", ThreadState->ThreadIndex); fwrite(buffer, 1, strlen(buffer), LogFile); } fwrite(StartPointer, 1, CountBytesRead, LogFile); } if (ProcessLine(ThreadState, StartPointer)) { FreeMem(&StartPointer, MT_THREADFILTER); return; } } }
//+---------------------------------------------------------------------------
//
// Function: ExecuteProgram
//
// Synopsis: Spawn a new thread to execute the given program and filter
// its output.
//
// Arguments: [ProgramName] --
// [CommandLine] --
// [MoreCommandLine] --
// [MustBeSynchronous] -- For synchronous operation on a
// multi-processor machine.
// [XMLDir] -- For XML output only - the name of
// the directory processed.
// [XMLAction] -- For XML output only - what we are
// doing with the directory.
//
// Returns: ERROR_SUCCESS, ERROR_NOTENOUGHMEMORY, or return code from
// PipeSpawnClose.
//
// Notes: On a multiprocessor machine, this will spawn a new thread
// and then return, letting the thread run asynchronously. Use
// WaitForParallelThreads() to ensure all threads are finished.
// By default, this routine will spawn as many threads as the
// machine has processors. This can be overridden with the -M
// option.
//
//----------------------------------------------------------------------------
char ExecuteProgramCmdLine[ 1024 ];
UINT ExecuteProgram( LPSTR ProgramName, LPSTR CommandLine, LPSTR MoreCommandLine, BOOL MustBeSynchronous, LPCSTR XMLDir, LPCSTR XMLAction) { LPSTR p; UINT rc; THREADSTATE *ThreadState; UINT OldErrorMode; DWORD dwStartTime;
AllocMem(sizeof(THREADSTATE), &ThreadState, MT_THREADSTATE);
memset(ThreadState, 0, sizeof(*ThreadState)); ThreadState->ChildState = STATE_UNKNOWN; ThreadState->ChildTarget = "Unknown Target"; ThreadState->IsStdErrTty = (BOOL) _isatty(_fileno(stderr)); ThreadState->CompileDirDB = CurrentCompileDirDB; ThreadState->FilterProc = NMakeFilter;
if (ThreadState->IsStdErrTty) { GetScreenSize(ThreadState); assert(ThreadState->cColTotal != 0); assert(ThreadState->cRowTotal != 0);
// We're displaying to the screen, so initialize the console.
if (!fConsoleInitialized) { StatusCell[1] = BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
ReadConsoleCells(ScreenCell, sizeof(ScreenCell), 2, 0);
// If we stumbled upon an old Status line in row 2 of the window,
// try the current row to avoid using the Status line background
// colors for fill when scrolling.
if (ScreenCell[1] == StatusCell[1]) { USHORT Row, Col;
GetCursorPosition(&Row, &Col, NULL); ReadConsoleCells(ScreenCell, sizeof(ScreenCell), Row, 0); } ScreenCell[0] = StatusCell[0] = ' ';
GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &OldConsoleMode); NewConsoleMode = OldConsoleMode; fConsoleInitialized = TRUE; } if (fStatus) { NewConsoleMode = OldConsoleMode & ~(ENABLE_WRAP_AT_EOL_OUTPUT); } else { NewConsoleMode = OldConsoleMode | ENABLE_WRAP_AT_EOL_OUTPUT; } SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), NewConsoleMode); } else { ThreadState->cRowTotal = 0; ThreadState->cColTotal = 0; }
p = ThreadState->ChildCurrentDirectory; GetCurrentDirectory(sizeof(ThreadState->ChildCurrentDirectory), p);
if (TestPrefix(&p, CurrentDirectory)) { if (*p == '\\') { p++; } strcpy(ThreadState->ChildCurrentDirectory, p); }
if (ThreadState->ChildCurrentDirectory[0]) { strcat(ThreadState->ChildCurrentDirectory, "\\"); }
sprintf( ExecuteProgramCmdLine, "%s %s%s", ProgramName, CommandLine, MoreCommandLine); LogMsg("'%s %s%s'\r\n", ProgramName, CommandLine, MoreCommandLine);
if (fParallel && !MustBeSynchronous) { PPARALLEL_CHILD ChildData; DWORD i; DWORD ThreadId;
AllocMem(sizeof(PARALLEL_CHILD), &ChildData, MT_CHILDDATA); strcpy(ChildData->ExecuteProgramCmdLine,ExecuteProgramCmdLine); ChildData->ThreadState = ThreadState;
if (ThreadsStarted < NumberProcesses) { if (ThreadsStarted == 0) { AllocMem( sizeof(HANDLE) * NumberProcesses, (VOID **) &WorkerThreads, MT_THREADHANDLES); AllocMem( sizeof(HANDLE) * NumberProcesses, (VOID **) &WorkerEvents, MT_EVENTHANDLES); } WorkerEvents[ThreadsStarted] = CreateEvent(NULL, FALSE, FALSE, NULL); ChildData->Event = WorkerEvents[ThreadsStarted];
ThreadState->ThreadIndex = ThreadsStarted+1; if (fXMLOutput || fXMLFragment) { ThreadState->XMLThreadIndex = ThreadState->ThreadIndex; }
/*
Thread-specific directory message that associates directory to build thread. */ if (fParallel && !fNoThreadIndex && ThreadState->CompileDirDB && fErrorLog) { char buffer[500]; ThreadState->CompileDirDB; sprintf(buffer, "%d>BUILDMSG: Processing %s\r\n", ThreadState->ThreadIndex, ThreadState->CompileDirDB->Name); fwrite(buffer, 1, strlen(buffer), LogFile); }
if (fXMLOutput || fXMLFragment) { char buffer[1024]; char* pszRelPath = (char*)XMLDir; if (TestPrefix(&pszRelPath, CurrentDirectory) && (*pszRelPath == '\\')) pszRelPath++; if (strlen(pszRelPath) == 0) { pszRelPath = ".\\"; } strcpy(buffer, "CMDLINE=\""); XMLEncodeBuiltInEntitiesCopy(ExecuteProgramCmdLine, buffer+strlen(buffer)); sprintf(buffer + strlen(buffer), "\" ACTION=\"%s\" FULLPATH=\"%s\" RELPATH=\"%s\"", XMLAction, ThreadState->ChildCurrentDirectory, pszRelPath); XMLThreadInitBuffer(ThreadState); XMLThreadOpenTag(ThreadState, "DIR", buffer); dwStartTime = GetTickCount(); } memset(&(ThreadState->BuildMetrics), 0, sizeof(BUILDMETRICS));
WorkerThreads[ThreadsStarted] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ParallelChildStart, ChildData, 0, &ThreadId); if ((WorkerThreads[ThreadsStarted] == NULL) || (WorkerEvents[ThreadsStarted] == NULL)) { FreeMem(&ChildData, MT_CHILDDATA); FreeMem(&ThreadState, MT_THREADSTATE); return (ERROR_NOT_ENOUGH_MEMORY); } else { WaitForSingleObject(WorkerEvents[ThreadsStarted],INFINITE); ++ThreadsStarted; } } else { //
// Wait for a thread to complete before starting
// the next one.
//
i = WaitForMultipleObjects(NumberProcesses, WorkerThreads, FALSE, INFINITE); CloseHandle(WorkerThreads[i]); ChildData->Event = WorkerEvents[i]; ThreadState->ThreadIndex = i+1; if (fXMLOutput || fXMLFragment) { ThreadState->XMLThreadIndex = ThreadState->ThreadIndex; }
/*
Thread-specific directory message that associates directory to build thread. */ if (fParallel && !fNoThreadIndex && ThreadState->CompileDirDB && fErrorLog) { char buffer[500]; ThreadState->CompileDirDB; sprintf(buffer, "%d>BUILDMSG: Processing %s\r\n", ThreadState->ThreadIndex, ThreadState->CompileDirDB->Name); fwrite(buffer, 1, strlen(buffer), LogFile); }
if (fXMLOutput || fXMLFragment) { char buffer[1024]; char* pszRelPath = (char*)XMLDir; if (TestPrefix(&pszRelPath, CurrentDirectory) && (*pszRelPath == '\\')) pszRelPath++; if (strlen(pszRelPath) == 0) { pszRelPath = ".\\"; } strcpy(buffer, "CMDLINE=\""); XMLEncodeBuiltInEntitiesCopy(ExecuteProgramCmdLine, buffer+strlen(buffer)); sprintf(buffer + strlen(buffer), "\" ACTION=\"%s\" FULLPATH=\"%s\" RELPATH=\"%s\"", XMLAction, XMLDir, pszRelPath); XMLThreadInitBuffer(ThreadState); XMLThreadOpenTag(ThreadState, "DIR", buffer); dwStartTime = GetTickCount(); } memset(&(ThreadState->BuildMetrics), 0, sizeof(BUILDMETRICS));
WorkerThreads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ParallelChildStart, ChildData, 0, &ThreadId); if (WorkerThreads[i] == NULL) { FreeMem(&ChildData, MT_CHILDDATA); FreeMem(&ThreadState, MT_THREADSTATE); return (ERROR_NOT_ENOUGH_MEMORY); } else { WaitForSingleObject(WorkerEvents[i],INFINITE); } }
return (ERROR_SUCCESS);
} else {
//
// Synchronous operation
//
StartCompileTime = 0L; ThreadState->ThreadIndex = 1; if (fXMLOutput || fXMLFragment) { // the synchronized directories are always processed by the main thread
ThreadState->XMLThreadIndex = 0; }
//
// Disable child error popups in child processes.
//
if (fClean) { OldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX ); }
/*
Thread-specific directory message that associates directory to build thread. */ if (fParallel && !fNoThreadIndex && ThreadState->CompileDirDB && fErrorLog) { char buffer[500]; ThreadState->CompileDirDB; sprintf(buffer, "%d>BUILDMSG: Processing %s\r\n", ThreadState->ThreadIndex, ThreadState->CompileDirDB->Name); fwrite(buffer, 1, strlen(buffer), LogFile); }
if (fXMLOutput || fXMLFragment) { char buffer[1024]; char* pszRelPath = (char*)XMLDir; if (TestPrefix(&pszRelPath, CurrentDirectory) && (*pszRelPath == '\\')) pszRelPath++; if (strlen(pszRelPath) == 0) { pszRelPath = ".\\"; } strcpy(buffer, "CMDLINE=\""); XMLEncodeBuiltInEntitiesCopy(ExecuteProgramCmdLine, buffer+strlen(buffer)); sprintf(buffer + strlen(buffer), "\" ACTION=\"%s\" FULLPATH=\"%s\" RELPATH=\"%s\"", XMLAction, XMLDir, pszRelPath); XMLThreadInitBuffer(ThreadState); XMLThreadOpenTag(ThreadState, "DIR", buffer); dwStartTime = GetTickCount(); } memset(&(ThreadState->BuildMetrics), 0, sizeof(BUILDMETRICS));
ThreadState->ChildOutput = PipeSpawn( ExecuteProgramCmdLine ); if (fClean) { SetErrorMode( OldErrorMode ); }
rc = ERROR_SUCCESS;
if (ThreadState->ChildOutput == NULL) { BuildError( "Exec of '%s' failed - errno = %d\r\n", ExecuteProgramCmdLine, errno); } else { FilterThread( ThreadState );
if (StartCompileTime) { ElapsedCompileTime += (ULONG)(time(NULL) - StartCompileTime); }
rc = PipeSpawnClose( ThreadState->ChildOutput ); if (rc == -1) { BuildError("Child Terminate failed - errno = %d\r\n", errno); } else if (rc) BuildColorError(COLOR_ERROR, "%s failed - rc = %d\r\n", ProgramName, rc); }
if (fXMLOutput || fXMLFragment) { XMLEnterCriticalSection(); AddBuildMetrics(&PassMetrics, &ThreadState->BuildMetrics);
if (PXMLThreadStates[ThreadState->XMLThreadIndex]->fXMLInAction) { // check for action errors or warnings
if (ThreadState->BuildMetrics.NumberActErrors || ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer, "<ACTIONSUMMARY "); if (ThreadState->BuildMetrics.NumberActErrors) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " ERRORS=\"%d\"", ThreadState->BuildMetrics.NumberActErrors); } if (ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " WARNINGS=\"%d\"", ThreadState->BuildMetrics.NumberActWarnings); } strcat(szXMLBuffer, "/>"); XMLThreadWrite(ThreadState, szXMLBuffer); } XMLThreadCloseTag(ThreadState, "ACTION"); PXMLThreadStates[ThreadState->XMLThreadIndex]->fXMLInAction = FALSE; } sprintf(szXMLBuffer, "ELAPSED=\"%s\" ACTIONS=\"%d\" ", FormatElapsedTime(dwStartTime), ThreadState->BuildMetrics.NumberDirActions); strcat(szXMLBuffer, XMLBuildMetricsString(&(ThreadState->BuildMetrics)));
XMLThreadWrite(ThreadState, "<DIRSUMMARY %s/>", szXMLBuffer); XMLThreadCloseTag(ThreadState, "DIR"); if (fXMLFragment) { LPSTR pszStart; PXMLTHREADSTATE XMLState = PXMLThreadStates[ThreadState->XMLThreadIndex]; XMLScanBackTag(XMLState->XMLBuffer + XMLState->iXMLBufferPos, XMLState->XMLBuffer, &pszStart); XMLWriteDirFragmentFile( strlen(ThreadState->ChildCurrentDirectory) == 0 ? ".\\" : ThreadState->ChildCurrentDirectory, pszStart, XMLState->XMLBuffer + XMLState->iXMLBufferPos - pszStart); } XMLThreadReleaseBuffer(ThreadState); XMLUpdateEndTag(FALSE);
XMLLeaveCriticalSection(); }
if (ThreadState->IsStdErrTty) { RestoreConsoleMode(); }
FreeMem(&ThreadState, MT_THREADSTATE);
//
// Signal completion
//
if (CurrentCompileDirDB && (CurrentCompileDirDB->DirFlags & DIRDB_SYNC_PRODUCES)) { PDEPENDENCY_WAIT Wait; PDEPENDENCY Dependency; PLIST_ENTRY List; PLIST_ENTRY WaitList;
List = CurrentCompileDirDB->Produces.Flink; while (List != &CurrentCompileDirDB->Produces) { Dependency = CONTAINING_RECORD(List, DEPENDENCY, DependencyList); List = List->Flink; Dependency->Done = TRUE; SetEvent(Dependency->hEvent); } }
return ( rc ); } }
//+---------------------------------------------------------------------------
//
// Function: WaitForParallelThreads
//
// Synopsis: Wait for all threads to finish before returning.
//
//----------------------------------------------------------------------------
VOID WaitForParallelThreads( IN PDIRREC Dir ) { CheckAllConsumer(TRUE); if (fParallel) { WaitForMultipleObjects(ThreadsStarted, WorkerThreads, TRUE, INFINITE); while (ThreadsStarted) { CloseHandle(WorkerThreads[--ThreadsStarted]); CloseHandle(WorkerEvents[ThreadsStarted]); } if (WorkerThreads != NULL) { FreeMem((VOID **) &WorkerThreads, MT_THREADHANDLES); FreeMem((VOID **) &WorkerEvents, MT_EVENTHANDLES); } } }
//+---------------------------------------------------------------------------
//
// Function: ParallelChildStart
//
// Synopsis: Function that is run once for each thread.
//
// Arguments: [Data] -- Data given to CreateThread.
//
//----------------------------------------------------------------------------
DWORD ParallelChildStart( PPARALLEL_CHILD Data ) { UINT OldErrorMode; UINT rc; DWORD dwStartTime = GetTickCount(); PDIRREC DirDB;
//
// Disable child error popups
//
if (fClean) { OldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX ); } Data->ThreadState->ChildOutput = PipeSpawn(Data->ExecuteProgramCmdLine);
if (fClean) { SetErrorMode(OldErrorMode); }
//
// Poke the event to indicate that the child process has
// started and it is ok for the main thread to change
// the current directory.
//
SetEvent(Data->Event);
if (Data->ThreadState->ChildOutput==NULL) { BuildError( "Exec of '%s' failed - errno = %d\r\n", ExecuteProgramCmdLine, errno); } else { FilterThread(Data->ThreadState); rc = PipeSpawnClose(Data->ThreadState->ChildOutput); if (rc == -1) { BuildError("Child terminate failed - errno = %d\r\n", errno); } else { if (rc) { BuildError("%s failed - rc = %d\r\n", Data->ExecuteProgramCmdLine, rc); } } }
if (Data->ThreadState->IsStdErrTty) { RestoreConsoleMode(); }
if (fXMLOutput || fXMLFragment) { XMLEnterCriticalSection(); AddBuildMetrics(&PassMetrics, &Data->ThreadState->BuildMetrics);
if (PXMLThreadStates[Data->ThreadState->XMLThreadIndex]->fXMLInAction) { // check for action errors or warnings
if (Data->ThreadState->BuildMetrics.NumberActErrors || Data->ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer, "<ACTIONSUMMARY "); if (Data->ThreadState->BuildMetrics.NumberActErrors) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " ERRORS=\"%d\"", Data->ThreadState->BuildMetrics.NumberActErrors); } if (Data->ThreadState->BuildMetrics.NumberActWarnings) { sprintf(szXMLBuffer + strlen(szXMLBuffer), " WARNINGS=\"%d\"", Data->ThreadState->BuildMetrics.NumberActWarnings); } strcat(szXMLBuffer, "/>"); XMLThreadWrite(Data->ThreadState, szXMLBuffer); } XMLThreadCloseTag(Data->ThreadState, "ACTION"); PXMLThreadStates[Data->ThreadState->XMLThreadIndex]->fXMLInAction = FALSE; } sprintf(szXMLBuffer, "ELAPSED=\"%s\" ACTIONS=\"%d\" ", FormatElapsedTime(dwStartTime), Data->ThreadState->BuildMetrics.NumberDirActions); strcat(szXMLBuffer, XMLBuildMetricsString(&(Data->ThreadState->BuildMetrics)));
XMLThreadWrite(Data->ThreadState, "<DIRSUMMARY %s/>", szXMLBuffer); XMLThreadCloseTag(Data->ThreadState, "DIR"); if (fXMLFragment) { LPSTR pszStart; PXMLTHREADSTATE XMLState = PXMLThreadStates[Data->ThreadState->XMLThreadIndex]; XMLScanBackTag(XMLState->XMLBuffer + XMLState->iXMLBufferPos, XMLState->XMLBuffer, &pszStart); XMLWriteDirFragmentFile( strlen(Data->ThreadState->ChildCurrentDirectory) == 0 ? ".\\" : Data->ThreadState->ChildCurrentDirectory, pszStart, XMLState->XMLBuffer + XMLState->iXMLBufferPos - pszStart); } XMLThreadReleaseBuffer(Data->ThreadState); XMLUpdateEndTag(FALSE); XMLLeaveCriticalSection(); }
//
// Signal completion
//
DirDB=Data->ThreadState->CompileDirDB;
if (DirDB && (DirDB->DirFlags & DIRDB_SYNC_PRODUCES)) { PDEPENDENCY_WAIT Wait; PDEPENDENCY Dependency; PLIST_ENTRY List; PLIST_ENTRY WaitList; List = DirDB->Produces.Flink; while (List != &DirDB->Produces) { Dependency = CONTAINING_RECORD(List, DEPENDENCY, DependencyList); List = List->Flink; Dependency->Done = TRUE; SetEvent(Dependency->hEvent); } }
FreeMem(&Data->ThreadState, MT_THREADSTATE); FreeMem(&Data, MT_CHILDDATA); return (rc);
}
//+---------------------------------------------------------------------------
//
// Function: ClearRows
//
//----------------------------------------------------------------------------
VOID ClearRows( THREADSTATE *ThreadState, USHORT Top, USHORT NumRows, BYTE *Cell) { COORD Coord; DWORD NumWritten;
Coord.X = 0; Coord.Y = Top;
FillConsoleOutputCharacter( GetStdHandle(STD_ERROR_HANDLE), Cell[0], ThreadState->cColTotal * NumRows, Coord, &NumWritten); FillConsoleOutputAttribute( GetStdHandle(STD_ERROR_HANDLE), (WORD) Cell[1], ThreadState->cColTotal * NumRows, Coord, &NumWritten); }
//+---------------------------------------------------------------------------
//
// Function: GetScreenSize
//
//----------------------------------------------------------------------------
VOID GetScreenSize(THREADSTATE *ThreadState) { CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)) { ThreadState->cRowTotal = 25; ThreadState->cColTotal = 80; } else { ThreadState->cRowTotal = csbi.srWindow.Bottom + 1; ThreadState->cColTotal = csbi.dwSize.X; } }
//+---------------------------------------------------------------------------
//
// Function: GetCursorPosition
//
//----------------------------------------------------------------------------
VOID GetCursorPosition( USHORT *pRow, USHORT *pCol, USHORT *pRowTop) { CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi); *pRow = csbi.dwCursorPosition.Y; *pCol = csbi.dwCursorPosition.X; if (pRowTop != NULL) { *pRowTop = csbi.srWindow.Top; } }
//+---------------------------------------------------------------------------
//
// Function: SetCursorPosition
//
//----------------------------------------------------------------------------
VOID SetCursorPosition(USHORT Row, USHORT Col) { COORD Coord;
Coord.X = Col; Coord.Y = Row; SetConsoleCursorPosition(GetStdHandle(STD_ERROR_HANDLE), Coord); }
//+---------------------------------------------------------------------------
//
// Function: WriteConsoleCells
//
//----------------------------------------------------------------------------
VOID WriteConsoleCells( LPSTR String, USHORT StringLength, USHORT Row, USHORT Col, BYTE *Attribute) { CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD NumWritten; WORD OldAttribute; COORD StartCoord;
//
// Get current default attribute and save it.
//
GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi);
OldAttribute = csbi.wAttributes;
//
// Set the default attribute to the passed parameter, along with
// the cursor position.
//
if ((BYTE) OldAttribute != *Attribute) { SetConsoleTextAttribute( GetStdHandle(STD_ERROR_HANDLE), (WORD) *Attribute); }
StartCoord.X = Col; StartCoord.Y = Row; SetConsoleCursorPosition(GetStdHandle(STD_ERROR_HANDLE), StartCoord);
//
// Write the passed string at the current cursor position, using the
// new default attribute.
//
WriteFile( GetStdHandle(STD_ERROR_HANDLE), String, StringLength, &NumWritten, NULL);
//
// Restore previous default attribute.
//
if ((BYTE) OldAttribute != *Attribute) { SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), OldAttribute); } }
//+---------------------------------------------------------------------------
//
// Function: MoveRectangleUp
//
//----------------------------------------------------------------------------
VOID MoveRectangleUp ( USHORT Top, USHORT Left, USHORT Bottom, USHORT Right, USHORT NumRow, BYTE *FillCell) { SMALL_RECT ScrollRectangle; COORD DestinationOrigin; CHAR_INFO Fill;
ScrollRectangle.Left = Left; ScrollRectangle.Top = Top; ScrollRectangle.Right = Right; ScrollRectangle.Bottom = Bottom; DestinationOrigin.X = Left; DestinationOrigin.Y = Top - NumRow; Fill.Char.AsciiChar = FillCell[0]; Fill.Attributes = (WORD) FillCell[1];
ScrollConsoleScreenBuffer( GetStdHandle(STD_ERROR_HANDLE), &ScrollRectangle, NULL, DestinationOrigin, &Fill); }
//+---------------------------------------------------------------------------
//
// Function: ReadConsoleCells
//
//----------------------------------------------------------------------------
VOID ReadConsoleCells( BYTE *ScreenCell, USHORT cb, USHORT Row, USHORT Column) { COORD BufferSize, BufferCoord; SMALL_RECT ReadRegion; CHAR_INFO CharInfo[1], *p; USHORT CountCells;
CountCells = cb >> 1; assert(CountCells * sizeof(CHAR_INFO) <= sizeof(CharInfo)); ReadRegion.Top = Row; ReadRegion.Left = Column; ReadRegion.Bottom = Row; ReadRegion.Right = Column + CountCells - 1; BufferSize.X = 1; BufferSize.Y = CountCells; BufferCoord.X = 0; BufferCoord.Y = 0; ReadConsoleOutput( GetStdHandle(STD_ERROR_HANDLE), CharInfo, BufferSize, BufferCoord, &ReadRegion);
p = CharInfo; while (CountCells--) { *ScreenCell++ = p->Char.AsciiChar; *ScreenCell++ = (BYTE) p->Attributes; p++; } }
//+---------------------------------------------------------------------------
//
// Function: ClearLine
//
//----------------------------------------------------------------------------
VOID ClearLine(VOID) { CONSOLE_SCREEN_BUFFER_INFO csbi; COORD Coord; DWORD NumWritten;
GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi);
Coord.Y = csbi.dwCursorPosition.Y; Coord.X = csbi.dwCursorPosition.X = 0; FillConsoleOutputCharacter( GetStdHandle(STD_ERROR_HANDLE), ' ', csbi.dwSize.X, csbi.dwCursorPosition, &NumWritten);
SetConsoleCursorPosition(GetStdHandle(STD_ERROR_HANDLE), Coord); fLineCleared = TRUE; }
// PipeSpawn variables. We can get away with one copy per thread.
__declspec(thread) HANDLE ProcHandle; __declspec(thread) FILE *pstream;
//+---------------------------------------------------------------------------
//
// Function: PipeSpawn (similar to _popen)
//
//----------------------------------------------------------------------------
FILE * PipeSpawn ( const CHAR *cmdstring ) { int PipeHandle[2]; HANDLE WriteHandle, ErrorHandle; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; BOOL Status; char CmdLine[1024];
if (cmdstring == NULL) return (NULL);
// Open the pipe where we'll collect the output.
_pipe(PipeHandle, 1024, _O_BINARY|_O_NOINHERIT);
DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle((LONG)PipeHandle[1]), GetCurrentProcess(), &WriteHandle, 0L, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle((LONG)PipeHandle[1]), GetCurrentProcess(), &ErrorHandle, 0L, TRUE, DUPLICATE_SAME_ACCESS);
_close(PipeHandle[1]);
pstream = _fdopen(PipeHandle[0], "rb" ); if (!pstream) { CloseHandle(WriteHandle); CloseHandle(ErrorHandle); _close(PipeHandle[0]); return (NULL); }
strcpy(CmdLine, cmdexe); strcat(CmdLine, " /c "); strcat(CmdLine, cmdstring);
memset(&StartupInfo, 0, sizeof(STARTUPINFO)); StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.hStdOutput = WriteHandle; StartupInfo.hStdError = ErrorHandle; StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); StartupInfo.dwFlags = STARTF_USESTDHANDLES;
memset(&ProcessInformation, 0, sizeof(PROCESS_INFORMATION));
// And start the process.
Status = CreateProcess(cmdexe, CmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation);
CloseHandle(WriteHandle); CloseHandle(ErrorHandle); CloseHandle(ProcessInformation.hThread);
if (!Status) { fclose(pstream); // This will close the read handle
pstream = NULL; ProcHandle = NULL; } else { ProcHandle = ProcessInformation.hProcess; }
return (pstream); }
//+---------------------------------------------------------------------------
//
// Function: PipeSpawnClose (similar to _pclose)
//
//----------------------------------------------------------------------------
DWORD PipeSpawnClose ( FILE *pstream ) { DWORD retval = 0; /* return value (to caller) */
if ( pstream == NULL) { return retval; }
(void)fclose(pstream);
if ( WaitForSingleObject(ProcHandle, (DWORD) -1L) == 0) { GetExitCodeProcess(ProcHandle, &retval); } CloseHandle(ProcHandle);
return (retval); }
|