Team Fortress 2 Source Code as on 22/4/2020
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.

535 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <time.h>
  9. #include "stdafx.h"
  10. #include <stdio.h>
  11. #include <windows.h>
  12. #include "depcheck_util.h"
  13. #include "codeprocessor.h"
  14. /*
  15. ================
  16. UTIL_FloatTime
  17. ================
  18. */
  19. double UTIL_FloatTime (void)
  20. {
  21. // more precise, less portable
  22. clock_t current;
  23. static clock_t base;
  24. static bool first = true;
  25. current = clock();
  26. if ( first )
  27. {
  28. first = false;
  29. base = current;
  30. }
  31. return (double)(current - base)/(double)CLOCKS_PER_SEC;
  32. }
  33. void CCodeProcessor::AddHeader( int depth, const char *filename, const char *rootmodule )
  34. {
  35. // if ( depth < 1 )
  36. // return;
  37. if ( depth != 1 )
  38. return;
  39. // Check header list
  40. for ( int i = 0; i < m_nHeaderCount; i++ )
  41. {
  42. if ( !stricmp( m_Headers[ i ].name, filename ) )
  43. {
  44. vprint( 0, "%s included twice in module %s\n", filename, rootmodule );
  45. return;
  46. }
  47. }
  48. // Add to list
  49. strcpy( m_Headers[ m_nHeaderCount++ ].name, filename );
  50. }
  51. void CCodeProcessor::CreateBackup( const char *filename, bool& wasreadonly )
  52. {
  53. assert( strstr( filename, ".cpp" ) );
  54. // attrib it, change extension, save it
  55. if ( GetFileAttributes( filename ) & FILE_ATTRIBUTE_READONLY )
  56. {
  57. wasreadonly = true;
  58. SetFileAttributes( filename, FILE_ATTRIBUTE_NORMAL );
  59. }
  60. else
  61. {
  62. wasreadonly = false;
  63. }
  64. char backupname[ 256 ];
  65. strcpy( backupname, filename );
  66. strcpy( (char *)&backupname[ strlen( filename ) - 4 ], ".bak" );
  67. unlink( backupname );
  68. rename( filename, backupname );
  69. }
  70. void CCodeProcessor::RestoreBackup( const char *filename, bool makereadonly )
  71. {
  72. assert( strstr( filename, ".cpp" ) );
  73. char backupname[ 256 ];
  74. strcpy( backupname, filename );
  75. strcpy( (char *)&backupname[ strlen( filename ) - 4 ], ".bak" );
  76. SetFileAttributes( filename, FILE_ATTRIBUTE_NORMAL );
  77. unlink( filename );
  78. rename( backupname, filename );
  79. if ( makereadonly )
  80. {
  81. SetFileAttributes( filename, FILE_ATTRIBUTE_READONLY );
  82. }
  83. unlink( backupname );
  84. }
  85. bool CCodeProcessor::TryBuild( const char *rootdir, const char *filename, unsigned char *buffer, int filelength )
  86. {
  87. // vprintf( "trying build\n" );
  88. FILE *fp;
  89. fp = fopen( filename, "wb" );
  90. if ( !fp )
  91. {
  92. assert( 0 );
  93. return false;
  94. }
  95. fwrite( buffer, filelength, 1, fp );
  96. fclose( fp );
  97. // if build is successful, return true
  98. //
  99. // return true;
  100. char commandline[ 512 ];
  101. char directory[ 512 ];
  102. sprintf( directory, rootdir );
  103. // sprintf( commandline, "msdev engdll.dsw /MAKE \"quiver - Win32 GL Debug\" /OUT log.txt" );
  104. // Builds the default configuration
  105. sprintf( commandline, "\"C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\Bin\\msdev.exe\" %s /MAKE \"%s\" /OUT log.txt", m_szDSP, m_szConfig );
  106. PROCESS_INFORMATION pi;
  107. memset( &pi, 0, sizeof( pi ) );
  108. STARTUPINFO si;
  109. memset( &si, 0, sizeof( si ) );
  110. si.cb = sizeof( si );
  111. if ( !CreateProcess( NULL, commandline, NULL, NULL, TRUE, 0, NULL, directory, &si, &pi ) )
  112. {
  113. LPVOID lpMsgBuf;
  114. FormatMessage(
  115. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  116. FORMAT_MESSAGE_FROM_SYSTEM |
  117. FORMAT_MESSAGE_IGNORE_INSERTS,
  118. NULL,
  119. GetLastError(),
  120. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  121. (LPTSTR) &lpMsgBuf,
  122. 0,
  123. NULL
  124. );
  125. // Process any inserts in lpMsgBuf.
  126. // ...
  127. // Display the string.
  128. MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
  129. // Free the buffer.
  130. LocalFree( lpMsgBuf );
  131. return false;
  132. }
  133. // Wait until child process exits.
  134. WaitForSingleObject( pi.hProcess, INFINITE );
  135. bool retval = false;
  136. DWORD exitCode = -1;
  137. if ( GetExitCodeProcess( pi.hProcess, &exitCode ) )
  138. {
  139. if ( !exitCode )
  140. {
  141. retval = true;
  142. }
  143. }
  144. // Close process and thread handles.
  145. CloseHandle( pi.hProcess );
  146. CloseHandle( pi.hThread );
  147. return retval;
  148. }
  149. void CCodeProcessor::ProcessModule( bool forcequiet, int depth, int& maxdepth, int& numheaders, int& skippedfiles, const char *baseroot, const char *root, const char *module )
  150. {
  151. char filename[ 256 ];
  152. bool checkroot = false;
  153. if ( depth > maxdepth )
  154. {
  155. maxdepth = depth;
  156. }
  157. // Load the base module
  158. sprintf( filename, "%s\\%s", root, module );
  159. strlwr( filename );
  160. bool firstheader = true;
  161. retry:
  162. // Check module list
  163. for ( int i = 0; i < m_nModuleCount; i++ )
  164. {
  165. if ( !stricmp( m_Modules[ i ].name, filename ) )
  166. {
  167. if ( forcequiet )
  168. {
  169. m_nHeadersProcessed++;
  170. numheaders++;
  171. if ( m_Modules[ i ].skipped )
  172. {
  173. skippedfiles++;
  174. }
  175. }
  176. AddHeader( depth, filename, m_szCurrentCPP );
  177. return;
  178. }
  179. }
  180. int filelength;
  181. char *buffer = (char *)COM_LoadFile( filename, &filelength );
  182. if ( !buffer )
  183. {
  184. if ( !checkroot )
  185. {
  186. checkroot = true;
  187. // Load the base module
  188. sprintf( filename, "%s\\%s", baseroot, module );
  189. goto retry;
  190. }
  191. m_Modules[ m_nModuleCount ].skipped = true;
  192. strcpy( m_Modules[ m_nModuleCount++ ].name, filename );
  193. skippedfiles++;
  194. return;
  195. }
  196. m_nBytesProcessed += filelength;
  197. m_Modules[ m_nModuleCount ].skipped = false;
  198. strcpy( m_Modules[ m_nModuleCount++ ].name, filename );
  199. bool readonly = false;
  200. bool madechanges = false;
  201. CreateBackup( filename, readonly );
  202. if ( !forcequiet )
  203. {
  204. strcpy( m_szCurrentCPP, filename );
  205. vprint( 0, "- %s\n", (char *)&filename[ m_nOffset ] );
  206. }
  207. // Parse tokens looking for #include directives or class starts
  208. char *current = buffer;
  209. char *startofline;
  210. current = CC_ParseToken( current );
  211. while ( current )
  212. {
  213. // No more tokens
  214. if ( strlen( com_token ) <= 0 )
  215. break;
  216. if ( !stricmp( com_token, "#include" ) )
  217. {
  218. startofline = current - strlen( "#include" );
  219. current = CC_ParseToken( current );
  220. if ( strlen( com_token ) > 0)
  221. {
  222. vprint( 1, "#include %s", com_token );
  223. m_nHeadersProcessed++;
  224. numheaders++;
  225. AddHeader( depth, filename, m_szCurrentCPP );
  226. bool dobuild = true;
  227. if ( firstheader )
  228. {
  229. if ( !stricmp( com_token, "cbase.h" ) )
  230. {
  231. dobuild = false;
  232. }
  233. if ( !TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) )
  234. {
  235. // build is broken, stop
  236. assert( 0 );
  237. }
  238. }
  239. firstheader = false;
  240. if ( dobuild )
  241. {
  242. // Try removing the header and compiling
  243. char saveinfo[2];
  244. memcpy( saveinfo, startofline, 2 );
  245. startofline[ 0 ] = '/';
  246. startofline[ 1 ] = '/';
  247. if ( TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) )
  248. {
  249. vprint( 0, ", unnecessary\n" );
  250. madechanges = true;
  251. }
  252. else
  253. {
  254. // Restore line
  255. memcpy( startofline, saveinfo, 2 );
  256. vprint( 0, "\n" );
  257. }
  258. }
  259. else
  260. {
  261. vprint( 0, "\n" );
  262. }
  263. }
  264. }
  265. current = CC_ParseToken( current );
  266. }
  267. // Save out last set of changes
  268. {
  269. FILE *fp;
  270. fp = fopen( filename, "wb" );
  271. if ( fp )
  272. {
  273. fwrite( buffer, filelength, 1, fp );
  274. fclose( fp );
  275. }
  276. }
  277. COM_FreeFile( (unsigned char *)buffer );
  278. if ( !madechanges )
  279. {
  280. RestoreBackup( filename, readonly );
  281. }
  282. if ( !forcequiet && !GetQuiet() )
  283. {
  284. vprint( 0, " %s: headers (%i)", (char *)&filename[ m_nOffset ], numheaders );
  285. if ( maxdepth > 1 )
  286. {
  287. vprint( 0, ", depth %i", maxdepth );
  288. }
  289. vprint( 0, "\n" );
  290. }
  291. m_nLinesOfCode += linesprocessed;
  292. linesprocessed = 0;
  293. }
  294. void CCodeProcessor::ProcessModules( const char *root, const char *rootmodule )
  295. {
  296. m_nFilesProcessed++;
  297. // Reset header list per module
  298. m_nHeaderCount = 0;
  299. m_nModuleCount = 0;
  300. int numheaders = 0;
  301. int maxdepth = 0;
  302. int skippedfiles = 0;
  303. bool canstart = false;
  304. if ( strstr( root, "tf2_hud" ) )
  305. {
  306. canstart = true;
  307. }
  308. if ( !canstart )
  309. {
  310. vprint( 0, "skipping %s\n", rootmodule );
  311. return;
  312. }
  313. ProcessModule( false, 0, maxdepth, numheaders, skippedfiles, root, root, rootmodule );
  314. }
  315. void CCodeProcessor::PrintResults( void )
  316. {
  317. vprint( 0, "\nChecking for errors and totaling...\n\n" );
  318. vprint( 0, "parsed from %i files ( %i headers parsed )\n",
  319. m_nFilesProcessed,
  320. m_nHeadersProcessed );
  321. vprint( 0, "%.3f K lines of code processed\n",
  322. (double)m_nLinesOfCode / 1024.0 );
  323. double elapsed = ( m_flEnd - m_flStart );
  324. if ( elapsed > 0.0 )
  325. {
  326. vprint( 0, "%.2f K processed in %.3f seconds, throughput %.2f KB/sec\n\n",
  327. (double)m_nBytesProcessed / 1024.0, elapsed, (double)m_nBytesProcessed / ( 1024.0 * elapsed ) );
  328. }
  329. }
  330. CCodeProcessor::CCodeProcessor( void )
  331. {
  332. m_nModuleCount = 0;
  333. m_bQuiet = false;
  334. m_bLogToFile = false;
  335. m_nFilesProcessed = 0;
  336. m_nHeadersProcessed = 0;
  337. m_nOffset = 0;
  338. m_nBytesProcessed = 0;
  339. m_nLinesOfCode = 0;
  340. m_flStart = 0.0;
  341. m_flEnd = 0.0;
  342. m_szCurrentCPP[ 0 ] = 0;
  343. }
  344. CCodeProcessor::~CCodeProcessor( void )
  345. {
  346. }
  347. char const *stristr( char const *src, char const *search )
  348. {
  349. char buf1[ 512 ];
  350. char buf2[ 512 ];
  351. strcpy( buf1, src );
  352. _strlwr( buf1 );
  353. strcpy( buf2, search );
  354. _strlwr( buf2 );
  355. char *p = strstr( buf1, buf2 );
  356. if ( p )
  357. {
  358. int len = p - buf1;
  359. return src + len;
  360. }
  361. return NULL;
  362. }
  363. void CCodeProcessor::ConstructModuleList_R( int level, const char *gamespecific, const char *root )
  364. {
  365. char directory[ 256 ];
  366. char filename[ 256 ];
  367. WIN32_FIND_DATA wfd;
  368. HANDLE ff;
  369. sprintf( directory, "%s\\*.*", root );
  370. if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
  371. return;
  372. do
  373. {
  374. if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  375. {
  376. if ( wfd.cFileName[ 0 ] == '.' )
  377. continue;
  378. // Once we descend down a branch, don't keep looking for hl2/tf2 in name, just recurse through all children
  379. if ( level == 0 && !stristr( wfd.cFileName, gamespecific ) )
  380. continue;
  381. // Recurse down directory
  382. sprintf( filename, "%s\\%s", root, wfd.cFileName );
  383. ConstructModuleList_R( level+1, gamespecific, filename );
  384. }
  385. else
  386. {
  387. if ( strstr( wfd.cFileName, ".cpp" ) )
  388. {
  389. ProcessModules( root, wfd.cFileName );
  390. }
  391. }
  392. } while ( FindNextFile( ff, &wfd ) );
  393. }
  394. void CCodeProcessor::Process( const char *gamespecific, const char *root, const char *dsp, const char *config )
  395. {
  396. strcpy( m_szDSP, dsp );
  397. strcpy( m_szConfig, config );
  398. m_nBytesProcessed = 0;
  399. m_nFilesProcessed = 0;
  400. m_nHeadersProcessed = 0;
  401. m_nLinesOfCode = 0;
  402. linesprocessed = 0;
  403. m_nOffset = strlen( root ) + 1;
  404. m_nModuleCount = 0;
  405. m_nHeaderCount = 0;
  406. m_flStart = UTIL_FloatTime();
  407. vprint( 0, "--- Processing %s\n\n", root );
  408. ConstructModuleList_R( 0, gamespecific, root );
  409. m_flEnd = UTIL_FloatTime();
  410. PrintResults();
  411. }
  412. void CCodeProcessor::SetQuiet( bool quiet )
  413. {
  414. m_bQuiet = quiet;
  415. }
  416. bool CCodeProcessor::GetQuiet( void ) const
  417. {
  418. return m_bQuiet;
  419. }
  420. void CCodeProcessor::SetLogFile( bool log )
  421. {
  422. m_bLogToFile = log;
  423. }
  424. bool CCodeProcessor::GetLogFile( void ) const
  425. {
  426. return m_bLogToFile;
  427. }
  428. static CCodeProcessor g_Processor;
  429. ICodeProcessor *processor = ( ICodeProcessor * )&g_Processor;