Counter Strike : Global Offensive Source Code
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.

1097 lines
32 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "vpc.h"
  7. #include "dependencies.h"
  8. #include "baseprojectdatacollector.h"
  9. #include "tier0/fasttimer.h"
  10. #define VPC_CRC_CACHE_VERSION 3
  11. extern char *g_IncludeSeparators[2];
  12. static const char *g_pDependencyRelevantProperties[] =
  13. {
  14. g_pOption_AdditionalProjectDependencies,
  15. g_pOption_AdditionalOutputFiles,
  16. g_pOption_AdditionalIncludeDirectories,
  17. g_pOption_ImportLibrary,
  18. g_pOption_OutputFile
  19. };
  20. static CRelevantPropertyNames g_DependencyRelevantPropertyNames =
  21. {
  22. g_pDependencyRelevantProperties,
  23. Q_ARRAYSIZE( g_pDependencyRelevantProperties )
  24. };
  25. bool IsSharedLibraryFile( const char *pFilename )
  26. {
  27. const char *pExt = V_GetFileExtension( pFilename );
  28. if ( pExt && ( V_stricmp( pExt, "so" ) == 0 || V_stricmp( pExt, "dylib" ) == 0 || V_stricmp( pExt, "dll" ) == 0 ) )
  29. {
  30. return true;
  31. }
  32. else
  33. {
  34. return false;
  35. }
  36. }
  37. bool IsLibraryFile( const char *pFilename )
  38. {
  39. const char *pExt = V_GetFileExtension( pFilename );
  40. if ( IsSharedLibraryFile( pFilename ) || ( pExt && ( V_stricmp( pExt, "lib" ) == 0 || V_stricmp( pExt, "a" ) == 0 ) ) )
  41. {
  42. return true;
  43. }
  44. else
  45. {
  46. return false;
  47. }
  48. }
  49. static inline bool IsSourceFile( const char *pFilename )
  50. {
  51. const char *pExt = V_GetFileExtension( pFilename );
  52. if ( pExt && ( V_stricmp( pExt, "cpp" ) == 0 || V_stricmp( pExt, "c" ) == 0 || V_stricmp( pExt, "cxx" ) == 0 || V_stricmp( pExt, "cc" ) == 0 ||
  53. V_stricmp( pExt, "rc" ) == 0 ||
  54. V_stricmp( pExt, "hxx" ) == 0 || V_stricmp( pExt, "h" ) == 0 ||
  55. V_stricmp( pExt, "inc" ) == 0 ) )
  56. {
  57. return true;
  58. }
  59. else
  60. {
  61. return false;
  62. }
  63. }
  64. // ------------------------------------------------------------------------------------------------------- //
  65. // CDependency functions.
  66. // ------------------------------------------------------------------------------------------------------- //
  67. CDependency::CDependency( CProjectDependencyGraph *pDependencyGraph ) :
  68. m_pDependencyGraph( pDependencyGraph )
  69. {
  70. m_iDependencyMark = m_pDependencyGraph->m_iDependencyMark - 1;
  71. m_bCheckedIncludes = false;
  72. m_nCacheModificationTime = m_nCacheFileSize = 0;
  73. }
  74. CDependency::~CDependency()
  75. {
  76. }
  77. const char* CDependency::GetName() const
  78. {
  79. return m_Filename.String();
  80. }
  81. bool CDependency::CompareAbsoluteFilename( const char *pAbsPath ) const
  82. {
  83. return ( V_stricmp( m_Filename.String(), pAbsPath ) == 0 );
  84. }
  85. bool CDependency::DependsOn( CDependency *pTest, int flags )
  86. {
  87. m_pDependencyGraph->ClearAllDependencyMarks();
  88. CUtlVector<CUtlBuffer> callTreeOutputStack;
  89. if ( FindDependency_Internal( callTreeOutputStack, pTest, flags, 1 ) )
  90. {
  91. if ( g_pVPC->IsShowDependencies() )
  92. {
  93. printf( "-------------------------------------------------------------------------------\n" );
  94. printf( "%s\n", GetName() );
  95. int i;
  96. for( i = callTreeOutputStack.Count() - 1; i >= 0; i-- )
  97. {
  98. printf( ( const char * )callTreeOutputStack[i].Base() );
  99. }
  100. printf( "-------------------------------------------------------------------------------\n" );
  101. }
  102. return true;
  103. }
  104. else
  105. {
  106. return false;
  107. }
  108. }
  109. bool CDependency::FindDependency_Internal( CUtlVector<CUtlBuffer> &callTreeOutputStack, CDependency *pTest, int flags, int depth )
  110. {
  111. if ( pTest == this )
  112. return true;
  113. // Don't revisit us.
  114. if ( HasBeenMarked() )
  115. return false;
  116. Mark();
  117. // Don't recurse further?
  118. if ( depth > 1 && !(flags & k_EDependsOnFlagRecurse) )
  119. return false;
  120. // Don't go into the children of libs if they don't want.
  121. if ( !(flags & k_EDependsOnFlagTraversePastLibs) && m_Type == k_eDependencyType_Library )
  122. return false;
  123. // Go through everything I depend on. If any of those things
  124. for ( int iDepList=0; iDepList < 2; iDepList++ )
  125. {
  126. if ( iDepList == 1 && !(flags & k_EDependsOnFlagCheckAdditionalDependencies) )
  127. continue;
  128. CUtlVector<CDependency*> &depList = (iDepList == 0 ? m_Dependencies : m_AdditionalDependencies);
  129. for ( int i=0; i < depList.Count(); i++ )
  130. {
  131. CDependency *pChild = depList[i];
  132. if ( pChild->FindDependency_Internal( callTreeOutputStack, pTest, flags, depth+1 ) )
  133. {
  134. if ( g_pVPC->IsShowDependencies() )
  135. {
  136. char buf[2048];
  137. Q_strncpy( buf, "depends on ", sizeof( buf ) );
  138. Q_strcat( buf, pChild->GetName(), sizeof( buf ) );
  139. Q_strcat( buf, "\n", sizeof( buf ) );
  140. int n = callTreeOutputStack.AddToTail();
  141. CUtlBuffer &b = callTreeOutputStack[n];
  142. b.EnsureCapacity( Q_strlen( buf ) + 2 );
  143. b.PutString( buf );
  144. b.PutChar( 0 );
  145. }
  146. return true;
  147. }
  148. }
  149. }
  150. return false;
  151. }
  152. void CDependency::Mark()
  153. {
  154. m_iDependencyMark = m_pDependencyGraph->m_iDependencyMark;
  155. }
  156. bool CDependency::HasBeenMarked()
  157. {
  158. return m_iDependencyMark == m_pDependencyGraph->m_iDependencyMark;
  159. }
  160. CDependency_Project::CDependency_Project( CProjectDependencyGraph *pDependencyGraph )
  161. : CDependency( pDependencyGraph )
  162. {
  163. }
  164. void CDependency_Project::StoreProjectParameters( const char *szScriptName )
  165. {
  166. m_StoredOutputFilename = g_pVPC->GetOutputFilename();
  167. V_GetCurrentDirectory( m_szStoredCurrentDirectory, sizeof( m_szStoredCurrentDirectory ) );
  168. V_strncpy( m_szStoredScriptName, szScriptName, sizeof( m_szStoredScriptName ) );
  169. m_StoredConditionalsActive.SetSize( g_pVPC->m_Conditionals.Count() );
  170. for ( int iConditional=0; iConditional < g_pVPC->m_Conditionals.Count(); iConditional++ )
  171. {
  172. m_StoredConditionalsActive[iConditional] = g_pVPC->m_Conditionals[iConditional].m_bGameConditionActive;
  173. }
  174. }
  175. void CDependency_Project::ExportProjectParameters()
  176. {
  177. g_pVPC->SetOutputFilename( m_StoredOutputFilename.Get() );
  178. V_SetCurrentDirectory( m_szStoredCurrentDirectory );
  179. if ( m_StoredConditionalsActive.Count() > g_pVPC->m_Conditionals.Count() )
  180. {
  181. g_pVPC->VPCError( "ExportProjectParameters( %s ) - too many defines stored.", m_szStoredScriptName );
  182. }
  183. for ( int iConditional=0; iConditional < g_pVPC->m_Conditionals.Count(); iConditional++ )
  184. {
  185. g_pVPC->m_Conditionals[iConditional].m_bGameConditionActive = m_StoredConditionalsActive[iConditional];
  186. }
  187. }
  188. int CDependency_Project::FindByProjectName( CUtlVector<CDependency_Project*> &projects, const char *pTestName )
  189. {
  190. for ( int i=0; i < projects.Count(); i++ )
  191. {
  192. CDependency_Project *pProject = projects[i];
  193. if ( V_stricmp( pProject->m_ProjectName.String(), pTestName ) == 0 )
  194. return i;
  195. }
  196. return -1;
  197. }
  198. // This is responsible for scanning a project file and pulling out:
  199. // - a list of libraries it uses
  200. // - the $AdditionalIncludeDirectories paths
  201. // - a list of source files it uses
  202. // - the name of the file it generates
  203. class CSingleProjectScanner : public CBaseProjectDataCollector
  204. {
  205. public:
  206. typedef CBaseProjectDataCollector BaseClass;
  207. CSingleProjectScanner() : CBaseProjectDataCollector( &g_DependencyRelevantPropertyNames )
  208. {
  209. m_bInLinker = false;
  210. }
  211. virtual void EndProject()
  212. {
  213. }
  214. void ScanProjectFile( CProjectDependencyGraph *pGraph, const char *szScriptName, CDependency_Project *pProject )
  215. {
  216. // Someday we'll pass this interface down into VPC_ParseProjectScript instead of using the global.
  217. IBaseProjectGenerator *pOldGenerator = g_pVPC->GetProjectGenerator();
  218. g_pVPC->SetProjectGenerator( this );
  219. // This has VPC parse the script and CBaseProjectDataCollector collects all the data into lists of the
  220. // stuff we care about like source files and include paths.
  221. m_ScriptName = szScriptName;
  222. g_pVPC->ParseProjectScript( szScriptName, 0, true, false );
  223. int iConfig = m_BaseConfigData.m_Configurations.First();
  224. if ( iConfig != m_BaseConfigData.m_Configurations.InvalidIndex() )
  225. {
  226. CSpecificConfig *pConfig = m_BaseConfigData.m_Configurations[iConfig];
  227. SetupIncludeDirectories( pConfig, szScriptName );
  228. SetupFilesList( pGraph, pProject );
  229. SetupImportLibrary( pGraph, pConfig, szScriptName );
  230. SetupAdditionalProjectDependencies( pProject, pConfig );
  231. SetupAdditionalOutputFiles( pProject, pConfig );
  232. }
  233. g_pVPC->SetProjectGenerator( pOldGenerator );
  234. Term();
  235. }
  236. void SetupFilesList( CProjectDependencyGraph *pGraph, CDependency_Project *pProject )
  237. {
  238. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next( i ) )
  239. {
  240. CFileConfig *pFile = m_Files[i];
  241. // If this file is excluded from all configs, then exclude it.
  242. if ( pFile->m_Configurations.Count() > 0 )
  243. {
  244. int nExcluded = 0;
  245. for ( int iSpecific=pFile->m_Configurations.First(); iSpecific != pFile->m_Configurations.InvalidIndex(); iSpecific=pFile->m_Configurations.Next( iSpecific ) )
  246. {
  247. CSpecificConfig *pTest = pFile->m_Configurations[iSpecific];
  248. if ( pTest->m_bFileExcluded && !pTest->m_bIsSchema )
  249. ++nExcluded;
  250. }
  251. if ( nExcluded == (int)m_BaseConfigData.m_Configurations.Count() )
  252. continue;
  253. }
  254. // Make this an absolute path.
  255. const char *pFilename = pFile->GetName();
  256. char sAbsolutePath[MAX_PATH];
  257. V_MakeAbsolutePath( sAbsolutePath, sizeof( sAbsolutePath ), pFilename );
  258. // Don't bother with source files if we're not building the full dependency set.
  259. if ( !pGraph->m_bFullDependencySet )
  260. if ( IsSourceFile( sAbsolutePath ) )
  261. continue;
  262. // For source files, don't bother with files that don't exist. If we do create entries
  263. // for files that don't exist, then they'll have a "cache file size"
  264. if ( !Sys_Exists( sAbsolutePath ) && IsSourceFile( sAbsolutePath ) )
  265. continue;
  266. // Add an entry for this file.
  267. CDependency *pDep = pGraph->FindOrCreateDependency( sAbsolutePath );
  268. pProject->m_Dependencies.AddToTail( pDep );
  269. // Add includes.
  270. if ( pDep->m_Type == k_eDependencyType_SourceFile )
  271. AddIncludesForFile( pGraph, pDep );
  272. }
  273. }
  274. void AddIncludesForFile( CProjectDependencyGraph *pGraph, CDependency *pFile )
  275. {
  276. // Have we already parsed this file for its includes?
  277. if ( pFile->m_bCheckedIncludes )
  278. return;
  279. pFile->m_bCheckedIncludes = true;
  280. // Setup all the include paths we want to search.
  281. CUtlVector<CUtlString> includeDirs;
  282. char szDir[MAX_PATH];
  283. if ( !V_ExtractFilePath( pFile->GetName(), szDir, sizeof( szDir ) ) )
  284. g_pVPC->VPCError( "AddIncludesForFile: V_ExtractFilePath( %s ) failed.", pFile->GetName() );
  285. includeDirs.AddToTail( szDir );
  286. includeDirs.AddMultipleToTail( m_IncludeDirectories.Count(), m_IncludeDirectories.Base() );
  287. // Get all the #include directives.
  288. CUtlVector<CUtlString> includes;
  289. GetIncludeFiles( pFile->GetName(), includes );
  290. ++pGraph->m_nFilesParsedForIncludes;
  291. // Now see which of them we can open.
  292. for ( int iIncludeFile=0; iIncludeFile < includes.Count(); iIncludeFile++ )
  293. {
  294. for ( int iIncludeDir=0; iIncludeDir < includeDirs.Count(); iIncludeDir++ )
  295. {
  296. char szFullName[MAX_PATH];
  297. V_ComposeFileName( includeDirs[iIncludeDir].String(), includes[iIncludeFile].String(), szFullName, sizeof( szFullName ) );
  298. CDependency *pIncludeFile = pGraph->FindDependency( szFullName );
  299. if ( !pIncludeFile )
  300. {
  301. if ( !Sys_Exists( szFullName ) )
  302. continue;
  303. // Find or add the dependency.
  304. pIncludeFile = pGraph->FindOrCreateDependency( szFullName );
  305. }
  306. pFile->m_Dependencies.AddToTail( pIncludeFile );
  307. // Recurse.
  308. AddIncludesForFile( pGraph, pIncludeFile );
  309. }
  310. }
  311. }
  312. bool SeekToIncludeStart( const char* &pSearchPos )
  313. {
  314. while ( 1 )
  315. {
  316. ++pSearchPos;
  317. if ( *pSearchPos == 0 || *pSearchPos == '\r' || *pSearchPos == '\n' )
  318. return false;
  319. if ( *pSearchPos == '\"' || *pSearchPos == '<' )
  320. {
  321. ++pSearchPos;
  322. return true;
  323. }
  324. }
  325. }
  326. bool SeekToIncludeEnd( const char* &pSearchPos )
  327. {
  328. while ( 1 )
  329. {
  330. ++pSearchPos;
  331. if ( *pSearchPos == 0 || *pSearchPos == '\r' || *pSearchPos == '\n' )
  332. return false;
  333. if ( *pSearchPos == '\"' || *pSearchPos == '>' )
  334. return true;
  335. }
  336. }
  337. void GetIncludeFiles( const char *pFilename, CUtlVector<CUtlString> &includes )
  338. {
  339. char *pFileData;
  340. int ret = Sys_LoadFile( pFilename, (void**)&pFileData, false );
  341. if ( ret == -1 )
  342. {
  343. if ( g_pVPC->IsVerbose() )
  344. {
  345. g_pVPC->VPCWarning( "GetIncludeFiles( %s ) - can't open file (included by project %s).", pFilename, m_ScriptName.String() );
  346. }
  347. return;
  348. }
  349. const char *pSearchPos = pFileData;
  350. while ( 1 )
  351. {
  352. const char *pLookFor = "#include";
  353. const char *pIncludeStatement = V_strstr( pSearchPos, pLookFor );
  354. if ( !pIncludeStatement )
  355. break;
  356. pSearchPos = pIncludeStatement + V_strlen( pLookFor );
  357. if ( !SeekToIncludeStart( pSearchPos ) )
  358. continue;
  359. const char *pFilenameStart = pSearchPos;
  360. if ( !SeekToIncludeEnd( pSearchPos ) )
  361. continue;
  362. const char *pFilenameEnd = pSearchPos;
  363. if ( (pFilenameEnd - pFilenameStart) > MAX_PATH-10 )
  364. g_pVPC->VPCError( "Include statement too long in %s.", pFilename );
  365. char szIncludeFilename[MAX_PATH], szFixed[MAX_PATH];
  366. V_strncpy( szIncludeFilename, pFilenameStart, pFilenameEnd - pFilenameStart + 1 );
  367. // Fixup double slashes.
  368. V_StrSubst( szIncludeFilename, "\\\\", "\\", szFixed, sizeof( szFixed ) );
  369. V_FixSlashes( szFixed );
  370. includes.AddToTail( szFixed );
  371. }
  372. free( pFileData );
  373. }
  374. void SetupIncludeDirectories( CSpecificConfig *pConfig, const char *szScriptName )
  375. {
  376. if ( m_BaseConfigData.m_Configurations.Count() == 0 )
  377. g_pVPC->VPCError( "No configurations for %s in project %s.", szScriptName, m_ScriptName.String() );
  378. const char *pIncludes = pConfig->m_pKV->GetString( g_pOption_AdditionalIncludeDirectories, "" );
  379. CSplitString relativeIncludeDirs( pIncludes, (const char**)g_IncludeSeparators, Q_ARRAYSIZE( g_IncludeSeparators ) );
  380. for ( int i=0; i < relativeIncludeDirs.Count(); i++ )
  381. {
  382. char sAbsolute[MAX_PATH];
  383. V_MakeAbsolutePath( sAbsolute, sizeof( sAbsolute ), relativeIncludeDirs[i] );
  384. m_IncludeDirectories.AddToTail( sAbsolute );
  385. }
  386. }
  387. void SetupImportLibrary( CProjectDependencyGraph *pGraph, CSpecificConfig *pConfig, const char *szScriptName )
  388. {
  389. m_ImportLibrary = pConfig->m_pKV->GetString( g_pOption_ImportLibrary, NULL );
  390. m_LinkerOutputFile = pConfig->m_pKV->GetString( g_pOption_OutputFile, NULL );
  391. }
  392. void SetupAdditionalProjectDependencies( CDependency_Project *pProject, CSpecificConfig *pConfig )
  393. {
  394. const char *pVal = pConfig->m_pKV->GetString( g_pOption_AdditionalProjectDependencies );
  395. if ( pVal )
  396. {
  397. pProject->m_AdditionalProjectDependencies.Purge();
  398. CSplitString outStrings ( pVal, ";" );
  399. for ( int i=0; i < outStrings.Count(); i++ )
  400. {
  401. char szProjectName[MAX_PATH];
  402. sprintf( szProjectName, "%s", outStrings[i] );
  403. if ( g_pVPC->IsDecorateProject() )
  404. {
  405. macro_t *pMacro = g_pVPC->FindOrCreateMacro( "PLATFORM", false, NULL );
  406. if ( pMacro )
  407. {
  408. char szPlatform[MAX_PATH];
  409. sprintf( szPlatform, " (%s)", pMacro->value.String() );
  410. strcat( szProjectName, szPlatform );
  411. }
  412. }
  413. pProject->m_AdditionalProjectDependencies.AddToTail( szProjectName );
  414. }
  415. }
  416. }
  417. void SetupAdditionalOutputFiles( CDependency_Project *pProject, CSpecificConfig *pConfig )
  418. {
  419. const char *pVal = pConfig->m_pKV->GetString( g_pOption_AdditionalOutputFiles );
  420. if ( pVal )
  421. {
  422. pProject->m_AdditionalOutputFiles.Purge();
  423. CSplitString outStrings( pVal, ";" );
  424. for ( int i=0; i < outStrings.Count(); i++ )
  425. {
  426. pProject->m_AdditionalOutputFiles.AddToTail( outStrings[i] );
  427. }
  428. }
  429. }
  430. virtual const char* GetProjectFileExtension()
  431. {
  432. return "UNUSED";
  433. }
  434. protected:
  435. virtual bool StartPropertySection( configKeyword_e keyword, bool *pbShouldSkip )
  436. {
  437. m_bInLinker = ( keyword == KEYWORD_LINKER || keyword == KEYWORD_LIBRARIAN );
  438. return true;
  439. }
  440. virtual void HandleProperty( const char *pProperty, const char *pCustomScriptData )
  441. {
  442. // We don't want the $OutputFile property from the $BrowseInformation section.
  443. if ( V_stricmp( pProperty, g_pOption_OutputFile ) == 0 && !m_bInLinker )
  444. return;
  445. BaseClass::HandleProperty( pProperty, pCustomScriptData );
  446. }
  447. virtual void EndPropertySection( configKeyword_e keyword )
  448. {
  449. m_bInLinker = false;
  450. }
  451. public:
  452. // Project include directories. These strings are deleted when the object goes away.
  453. CUtlVector<CUtlString> m_IncludeDirectories;
  454. CUtlString m_ImportLibrary;
  455. CUtlString m_LinkerOutputFile;
  456. CUtlString m_ScriptName;
  457. bool m_bInLinker;
  458. };
  459. CProjectDependencyGraph::CProjectDependencyGraph()
  460. {
  461. m_iDependencyMark = 0;
  462. m_bFullDependencySet = false;
  463. m_bHasGeneratedDependencies = false;
  464. }
  465. void CProjectDependencyGraph::BuildProjectDependencies( int nBuildProjectDepsFlags )
  466. {
  467. m_bFullDependencySet = ( ( nBuildProjectDepsFlags & BUILDPROJDEPS_FULL_DEPENDENCY_SET ) != 0 );
  468. m_nFilesParsedForIncludes = 0;
  469. if ( m_bFullDependencySet )
  470. {
  471. Log_Msg( LOG_VPC, "\nBuilding full dependency set (all sources and headers)..." );
  472. }
  473. else
  474. {
  475. Log_Msg( LOG_VPC, "\nBuilding partial dependency set (libs only)..." );
  476. }
  477. // Have it iterate ALL projects in the list, with whatever platform conditionals are around.
  478. // When it visits a
  479. CUtlVector<projectIndex_t> projectList;
  480. CUtlVector<int> oldState;
  481. if ( nBuildProjectDepsFlags & BUILDPROJDEPS_CHECK_ALL_PROJECTS )
  482. {
  483. // So iterate all projects.
  484. projectList.SetSize( g_pVPC->m_Projects.Count() );
  485. for ( int i=0; i < g_pVPC->m_Projects.Count(); i++ )
  486. projectList[i] = i;
  487. // Simulate /allgames but remember the old state too.
  488. for ( int j=0; j<g_pVPC->m_Conditionals.Count(); j++ )
  489. {
  490. if ( g_pVPC->m_Conditionals[j].type == CONDITIONAL_GAME )
  491. {
  492. oldState.AddToTail( (j << 16) + (int)g_pVPC->m_Conditionals[j].m_bDefined );
  493. g_pVPC->m_Conditionals[j].m_bDefined = true;
  494. }
  495. }
  496. }
  497. else
  498. {
  499. projectList.AddMultipleToTail( g_pVPC->m_TargetProjects.Count(), g_pVPC->m_TargetProjects.Base() );
  500. }
  501. // Load any prior results so we don't have to regenerate the whole cache (which can take a couple minutes).
  502. char sCacheFile[MAX_PATH] = {0};
  503. V_ComposeFileName( g_pVPC->GetSourcePath(), "vpc.cache", sCacheFile, sizeof( sCacheFile ) );
  504. if ( m_bFullDependencySet )
  505. {
  506. if ( !LoadCache( sCacheFile ) )
  507. {
  508. Log_Msg( LOG_VPC, "\n\nNo vpc.cache file found.\nThis will take a minute to generate dependency info from all the sources.\nPut the kleenex down.\nNext time it will have a cache file and be fast.\n\n" );
  509. }
  510. }
  511. CFastTimer timer;
  512. timer.Start();
  513. g_pVPC->IterateTargetProjects( projectList, this );
  514. timer.End();
  515. ResolveAdditionalProjectDependencies();
  516. // Restore the old game defines state?
  517. if ( nBuildProjectDepsFlags & BUILDPROJDEPS_CHECK_ALL_PROJECTS )
  518. {
  519. for ( int i=0; i < oldState.Count(); i++ )
  520. {
  521. int iDefine = oldState[i] >> 16;
  522. g_pVPC->m_Conditionals[iDefine].m_bDefined = ( (oldState[i] & 1) != 0 );
  523. }
  524. }
  525. // Save the expensive work we did into a cache file so it can be used next time.
  526. if ( m_bFullDependencySet )
  527. {
  528. SaveCache( sCacheFile );
  529. }
  530. Log_Msg( LOG_VPC, "\n\n" );
  531. if ( m_nFilesParsedForIncludes > 0 )
  532. {
  533. Log_Msg( LOG_VPC, "%d files parsed in %.2f seconds for #includes.\n", m_nFilesParsedForIncludes, timer.GetDuration().GetSeconds() );
  534. }
  535. m_bHasGeneratedDependencies = true;
  536. }
  537. void CProjectDependencyGraph::ResolveAdditionalProjectDependencies()
  538. {
  539. for ( int iMainProject=0; iMainProject < m_Projects.Count(); iMainProject++ )
  540. {
  541. CDependency_Project *pMainProject = m_Projects[iMainProject];
  542. for ( int i=0; i < pMainProject->m_AdditionalProjectDependencies.Count(); i++ )
  543. {
  544. const char *pLookingFor = pMainProject->m_AdditionalProjectDependencies[i].String();
  545. // Look for this project name among all the projects.
  546. int j;
  547. for ( j=0; j < m_Projects.Count(); j++ )
  548. {
  549. if ( V_stricmp( m_Projects[j]->m_ProjectName.String(), pLookingFor ) == 0 )
  550. break;
  551. }
  552. if ( j == m_Projects.Count() )
  553. {
  554. //VPCError( "Project %s lists '%s' in its $AdditionalProjectDependencies, but there is no project by that name.", pMainProject->GetName(), pLookingFor );
  555. continue;
  556. }
  557. if ( pMainProject->m_AdditionalDependencies.Find( m_Projects[j] ) == pMainProject->m_AdditionalDependencies.InvalidIndex() )
  558. pMainProject->m_AdditionalDependencies.AddToTail( m_Projects[j] );
  559. }
  560. }
  561. }
  562. bool CProjectDependencyGraph::HasGeneratedDependencies() const
  563. {
  564. return m_bHasGeneratedDependencies;
  565. }
  566. bool CProjectDependencyGraph::VisitProject( projectIndex_t iProject, const char *szProjectName )
  567. {
  568. // Read in the project.
  569. if ( !Sys_Exists( szProjectName ) )
  570. {
  571. return false;
  572. }
  573. // Add another dot for the pacifier.
  574. Log_Msg( LOG_VPC, "." );
  575. // Add this project.
  576. CDependency_Project *pProject = new CDependency_Project( this );
  577. char szAbsolute[MAX_PATH];
  578. V_MakeAbsolutePath( szAbsolute, sizeof( szAbsolute ), szProjectName );
  579. pProject->m_Filename = szAbsolute;
  580. pProject->m_Type = k_eDependencyType_Project;
  581. pProject->m_iProjectIndex = iProject;
  582. m_Projects.AddToTail( pProject );
  583. m_AllFiles.Insert( szAbsolute, pProject );
  584. // Remember various parameters passed to us so we can regenerate this project without having
  585. // to call VPC_IterateTargetProjects.
  586. pProject->StoreProjectParameters( szProjectName );
  587. char sAbsProjectFilename[MAX_PATH];
  588. V_MakeAbsolutePath( sAbsProjectFilename, sizeof( sAbsProjectFilename ), g_pVPC->GetOutputFilename() );
  589. pProject->m_ProjectFilename = sAbsProjectFilename;
  590. // Scan the project file and get all its libs, cpp, and h files.
  591. CSingleProjectScanner scanner;
  592. scanner.ScanProjectFile( this, szAbsolute, pProject );
  593. pProject->m_IncludeDirectories = scanner.m_IncludeDirectories;
  594. pProject->m_ProjectName = scanner.m_ProjectName;
  595. // Get a list of all files that depend on this project, starting with the .lib if it generates one.
  596. CUtlVector<CUtlString> outputFiles;
  597. outputFiles = pProject->m_AdditionalOutputFiles;
  598. // Now note that the import library depends on this project.
  599. // $(ImportLibrary) will be a lib in the case of DLLs that create libs (like tier0).
  600. // $(OutputFile) will be a lib in the case of static libs (like tier1).
  601. const char *pLinkerOutputFile = scanner.m_LinkerOutputFile.String();
  602. const char *pImportLibrary = scanner.m_ImportLibrary.String();
  603. if ( !IsLibraryFile( pImportLibrary ) )
  604. {
  605. pImportLibrary = pLinkerOutputFile;
  606. }
  607. if ( IsLibraryFile( pImportLibrary ) )
  608. {
  609. outputFiles.AddToTail( pImportLibrary );
  610. }
  611. // The string that we replace $(TargetName) with is the output project filename without the path or extension.
  612. // That'll be something like "tier0_360".
  613. char sTargetNameReplacement[MAX_PATH];
  614. V_FileBase( pLinkerOutputFile, sTargetNameReplacement, sizeof( sTargetNameReplacement ) );
  615. // Now add a CDependency for each file.
  616. for ( int i=0; i < outputFiles.Count(); i++ )
  617. {
  618. const char *pFilename = outputFiles[i].String();
  619. // Replace $(TargetName) and fixup the path.
  620. char sReplaced[MAX_PATH], sAbsImportLibrary[MAX_PATH];
  621. V_StrSubst( pFilename, "$(TargetName)", sTargetNameReplacement, sReplaced, sizeof( sReplaced ) );
  622. V_MakeAbsolutePath( sAbsImportLibrary, sizeof( sAbsImportLibrary ), sReplaced );
  623. CDependency *pImportLibrary = FindOrCreateDependency( sAbsImportLibrary );
  624. pImportLibrary->m_Dependencies.AddToTail( pProject );
  625. }
  626. return true;
  627. }
  628. void CProjectDependencyGraph::GetProjectDependencyTree( projectIndex_t iProject, CUtlVector<projectIndex_t> &dependentProjects, bool bDownwards )
  629. {
  630. // First add the project itself.
  631. if ( dependentProjects.Find( iProject ) == dependentProjects.InvalidIndex() )
  632. dependentProjects.AddToTail( iProject );
  633. // Now add anything that depends on it.
  634. for ( int i=0; i < m_Projects.Count(); i++)
  635. {
  636. CDependency_Project *pProject = m_Projects[i];
  637. if ( pProject->m_iProjectIndex != iProject )
  638. continue;
  639. // Ok, this project/game/platform combo comes from iProject. Now find anything that depends on it.
  640. for ( int iOther=0; iOther < m_Projects.Count(); iOther++ )
  641. {
  642. CDependency_Project *pOther = m_Projects[iOther];
  643. if ( pOther->m_iProjectIndex == iProject )
  644. continue;
  645. bool bThereIsADependency;
  646. if ( bDownwards )
  647. bThereIsADependency = pProject->DependsOn( pOther, k_EDependsOnFlagCheckNormalDependencies | k_EDependsOnFlagCheckAdditionalDependencies | k_EDependsOnFlagRecurse | k_EDependsOnFlagTraversePastLibs );
  648. else
  649. bThereIsADependency = pOther->DependsOn( pProject, k_EDependsOnFlagCheckNormalDependencies | k_EDependsOnFlagCheckAdditionalDependencies | k_EDependsOnFlagRecurse | k_EDependsOnFlagTraversePastLibs );
  650. if ( bThereIsADependency )
  651. {
  652. if ( dependentProjects.Find( pOther->m_iProjectIndex ) == dependentProjects.InvalidIndex() )
  653. dependentProjects.AddToTail( pOther->m_iProjectIndex );
  654. }
  655. }
  656. }
  657. }
  658. CDependency* CProjectDependencyGraph::FindDependency( const char *pFilename )
  659. {
  660. int i = m_AllFiles.Find( pFilename );
  661. if ( i == m_AllFiles.InvalidIndex() )
  662. return NULL;
  663. else
  664. return m_AllFiles[i];
  665. }
  666. CDependency* CProjectDependencyGraph::FindOrCreateDependency( const char *pFilename )
  667. {
  668. // Fix up stuff like blah/../blah
  669. char sFixed[MAX_PATH];
  670. V_FixupPathName( sFixed, sizeof( sFixed ), pFilename );
  671. pFilename = sFixed;
  672. CDependency *pDependency = FindDependency( pFilename );
  673. if ( pDependency )
  674. return pDependency;
  675. // Couldn't find it. Create one.
  676. pDependency = new CDependency( this );
  677. pDependency->m_Filename = pFilename;
  678. m_AllFiles.Insert( pFilename, pDependency );
  679. Sys_FileInfo( pFilename, pDependency->m_nCacheFileSize, pDependency->m_nCacheModificationTime );
  680. if ( IsSourceFile( pFilename ) )
  681. pDependency->m_Type = k_eDependencyType_SourceFile;
  682. else if ( IsLibraryFile( pFilename ) )
  683. pDependency->m_Type = k_eDependencyType_Library;
  684. else
  685. pDependency->m_Type = k_eDependencyType_Unknown;
  686. return pDependency;
  687. }
  688. void CProjectDependencyGraph::ClearAllDependencyMarks()
  689. {
  690. if ( m_iDependencyMark == 0xFFFFFFFF )
  691. {
  692. m_iDependencyMark = 1;
  693. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=m_AllFiles.Next(i) )
  694. {
  695. m_AllFiles[i]->m_iDependencyMark = 0;
  696. }
  697. }
  698. else
  699. {
  700. // The 99.9999999% chance case.
  701. ++m_iDependencyMark;
  702. }
  703. }
  704. bool CProjectDependencyGraph::LoadCache( const char *pFilename )
  705. {
  706. FILE *fp = fopen( pFilename, "rb" );
  707. if ( !fp )
  708. return false;
  709. int version;
  710. fread( &version, sizeof( version ), 1, fp );
  711. if ( version != VPC_CRC_CACHE_VERSION )
  712. {
  713. g_pVPC->VPCWarning( "Invalid dependency cache file version in %s.", pFilename );
  714. return false;
  715. }
  716. while ( 1 )
  717. {
  718. byte bMore;
  719. if ( fread( &bMore, 1, 1, fp ) != 1 || bMore == 0 )
  720. break;
  721. CUtlString filename = ReadString( fp );
  722. CDependency *pDep = FindOrCreateDependency( filename.String() );
  723. if ( pDep->m_Dependencies.Count() != 0 )
  724. g_pVPC->VPCError( "Cache loading dependency %s but it already exists!", filename.String() );
  725. fread( &pDep->m_nCacheFileSize, sizeof( pDep->m_nCacheFileSize ), 1, fp );
  726. fread( &pDep->m_nCacheModificationTime, sizeof( pDep->m_nCacheModificationTime ), 1, fp );
  727. int nDependencies;
  728. fread( &nDependencies, sizeof( nDependencies ), 1, fp );
  729. pDep->m_Dependencies.SetSize( nDependencies );
  730. for ( int iDependency=0; iDependency < nDependencies; iDependency++ )
  731. {
  732. CUtlString childDepName = ReadString( fp );
  733. CDependency *pChildDep = FindOrCreateDependency( childDepName.String() );
  734. pDep->m_Dependencies[iDependency] = pChildDep;
  735. }
  736. }
  737. fclose( fp );
  738. int nOriginalEntries = m_AllFiles.Count();
  739. CheckCacheEntries();
  740. RemoveDirtyCacheEntries();
  741. MarkAllCacheEntriesValid();
  742. Log_Msg( LOG_VPC, "\n\nLoaded %d valid dependency cache entries (%d were out of date).\n\n", m_AllFiles.Count(), nOriginalEntries-m_AllFiles.Count() );
  743. return true;
  744. }
  745. bool CProjectDependencyGraph::SaveCache( const char *pFilename )
  746. {
  747. FILE *fp = fopen( pFilename, "wb" );
  748. if ( !fp )
  749. return false;
  750. // Write the version.
  751. int version = VPC_CRC_CACHE_VERSION;
  752. fwrite( &version, sizeof( version ), 1, fp );
  753. // Write each file.
  754. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=m_AllFiles.Next( i ) )
  755. {
  756. CDependency *pDep = m_AllFiles[i];
  757. // We only care about source files.
  758. if ( pDep->m_Type != k_eDependencyType_SourceFile )
  759. continue;
  760. // Write that there's a file here.
  761. byte bYesThereIsAFileHere = 1;
  762. fwrite( &bYesThereIsAFileHere, 1, 1, fp );
  763. WriteString( fp, pDep->m_Filename );
  764. fwrite( &pDep->m_nCacheFileSize, sizeof( pDep->m_nCacheFileSize ), 1, fp );
  765. fwrite( &pDep->m_nCacheModificationTime, sizeof( pDep->m_nCacheModificationTime ), 1, fp );
  766. int nDependencies = pDep->m_Dependencies.Count();
  767. fwrite( &nDependencies, sizeof( nDependencies ), 1, fp );
  768. for ( int iDependency=0; iDependency < pDep->m_Dependencies.Count(); iDependency++ )
  769. {
  770. WriteString( fp, pDep->m_Dependencies[iDependency]->m_Filename );
  771. }
  772. }
  773. // Write a terminator.
  774. byte bNoMore = 0;
  775. fwrite( &bNoMore, 1, 1, fp );
  776. fclose( fp );
  777. return true;
  778. }
  779. void CProjectDependencyGraph::WriteString( FILE *fp, CUtlString &utlString )
  780. {
  781. const char *pStr = utlString.String();
  782. int len = V_strlen( pStr );
  783. fwrite( &len, sizeof( len ), 1, fp );
  784. fwrite( pStr, len, 1, fp );
  785. }
  786. CUtlString CProjectDependencyGraph::ReadString( FILE *fp )
  787. {
  788. int len;
  789. fread( &len, sizeof( len ), 1, fp );
  790. char *pTemp = new char[len+1];
  791. fread( pTemp, len, 1, fp );
  792. pTemp[len] = 0;
  793. CUtlString ret = pTemp;
  794. delete [] pTemp;
  795. return ret;
  796. }
  797. void CProjectDependencyGraph::CheckCacheEntries()
  798. {
  799. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=m_AllFiles.Next( i ) )
  800. {
  801. CDependency *pDep = m_AllFiles[i];
  802. pDep->m_bCacheDirty = false;
  803. if ( pDep->m_Type != k_eDependencyType_SourceFile )
  804. continue;
  805. int64 fileSize, modTime;
  806. if ( !Sys_FileInfo( pDep->m_Filename.String(), fileSize, modTime ) ||
  807. pDep->m_nCacheFileSize != fileSize ||
  808. pDep->m_nCacheModificationTime != modTime )
  809. {
  810. pDep->m_bCacheDirty = true;
  811. }
  812. }
  813. }
  814. void CProjectDependencyGraph::RemoveDirtyCacheEntries()
  815. {
  816. // NOTE: This could be waaaay more efficient by pointing files at their parents and removing all the way
  817. // up the chain rather than iterating over and over but this keeps the data structures simple.
  818. bool bAnyDirty = true;
  819. while ( bAnyDirty )
  820. {
  821. bAnyDirty = false;
  822. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=m_AllFiles.Next( i ) )
  823. {
  824. CDependency *pDep = m_AllFiles[i];
  825. if ( pDep->m_bCacheDirty )
  826. continue;
  827. // If any of its children are dirty, then mark this guy as dirty and make sure to remove the child.
  828. for ( int iChild=0; iChild < pDep->m_Dependencies.Count(); iChild++ )
  829. {
  830. CDependency *pChild = pDep->m_Dependencies[iChild];
  831. if ( pChild->m_bCacheDirty )
  832. {
  833. pDep->m_bCacheDirty = true;
  834. bAnyDirty = true;
  835. }
  836. }
  837. }
  838. }
  839. // Now that any dirty children have flagged their parents as dirty, we can remove them.
  840. int iNext;
  841. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=iNext )
  842. {
  843. iNext = m_AllFiles.Next( i );
  844. if ( m_AllFiles[i]->m_bCacheDirty )
  845. {
  846. delete m_AllFiles[ i ];
  847. m_AllFiles.RemoveAt( i );
  848. }
  849. }
  850. }
  851. void CProjectDependencyGraph::MarkAllCacheEntriesValid()
  852. {
  853. for ( int i=m_AllFiles.First(); i != m_AllFiles.InvalidIndex(); i=m_AllFiles.Next( i ) )
  854. {
  855. CDependency *pDep = m_AllFiles[i];
  856. pDep->m_bCheckedIncludes = true;
  857. }
  858. }
  859. // This is called by VPC_IterateTargetProjects and all it does is look forf a
  860. class CGameFilterProjectIterator : public IProjectIterator
  861. {
  862. public:
  863. virtual bool VisitProject( projectIndex_t iProject, const char *szProjectName )
  864. {
  865. char szAbsolute[MAX_PATH];
  866. V_MakeAbsolutePath( szAbsolute, sizeof( szAbsolute ), szProjectName );
  867. // Ok, we've got an (absolute) project filename. Search all the projects for one with that name.
  868. bool bAdded = false;
  869. for ( int i=0; i < m_pAllProjectsList->Count(); i++ )
  870. {
  871. CDependency_Project *pProject = m_pAllProjectsList->Element( i );
  872. if ( pProject->CompareAbsoluteFilename( szAbsolute ) )
  873. {
  874. m_pOutProjectsList->AddToTail( pProject );
  875. bAdded = true;
  876. break;
  877. }
  878. }
  879. if ( !bAdded )
  880. {
  881. g_pVPC->VPCError( "CGameFilterProjectIterator::VisitProject( %s ) - no project found by that name.", szProjectName );
  882. return false;
  883. }
  884. return true;
  885. }
  886. public:
  887. const CUtlVector<CDependency_Project*> *m_pAllProjectsList;
  888. CUtlVector<CDependency_Project*> *m_pOutProjectsList;
  889. };
  890. void CProjectDependencyGraph::TranslateProjectIndicesToDependencyProjects( CUtlVector<projectIndex_t> &projectList, CUtlVector<CDependency_Project*> &out )
  891. {
  892. CGameFilterProjectIterator iterator;
  893. iterator.m_pAllProjectsList = &m_Projects;
  894. iterator.m_pOutProjectsList = &out;
  895. g_pVPC->IterateTargetProjects( projectList, &iterator );
  896. }