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.

706 lines
26 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "vpc.h"
  7. #include "baseprojectdatacollector.h"
  8. #include "tier1/utlstack.h"
  9. #include "projectgenerator_xcode.h"
  10. extern bool g_bForceGenerate;
  11. static const char *g_pOption_OptimizerLevel = "$OptimizerLevel";
  12. static const char *g_pOption_GameOutputFile = "$GameOutputFile";
  13. static const char *g_pOption_SymbolVisibility = "$SymbolVisibility";
  14. static const char *g_pOption_ConfigurationType = "$ConfigurationType";
  15. static const char *g_pOption_PrecompiledHeader = "$Create/UsePrecompiledHeader";
  16. static const char *g_pOption_UsePCHThroughFile = "$Create/UsePCHThroughFile";
  17. static const char *g_pOption_PrecompiledHeaderFile = "$PrecompiledHeaderFile";
  18. static const char *g_pOption_SystemLibraries = "$SystemLibraries";
  19. static const char *g_pOption_SystemFrameworks = "$SystemFrameworks";
  20. static const char *g_pOption_LocalFrameworks = "$LocalFrameworks";
  21. static const char *g_pOption_ExtraCompilerFlags = "$GCC_ExtraCompilerFlags";
  22. static const char *g_pOption_ExtraLinkerFlags = "$GCC_ExtraLinkerFlags";
  23. static const char *g_pOption_ForceInclude = "$ForceIncludes";
  24. static const char *g_pOption_LowerCaseFileNames = "$LowerCaseFileNames";
  25. static const char *g_pOption_PostBuildEvent = "$PostBuildEvent";
  26. static const char *g_pOption_CommandLine = "$CommandLine";
  27. static const char *g_pOption_Description = "$Description";
  28. static const char *g_pOption_Outputs = "$Outputs";
  29. static const char *k_pszBase_Makefile = "$(SRCROOT)/devtools/makefile_base_posix.mak";
  30. // These are the only properties we care about for makefiles.
  31. static const char *g_pRelevantProperties[] =
  32. {
  33. g_pOption_AdditionalIncludeDirectories,
  34. g_pOption_OptimizerLevel,
  35. g_pOption_OutputFile,
  36. g_pOption_GameOutputFile,
  37. g_pOption_SymbolVisibility,
  38. g_pOption_PreprocessorDefinitions,
  39. g_pOption_ConfigurationType,
  40. g_pOption_ImportLibrary,
  41. g_pOption_PrecompiledHeader,
  42. g_pOption_UsePCHThroughFile,
  43. g_pOption_PrecompiledHeaderFile,
  44. g_pOption_CommandLine,
  45. g_pOption_Outputs,
  46. g_pOption_Description,
  47. g_pOption_SystemLibraries,
  48. g_pOption_SystemFrameworks,
  49. g_pOption_LocalFrameworks,
  50. g_pOption_ExtraCompilerFlags,
  51. g_pOption_ExtraLinkerFlags,
  52. g_pOption_ForceInclude,
  53. };
  54. CRelevantPropertyNames g_RelevantPropertyNames =
  55. {
  56. g_pRelevantProperties,
  57. Q_ARRAYSIZE( g_pRelevantProperties )
  58. };
  59. void MakeFriendlyProjectName( char *pchProject );
  60. void V_MakeAbsoluteCygwinPath( char *pOut, int outLen, const char *pRelativePath )
  61. {
  62. // While generating makefiles under Win32, we must translate drive letters like c:\
  63. // to Cygwin-style paths like /cygdrive/c/
  64. #ifdef _WIN32
  65. char tmp[MAX_PATH];
  66. V_MakeAbsolutePath( tmp, sizeof( tmp ), pRelativePath );
  67. V_FixSlashes( tmp, '/' );
  68. if ( tmp[0] != 0 && tmp[1] == ':' && tmp[2] == '/' )
  69. {
  70. // Ok, this is an absolute path
  71. V_snprintf( pOut, outLen, "/cygdrive/%c/", tmp[0] );
  72. V_strncat( pOut, &tmp[3], outLen );
  73. }
  74. else
  75. #endif // _WIN32
  76. {
  77. V_MakeAbsolutePath( pOut, outLen, pRelativePath );
  78. V_RemoveDotSlashes( pOut );
  79. }
  80. }
  81. static const char* UsePOSIXSlashes( const char *pStr )
  82. {
  83. static char str[2048];
  84. V_strncpy( str, pStr, sizeof( str ) );
  85. V_FixSlashes( str, '/' );
  86. return str;
  87. }
  88. // pExt should be the bare extension without the . in front. i.e. "h", "cpp", "lib".
  89. static inline bool CheckExtension( const char *pFilename, const char *pExt )
  90. {
  91. Assert( pExt[0] != '.' );
  92. int nFilenameLen = V_strlen( pFilename );
  93. int nExtensionLen = V_strlen( pExt );
  94. return (nFilenameLen > nExtensionLen && pFilename[nFilenameLen-nExtensionLen-1] == '.' && V_stricmp( &pFilename[nFilenameLen-nExtensionLen], pExt ) == 0 );
  95. }
  96. static bool CheckExtensions( const char *pFilename, const char **ppExtensions )
  97. {
  98. for ( int i=0; ppExtensions[i] != NULL; i++ )
  99. {
  100. if ( CheckExtension( pFilename, ppExtensions[i] ) )
  101. return true;
  102. }
  103. return false;
  104. }
  105. static void GetObjFilenameForFile( const char *pConfigName, const char *pFilename, char *pOut, int maxLen )
  106. {
  107. char sBaseFilename[MAX_PATH];
  108. V_FileBase( pFilename, sBaseFilename, sizeof( sBaseFilename ) );
  109. //V_strlower( sBaseFilename );
  110. const char *pObjExtension = (CheckExtension( pFilename, "cxx" ) ? "oxx" : "o");
  111. V_snprintf( pOut, maxLen, "$(OBJ_DIR)/%s.%s", sBaseFilename, pObjExtension );
  112. }
  113. // This class drastically accelerates looking up which file creates which precompiled header.
  114. class CPrecompiledHeaderAccel
  115. {
  116. public:
  117. void Setup( CUtlDict<CFileConfig*,int> &files, CFileConfig *pBaseConfig )
  118. {
  119. for ( int i=files.First(); i != files.InvalidIndex(); i=files.Next( i ) )
  120. {
  121. CFileConfig *pFile = files[i];
  122. for ( int iSpecific=pFile->m_Configurations.First(); iSpecific != pFile->m_Configurations.InvalidIndex(); iSpecific=pFile->m_Configurations.Next( iSpecific ) )
  123. {
  124. CSpecificConfig *pSpecific = pFile->m_Configurations[iSpecific];
  125. if ( pSpecific->m_bFileExcluded )
  126. continue;
  127. // Does this file create a precompiled header?
  128. const char *pPrecompiledHeaderOption = pSpecific->GetOption( g_pOption_PrecompiledHeader );
  129. if ( pPrecompiledHeaderOption && V_stristr( pPrecompiledHeaderOption, "Create" ) )
  130. {
  131. // Ok, which header do we scan through?
  132. const char *pUsePCHThroughFile = pSpecific->GetOption( g_pOption_UsePCHThroughFile );
  133. if ( !pUsePCHThroughFile )
  134. {
  135. g_pVPC->VPCError( "File %s creates a precompiled header in config %s but no UsePCHThroughFile option specified.", pFile->m_Filename.String(), pSpecific->GetConfigName() );
  136. }
  137. char sLookup[1024];
  138. V_snprintf( sLookup, sizeof( sLookup ), "%s__%s", pSpecific->GetConfigName(), pUsePCHThroughFile );
  139. if ( m_Lookup.Find( sLookup ) != m_Lookup.InvalidIndex() )
  140. {
  141. g_pVPC->VPCError( "File %s has UsePCHThroughFile of %s but another file already does.", pFile->m_Filename.String(), pUsePCHThroughFile );
  142. }
  143. m_Lookup.Insert( sLookup, pFile );
  144. }
  145. }
  146. }
  147. }
  148. CFileConfig* FindFileThatCreatesPrecompiledHeader( const char *pConfigName, const char *pUsePCHThroughFile )
  149. {
  150. char sLookup[1024];
  151. V_snprintf( sLookup, sizeof( sLookup ), "%s__%s", pConfigName, pUsePCHThroughFile );
  152. int i = m_Lookup.Find( sLookup );
  153. if ( i == m_Lookup.InvalidIndex() )
  154. return NULL;
  155. else
  156. return m_Lookup[i];
  157. }
  158. private:
  159. // This indexes whatever file creates a certain precompiled header for a certain config.
  160. // These are indexed as <config name>_<pchthroughfile>.
  161. // So an entry might look like release_cbase.h
  162. CUtlDict<CFileConfig*,int> m_Lookup;
  163. };
  164. class CProjectGenerator_Makefile : public CBaseProjectDataCollector
  165. {
  166. public:
  167. typedef CBaseProjectDataCollector BaseClass;
  168. CProjectGenerator_Makefile() : BaseClass( &g_RelevantPropertyNames )
  169. {
  170. }
  171. virtual void Setup()
  172. {
  173. }
  174. virtual const char* GetProjectFileExtension()
  175. {
  176. return "mak";
  177. }
  178. virtual void EndProject()
  179. {
  180. const char *pMakefileFilename = g_pVPC->GetOutputFilename();
  181. CUtlString strProjectName = GetProjectName();
  182. bool bProjectIsCurrent = g_pVPC->IsProjectCurrent( pMakefileFilename );
  183. if ( g_pVPC->IsForceGenerate() || !bProjectIsCurrent )
  184. {
  185. g_pVPC->VPCStatus( true, "Saving makefile project for: '%s' File: '%s'", strProjectName.String(), g_pVPC->GetOutputFilename() );
  186. WriteMakefile( pMakefileFilename );
  187. }
  188. const char *pTargetPlatformName = g_pVPC->GetTargetPlatformName();
  189. if ( !pTargetPlatformName )
  190. g_pVPC->VPCError( "GetTargetPlatformName failed." );
  191. if ( !V_stricmp( pTargetPlatformName, "OSX32" ) || !V_stricmp( pTargetPlatformName, "OSX64" ) )
  192. {
  193. if ( g_pVPC->IsForceGenerate() || !bProjectIsCurrent )
  194. {
  195. // Write a XCode project as well.
  196. char sFilename[MAX_PATH];
  197. V_StripExtension( g_pVPC->GetOutputFilename(), sFilename, sizeof( sFilename ) );
  198. CProjectGenerator_XCode xcodeGenerator;
  199. xcodeGenerator.GenerateXCodeProject( this, sFilename, pMakefileFilename );
  200. }
  201. extern CUtlVector<CBaseProjectDataCollector*> g_vecPGenerators;
  202. g_vecPGenerators.AddToTail( this );
  203. extern IBaseProjectGenerator *g_pGenerator;
  204. g_pVPC->SetProjectGenerator( new CProjectGenerator_Makefile() );
  205. }
  206. else
  207. {
  208. Term();
  209. }
  210. }
  211. void WriteSourceFilesList( FILE *fp, const char *pListName, const char **pExtensions, const char *pConfigName )
  212. {
  213. fprintf( fp, "%s= \\\n", pListName );
  214. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next(i) )
  215. {
  216. CFileConfig *pFileConfig = m_Files[i];
  217. if ( pFileConfig->IsExcludedFrom( pConfigName ) )
  218. continue;
  219. const char *pFilename = m_Files[i]->m_Filename.String();
  220. if ( CheckExtensions( pFilename, pExtensions ) )
  221. {
  222. fprintf( fp, " %s \\\n", UsePOSIXSlashes( pFilename ) );
  223. }
  224. }
  225. fprintf( fp, "\n\n" );
  226. }
  227. void WriteNonConfigSpecificStuff( FILE *fp )
  228. {
  229. // NAME
  230. char szName[256];
  231. V_strncpy( szName, m_ProjectName.String(), sizeof(szName) );
  232. MakeFriendlyProjectName( szName );
  233. fprintf( fp, "NAME=%s\n", szName );
  234. // SRCDIR
  235. char sSrcRootRelative[MAX_PATH];
  236. g_pVPC->ResolveMacrosInString( "$SRCDIR", sSrcRootRelative, sizeof( sSrcRootRelative ) );
  237. fprintf( fp, "SRCROOT=%s\n", UsePOSIXSlashes( sSrcRootRelative ) );
  238. // TargetPlatformName
  239. const char *pTargetPlatformName = g_pVPC->GetTargetPlatformName();
  240. if ( !pTargetPlatformName )
  241. g_pVPC->VPCError( "GetTargetPlatformName failed." );
  242. fprintf( fp, "TARGET_PLATFORM=%s\n", pTargetPlatformName );
  243. fprintf( fp, "PWD:=$(shell pwd)\n" );
  244. // Select debug config if no config is specified.
  245. fprintf( fp, "# If no configuration is specified, \"release\" will be used.\n" );
  246. fprintf( fp, "ifeq \"$(CFG)\" \"\"\n" );
  247. fprintf( fp, "CFG=release\n" );
  248. fprintf( fp, "endif\n\n" );
  249. }
  250. void WriteConfigSpecificStuff( CSpecificConfig *pConfig, FILE *fp, CPrecompiledHeaderAccel *pAccel )
  251. {
  252. KeyValues *pKV = pConfig->m_pKV;
  253. fprintf( fp, "#\n#\n# CFG=%s\n#\n#\n\n", pConfig->GetConfigName() );
  254. fprintf( fp, "ifeq \"$(CFG)\" \"%s\"\n\n", pConfig->GetConfigName() );
  255. // GCC_ExtraCompilerFlags
  256. // Hopefully, they don't ever need to use backslashes because we're turning them into forward slashes here.
  257. // If that does become a problem, we can put some token around the pathnames we need to be fixed up and leave the rest alone.
  258. fprintf( fp, "GCC_ExtraCompilerFlags=%s\n", UsePOSIXSlashes( pKV->GetString( g_pOption_ExtraCompilerFlags, "" ) ) );
  259. // GCC_ExtraLinkerFlags
  260. fprintf( fp, "GCC_ExtraLinkerFlags=%s\n", pKV->GetString( g_pOption_ExtraLinkerFlags, "" ) );
  261. // SymbolVisibility
  262. fprintf( fp, "SymbolVisibility=%s\n", pKV->GetString( g_pOption_SymbolVisibility, "hidden" ) );
  263. // OptimizerLevel
  264. fprintf( fp, "OptimizerLevel=%s\n", pKV->GetString( g_pOption_OptimizerLevel, "$(SAFE_OPTFLAGS_GCC_422)" ) );
  265. // system libraries
  266. {
  267. fprintf( fp, "SystemLibraries=" );
  268. {
  269. CSplitString libs( pKV->GetString( g_pOption_SystemLibraries ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  270. for ( int i=0; i < libs.Count(); i++ )
  271. {
  272. fprintf( fp, "-l%s ", libs[i] );
  273. }
  274. }
  275. if ( !V_stricmp( g_pVPC->GetTargetPlatformName(), "OSX32" ) || !V_stricmp( g_pVPC->GetTargetPlatformName(), "OSX64" ) )
  276. {
  277. char rgchFrameworkCompilerFlags[1024]; rgchFrameworkCompilerFlags[0] = '\0';
  278. CSplitString systemFrameworks( pKV->GetString( g_pOption_SystemFrameworks ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  279. for ( int i=0; i < systemFrameworks.Count(); i++ )
  280. {
  281. fprintf( fp, "-framework %s ", systemFrameworks[i] );
  282. }
  283. CSplitString localFrameworks( pKV->GetString( g_pOption_LocalFrameworks ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  284. for ( int i=0; i < localFrameworks.Count(); i++ )
  285. {
  286. char rgchFrameworkName[MAX_PATH];
  287. V_StripExtension( V_UnqualifiedFileName( localFrameworks[i] ), rgchFrameworkName, sizeof( rgchFrameworkName ) );
  288. V_StripFilename( localFrameworks[i] );
  289. fprintf( fp, "-F%s ", localFrameworks[i] );
  290. fprintf( fp, "-framework %s ", rgchFrameworkName );
  291. strcat( rgchFrameworkCompilerFlags, "-F" );
  292. strcat( rgchFrameworkCompilerFlags, localFrameworks[i] );
  293. }
  294. fprintf( fp, "\n" );
  295. if ( rgchFrameworkCompilerFlags[0] )
  296. // the colon here is important - and should probably get percolated to more places in our generated
  297. // makefiles - it means to perform the assignment once, rather than at evaluation time
  298. fprintf( fp, "GCC_ExtraCompilerFlags:=$(GCC_ExtraCompilerFlags) %s\n", rgchFrameworkCompilerFlags );
  299. }
  300. else
  301. fprintf( fp, "\n" );
  302. }
  303. macro_t *pMacro = g_pVPC->FindOrCreateMacro( "_DLL_EXT", false, NULL );
  304. if ( pMacro )
  305. fprintf( fp, "DLL_EXT=%s\n", pMacro->value.String() );
  306. pMacro = g_pVPC->FindOrCreateMacro( "_SYM_EXT", false, NULL );
  307. if ( pMacro )
  308. fprintf( fp, "SYM_EXT=%s\n", pMacro->value.String() );
  309. // ForceIncludes
  310. {
  311. CSplitString outStrings( pKV->GetString( g_pOption_ForceInclude ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  312. fprintf( fp, "FORCEINCLUDES= " );
  313. for ( int i=0; i < outStrings.Count(); i++ )
  314. {
  315. if ( V_strlen( outStrings[i] ) > 2 )
  316. fprintf( fp, "-include %s ", UsePOSIXSlashes( outStrings[i] ) );
  317. }
  318. }
  319. fprintf( fp, "\n" );
  320. // DEFINES
  321. {
  322. CSplitString outStrings( pKV->GetString( g_pOption_PreprocessorDefinitions ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  323. fprintf( fp, "DEFINES= " );
  324. for ( int i=0; i < outStrings.Count(); i++ )
  325. {
  326. fprintf( fp, "-D%s ", outStrings[i] );
  327. }
  328. }
  329. // Add VPC macros marked to become defines.
  330. CUtlVector< macro_t* > macroDefines;
  331. g_pVPC->GetMacrosMarkedForCompilerDefines( macroDefines );
  332. for ( int i=0; i < macroDefines.Count(); i++ )
  333. {
  334. macro_t *pMacro = macroDefines[i];
  335. fprintf( fp, "-D%s=%s ", pMacro->name.String(), pMacro->value.String() );
  336. }
  337. fprintf( fp, "\n" );
  338. // INCLUDEDIRS
  339. {
  340. CSplitString outStrings( pKV->GetString( g_pOption_AdditionalIncludeDirectories ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  341. fprintf( fp, "INCLUDEDIRS= " );
  342. for ( int i=0; i < outStrings.Count(); i++ )
  343. {
  344. char sDir[MAX_PATH];
  345. V_strncpy( sDir, outStrings[i], sizeof( sDir ) );
  346. if ( !V_stricmp( sDir, "$(IntDir)" ) )
  347. V_strncpy( sDir, "$(OBJ_DIR)", sizeof( sDir ) );
  348. V_FixSlashes( sDir, '/' );
  349. fprintf( fp, "%s ", sDir );
  350. }
  351. fprintf( fp, "\n" );
  352. }
  353. // CONFTYPE
  354. if ( V_stristr( pKV->GetString( g_pOption_ConfigurationType ), "dll" ) )
  355. {
  356. fprintf( fp, "CONFTYPE=dll\n" );
  357. // Write ImportLibrary for dll (so) builds.
  358. const char *pRelative = pKV->GetString( g_pOption_ImportLibrary, "" );
  359. fprintf( fp, "IMPORTLIBRARY=%s\n", UsePOSIXSlashes( pRelative ) );
  360. // GameOutputFile is where it copies OutputFile to.
  361. fprintf( fp, "GAMEOUTPUTFILE=%s\n", UsePOSIXSlashes( pKV->GetString( g_pOption_GameOutputFile, "" ) ) );
  362. }
  363. else if ( V_stristr( pKV->GetString( g_pOption_ConfigurationType ), "lib" ) )
  364. {
  365. fprintf( fp, "CONFTYPE=lib\n" );
  366. }
  367. else if ( V_stristr( pKV->GetString( g_pOption_ConfigurationType ), "exe" ) )
  368. {
  369. fprintf( fp, "CONFTYPE=exe\n" );
  370. }
  371. else
  372. {
  373. fprintf( fp, "CONFTYPE=***UNKNOWN***\n" );
  374. }
  375. // OutputFile is where it builds to.
  376. char szFixedOutputFile[MAX_PATH];
  377. V_strncpy( szFixedOutputFile, pKV->GetString( g_pOption_OutputFile ), sizeof( szFixedOutputFile ) );
  378. V_FixSlashes( szFixedOutputFile, '/' );
  379. fprintf( fp, "OUTPUTFILE=%s\n", szFixedOutputFile );
  380. fprintf( fp, "\n\n" );
  381. // post build event
  382. char rgchPostBuildCommand[2048]; rgchPostBuildCommand[0] = '\0';
  383. if ( pKV->GetString( g_pOption_CommandLine, NULL ) )
  384. {
  385. V_strncpy( rgchPostBuildCommand, pKV->GetString( g_pOption_CommandLine, NULL ), sizeof( rgchPostBuildCommand ) );
  386. // V_StripPrecedingAndTrailingWhitespace( rgchPostBuildCommand );
  387. }
  388. if ( Q_strlen( rgchPostBuildCommand ) )
  389. fprintf( fp, "POSTBUILDCOMMAND=%s\n", rgchPostBuildCommand );
  390. else
  391. fprintf( fp, "POSTBUILDCOMMAND=true\n" );
  392. fprintf( fp, "\n\n" );
  393. // Write all the filenames.
  394. const char *sSourceFileExtensions[] = {"cpp","cxx","cc","c","mm","cc",NULL};
  395. fprintf( fp, "\n" );
  396. WriteSourceFilesList( fp, "CPPFILES", (const char**)sSourceFileExtensions, pConfig->GetConfigName() );
  397. // LIBFILES
  398. char sImportLibraryFile[MAX_PATH];
  399. const char *pRelative = pKV->GetString( g_pOption_ImportLibrary, "" );
  400. V_strncpy( sImportLibraryFile, UsePOSIXSlashes( pRelative ), sizeof( sImportLibraryFile ) );
  401. V_RemoveDotSlashes( sImportLibraryFile );
  402. fprintf( fp, "LIBFILES = \\\n" );
  403. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next(i) )
  404. {
  405. CFileConfig *pFileConfig = m_Files[i];
  406. if ( pFileConfig->IsExcludedFrom( pConfig->GetConfigName() ) )
  407. continue;
  408. char szFilename[MAX_PATH];
  409. V_strncpy( szFilename, UsePOSIXSlashes( pFileConfig->m_Filename.String() ), sizeof( szFilename ) );
  410. const char *pFilename = szFilename;
  411. if ( IsLibraryFile( pFilename ) )
  412. {
  413. char *pchFileName = (char*)Q_strrchr( pFilename, '/' ) + 1;
  414. if ( !sImportLibraryFile[0] || Q_stricmp( sImportLibraryFile, pFilename ) ) // only link this as a library if it isn't our own output!
  415. {
  416. char szExt[32];
  417. Q_ExtractFileExtension( pFilename, szExt, sizeof(szExt) );
  418. if ( IsLibraryFile( pFilename ) && (pchFileName-1) && pchFileName[0] == 'l' && pchFileName[1] == 'i' && pchFileName[2] == 'b'
  419. && szExt[0] != 'a' ) // its a lib ext but not an archive file, link like a library
  420. {
  421. *(pchFileName-1) = 0;
  422. // Cygwin import libraries use ".dll.a", so get rid of any file extensions here.
  423. char *pExt;
  424. while ( 1 )
  425. {
  426. pExt = (char*)Q_strrchr( pchFileName, '.' );
  427. if ( !pExt || Q_strrchr( pchFileName, '/' ) > pExt || Q_strrchr( pchFileName, '\\' ) > pExt )
  428. break;
  429. *pExt = 0;
  430. }
  431. fprintf( fp, " -L%s -l%s \\\n", pFilename, pchFileName + 3 ); // +3 to dodge the lib ext
  432. }
  433. else
  434. {
  435. fprintf( fp, " %s \\\n", pFilename );
  436. }
  437. }
  438. }
  439. }
  440. fprintf( fp, "\n\n" );
  441. fprintf( fp, "LIBFILENAMES = \\\n" );
  442. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next(i) )
  443. {
  444. CFileConfig *pFileConfig = m_Files[i];
  445. if ( pFileConfig->IsExcludedFrom( pConfig->GetConfigName() ) )
  446. continue;
  447. const char *pFilename = pFileConfig->m_Filename.String();
  448. if ( IsLibraryFile( pFilename ) )
  449. {
  450. if ( !sImportLibraryFile[0] || Q_stricmp( sImportLibraryFile, pFilename ) ) // only link this as a library if it isn't our own output!
  451. {
  452. fprintf( fp, " %s \\\n", UsePOSIXSlashes( pFilename ) );
  453. }
  454. }
  455. }
  456. fprintf( fp, "\n\n" );
  457. // Include the base makefile before the rules to build the .o files.
  458. fprintf( fp, "# Include the base makefile now.\n" );
  459. if ( g_pVPC->FindOrCreateConditional( "POSIX", false, CONDITIONAL_NULL ) )
  460. {
  461. fprintf( fp, "include %s\n\n\n", k_pszBase_Makefile );
  462. }
  463. CUtlVector< CUtlString > otherDependencies;
  464. // Scan the list of files for any generated dependencies so we can pull them up front
  465. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next(i) )
  466. {
  467. CFileConfig *pFileConfig = m_Files[i];
  468. CSpecificConfig *pFileSpecificData = pFileConfig->GetOrCreateConfig( pConfig->GetConfigName(), pConfig );
  469. if ( pFileConfig->IsExcludedFrom( pConfig->GetConfigName() ) )
  470. {
  471. continue;
  472. }
  473. const char *pCustomBuildCommandLine = pFileSpecificData->GetOption( g_pOption_CommandLine );
  474. const char *pOutputFile = pFileSpecificData->GetOption( g_pOption_Outputs );
  475. if ( pOutputFile && pCustomBuildCommandLine && V_strlen( pCustomBuildCommandLine ) > 0 )
  476. {
  477. char szTempFilename[MAX_PATH];
  478. V_strncpy( szTempFilename, UsePOSIXSlashes( pFileConfig->m_Filename.String() ), sizeof( szTempFilename ) );
  479. const char *pFilename = szTempFilename;
  480. // This file uses a custom build step.
  481. char sFormattedOutputFile[MAX_PATH];
  482. char szAbsPath[MAX_PATH];
  483. V_MakeAbsolutePath( szAbsPath, sizeof(szAbsPath), pFilename );
  484. DoStandardVisualStudioReplacements( pOutputFile, szAbsPath, sFormattedOutputFile, sizeof( sFormattedOutputFile ) );
  485. CSplitString outFiles( sFormattedOutputFile, ";" );
  486. for ( int i = 0; i < outFiles.Count(); i ++ )
  487. {
  488. // Remember this as a dependency so the executable will depend on it.
  489. if ( otherDependencies.Find( outFiles[i] ) == otherDependencies.InvalidIndex() )
  490. otherDependencies.AddToTail( outFiles[i] );
  491. }
  492. }
  493. }
  494. WriteOtherDependencies( fp, otherDependencies );
  495. fprintf( fp, "\n\n" );
  496. // Now write the rules to build the .o files.
  497. // .o files go in [project dir]/obj/[config]/[base filename]
  498. for ( int i=m_Files.First(); i != m_Files.InvalidIndex(); i=m_Files.Next(i) )
  499. {
  500. CFileConfig *pFileConfig = m_Files[i];
  501. CSpecificConfig *pFileSpecificData = pFileConfig->GetOrCreateConfig( pConfig->GetConfigName(), pConfig );
  502. if ( pFileConfig->IsExcludedFrom( pConfig->GetConfigName() ) )
  503. {
  504. continue;
  505. }
  506. char szTempFilename[MAX_PATH];
  507. V_strncpy( szTempFilename, UsePOSIXSlashes( pFileConfig->m_Filename.String() ), sizeof( szTempFilename ) );
  508. const char *pFilename = szTempFilename;
  509. char sAbsFilename[MAX_PATH];
  510. V_strncpy( sAbsFilename, pFilename, sizeof( sAbsFilename ) );
  511. // Custom build steps??
  512. const char *pCustomBuildCommandLine = pFileSpecificData->GetOption( g_pOption_CommandLine );
  513. const char *pOutputFile = pFileSpecificData->GetOption( g_pOption_Outputs );
  514. if ( pOutputFile && pCustomBuildCommandLine && V_strlen( pCustomBuildCommandLine ) > 0 )
  515. {
  516. // This file uses a custom build step.
  517. char sFormattedOutputFile[MAX_PATH];
  518. char sFormattedCommandLine[8192];
  519. char szAbsPath[MAX_PATH];
  520. V_MakeAbsolutePath( szAbsPath, sizeof(szAbsPath), sAbsFilename );
  521. DoStandardVisualStudioReplacements( pCustomBuildCommandLine, szAbsPath, sFormattedCommandLine, sizeof( sFormattedCommandLine ) );
  522. DoStandardVisualStudioReplacements( pOutputFile, szAbsPath, sFormattedOutputFile, sizeof( sFormattedOutputFile ) );
  523. CSplitString outFiles( sFormattedOutputFile, ";" );
  524. for ( int i = 0; i < outFiles.Count(); i ++ )
  525. {
  526. fprintf( fp, "%s ", outFiles[i] );
  527. }
  528. fprintf( fp, ": %s\n", UsePOSIXSlashes( szAbsPath ) );
  529. const char *pDescription = pFileSpecificData->GetOption( g_pOption_Description );
  530. DoStandardVisualStudioReplacements( pDescription, szAbsPath, sFormattedOutputFile, sizeof( sFormattedOutputFile ) );
  531. fprintf( fp, "\t @echo \"%s\";mkdir -p $(OBJ_DIR) 2> /dev/null;%s\n\n", sFormattedOutputFile, sFormattedCommandLine );
  532. }
  533. else if ( CheckExtensions( pFilename, (const char**)sSourceFileExtensions ) )
  534. {
  535. //V_strlower( sAbsFilename );
  536. char sObjFilename[MAX_PATH];
  537. GetObjFilenameForFile( pConfig->GetConfigName(), pFilename, sObjFilename, sizeof( sObjFilename ) );
  538. // Get the base obj filename for the .P file.
  539. char sPFileBase[MAX_PATH];
  540. V_StripExtension( sObjFilename, sPFileBase, sizeof( sPFileBase ) );
  541. // include the .P file which will include dependency information.
  542. fprintf( fp, "ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))\n" );
  543. fprintf( fp, "\n%s.P: %s $(PWD)/%s %s $(OTHER_DEPENDENCIES)\n", sPFileBase, UsePOSIXSlashes( sAbsFilename ), g_pVPC->GetOutputFilename(),
  544. g_pVPC->FindOrCreateConditional( "POSIX", false, CONDITIONAL_NULL ) == NULL ? "" : k_pszBase_Makefile );
  545. fprintf( fp, "\t$(GEN_DEP_FILE)\n");
  546. fprintf( fp, "\n-include %s.P\n", sPFileBase );
  547. fprintf( fp, "endif\n" );
  548. fprintf( fp, "\n%s : $(PWD)/%s $(PWD)/%s %s\n", sObjFilename, sAbsFilename, g_pVPC->GetOutputFilename(),
  549. g_pVPC->FindOrCreateConditional( "POSIX", false, CONDITIONAL_NULL ) == NULL ? "" : k_pszBase_Makefile );
  550. fprintf( fp, "\t$(PRE_COMPILE_FILE)\n" );
  551. fprintf( fp, "\t$(COMPILE_FILE) $(POST_COMPILE_FILE)\n" );
  552. }
  553. }
  554. fprintf( fp, "\n\nendif # (CFG=%s)\n\n", pConfig->GetConfigName() );
  555. fprintf( fp, "\n\n" );
  556. }
  557. void WriteOtherDependencies( FILE *fp, CUtlVector< CUtlString > &otherDependencies )
  558. {
  559. fprintf( fp, "\nOTHER_DEPENDENCIES = \\\n" );
  560. for ( int i=0; i < otherDependencies.Count(); i++ )
  561. {
  562. fprintf( fp, "\t%s%s\n", otherDependencies[i].String(),
  563. (i == otherDependencies.Count()-1) ? "" : " \\" );
  564. }
  565. fprintf( fp, "\n\n" );
  566. }
  567. void WriteMakefile( const char *pFilename )
  568. {
  569. FILE *fp = fopen( pFilename, "wt" );
  570. CPrecompiledHeaderAccel accel;
  571. accel.Setup( m_Files, &m_BaseConfigData );
  572. // Write all the non-config-specific stuff.
  573. WriteNonConfigSpecificStuff( fp );
  574. // Write each config out.
  575. for ( int i=m_BaseConfigData.m_Configurations.First(); i != m_BaseConfigData.m_Configurations.InvalidIndex(); i=m_BaseConfigData.m_Configurations.Next( i ) )
  576. {
  577. CSpecificConfig *pConfig = m_BaseConfigData.m_Configurations[i];
  578. WriteConfigSpecificStuff( pConfig, fp, &accel );
  579. }
  580. fclose( fp );
  581. }
  582. bool m_bForceLowerCaseFileName;
  583. };
  584. static CProjectGenerator_Makefile g_ProjectGenerator_Makefile;
  585. IBaseProjectGenerator* GetMakefileProjectGenerator()
  586. {
  587. return &g_ProjectGenerator_Makefile;
  588. }