You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
536 lines
11 KiB
536 lines
11 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include <assert.h> |
|
#include <time.h> |
|
#include "stdafx.h" |
|
#include <stdio.h> |
|
#include <windows.h> |
|
#include "depcheck_util.h" |
|
#include "codeprocessor.h" |
|
|
|
/* |
|
================ |
|
UTIL_FloatTime |
|
================ |
|
*/ |
|
double UTIL_FloatTime (void) |
|
{ |
|
// more precise, less portable |
|
clock_t current; |
|
static clock_t base; |
|
static bool first = true; |
|
|
|
current = clock(); |
|
|
|
if ( first ) |
|
{ |
|
first = false; |
|
|
|
base = current; |
|
} |
|
|
|
return (double)(current - base)/(double)CLOCKS_PER_SEC; |
|
} |
|
|
|
void CCodeProcessor::AddHeader( int depth, const char *filename, const char *rootmodule ) |
|
{ |
|
// if ( depth < 1 ) |
|
// return; |
|
if ( depth != 1 ) |
|
return; |
|
|
|
// Check header list |
|
for ( int i = 0; i < m_nHeaderCount; i++ ) |
|
{ |
|
if ( !stricmp( m_Headers[ i ].name, filename ) ) |
|
{ |
|
vprint( 0, "%s included twice in module %s\n", filename, rootmodule ); |
|
return; |
|
} |
|
} |
|
|
|
// Add to list |
|
strcpy( m_Headers[ m_nHeaderCount++ ].name, filename ); |
|
} |
|
|
|
void CCodeProcessor::CreateBackup( const char *filename, bool& wasreadonly ) |
|
{ |
|
assert( strstr( filename, ".cpp" ) ); |
|
|
|
// attrib it, change extension, save it |
|
if ( GetFileAttributes( filename ) & FILE_ATTRIBUTE_READONLY ) |
|
{ |
|
wasreadonly = true; |
|
|
|
SetFileAttributes( filename, FILE_ATTRIBUTE_NORMAL ); |
|
} |
|
else |
|
{ |
|
wasreadonly = false; |
|
} |
|
|
|
char backupname[ 256 ]; |
|
strcpy( backupname, filename ); |
|
strcpy( (char *)&backupname[ strlen( filename ) - 4 ], ".bak" ); |
|
|
|
unlink( backupname ); |
|
rename( filename, backupname ); |
|
|
|
} |
|
|
|
void CCodeProcessor::RestoreBackup( const char *filename, bool makereadonly ) |
|
{ |
|
assert( strstr( filename, ".cpp" ) ); |
|
|
|
char backupname[ 256 ]; |
|
strcpy( backupname, filename ); |
|
strcpy( (char *)&backupname[ strlen( filename ) - 4 ], ".bak" ); |
|
|
|
SetFileAttributes( filename, FILE_ATTRIBUTE_NORMAL ); |
|
|
|
unlink( filename ); |
|
rename( backupname, filename ); |
|
|
|
if ( makereadonly ) |
|
{ |
|
SetFileAttributes( filename, FILE_ATTRIBUTE_READONLY ); |
|
} |
|
|
|
unlink( backupname ); |
|
} |
|
|
|
bool CCodeProcessor::TryBuild( const char *rootdir, const char *filename, unsigned char *buffer, int filelength ) |
|
{ |
|
// vprintf( "trying build\n" ); |
|
|
|
FILE *fp; |
|
fp = fopen( filename, "wb" ); |
|
if ( !fp ) |
|
{ |
|
assert( 0 ); |
|
return false; |
|
} |
|
|
|
fwrite( buffer, filelength, 1, fp ); |
|
fclose( fp ); |
|
|
|
// if build is successful, return true |
|
// |
|
// return true; |
|
char commandline[ 512 ]; |
|
char directory[ 512 ]; |
|
|
|
sprintf( directory, rootdir ); |
|
|
|
// sprintf( commandline, "msdev engdll.dsw /MAKE \"quiver - Win32 GL Debug\" /OUT log.txt" ); |
|
|
|
// Builds the default configuration |
|
sprintf( commandline, "\"C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\Bin\\msdev.exe\" %s /MAKE \"%s\" /OUT log.txt", m_szDSP, m_szConfig ); |
|
|
|
PROCESS_INFORMATION pi; |
|
memset( &pi, 0, sizeof( pi ) ); |
|
|
|
STARTUPINFO si; |
|
memset( &si, 0, sizeof( si ) ); |
|
si.cb = sizeof( si ); |
|
|
|
if ( !CreateProcess( NULL, commandline, NULL, NULL, TRUE, 0, NULL, directory, &si, &pi ) ) |
|
{ |
|
LPVOID lpMsgBuf; |
|
FormatMessage( |
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | |
|
FORMAT_MESSAGE_FROM_SYSTEM | |
|
FORMAT_MESSAGE_IGNORE_INSERTS, |
|
NULL, |
|
GetLastError(), |
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language |
|
(LPTSTR) &lpMsgBuf, |
|
0, |
|
NULL |
|
); |
|
// Process any inserts in lpMsgBuf. |
|
// ... |
|
// Display the string. |
|
MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION ); |
|
// Free the buffer. |
|
LocalFree( lpMsgBuf ); |
|
return false; |
|
} |
|
|
|
// Wait until child process exits. |
|
WaitForSingleObject( pi.hProcess, INFINITE ); |
|
|
|
bool retval = false; |
|
DWORD exitCode = -1; |
|
if ( GetExitCodeProcess( pi.hProcess, &exitCode ) ) |
|
{ |
|
if ( !exitCode ) |
|
{ |
|
retval = true; |
|
} |
|
} |
|
|
|
// Close process and thread handles. |
|
CloseHandle( pi.hProcess ); |
|
CloseHandle( pi.hThread ); |
|
|
|
return retval; |
|
} |
|
|
|
void CCodeProcessor::ProcessModule( bool forcequiet, int depth, int& maxdepth, int& numheaders, int& skippedfiles, const char *baseroot, const char *root, const char *module ) |
|
{ |
|
char filename[ 256 ]; |
|
|
|
bool checkroot = false; |
|
|
|
if ( depth > maxdepth ) |
|
{ |
|
maxdepth = depth; |
|
} |
|
|
|
// Load the base module |
|
sprintf( filename, "%s\\%s", root, module ); |
|
strlwr( filename ); |
|
|
|
bool firstheader = true; |
|
retry: |
|
|
|
// Check module list |
|
for ( int i = 0; i < m_nModuleCount; i++ ) |
|
{ |
|
if ( !stricmp( m_Modules[ i ].name, filename ) ) |
|
{ |
|
if ( forcequiet ) |
|
{ |
|
m_nHeadersProcessed++; |
|
numheaders++; |
|
|
|
if ( m_Modules[ i ].skipped ) |
|
{ |
|
skippedfiles++; |
|
} |
|
} |
|
|
|
AddHeader( depth, filename, m_szCurrentCPP ); |
|
|
|
return; |
|
} |
|
} |
|
|
|
int filelength; |
|
char *buffer = (char *)COM_LoadFile( filename, &filelength ); |
|
if ( !buffer ) |
|
{ |
|
if ( !checkroot ) |
|
{ |
|
checkroot = true; |
|
// Load the base module |
|
sprintf( filename, "%s\\%s", baseroot, module ); |
|
goto retry; |
|
} |
|
m_Modules[ m_nModuleCount ].skipped = true; |
|
strcpy( m_Modules[ m_nModuleCount++ ].name, filename ); |
|
|
|
skippedfiles++; |
|
return; |
|
} |
|
|
|
m_nBytesProcessed += filelength; |
|
|
|
m_Modules[ m_nModuleCount ].skipped = false; |
|
strcpy( m_Modules[ m_nModuleCount++ ].name, filename ); |
|
|
|
bool readonly = false; |
|
bool madechanges = false; |
|
CreateBackup( filename, readonly ); |
|
|
|
if ( !forcequiet ) |
|
{ |
|
strcpy( m_szCurrentCPP, filename ); |
|
|
|
vprint( 0, "- %s\n", (char *)&filename[ m_nOffset ] ); |
|
} |
|
|
|
// Parse tokens looking for #include directives or class starts |
|
char *current = buffer; |
|
char *startofline; |
|
|
|
current = CC_ParseToken( current ); |
|
while ( current ) |
|
{ |
|
// No more tokens |
|
if ( strlen( com_token ) <= 0 ) |
|
break; |
|
|
|
if ( !stricmp( com_token, "#include" ) ) |
|
{ |
|
startofline = current - strlen( "#include" ); |
|
|
|
current = CC_ParseToken( current ); |
|
|
|
if ( strlen( com_token ) > 0) |
|
{ |
|
vprint( 1, "#include %s", com_token ); |
|
m_nHeadersProcessed++; |
|
numheaders++; |
|
|
|
AddHeader( depth, filename, m_szCurrentCPP ); |
|
|
|
bool dobuild = true; |
|
if ( firstheader ) |
|
{ |
|
if ( !stricmp( com_token, "cbase.h" ) ) |
|
{ |
|
dobuild = false; |
|
} |
|
|
|
if ( !TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) ) |
|
{ |
|
// build is broken, stop |
|
assert( 0 ); |
|
} |
|
} |
|
|
|
firstheader = false; |
|
|
|
if ( dobuild ) |
|
{ |
|
// Try removing the header and compiling |
|
char saveinfo[2]; |
|
memcpy( saveinfo, startofline, 2 ); |
|
startofline[ 0 ] = '/'; |
|
startofline[ 1 ] = '/'; |
|
|
|
if ( TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) ) |
|
{ |
|
vprint( 0, ", unnecessary\n" ); |
|
madechanges = true; |
|
} |
|
else |
|
{ |
|
// Restore line |
|
memcpy( startofline, saveinfo, 2 ); |
|
vprint( 0, "\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
vprint( 0, "\n" ); |
|
} |
|
} |
|
} |
|
|
|
current = CC_ParseToken( current ); |
|
} |
|
|
|
// Save out last set of changes |
|
{ |
|
FILE *fp; |
|
fp = fopen( filename, "wb" ); |
|
if ( fp ) |
|
{ |
|
fwrite( buffer, filelength, 1, fp ); |
|
fclose( fp ); |
|
} |
|
} |
|
|
|
COM_FreeFile( (unsigned char *)buffer ); |
|
|
|
if ( !madechanges ) |
|
{ |
|
RestoreBackup( filename, readonly ); |
|
} |
|
|
|
if ( !forcequiet && !GetQuiet() ) |
|
{ |
|
vprint( 0, " %s: headers (%i)", (char *)&filename[ m_nOffset ], numheaders ); |
|
if ( maxdepth > 1 ) |
|
{ |
|
vprint( 0, ", depth %i", maxdepth ); |
|
} |
|
vprint( 0, "\n" ); |
|
} |
|
|
|
m_nLinesOfCode += linesprocessed; |
|
linesprocessed = 0; |
|
} |
|
|
|
void CCodeProcessor::ProcessModules( const char *root, const char *rootmodule ) |
|
{ |
|
m_nFilesProcessed++; |
|
|
|
// Reset header list per module |
|
m_nHeaderCount = 0; |
|
m_nModuleCount = 0; |
|
|
|
int numheaders = 0; |
|
int maxdepth = 0; |
|
int skippedfiles = 0; |
|
|
|
bool canstart = false; |
|
|
|
if ( strstr( root, "tf2_hud" ) ) |
|
{ |
|
canstart = true; |
|
} |
|
if ( !canstart ) |
|
{ |
|
vprint( 0, "skipping %s\n", rootmodule ); |
|
return; |
|
} |
|
ProcessModule( false, 0, maxdepth, numheaders, skippedfiles, root, root, rootmodule ); |
|
} |
|
|
|
void CCodeProcessor::PrintResults( void ) |
|
{ |
|
vprint( 0, "\nChecking for errors and totaling...\n\n" ); |
|
|
|
vprint( 0, "parsed from %i files ( %i headers parsed )\n", |
|
m_nFilesProcessed, |
|
m_nHeadersProcessed ); |
|
|
|
vprint( 0, "%.3f K lines of code processed\n", |
|
(double)m_nLinesOfCode / 1024.0 ); |
|
|
|
double elapsed = ( m_flEnd - m_flStart ); |
|
|
|
if ( elapsed > 0.0 ) |
|
{ |
|
vprint( 0, "%.2f K processed in %.3f seconds, throughput %.2f KB/sec\n\n", |
|
(double)m_nBytesProcessed / 1024.0, elapsed, (double)m_nBytesProcessed / ( 1024.0 * elapsed ) ); |
|
} |
|
} |
|
|
|
CCodeProcessor::CCodeProcessor( void ) |
|
{ |
|
m_nModuleCount = 0; |
|
|
|
m_bQuiet = false; |
|
m_bLogToFile = false; |
|
|
|
m_nFilesProcessed = 0; |
|
m_nHeadersProcessed = 0; |
|
m_nOffset = 0; |
|
m_nBytesProcessed = 0; |
|
m_nLinesOfCode = 0; |
|
m_flStart = 0.0; |
|
m_flEnd = 0.0; |
|
|
|
m_szCurrentCPP[ 0 ] = 0; |
|
} |
|
|
|
CCodeProcessor::~CCodeProcessor( void ) |
|
{ |
|
} |
|
|
|
char const *stristr( char const *src, char const *search ) |
|
{ |
|
char buf1[ 512 ]; |
|
char buf2[ 512 ]; |
|
|
|
strcpy( buf1, src ); |
|
_strlwr( buf1 ); |
|
|
|
strcpy( buf2, search ); |
|
_strlwr( buf2 ); |
|
|
|
char *p = strstr( buf1, buf2 ); |
|
if ( p ) |
|
{ |
|
int len = p - buf1; |
|
return src + len; |
|
} |
|
return NULL; |
|
} |
|
|
|
void CCodeProcessor::ConstructModuleList_R( int level, const char *gamespecific, const char *root ) |
|
{ |
|
char directory[ 256 ]; |
|
char filename[ 256 ]; |
|
WIN32_FIND_DATA wfd; |
|
HANDLE ff; |
|
|
|
sprintf( directory, "%s\\*.*", root ); |
|
|
|
if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE ) |
|
return; |
|
|
|
do |
|
{ |
|
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) |
|
{ |
|
|
|
if ( wfd.cFileName[ 0 ] == '.' ) |
|
continue; |
|
|
|
// Once we descend down a branch, don't keep looking for hl2/tf2 in name, just recurse through all children |
|
if ( level == 0 && !stristr( wfd.cFileName, gamespecific ) ) |
|
continue; |
|
|
|
// Recurse down directory |
|
sprintf( filename, "%s\\%s", root, wfd.cFileName ); |
|
ConstructModuleList_R( level+1, gamespecific, filename ); |
|
} |
|
else |
|
{ |
|
if ( strstr( wfd.cFileName, ".cpp" ) ) |
|
{ |
|
ProcessModules( root, wfd.cFileName ); |
|
} |
|
} |
|
} while ( FindNextFile( ff, &wfd ) ); |
|
} |
|
|
|
void CCodeProcessor::Process( const char *gamespecific, const char *root, const char *dsp, const char *config ) |
|
{ |
|
strcpy( m_szDSP, dsp ); |
|
strcpy( m_szConfig, config ); |
|
|
|
m_nBytesProcessed = 0; |
|
m_nFilesProcessed = 0; |
|
m_nHeadersProcessed = 0; |
|
m_nLinesOfCode = 0; |
|
|
|
linesprocessed = 0; |
|
|
|
m_nOffset = strlen( root ) + 1; |
|
|
|
m_nModuleCount = 0; |
|
m_nHeaderCount = 0; |
|
m_flStart = UTIL_FloatTime(); |
|
|
|
vprint( 0, "--- Processing %s\n\n", root ); |
|
|
|
ConstructModuleList_R( 0, gamespecific, root ); |
|
|
|
m_flEnd = UTIL_FloatTime(); |
|
|
|
PrintResults(); |
|
} |
|
|
|
void CCodeProcessor::SetQuiet( bool quiet ) |
|
{ |
|
m_bQuiet = quiet; |
|
} |
|
|
|
bool CCodeProcessor::GetQuiet( void ) const |
|
{ |
|
return m_bQuiet; |
|
} |
|
|
|
void CCodeProcessor::SetLogFile( bool log ) |
|
{ |
|
m_bLogToFile = log; |
|
} |
|
|
|
bool CCodeProcessor::GetLogFile( void ) const |
|
{ |
|
return m_bLogToFile; |
|
} |
|
|
|
static CCodeProcessor g_Processor; |
|
ICodeProcessor *processor = ( ICodeProcessor * )&g_Processor; |