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.

2270 lines
68 KiB

  1. //========= Copyright 1996-2006, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: VPC
  4. //
  5. //=====================================================================================//
  6. #include "vpc.h"
  7. #include "dependencies.h"
  8. #include "p4sln.h"
  9. #include "ilaunchabledll.h"
  10. DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_VPC, "VPC" );
  11. CVPC *g_pVPC;
  12. CPosixSolutionGenerator g_PosixSolutionGenerator;
  13. class CBaseProjectDataCollector;
  14. CUtlVector<CBaseProjectDataCollector*> g_vecPGenerators;
  15. // Stuff that we might encounter in a vpc file that parts of vpc care about
  16. const char *g_pOption_ImportLibrary = "$ImportLibrary";
  17. const char *g_pOption_OutputFile = "$OutputFile";
  18. const char *g_pOption_AdditionalIncludeDirectories = "$AdditionalIncludeDirectories";
  19. const char *g_pOption_AdditionalProjectDependencies = "$AdditionalProjectDependencies";
  20. const char *g_pOption_AdditionalOutputFiles = "$AdditionalOutputFiles";
  21. const char *g_pOption_PreprocessorDefinitions = "$PreprocessorDefinitions";
  22. char *g_IncludeSeparators[2] = {";",","};
  23. #ifdef POSIX
  24. #define _unlink unlink
  25. #define _stat stat
  26. #endif
  27. CVPC::CVPC()
  28. {
  29. m_pP4Module = NULL;
  30. m_pFilesystemModule = NULL;
  31. m_nArgc = 0;
  32. m_ppArgv = NULL;
  33. m_bVerbose = false;
  34. m_bQuiet = false;
  35. m_bUsageOnly = false;
  36. m_bHelp = false;
  37. m_bSpewPlatforms = false;
  38. m_bSpewGames = false;
  39. m_bSpewGroups = false;
  40. m_bSpewProjects = false;
  41. m_bIgnoreRedundancyWarning = false;
  42. m_bSpewProperties = false;
  43. m_bTestMode = false;
  44. m_bGeneratedProject = false;
  45. m_bAnyProjectQualified = false;
  46. m_bForceGenerate = false;
  47. m_bEnableVpcGameMacro = true;
  48. m_bDecorateProject = false;
  49. m_bShowDeps = false;
  50. m_bP4AutoAdd = false;
  51. m_bP4SlnCheckEverything = false;
  52. m_bPS3SDKPresent = false;
  53. m_bXDKPresent = false;
  54. m_bShowCaseIssues = false;
  55. // the default is now 2013
  56. m_bWants2010 = false;
  57. m_bUse2010 = false;
  58. m_bWants2012 = false;
  59. m_bUse2012 = false;
  60. m_bWants2013 = true;
  61. m_bUse2013 = false;
  62. // need to check files by default, otherwise dependency failure (due to missing file) cause needles rebuilds
  63. m_bCheckFiles = true;
  64. m_pProjectGenerator = NULL;
  65. m_pSolutionGenerator = NULL;
  66. #ifdef OSX
  67. m_bForceIterate = true;
  68. #else
  69. m_bForceIterate = false;
  70. #endif
  71. }
  72. //-----------------------------------------------------------------------------
  73. //-----------------------------------------------------------------------------
  74. bool CVPC::Init( int argc, char **argv )
  75. {
  76. m_nArgc = argc;
  77. m_ppArgv = argv;
  78. CommandLine()->CreateCmdLine( m_nArgc, m_ppArgv );
  79. // vpc operates tersely by preferred company opinion
  80. // verbosity necessary for debugging
  81. m_bVerbose = ( HasCommandLineParameter( "/v" ) || HasCommandLineParameter( "/verbose" ) );
  82. m_bQuiet = ( HasCommandLineParameter( "/q" ) ||
  83. HasCommandLineParameter( "/quiet" ) ||
  84. ( getenv( "VPC_QUIET" ) && V_stricmp( getenv( "VPC_QUIET" ), "0" ) ) );
  85. #ifndef STEAM
  86. // We don't really need to pop the logging state since the process will terminate when we're done.
  87. LoggingSystem_PushLoggingState();
  88. m_LoggingListener.m_bQuietPrintf = m_bQuiet;
  89. LoggingSystem_RegisterLoggingListener( &m_LoggingListener );
  90. #endif
  91. // needs to occur early and before any other expensive setup, a crc check just exits with an error code used by caller
  92. InProcessCRCCheck();
  93. LoadPerforceInterface();
  94. // vpc may have been run from wrong location, restart self
  95. bool bIsRestart = false;
  96. if ( RestartFromCorrectLocation( &bIsRestart ) )
  97. {
  98. // successfully ran under restart condition, all done
  99. return false;
  100. }
  101. if ( bIsRestart )
  102. {
  103. // this process is the restart child, cull the internal private restart guard option
  104. // otherwise it gets confused as a build option
  105. m_nArgc--;
  106. }
  107. Log_Msg( LOG_VPC, "VPC - Valve Project Creator For " );
  108. #ifdef POSIX
  109. Log_Msg( LOG_VPC, "XCode and Makefiles (Build: %s %s)\n", __DATE__, __TIME__ );
  110. #else
  111. Log_Msg( LOG_VPC, "Visual Studio, Xbox 360, PlayStation 3 (Build: %s %s)\n", __DATE__, __TIME__ );
  112. #endif
  113. Log_Msg( LOG_VPC, "(C) Copyright 1996-2010, Valve Corporation, All rights reserved.\n" );
  114. Log_Msg( LOG_VPC, "\n" );
  115. return true;
  116. }
  117. //-----------------------------------------------------------------------------
  118. //-----------------------------------------------------------------------------
  119. void CVPC::Shutdown( bool bHasError )
  120. {
  121. if ( !bHasError )
  122. {
  123. GetScript().EnsureScriptStackEmpty();
  124. }
  125. if ( !m_TempGroupScriptFilename.IsEmpty() )
  126. {
  127. // delete temp work file
  128. _unlink( m_TempGroupScriptFilename.Get() );
  129. m_TempGroupScriptFilename.Clear();
  130. }
  131. UnloadPerforceInterface();
  132. }
  133. //-----------------------------------------------------------------------------
  134. //-----------------------------------------------------------------------------
  135. bool CVPC::LoadPerforceInterface()
  136. {
  137. if ( p4 )
  138. {
  139. // already loaded
  140. return true;
  141. }
  142. //
  143. // Try to load p4lib.dll and the filesystem since the p4lib relies on it
  144. //
  145. char p4libdll[MAX_PATH];
  146. char filesystemdll[MAX_PATH];
  147. #ifdef _WIN32
  148. // Don't require them to have game\bin in their path. Since we know where vpc.exe is,
  149. // point directly to p4lib.dll in its rightful place.
  150. char szModuleBinPath[MAX_PATH];
  151. GetModuleFileName( NULL, szModuleBinPath, sizeof( szModuleBinPath ) );
  152. V_ExtractFilePath( szModuleBinPath, p4libdll, sizeof( p4libdll ) );
  153. V_AppendSlash( p4libdll, sizeof( p4libdll ) );
  154. V_strncpy( filesystemdll, p4libdll, sizeof( filesystemdll ) );
  155. V_strncat( p4libdll, "..\\..\\..\\game\\bin\\p4lib.dll", sizeof( p4libdll ) );
  156. V_strncat( filesystemdll, "..\\..\\..\\game\\bin\\filesystem_stdio.dll", sizeof( filesystemdll ) );
  157. #else
  158. V_strncpy( p4libdll, "p4lib", sizeof( p4libdll ) );
  159. V_strncpy( filesystemdll, "filesystem_stdio", sizeof( filesystemdll ) );
  160. #endif
  161. if ( !Sys_LoadInterface( p4libdll, P4_INTERFACE_VERSION, &m_pP4Module, (void**)&p4 ) )
  162. {
  163. #ifdef _WIN32
  164. // This always fails on non-Windows build machines -- the warning is
  165. // annoying and not helpful.
  166. VPCWarning( "Unable to get Perforce interface from p4lib.dll." );
  167. #endif
  168. return false;
  169. }
  170. // Let the P4 module get its interface to the filesystem - hate this
  171. // This method is not available in portal2, but is in source2.
  172. // p4->SetVerbose( false );
  173. m_pFilesystemModule = Sys_LoadModule( filesystemdll );
  174. p4->Connect( Sys_GetFactory( m_pFilesystemModule ) );
  175. return true;
  176. }
  177. //-----------------------------------------------------------------------------
  178. //-----------------------------------------------------------------------------
  179. void CVPC::UnloadPerforceInterface()
  180. {
  181. // Unload P4 if it was loaded
  182. if ( m_pP4Module )
  183. {
  184. Sys_UnloadModule( m_pP4Module );
  185. m_pP4Module = NULL;
  186. }
  187. if ( m_pFilesystemModule )
  188. {
  189. Sys_UnloadModule( m_pFilesystemModule );
  190. m_pFilesystemModule = NULL;
  191. }
  192. }
  193. bool VPC_Config_IgnoreOption( const char *pPropertyName )
  194. {
  195. char buff[MAX_SYSTOKENCHARS];
  196. g_pVPC->GetScript().ParsePropertyValue( NULL, buff, sizeof( buff ) );
  197. return true;
  198. }
  199. //-----------------------------------------------------------------------------
  200. //-----------------------------------------------------------------------------
  201. void CVPC::VPCError( const char* format, ... )
  202. {
  203. va_list argptr;
  204. char msg[MAX_SYSPRINTMSG];
  205. va_start( argptr,format );
  206. vsprintf( msg,format,argptr );
  207. va_end( argptr );
  208. // spew in red
  209. Log_Warning( LOG_VPC, Color( 255, 0, 0, 255 ), "ERROR: %s\n", msg );
  210. // dump the script stack to assist in user understading of the include chain
  211. GetScript().SpewScriptStack();
  212. // do proper shutdown in an error context
  213. Shutdown( true );
  214. // errors are expected to be fatal by all calling code
  215. // otherwise it would have been a warning
  216. exit( 1 );
  217. }
  218. //-----------------------------------------------------------------------------
  219. //-----------------------------------------------------------------------------
  220. void CVPC::VPCSyntaxError( const char* format, ... )
  221. {
  222. va_list argptr;
  223. char msg[MAX_SYSPRINTMSG];
  224. va_start( argptr, format );
  225. if ( format )
  226. {
  227. vsprintf( msg, format, argptr );
  228. }
  229. va_end( argptr );
  230. if ( format )
  231. {
  232. Log_Warning( LOG_VPC, Color( 255, 0, 0, 255 ), "Bad Syntax: %s\n", msg );
  233. }
  234. // syntax errors are fatal
  235. VPCError( "Bad Syntax in '%s' line:%d\n", GetScript().GetName(), GetScript().GetLine() );
  236. }
  237. //-----------------------------------------------------------------------------
  238. //-----------------------------------------------------------------------------
  239. void CVPC::VPCWarning( const char* format, ... )
  240. {
  241. va_list argptr;
  242. char msg[MAX_SYSPRINTMSG];
  243. va_start( argptr,format );
  244. vsprintf( msg,format,argptr );
  245. va_end( argptr );
  246. if ( m_bIgnoreRedundancyWarning )
  247. {
  248. if ( V_stristr( msg, "matches default setting" ) )
  249. return;
  250. if ( V_stristr( msg, "already exists in project" ) )
  251. return;
  252. }
  253. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "WARNING: %s\n", msg );
  254. }
  255. //-----------------------------------------------------------------------------
  256. //-----------------------------------------------------------------------------
  257. void CVPC::VPCStatus( bool bAlwaysSpew, const char* format, ... )
  258. {
  259. if ( m_bQuiet )
  260. return;
  261. va_list argptr;
  262. char msg[MAX_SYSPRINTMSG];
  263. va_start( argptr,format );
  264. vsprintf( msg,format,argptr );
  265. va_end( argptr );
  266. if ( bAlwaysSpew || m_bVerbose )
  267. {
  268. Log_Msg( LOG_VPC, "%s\n", msg );
  269. }
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Checks to ensure the bin path is in the same tree as the vpc_scripts
  273. // Returns true if bin path valid
  274. //-----------------------------------------------------------------------------
  275. #if !defined( POSIX )
  276. bool CVPC::CheckBinPath( char *pOutBinPath, int outBinPathSize )
  277. {
  278. char szScriptPath[MAX_PATH];
  279. char szDirectory[MAX_PATH];
  280. char szLastDirectory[MAX_PATH];
  281. // non destructively determine the vpc_scripts directory
  282. bool bFound = false;
  283. szLastDirectory[0] = '\0';
  284. szScriptPath[0] = '\0';
  285. V_GetCurrentDirectory( szDirectory, sizeof( szDirectory ) );
  286. while ( 1 )
  287. {
  288. V_ComposeFileName( szDirectory, "vpc_scripts", szScriptPath, sizeof( szScriptPath ) );
  289. struct _stat statBuf;
  290. if ( _stat( szScriptPath, &statBuf ) != -1 )
  291. {
  292. bFound = true;
  293. break;
  294. }
  295. // previous dir
  296. V_ComposeFileName( szDirectory, "..", szScriptPath, sizeof( szScriptPath ) );
  297. char fullPath[MAX_PATH];
  298. if ( _fullpath( fullPath, szScriptPath, sizeof( fullPath ) ) )
  299. {
  300. V_strncpy( szDirectory, fullPath, sizeof( szDirectory ) );
  301. }
  302. if ( !V_stricmp( szDirectory, szLastDirectory ) )
  303. {
  304. // can back up no further
  305. break;
  306. }
  307. strcpy( szLastDirectory, szDirectory );
  308. }
  309. if ( !bFound )
  310. {
  311. VPCError( "Failed to determine source directory from current path. Expecting 'vpc_scripts' in source path." );
  312. }
  313. char szSourcePath[MAX_PATH];
  314. strcpy( szSourcePath, szDirectory );
  315. // check to ensure that executeable and src directory are in the same tree
  316. // executeable needs to be tightly bound to its vpc_scripts
  317. char szModuleBinPath[MAX_PATH];
  318. GetModuleFileName( NULL, szModuleBinPath, sizeof( szModuleBinPath ) );
  319. // cannot trust output from GetModuleFileName(), occasionally has ./ or ../ screwing up comparisons
  320. V_RemoveDotSlashes( szModuleBinPath, '\\' );
  321. V_strlower( szModuleBinPath );
  322. V_strncpy( pOutBinPath, szModuleBinPath, outBinPathSize );
  323. // allowed to run from a root "devbin", for use with junctions
  324. if ( Sys_StringPatternMatch( "?:\\devbin\\vpc.exe", szModuleBinPath ) )
  325. return true;
  326. char *pString = V_stristr( szModuleBinPath, "\\devtools\\bin\\" );
  327. if ( pString )
  328. {
  329. // source dirs should match
  330. char chSave = *pString;
  331. *pString = '\0';
  332. bool bSame = V_stricmp( szSourcePath, szModuleBinPath ) == 0;
  333. *pString = chSave;
  334. if ( bSame )
  335. {
  336. return true;
  337. }
  338. }
  339. else
  340. {
  341. VPCError( "Executable not running from 'devtools/bin' but from unexpected directory '%s'", szModuleBinPath );
  342. }
  343. // mismatched, wierd bin patch could have been a result of user's environment path
  344. // use expected source path which is based on user's cwd to get the real bin path
  345. V_strncpy( pOutBinPath, szSourcePath, outBinPathSize );
  346. V_strncat( pOutBinPath, "\\devtools\\bin\\vpc.exe", outBinPathSize );
  347. struct _stat statBuf;
  348. if ( _stat( pOutBinPath, &statBuf ) == -1 )
  349. {
  350. VPCError( "Correct executeable missing, should be at '%s'", pOutBinPath );
  351. }
  352. // yikes, wrong executeable was started, agreed behavior was to restart based on user's cwd
  353. // REALLY want users to see this, it indicates a possible hazard of using the wrong vpc
  354. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "********************************************************************************\n" );
  355. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "Wrong Executable '%s' Running!\nRestarting at '%s'\n", szModuleBinPath, pOutBinPath );
  356. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "********************************************************************************\n" );
  357. return false;
  358. }
  359. #endif
  360. //-----------------------------------------------------------------------------
  361. //-----------------------------------------------------------------------------
  362. void CVPC::DetermineSourcePath()
  363. {
  364. char szSourcePath[MAX_PATH];
  365. char szLastDirectory[MAX_PATH];
  366. char szOldPath[MAX_PATH];
  367. V_GetCurrentDirectory( szOldPath, sizeof( szOldPath ) );
  368. // find vpc_scripts from cwd
  369. szLastDirectory[0] = '\0';
  370. bool bFound = false;
  371. while ( 1 )
  372. {
  373. V_GetCurrentDirectory( szSourcePath, sizeof( szSourcePath ) );
  374. if ( !V_stricmp( szSourcePath, szLastDirectory ) )
  375. {
  376. // can back up no further
  377. break;
  378. }
  379. V_strncpy( szLastDirectory, szSourcePath, sizeof( szLastDirectory ) );
  380. char szTestDir[MAX_PATH];
  381. V_ComposeFileName( szSourcePath, "vpc_scripts", szTestDir, sizeof( szTestDir ) );
  382. struct _stat statBuf;
  383. if ( _stat( szTestDir, &statBuf ) != -1 )
  384. {
  385. bFound = true;
  386. break;
  387. }
  388. // previous dir
  389. char szPrevDir[MAX_PATH];
  390. V_ComposeFileName( szSourcePath, "..", szPrevDir, sizeof( szPrevDir ) );
  391. V_SetCurrentDirectory( szPrevDir );
  392. }
  393. if ( !bFound )
  394. {
  395. VPCError( "Failed to determine source directory from current path. Expecting 'vpc_scripts' in source path." );
  396. }
  397. // Remember the source path and restore the path to where it was.
  398. m_SourcePath = szSourcePath;
  399. V_SetCurrentDirectory( szOldPath );
  400. // always emit source path, identifies MANY redundant user problems
  401. // users can easily run from an unintended place due to botched path, mangled directories, etc
  402. Log_Msg( LOG_VPC, "Source Path: %s\n", m_SourcePath.Get() );
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Sets the working directory to .../vpc_scripts as all scripts are
  406. // guaranteed relative to the vpc script directory.
  407. //-----------------------------------------------------------------------------
  408. void CVPC::SetDefaultSourcePath()
  409. {
  410. V_SetCurrentDirectory( m_SourcePath.Get() );
  411. }
  412. //-----------------------------------------------------------------------------
  413. //-----------------------------------------------------------------------------
  414. bool CVPC::IsProjectCurrent( const char *pOutputFilename )
  415. {
  416. // default is project is stale
  417. if ( !Sys_Exists( pOutputFilename ) )
  418. {
  419. return false;
  420. }
  421. if ( Is2010() && !Sys_Exists( CFmtStr( "%s.filters", pOutputFilename ) ) )
  422. {
  423. return false;
  424. }
  425. char errorString[1024];
  426. bool bCRCValid = VPC_CheckProjectDependencyCRCs( pOutputFilename, m_SupplementalCRCString.Get(), errorString, sizeof( errorString ) );
  427. if ( bCRCValid )
  428. {
  429. VPCStatus( true, "Valid: '%s' Passes CRC Checks.", pOutputFilename );
  430. }
  431. else
  432. {
  433. VPCStatus( true, "Stale: '%s' Requires Rebuild.", pOutputFilename );
  434. }
  435. return bCRCValid;
  436. }
  437. //-----------------------------------------------------------------------------
  438. //-----------------------------------------------------------------------------
  439. void CVPC::SpewUsage( void )
  440. {
  441. // skip header if user requested specific detail
  442. bool bNoHeader =
  443. m_bSpewPlatforms ||
  444. m_bSpewGames ||
  445. m_bSpewProjects ||
  446. m_bSpewGroups ||
  447. m_bSpewProperties ||
  448. m_BuildCommands.Count();
  449. if ( !bNoHeader )
  450. {
  451. Log_Msg( LOG_VPC, "\n" );
  452. if ( !m_bHelp )
  453. {
  454. // terse
  455. Log_Msg( LOG_VPC, "Type vpc /h for help...\n" );
  456. }
  457. else
  458. {
  459. // verbose
  460. Log_Msg( LOG_VPC, "usage: vpc [options] <+/-/*project or group>\n");
  461. Log_Msg( LOG_VPC, "\n" );
  462. Log_Msg( LOG_VPC, "Examples:\n" );
  463. Log_Msg( LOG_VPC, "\n" );
  464. Log_Msg( LOG_VPC, " Single .vcproj generation:\n" );
  465. Log_Msg( LOG_VPC, " vpc +client /hl2 <-- Creates a Win32 .vcproj for the HL2 client.\n" );
  466. Log_Msg( LOG_VPC, " vpc +shaderapi /x360 <-- Creates a Xbox360 .vcproj for the shaderapi.\n" );
  467. Log_Msg( LOG_VPC, "\n" );
  468. Log_Msg( LOG_VPC, " Multiple .vcproj generation - Multiple Projects for Games and Platforms:\n" );
  469. Log_Msg( LOG_VPC, " vpc +client /hl2 /tf <-- Creates ALL the Win32 .vcprojs for the HL2 and TF client.\n" );
  470. Log_Msg( LOG_VPC, " vpc +gamedlls /allgames <-- Creates ALL the Win32 .vcprojs for client and server for all GAMES.\n" );
  471. Log_Msg( LOG_VPC, " vpc +tools -tier0 /win32 /x360 <-- Creates ALL the Win32 and Xbox360 .vcprojs for the tool projects but not the tier0 project.\n" );
  472. Log_Msg( LOG_VPC, "\n" );
  473. Log_Msg( LOG_VPC, " Use +/- to add or remove projects or groups.\n");
  474. Log_Msg( LOG_VPC, " Use * to add a project and all projects that depend on it.\n");
  475. Log_Msg( LOG_VPC, " Use @ to add a project and all projects that it depends on.\n");
  476. Log_Msg( LOG_VPC, " Use /h spew final target build set only (no .vcproj created).\n");
  477. Log_Msg( LOG_VPC, "\n" );
  478. Log_Msg( LOG_VPC, " Further details can be found on Valve Internal Wiki on VPC.\n" );
  479. Log_Msg( LOG_VPC, "\n--- OPTIONS ---\n" );
  480. Log_Msg( LOG_VPC, "[/q]: Quiet mode (quiet mode is automatically on if the VPC_QUIET environment variable is set)\n" );
  481. Log_Msg( LOG_VPC, "[/v]: Verbose\n" );
  482. Log_Msg( LOG_VPC, "[/f]: Force generate .vcproj, otherwise use crc checks\n" );
  483. Log_Msg( LOG_VPC, "[/dp]: Decorate project names with platform\n" );
  484. Log_Msg( LOG_VPC, "[/testmode]: Override output .vcproj file to be named 'test.vcproj'\n" );
  485. Log_Msg( LOG_VPC, "[/2013]: Generate projects and solutions for Visual Studio 2013 [default]\n" );
  486. Log_Msg( LOG_VPC, "[/2012]: Generate projects and solutions for Visual Studio 2012\n" );
  487. Log_Msg( LOG_VPC, "[/2010]: Generate projects and solutions for Visual Studio 2010\n" );
  488. Log_Msg( LOG_VPC, "[/2005]: Generate projects and solutions for Visual Studio 2005\n" );
  489. Log_Msg( LOG_VPC, "\n--- Help ---\n" );
  490. Log_Msg( LOG_VPC, "[/h]: Help\n" );
  491. Log_Msg( LOG_VPC, "[/?]: Help\n" );
  492. Log_Msg( LOG_VPC, "[/platforms]: Spew Platforms\n" );
  493. Log_Msg( LOG_VPC, "[/games]: Spew Games\n" );
  494. Log_Msg( LOG_VPC, "[/projects]: Spew Projects\n" );
  495. Log_Msg( LOG_VPC, "[/groups]: Spew Groups\n" );
  496. Log_Msg( LOG_VPC, "[/properties]: Spew VS2005 Properties\n" );
  497. Log_Msg( LOG_VPC, "\n--- Conditionals ---\n" );
  498. Log_Msg( LOG_VPC, "[/profile]: Set Reserved $PROFILE=1\n" );
  499. Log_Msg( LOG_VPC, "[/retail]: Set Reserved $RETAIL=1\n" );
  500. Log_Msg( LOG_VPC, "[/callcap]: Set Reserved $CALLCAP=1\n" );
  501. Log_Msg( LOG_VPC, "[/fastcap]: Set Reserved $FASTCAP=1\n" );
  502. Log_Msg( LOG_VPC, "[/cert]: Set Reserved $CERT=1\n" );
  503. Log_Msg( LOG_VPC, "[/memtest]: Set Reserved $MEMTEST=1\n" );
  504. Log_Msg( LOG_VPC, "[/nofpo]: Set Reserved $NOFPO=1\n" );
  505. Log_Msg( LOG_VPC, "[/lv]: Set Reserved $LV=1\n" );
  506. Log_Msg( LOG_VPC, "[/demo]: Set Reserved $DEMO=1\n" );
  507. Log_Msg( LOG_VPC, "[/no_steam]: Set Reserved $NO_STEAM=1\n" );
  508. Log_Msg( LOG_VPC, "[/qtdebug]: Set Reserved $QTDEBUG=1\n" );
  509. Log_Msg( LOG_VPC, "[/no_ceg]: Set Reserved $NO_CEG=1\n" );
  510. Log_Msg( LOG_VPC, "[/upload_ceg]: Set Reserved $UPLOAD_CEG=1\n" );
  511. Log_Msg( LOG_VPC, "\n--- Other ---\n" );
  512. Log_Msg( LOG_VPC, "[/mksln]: <.sln filename> - make a solution file\n" );
  513. Log_Msg( LOG_VPC, "[/p4sln]: <.sln filename> <changelists...> - make a solution file based on\n" );
  514. Log_Msg( LOG_VPC, " the changelist. Changelists can be specific numbers, 0 or \"default\"\n" );
  515. Log_Msg( LOG_VPC, " for the default changelist, or \"all\" for all active changelists.\n" );
  516. Log_Msg( LOG_VPC, "[/nop4add]: Don't automatically add project files to Perforce\n" );
  517. Log_Msg( LOG_VPC, "[/slnitems]: <filename> - adds all files listed in <filename> to generated\n" );
  518. Log_Msg( LOG_VPC, " solutions\n" );
  519. Log_Msg( LOG_VPC, "[/showdeps]: Show an example dependency chain for each project that depends\n" );
  520. Log_Msg( LOG_VPC, " on your p4 change list(s). Use with /p4sln.\n" );
  521. Log_Msg( LOG_VPC, "[/checkfiles]: Check for the existence of files in $file commands. For debugging vpc files.\n" );
  522. Log_Msg( LOG_VPC, " Only works if the currrent directory is the project directory.\n" );
  523. // Log_Msg( LOG_VPC, "[/novpcgame]: Disable reserved vpc macro $VPCGAME and $VPCGAMECAPS.\n" );
  524. // Log_Msg( LOG_VPC, " By default if a single game is specified on command line, then that specified\n" );
  525. // Log_Msg( LOG_VPC, " game name will be used as a value for $VPCGAME and $VPCGAMECAPS macros.\n" );
  526. Log_Msg( LOG_VPC, "[/define:xxx]: Enable a custom conditional $XXX to use for quick testing in VPC files.\n" );
  527. }
  528. }
  529. if ( m_Conditionals.Count() && m_bSpewPlatforms )
  530. {
  531. bool bFirstDefine = false;
  532. for ( int i = 0; i < m_Conditionals.Count(); i++ )
  533. {
  534. if ( m_Conditionals[i].type != CONDITIONAL_PLATFORM )
  535. continue;
  536. if ( !bFirstDefine )
  537. {
  538. Log_Msg( LOG_VPC, "\n--- PLATFORMS ---\n" );
  539. bFirstDefine = true;
  540. }
  541. Log_Msg( LOG_VPC, "%s%s\n", m_Conditionals[i].upperCaseName.String(), m_Conditionals[i].m_bDefined ? " = 1" : "" );
  542. }
  543. }
  544. if ( m_Conditionals.Count() && m_bSpewGames )
  545. {
  546. bool bFirstGame = false;
  547. for ( int i = 0; i < m_Conditionals.Count(); i++ )
  548. {
  549. if ( m_Conditionals[i].type != CONDITIONAL_GAME )
  550. continue;
  551. if ( !bFirstGame )
  552. {
  553. Log_Msg( LOG_VPC, "\n--- GAMES ---\n" );
  554. bFirstGame = true;
  555. }
  556. Log_Msg( LOG_VPC, "%s%s\n", m_Conditionals[i].upperCaseName.String(), m_Conditionals[i].m_bDefined ? " = 1" : "" );
  557. }
  558. }
  559. if ( m_Projects.Count() && m_bSpewProjects )
  560. {
  561. // spew all sorted projects
  562. Log_Msg( LOG_VPC, "\n--- PROJECTS ---\n" );
  563. CUtlRBTree< const char * > sorted( 0, 0, CaselessStringLessThan );
  564. for ( int i = 0; i < m_Projects.Count(); i++ )
  565. {
  566. sorted.Insert( m_Projects[i].name.String() );
  567. }
  568. for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
  569. {
  570. Log_Msg( LOG_VPC, "[+/-] %s\n", sorted[i] );
  571. }
  572. }
  573. if ( g_pVPC->m_GroupTags.Count() && m_bSpewGroups )
  574. {
  575. // spew all sorted groups
  576. Log_Msg( LOG_VPC, "\n--- GROUPS ---\n" );
  577. CUtlRBTree< const char * > sorted( 0, 0, CaselessStringLessThan );
  578. for ( int i = 0; i < g_pVPC->m_GroupTags.Count(); i++ )
  579. {
  580. if ( !g_pVPC->m_GroupTags[i].bSameAsProject )
  581. {
  582. sorted.Insert( g_pVPC->m_GroupTags[i].name.String() );
  583. }
  584. }
  585. for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
  586. {
  587. Log_Msg( LOG_VPC, "[+/-] %s\n", sorted[i] );
  588. }
  589. }
  590. #if 0
  591. #if defined( _WIN32 )
  592. if ( m_bSpewProperties )
  593. {
  594. for ( int i = 0; i < KEYWORD_MAX; i++ )
  595. {
  596. VPC_Config_SpewProperties( (configKeyword_e)i );
  597. }
  598. }
  599. #endif
  600. #endif
  601. if ( m_BuildCommands.Count() )
  602. {
  603. // spew details about each command
  604. Log_Msg( LOG_VPC, "\nUser Build Commands:\n" );
  605. Log_Msg( LOG_VPC, "--------------------\n" );
  606. for ( int i = 0; i < m_BuildCommands.Count(); i++ )
  607. {
  608. Log_Msg( LOG_VPC, "%s\n", m_BuildCommands[i].String() );
  609. groupTagIndex_t groupTagIndex = VPC_Group_FindOrCreateGroupTag( m_BuildCommands[i].Get()+1, false );
  610. if ( groupTagIndex == INVALID_INDEX )
  611. {
  612. Log_Msg( LOG_VPC, " ??? (Unknown Group)\n" );
  613. }
  614. else
  615. {
  616. groupTag_t *pGroupTag = &g_pVPC->m_GroupTags[groupTagIndex];
  617. for (int j=0; j<pGroupTag->groups.Count(); j++ )
  618. {
  619. group_t *pGroup = &m_Groups[pGroupTag->groups[j]];
  620. for ( int k=0; k<pGroup->projects.Count(); k++ )
  621. {
  622. Log_Msg( LOG_VPC, " %s\n", m_Projects[pGroup->projects[k]].name.String() );
  623. }
  624. }
  625. }
  626. }
  627. Log_Msg( LOG_VPC, "\nTarget Projects:\n" );
  628. Log_Msg( LOG_VPC, "----------------\n" );
  629. if ( m_TargetProjects.Count() )
  630. {
  631. for ( int i=0; i<m_TargetProjects.Count(); i++ )
  632. {
  633. Log_Msg( LOG_VPC, "%s\n", m_Projects[m_TargetProjects[i]].name.String() );
  634. }
  635. }
  636. else
  637. {
  638. Log_Msg( LOG_VPC, "Empty Set (no output)\n" );
  639. }
  640. Log_Msg( LOG_VPC, "\nTarget Games:\n" );
  641. Log_Msg( LOG_VPC, "-------------\n" );
  642. bool bHasDefine = false;
  643. for ( int i=0; i<m_Conditionals.Count(); i++ )
  644. {
  645. if ( m_Conditionals[i].type != CONDITIONAL_GAME )
  646. continue;
  647. if ( m_Conditionals[i].m_bDefined )
  648. {
  649. Log_Msg( LOG_VPC, "$%s = 1\n", m_Conditionals[i].upperCaseName.String() );
  650. bHasDefine = true;
  651. }
  652. }
  653. if ( !bHasDefine )
  654. {
  655. Log_Msg( LOG_VPC, "No Game Set!\n" );
  656. }
  657. Log_Msg( LOG_VPC, "\nTarget Platforms:\n" );
  658. Log_Msg( LOG_VPC, "-----------------\n" );
  659. bHasDefine = false;
  660. for ( int i=0; i<m_Conditionals.Count(); i++ )
  661. {
  662. if ( m_Conditionals[i].type != CONDITIONAL_PLATFORM )
  663. continue;
  664. if ( m_Conditionals[i].m_bDefined )
  665. {
  666. Log_Msg( LOG_VPC, "$%s = 1\n", m_Conditionals[i].upperCaseName.String() );
  667. bHasDefine = true;
  668. }
  669. }
  670. if ( !bHasDefine )
  671. {
  672. Log_Msg( LOG_VPC, "No Platform Set!\n" );
  673. }
  674. Log_Msg( LOG_VPC, "\nCustom Conditionals:\n" );
  675. Log_Msg( LOG_VPC, "---------------------\n" );
  676. bHasDefine = false;
  677. for ( int i=0; i<m_Conditionals.Count(); i++ )
  678. {
  679. if ( m_Conditionals[i].type != CONDITIONAL_CUSTOM )
  680. continue;
  681. if ( m_Conditionals[i].m_bDefined )
  682. {
  683. Log_Msg( LOG_VPC, "$%s = 1\n", m_Conditionals[i].upperCaseName.String() );
  684. bHasDefine = true;
  685. }
  686. }
  687. if ( !bHasDefine )
  688. {
  689. Log_Msg( LOG_VPC, "No Custom Defines Set!\n" );
  690. }
  691. }
  692. }
  693. //-----------------------------------------------------------------------------
  694. //-----------------------------------------------------------------------------
  695. void CVPC::HandleSingleCommandLineArg( const char *pArg )
  696. {
  697. if ( ( pArg[0] == '-' ) || ( pArg[0] == '/' ) )
  698. {
  699. // skip past arg prefix
  700. const char *pArgName = pArg+1;
  701. // check options
  702. if ( !V_stricmp( pArgName, "h" ) || !V_stricmp( pArgName, "?" ) || !V_stricmp( pArgName, "help" ) )
  703. {
  704. m_bHelp = true;
  705. m_bUsageOnly = true;
  706. }
  707. else if ( !V_stricmp( pArgName, "v" ) || !V_stricmp( pArgName, "verbose" ) )
  708. {
  709. m_bVerbose = true;
  710. }
  711. else if ( !V_stricmp( pArgName, "testmode" ) || !V_stricmp( pArgName, "test" ) )
  712. {
  713. m_bTestMode = true;
  714. }
  715. else if ( !V_stricmp( pArgName, "f" ) || !V_stricmp( pArgName, "force" ) )
  716. {
  717. m_bForceGenerate = true;
  718. }
  719. else if ( !V_stricmp( pArgName, "novpcgame" ) )
  720. {
  721. m_bEnableVpcGameMacro = false;
  722. }
  723. else if ( !V_stricmp( pArgName, "checkfiles" ) )
  724. {
  725. m_bCheckFiles = true;
  726. }
  727. else if ( !V_stricmp( pArgName, "nocheckfiles" ) )
  728. {
  729. m_bCheckFiles = false;
  730. }
  731. else if ( !V_stricmp( pArgName, "showcaseissues" ) || !V_stricmp( pArgName, "showcase" ) )
  732. {
  733. m_bShowCaseIssues = true;
  734. }
  735. else if ( !V_stricmp( pArgName, "dp" ) )
  736. {
  737. m_bDecorateProject = true;
  738. }
  739. else if ( !V_stricmp( pArgName, "dedicated" ) )
  740. {
  741. m_bDedicatedBuild = true;
  742. }
  743. else if ( !V_stricmp( pArgName, "platforms" ) || !V_stricmp( pArgName, "plats" ) )
  744. {
  745. m_bSpewPlatforms = true;
  746. m_bUsageOnly = true;
  747. }
  748. else if ( !V_stricmp( pArgName, "games" ) )
  749. {
  750. m_bSpewGames = true;
  751. m_bUsageOnly = true;
  752. }
  753. else if ( !V_stricmp( pArgName, "projects" ) )
  754. {
  755. m_bSpewProjects = true;
  756. m_bUsageOnly = true;
  757. }
  758. else if ( !V_stricmp( pArgName, "groups" ) )
  759. {
  760. m_bSpewGroups = true;
  761. m_bUsageOnly = true;
  762. }
  763. else if ( !V_stricmp( pArgName, "properties" ) )
  764. {
  765. m_bSpewProperties = true;
  766. m_bUsageOnly = true;
  767. }
  768. else if ( !V_stricmp( pArgName, "allgames" ) )
  769. {
  770. // shortcut for all games defined
  771. for ( int j=0; j<m_Conditionals.Count(); j++ )
  772. {
  773. if ( m_Conditionals[j].type == CONDITIONAL_GAME )
  774. {
  775. m_Conditionals[j].m_bDefined = true;
  776. }
  777. }
  778. }
  779. else if ( !V_stricmp( pArgName, "showdeps" ) )
  780. {
  781. m_bShowDeps = true;
  782. }
  783. else if ( !V_stricmp( pArgName, "nop4add" ) )
  784. {
  785. m_bP4AutoAdd = false;
  786. }
  787. else if ( !V_stricmp( pArgName, "2005" ) )
  788. {
  789. m_bWants2010 = false;
  790. m_bWants2012 = false;
  791. m_bWants2013 = false;
  792. }
  793. else if ( !V_stricmp( pArgName, "2010" ) )
  794. {
  795. m_bWants2010 = true;
  796. m_bWants2012 = false;
  797. m_bWants2013 = false;
  798. }
  799. else if ( !V_stricmp( pArgName, "2012" ) )
  800. {
  801. m_bWants2010 = false;
  802. m_bWants2012 = true;
  803. m_bWants2013 = false;
  804. }
  805. else if ( !V_stricmp( pArgName, "2013" ) )
  806. {
  807. m_bWants2010 = false;
  808. m_bWants2012 = false;
  809. m_bWants2013 = true;
  810. }
  811. else if ( char const *szActualDefineName = StringAfterPrefix( pArgName, "define:" ) )
  812. {
  813. // allow setting custom defines straight from command line
  814. conditional_t *pConditional = FindOrCreateConditional( szActualDefineName, true, CONDITIONAL_CUSTOM );
  815. if ( pConditional )
  816. {
  817. pConditional->m_bDefined = true;
  818. m_ExtraOptionsCRCString += "/define:"; // force this into additional CRC string
  819. m_ExtraOptionsCRCString += pConditional->name.Get(); // force this into additional CRC string
  820. }
  821. }
  822. else
  823. {
  824. // not a recogined option, try conditionals
  825. // find in list of conditionals
  826. conditional_t *pConditional = FindOrCreateConditional( pArgName, false, CONDITIONAL_NULL );
  827. if ( !pConditional )
  828. {
  829. // not a recognized conditional, add to build commands
  830. int index = m_BuildCommands.AddToTail();
  831. m_BuildCommands[index] = pArg;
  832. }
  833. else
  834. {
  835. // found conditional, mark as defined
  836. pConditional->m_bDefined = true;
  837. }
  838. }
  839. }
  840. else if ( pArg[0] == '+' || pArg[0] == '*' || pArg[0] == '@' )
  841. {
  842. // add to build commands
  843. int index = m_BuildCommands.AddToTail();
  844. m_BuildCommands[index] = pArg;
  845. }
  846. }
  847. //-----------------------------------------------------------------------------
  848. //-----------------------------------------------------------------------------
  849. void CVPC::ParseBuildOptions( int argc, char *argv[] )
  850. {
  851. #if defined( LINUX )
  852. m_bDedicatedBuild = true;
  853. #else
  854. m_bDedicatedBuild = false;
  855. #endif
  856. // parse options
  857. // prefer +??? or -??? prefix syntax for groups and /??? for options because less confusing for new vpc users
  858. // for ease we will support -??? prefix syntax for matched options as well
  859. for ( int i = 1; i < argc; i++ )
  860. {
  861. const char *pArg = argv[i];
  862. if ( !V_stricmp( pArg, "/mksln" ) )
  863. {
  864. if ( !m_P4SolutionFilename.IsEmpty() )
  865. {
  866. VPCError( "Can't use /mksln with /p4sln." );
  867. }
  868. if ( (i+1) >= argc )
  869. {
  870. VPCError( "/mksln requires a filename after it." );
  871. }
  872. // If the next parameter is a standard + or - or / or * parameter, then we take that to be the name of the solution file.
  873. // So vpc /mksln +engine would generate engine.sln.
  874. if ( argv[i+1][0] == '+' || argv[i+1][0] == '-' || argv[i+1][0] == '/' || argv[i+1][0] == '*' || argv[i+1][0] == '@' )
  875. {
  876. m_MKSolutionFilename = &argv[i+1][1];
  877. }
  878. else
  879. {
  880. m_MKSolutionFilename = argv[i+1];
  881. ++i;
  882. }
  883. }
  884. else if ( !V_stricmp( pArg, "/p4sln" ) )
  885. {
  886. if ( !m_MKSolutionFilename.IsEmpty() )
  887. {
  888. VPCError( "Can't use /mksln with /p4sln." );
  889. }
  890. // Get the solution filename.
  891. ++i;
  892. if ( i >= argc || argv[i][0] == '+' || argv[i][0] == '-' || argv[i][0] == '/' || argv[i][0] == '*' || argv[i][0] == '@' )
  893. {
  894. VPCError( "/p4sln <solution filename> <changelist number>." );
  895. }
  896. m_P4SolutionFilename = argv[i];
  897. // Get the changelist number.
  898. while ( 1 )
  899. {
  900. ++i;
  901. // No more args?
  902. if ( i >= argc )
  903. break;
  904. // Special syntax for including all changelists.
  905. if ( V_stricmp( argv[i], "all" ) == 0 )
  906. {
  907. m_iP4Changelists.AddToTail( -1 );
  908. continue;
  909. }
  910. // Special syntax for including default changelists.
  911. if ( V_stricmp( argv[i], "default" ) == 0 )
  912. {
  913. m_iP4Changelists.AddToTail( 0 );
  914. continue;
  915. }
  916. // This arg isn't a changelist number?
  917. if ( argv[i][0] < '0' || argv[i][0] > '9' )
  918. {
  919. --i;
  920. break;
  921. }
  922. // Add the changelist number.
  923. m_iP4Changelists.AddToTail( atoi( argv[i] ) );
  924. }
  925. // Make sure at least one changelist number was specified.
  926. if ( m_iP4Changelists.Count() == 0 )
  927. {
  928. VPCError( "/p4sln <solution filename> <changelist number> [additional changelist numbers]." );
  929. }
  930. }
  931. else if ( !V_stricmp( pArg, "/slnitems" ) )
  932. {
  933. // Get the solution items filename
  934. ++i;
  935. if ( i >= argc || argv[i][0] == '+' || argv[i][0] == '-' || argv[i][0] == '/' || argv[i][0] == '*' || argv[i][0] == '@' )
  936. {
  937. VPCError( "/slnitems <solution items filename>." );
  938. }
  939. m_SolutionItemsFilename = argv[i];
  940. }
  941. else
  942. {
  943. HandleSingleCommandLineArg( pArg );
  944. }
  945. }
  946. // If they did /p4sln but didn't specify any build commands, then have it check everything.
  947. if ( m_iP4Changelists.Count() > 0 && m_BuildCommands.Count() == 0 )
  948. {
  949. m_bP4SlnCheckEverything = true;
  950. }
  951. CheckForInstalledXDK();
  952. CheckForInstalledPS3SDK();
  953. }
  954. //-----------------------------------------------------------------------------
  955. // Generate a string supplemental to CRC data, derived from command-line options,
  956. // so varying certain command-line options can cause .VCPROJ rebuilds.
  957. //-----------------------------------------------------------------------------
  958. void CVPC::GenerateOptionsCRCString()
  959. {
  960. m_SupplementalCRCString = "_";
  961. conditional_t *pConditional = FindOrCreateConditional( "PROFILE", false, CONDITIONAL_NULL );
  962. if ( pConditional && pConditional->m_bDefined )
  963. {
  964. m_SupplementalCRCString += "Pr";
  965. }
  966. pConditional = FindOrCreateConditional( "RETAIL", false, CONDITIONAL_NULL );
  967. if ( pConditional && pConditional->m_bDefined )
  968. {
  969. m_SupplementalCRCString += "Rt";
  970. }
  971. pConditional = FindOrCreateConditional( "CALLCAP", false, CONDITIONAL_NULL );
  972. if ( pConditional && pConditional->m_bDefined )
  973. {
  974. m_SupplementalCRCString += "Cc";
  975. }
  976. pConditional = FindOrCreateConditional( "FASTCAP", false, CONDITIONAL_NULL );
  977. if ( pConditional && pConditional->m_bDefined )
  978. {
  979. m_SupplementalCRCString += "Fc";
  980. }
  981. pConditional = FindOrCreateConditional( "CERT", false, CONDITIONAL_NULL );
  982. if ( pConditional && pConditional->m_bDefined )
  983. {
  984. m_SupplementalCRCString += "Ct";
  985. }
  986. pConditional = FindOrCreateConditional( "MEMTEST", false, CONDITIONAL_NULL );
  987. if ( pConditional && pConditional->m_bDefined )
  988. {
  989. m_SupplementalCRCString += "Mt";
  990. }
  991. pConditional = FindOrCreateConditional( "NOFPO", false, CONDITIONAL_NULL );
  992. if ( pConditional && pConditional->m_bDefined )
  993. {
  994. m_SupplementalCRCString += "Nf";
  995. }
  996. pConditional = FindOrCreateConditional( "LV", false, CONDITIONAL_NULL );
  997. if ( pConditional && pConditional->m_bDefined )
  998. {
  999. m_SupplementalCRCString += "Lv";
  1000. }
  1001. pConditional = FindOrCreateConditional( "DEMO", false, CONDITIONAL_NULL );
  1002. if ( pConditional && pConditional->m_bDefined )
  1003. {
  1004. m_SupplementalCRCString += "Dm";
  1005. }
  1006. pConditional = FindOrCreateConditional( "NO_STEAM", false, CONDITIONAL_NULL );
  1007. if ( pConditional && pConditional->m_bDefined )
  1008. {
  1009. m_SupplementalCRCString += "Ns";
  1010. }
  1011. pConditional = FindOrCreateConditional( "QTDEBUG", false, CONDITIONAL_NULL );
  1012. if ( pConditional && pConditional->m_bDefined )
  1013. {
  1014. m_SupplementalCRCString += "Qt";
  1015. }
  1016. pConditional = FindOrCreateConditional( "NO_CEG", false, CONDITIONAL_NULL );
  1017. //
  1018. // !!NEVER INTEGRATE THIS CHANGE TO NON-RELEASE BRANCHES!!
  1019. // !!THIS WILL ENABLE CEG AND BREAK NON-REL BRANCHES!!
  1020. // -AaronN
  1021. //
  1022. //if ( pConditional && pConditional->m_bDefined )
  1023. if ( pConditional && pConditional->m_bDefined )
  1024. {
  1025. m_SupplementalCRCString += "Nc";
  1026. }
  1027. pConditional = FindOrCreateConditional( "UPLOAD_CEG", false, CONDITIONAL_NULL );
  1028. if ( pConditional && pConditional->m_bDefined )
  1029. {
  1030. m_SupplementalCRCString += "Uc";
  1031. }
  1032. if ( !m_ExtraOptionsCRCString.IsEmpty() )
  1033. {
  1034. m_SupplementalCRCString += CFmtStr( "_%s_", m_ExtraOptionsCRCString.Get() );
  1035. }
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Restart self from correct location and re-run. Returns FALSE if not applicable,
  1039. // otherwise TRUE if restart occurred.
  1040. //-----------------------------------------------------------------------------
  1041. bool CVPC::RestartFromCorrectLocation( bool *pIsChild )
  1042. {
  1043. #if defined( POSIX )
  1044. return false;
  1045. #else
  1046. // recursive restart guard
  1047. // restart is a hidden internal param, always the last argument
  1048. // presence identifies spawned process
  1049. bool bIsRestart = false;
  1050. if ( !V_stricmp( m_ppArgv[m_nArgc-1], "/restart" ) )
  1051. {
  1052. bIsRestart = true;
  1053. }
  1054. *pIsChild = bIsRestart;
  1055. char szBinPath[MAX_PATH];
  1056. if ( !CheckBinPath( szBinPath, sizeof( szBinPath ) ) )
  1057. {
  1058. if ( bIsRestart )
  1059. {
  1060. VPCError( "Cyclical Restart: Tell A Programmer!, Aborting." );
  1061. }
  1062. // replicate arguments, add -restart as a recursion guard for the new process
  1063. char *newArgs[128];
  1064. if ( m_nArgc >= Q_ARRAYSIZE( newArgs ) - 2 )
  1065. {
  1066. VPCError( "Excessive Arguments: Tell A Programmer!, Aborting." );
  1067. }
  1068. int i;
  1069. for ( i = 0; i < m_nArgc; i++ )
  1070. {
  1071. newArgs[i] = m_ppArgv[i];
  1072. }
  1073. newArgs[i++] = "/restart";
  1074. newArgs[i++] = NULL;
  1075. // restart using synchronous semantic, async semantic causes wierd hang
  1076. int status = _spawnv( _P_WAIT, szBinPath, newArgs );
  1077. if ( !status )
  1078. {
  1079. // called process exited normally
  1080. return true;
  1081. }
  1082. else if ( status > 0 )
  1083. {
  1084. // called process exited with error, pass it along
  1085. exit( status );
  1086. }
  1087. // called process could not be started
  1088. VPCError( "Restart of '%s' failed\n", szBinPath );
  1089. }
  1090. // process is running from correct location
  1091. return false;
  1092. #endif
  1093. }
  1094. //-----------------------------------------------------------------------------
  1095. //-----------------------------------------------------------------------------
  1096. void CVPC::CheckForInstalledXDK()
  1097. {
  1098. #ifndef POSIX
  1099. // quick and dirty early check for 360 XDK ability
  1100. // can only detect simplistic condition, VPC can't validate a perfect XDK/MSDEV installation
  1101. bool bHasXDK = false;
  1102. const char *pXDK = getenv( "XEDK" );
  1103. if ( pXDK && pXDK[0] )
  1104. {
  1105. // look for expected compiler
  1106. char fullPath[MAX_PATH];
  1107. Q_strncpy( fullPath, pXDK, sizeof( fullPath ) );
  1108. V_AppendSlash( fullPath, sizeof( fullPath ) );
  1109. V_strncat( fullPath, "bin\\win32\\cl.exe", sizeof( fullPath ) );
  1110. int fileSize = Sys_FileLength( fullPath, false );
  1111. if ( fileSize > 0 )
  1112. {
  1113. bHasXDK = true;
  1114. m_bXDKPresent = true;
  1115. }
  1116. }
  1117. if ( !bHasXDK && IsPlatformDefined( "X360" ) )
  1118. {
  1119. VPCError( "Cannot Build For Xbox 360, XDK is missing or damaged. Remove /x360 from command line." );
  1120. }
  1121. #endif
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. //-----------------------------------------------------------------------------
  1125. void CVPC::CheckForInstalledPS3SDK()
  1126. {
  1127. #ifndef POSIX
  1128. // quick and dirty early check for 360 XDK ability
  1129. // can only detect simplistic condition, VPC can't validate a perfect XDK/MSDEV installation
  1130. bool bHasSDK = false;
  1131. const char *pSDK = getenv( "SN_PS3_PATH" );
  1132. if ( pSDK && pSDK[0] )
  1133. {
  1134. // Check for presence of 'src/ps3sdk'
  1135. char ps3SDKDir[MAX_PATH];
  1136. V_GetCurrentDirectory( ps3SDKDir, sizeof( ps3SDKDir ) );
  1137. V_snprintf( ps3SDKDir, sizeof( ps3SDKDir ), "%s%s", ps3SDKDir, "\\ps3sdk\\cell\\target\\common\\include\\sdk_version.h" );
  1138. if ( Sys_FileLength( ps3SDKDir ) > 0 )
  1139. {
  1140. bHasSDK = true;
  1141. m_bPS3SDKPresent = true;
  1142. }
  1143. }
  1144. if ( !bHasSDK && IsPlatformDefined( "PS3" ) )
  1145. {
  1146. VPCError( "Cannot Build For PS3, SDK is missing or damaged. Remove /ps3 from command line." );
  1147. }
  1148. #endif
  1149. }
  1150. void CVPC::CreateOutputFilename( project_t *pProject, const char *pchPlatform, const char *pGameName, const char *pchExtension )
  1151. {
  1152. const char *pProjectFileNamePrefix = m_bTestMode ? "test" : pProject->name.String();
  1153. m_OutputFilename = pProjectFileNamePrefix;
  1154. if ( pchPlatform && pchPlatform[0] )
  1155. {
  1156. // non-pc platforms get decorated
  1157. m_OutputFilename += "_";
  1158. m_OutputFilename += pchPlatform;
  1159. }
  1160. if ( pGameName && pGameName[0] )
  1161. {
  1162. // game projects get decorated
  1163. m_OutputFilename += "_";
  1164. m_OutputFilename += pGameName;
  1165. }
  1166. if ( pchExtension && pchExtension[0] )
  1167. {
  1168. m_OutputFilename += ".";
  1169. m_OutputFilename += pchExtension;
  1170. }
  1171. }
  1172. bool CVPC::BuildTargetProject( IProjectIterator *pIterator, projectIndex_t projectIndex, script_t *pProjectScript, const char *pGameName )
  1173. {
  1174. // evaluate the project's script conditional which determines game/platform
  1175. if ( !EvaluateConditionalExpression( pProjectScript->m_condition.String() ) )
  1176. {
  1177. // conditionals prevent this project from consideration
  1178. return false;
  1179. }
  1180. // set once anything is expected to output
  1181. m_bAnyProjectQualified = true;
  1182. if ( !m_Projects.IsValidIndex( projectIndex ) )
  1183. {
  1184. // unexpected bad project index
  1185. Assert( 0 );
  1186. return false;
  1187. }
  1188. project_t *pProject = &m_Projects[projectIndex];
  1189. // track the internal project name, unaffected by user name mangling
  1190. m_ProjectName = pProject->name.String();
  1191. m_LoadAddressName = pProject->name.String();
  1192. // win32 projects are the most prevalent, so by popular demand they have no decoration
  1193. // all other platforms use their platform name as a suffix
  1194. const char *pPlatformName = NULL;
  1195. if ( !IsPlatformDefined( "win32" ) )
  1196. {
  1197. pPlatformName = GetTargetPlatformName();
  1198. }
  1199. // create a decorated project filename based on project/game/platform/etc
  1200. CreateOutputFilename(
  1201. pProject,
  1202. pPlatformName,
  1203. pGameName,
  1204. g_pVPC->GetProjectGenerator()->GetProjectFileExtension() );
  1205. // each vpc script is written with paths relative to their base
  1206. // force each script needs to start relative to their script location
  1207. // this allows vpc to be invoked anywhere, but the groups resolve their projects correctly
  1208. char szScriptPath[MAX_PATH];
  1209. V_ComposeFileName( g_pVPC->GetStartDirectory(), pProjectScript->name.String(), szScriptPath, sizeof( szScriptPath ) );
  1210. V_StripFilename( szScriptPath );
  1211. V_SetCurrentDirectory( szScriptPath );
  1212. // build it
  1213. char szScriptName[MAX_PATH];
  1214. Sys_StripPath( pProjectScript->name.String(), szScriptName );
  1215. return pIterator->VisitProject( projectIndex, szScriptName );
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Iterate and build each of the projects. Game projects can themselves be
  1219. // auto-iterated to apply each of their mod variant.
  1220. //-----------------------------------------------------------------------------
  1221. void CVPC::IterateTargetProjects( CUtlVector<projectIndex_t> &projectList, IProjectIterator *pIterator )
  1222. {
  1223. m_bGeneratedProject = false;
  1224. m_bAnyProjectQualified = false;
  1225. if ( !projectList.Count() )
  1226. {
  1227. // nothing to do
  1228. return;
  1229. }
  1230. for ( int nProject = 0; nProject < projectList.Count(); nProject++ )
  1231. {
  1232. project_t *pProject = &m_Projects[projectList[nProject]];
  1233. // each project can have 1 or more scripts that are predicated by game/platform conditionals (i.e. client or server)
  1234. for ( int nScript = 0; nScript < pProject->scripts.Count(); nScript++ )
  1235. {
  1236. script_t *pProjectScript = &pProject->scripts[nScript];
  1237. // occurrence of game condition(s) dictates iteration behavior
  1238. // client/server would have multiple game conditions
  1239. bool bHasGameCondition = g_pVPC->ConditionHasDefinedType( pProjectScript->m_condition.String(), CONDITIONAL_GAME );
  1240. if ( !bHasGameCondition )
  1241. {
  1242. // no game condition
  1243. BuildTargetProject( pIterator, projectList[nProject], pProjectScript, NULL );
  1244. }
  1245. else
  1246. {
  1247. // auto iterate through all defined game conditionals, setting each in turn
  1248. // this provides for building say client for all mod(s) that it can support
  1249. for ( int nTargetGame = 0; nTargetGame < m_Conditionals.Count(); nTargetGame++ )
  1250. {
  1251. if ( m_Conditionals[nTargetGame].type != CONDITIONAL_GAME || !m_Conditionals[nTargetGame].m_bDefined )
  1252. {
  1253. // the game conditions must be defined to be considered
  1254. // i.e. the user has specified to build /hl2 /tf2, but not /portal
  1255. continue;
  1256. }
  1257. // only one game condition is active during project generation
  1258. for ( int k = 0; k < m_Conditionals.Count(); k++ )
  1259. {
  1260. // unmark all game conditionals
  1261. if ( m_Conditionals[k].type == CONDITIONAL_GAME )
  1262. {
  1263. m_Conditionals[k].m_bGameConditionActive = false;
  1264. }
  1265. }
  1266. m_Conditionals[nTargetGame].m_bGameConditionActive = true;
  1267. BuildTargetProject( pIterator, projectList[nProject], pProjectScript, m_Conditionals[nTargetGame].name.String() );
  1268. }
  1269. }
  1270. }
  1271. }
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Build all the projects in m_targetProjects.
  1275. //-----------------------------------------------------------------------------
  1276. bool CVPC::BuildTargetProjects()
  1277. {
  1278. class CDefaultProjectIterator : public IProjectIterator
  1279. {
  1280. public:
  1281. virtual bool VisitProject( projectIndex_t iProject, const char *pScriptPath )
  1282. {
  1283. Log_Msg( LOG_VPC, "\n" );
  1284. // check project's crc signature
  1285. if ( !g_pVPC->IsForceGenerate() && !g_pVPC->IsForceIterate() && g_pVPC->IsProjectCurrent( g_pVPC->GetOutputFilename() ) )
  1286. {
  1287. // valid, does not need to build
  1288. return false;
  1289. }
  1290. return g_pVPC->ParseProjectScript( pScriptPath, 0, false, true );
  1291. }
  1292. };
  1293. if ( !m_TargetProjects.Count() )
  1294. {
  1295. VPCError( "No recognized project(s) to build. Use /h or /projects or /groups to spew more info." );
  1296. }
  1297. CDefaultProjectIterator iterator;
  1298. IterateTargetProjects( m_TargetProjects, &iterator );
  1299. // Catch user attention to notify lack of any expected output
  1300. // Novice users would not be aware of expected conditionals
  1301. if ( !m_bGeneratedProject && !m_bAnyProjectQualified )
  1302. {
  1303. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "\n" );
  1304. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "----------------------------\n" );
  1305. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "!!! No Project Generated !!!\n" );
  1306. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "----------------------------\n" );
  1307. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "\n" );
  1308. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "Possibly missing game, platform, or other conditional expected by script.\n" );
  1309. Log_Warning( LOG_VPC, Color( 255, 255, 0, 255 ), "Use /h verify desired target build set.\n" );
  1310. return false;
  1311. }
  1312. return true;
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Find the project that corresponds to the specified vcproj and setup
  1316. // to build that project.
  1317. //-----------------------------------------------------------------------------
  1318. void CVPC::FindProjectFromVCPROJ( const char *pScriptNameVCProj )
  1319. {
  1320. // caller is specifying the output vcproj, i.e. via tool shortcut from within MSDEV to re-gen
  1321. // use the vpc standardized output vcproj name to determine re-gen parameters
  1322. // mod and platform will be seperated by '_' after the project name
  1323. // resolve to correct project, best will be longest match, due to project names like foo_? and foo_bar_?
  1324. char szProject[MAX_PATH];
  1325. unsigned int bestLen = 0;
  1326. for ( int i = 0; i < m_Projects.Count(); i++ )
  1327. {
  1328. if ( V_stristr( pScriptNameVCProj, m_Projects[i].name.String() ) )
  1329. {
  1330. if ( bestLen < strlen( m_Projects[i].name.String() ) )
  1331. {
  1332. bestLen = strlen( m_Projects[i].name.String() );
  1333. strcpy( szProject, m_Projects[i].name.String() );
  1334. }
  1335. }
  1336. }
  1337. if ( bestLen == 0 )
  1338. {
  1339. VPCError( "Could not resolve '%s' to any known projects", pScriptNameVCProj );
  1340. }
  1341. // skip past known project
  1342. char szBuffer[MAX_PATH];
  1343. V_StripExtension( pScriptNameVCProj + strlen( szProject ), szBuffer, sizeof( szBuffer ) );
  1344. // each token is seperated by '_'
  1345. int numTokens = 0;
  1346. char *pToken = szBuffer;
  1347. char *pStart = pToken;
  1348. char szTokens[2][MAX_PATH];
  1349. while ( numTokens < 2 )
  1350. {
  1351. if ( pStart[0] == '_' )
  1352. {
  1353. pStart++;
  1354. pToken = strchr( pStart, '_' );
  1355. if ( !pToken )
  1356. {
  1357. strcpy( szTokens[numTokens++], pStart );
  1358. break;
  1359. }
  1360. else
  1361. {
  1362. strncpy( szTokens[numTokens], pStart, pToken-pStart );
  1363. szTokens[numTokens][pToken-pStart] = '\0';
  1364. numTokens++;
  1365. pStart = pToken;
  1366. }
  1367. }
  1368. else
  1369. {
  1370. break;
  1371. }
  1372. }
  1373. // re-build a commandline
  1374. int localArgc = 0;
  1375. char *localArgv[16];
  1376. char argBuffers[16][MAX_PATH];
  1377. for ( int i=0; i<Q_ARRAYSIZE( localArgv ); i++ )
  1378. {
  1379. localArgv[i] = argBuffers[i];
  1380. }
  1381. strcpy( localArgv[localArgc++], "vpc.exe" );
  1382. sprintf( localArgv[localArgc++], "+%s", szProject );
  1383. for ( int i=0; i<numTokens; i++ )
  1384. {
  1385. sprintf( localArgv[localArgc++], "/%s", szTokens[i] );
  1386. }
  1387. ParseBuildOptions( localArgc, localArgv );
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // This sets up various defines that are funneled into the .vpc script and the #defines in the engine.
  1391. //
  1392. // VPC makes a distinction between defines and macros (defines are just binary on/off things that are used like this:
  1393. // $File "blah.cpp" [$somedefine]
  1394. //
  1395. // macros are used for substitutions like this:
  1396. // $File "blah.$SOMEMACRO"
  1397. //-----------------------------------------------------------------------------
  1398. void CVPC::SetMacrosAndConditionals()
  1399. {
  1400. // Find the target platform.
  1401. conditional_t *pPlatformConditional = NULL;
  1402. for ( int i = 0; i < m_Conditionals.Count(); i++ )
  1403. {
  1404. if ( m_Conditionals[i].type == CONDITIONAL_PLATFORM && m_Conditionals[i].m_bDefined )
  1405. {
  1406. pPlatformConditional = &m_Conditionals[i];
  1407. break;
  1408. }
  1409. }
  1410. // Only one platform is allowed to be defined.
  1411. for ( int i = 0; i < m_Conditionals.Count(); i++ )
  1412. {
  1413. if ( &m_Conditionals[i] != pPlatformConditional && m_Conditionals[i].type == CONDITIONAL_PLATFORM && m_Conditionals[i].m_bDefined )
  1414. {
  1415. // no no no, the user is not allowed to build multiple platforms simultaneously
  1416. // this prior feature really confused/crapped up the code, so absolutely not supporting that
  1417. VPCWarning( "Detected multiple target platforms...Disabling '%s'", m_Conditionals[i].name.String() );
  1418. m_Conditionals[i].m_bDefined = false;
  1419. }
  1420. }
  1421. if ( !pPlatformConditional )
  1422. {
  1423. // no user specified platform defined, defaults to primary vpc.exe built platform
  1424. #if defined( WIN32 )
  1425. pPlatformConditional = FindOrCreateConditional( "WIN32", false, CONDITIONAL_PLATFORM );
  1426. #elif defined( OSX )
  1427. pPlatformConditional = FindOrCreateConditional( "OSX32", false, CONDITIONAL_PLATFORM );
  1428. #elif defined( LINUX )
  1429. pPlatformConditional = FindOrCreateConditional( "LINUX32", false, CONDITIONAL_PLATFORM );
  1430. #elif defined( CYGWIN )
  1431. pPlatformConditional = FindOrCreateConditional( "CYGWIN", false, CONDITIONAL_PLATFORM );
  1432. #else
  1433. #error "Unsupported platform."
  1434. #endif
  1435. pPlatformConditional->m_bDefined = true;
  1436. }
  1437. // save off the platform name (rather than the pointer to the conditional, since it can be invalidated)
  1438. CUtlString platformName = pPlatformConditional->name;
  1439. VPCStatus( true, "Target Platform: %s", platformName.String() );
  1440. // src_main doesn't want this #define because it conflicts with Python's SDK.
  1441. // It really should be called something else that won't conflict with the rest of the world.
  1442. bool bIncludePlatformDefineInProjects = false;
  1443. #ifdef STEAM
  1444. bIncludePlatformDefineInProjects = true;
  1445. #endif
  1446. SetMacro( "PLATFORM", platformName.String(), bIncludePlatformDefineInProjects );
  1447. // create reserved $QUOTE - used for embedding quotes, or use msdev's &quot
  1448. SetMacro( "QUOTE", "\"", false );
  1449. if ( !V_stricmp( platformName.String(), "WIN32" ) || !V_stricmp( platformName.String(), "WIN64" ) || !V_stricmp( platformName.String(), "X360" ) )
  1450. {
  1451. // VS2010 is strictly win32/xbox360
  1452. // Set the CRC so that compiler changes will always alter the CRC
  1453. if ( m_bWants2013 )
  1454. {
  1455. m_bUse2013 = true;
  1456. m_ExtraOptionsCRCString += "VS2013";
  1457. }
  1458. else if ( m_bWants2012 )
  1459. {
  1460. m_bUse2012 = true;
  1461. m_ExtraOptionsCRCString += "VS2012";
  1462. }
  1463. else if ( m_bWants2010 )
  1464. {
  1465. m_bUse2010 = true;
  1466. m_ExtraOptionsCRCString += "VS2010";
  1467. }
  1468. else
  1469. m_ExtraOptionsCRCString += "VS2005";
  1470. }
  1471. // For backwards compatibility with /define:vs2012 don't set this to false if m_bUse2012 isn't set.
  1472. if ( m_bUse2013 )
  1473. {
  1474. SetConditional( "VS2013", true );
  1475. m_bUse2010 = true; // Request the 2010 file-format.
  1476. }
  1477. else if (m_bUse2012)
  1478. {
  1479. SetConditional( "VS2012", true );
  1480. m_bUse2010 = true; // Request the 2010 file-format.
  1481. }
  1482. else if ( m_bUse2010 )
  1483. SetConditional( "VS2010", true );
  1484. else
  1485. SetConditional( "VS2005", true );
  1486. // create and define various other platform related helper conditionals andmacros
  1487. if ( V_stricmp( platformName.String(), "WIN32" ) == 0 || V_stricmp( platformName.String(), "WIN64" ) == 0 )
  1488. {
  1489. SetConditional( "WINDOWS" );
  1490. SetMacro( "_DLL_EXT", ".dll", true );
  1491. SetMacro( "_IMPLIB_EXT", ".lib", false );
  1492. SetMacro( "_IMPLIB_PREFIX", "", false );
  1493. SetMacro( "_IMPLIB_DLL_PREFIX", "", false );
  1494. SetMacro( "_STATICLIB_EXT", ".lib", false );
  1495. SetMacro( "_EXE_EXT", ".exe", false );
  1496. }
  1497. else if ( V_stricmp( platformName.String(), "X360" ) == 0 )
  1498. {
  1499. SetMacro( "_DLL_EXT", "_360.dll", true );
  1500. SetMacro( "_IMPLIB_EXT", "_360.lib", false );
  1501. SetMacro( "_IMPLIB_PREFIX", "", false );
  1502. SetMacro( "_IMPLIB_DLL_PREFIX", "", false );
  1503. SetMacro( "_STATICLIB_EXT", "_360.lib", false );
  1504. SetMacro( "_EXE_EXT", ".exe", false );
  1505. }
  1506. else if ( V_stricmp( platformName.String(), "PS3" ) == 0 )
  1507. {
  1508. SetMacro( "_DLL_EXT", "_ps3.sprx", true );
  1509. SetMacro( "_IMPLIB_EXT", "_ps3.lib", false );
  1510. SetMacro( "_IMPLIB_PREFIX", "", false );
  1511. SetMacro( "_IMPLIB_DLL_PREFIX", "", false );
  1512. SetMacro( "_STATICLIB_EXT", "_ps3.lib", false );
  1513. SetMacro( "_EXE_EXT", ".self", false );
  1514. }
  1515. else if ( V_stricmp( platformName.String(), "LINUX32" ) == 0 || V_stricmp( platformName.String(), "LINUX64" ) == 0 )
  1516. {
  1517. SetConditional( "LINUXALL" );
  1518. if ( m_bDedicatedBuild )
  1519. {
  1520. SetConditional( "DEDICATED" );
  1521. }
  1522. SetConditional( "POSIX" );
  1523. SetMacro( "LINUX", "1", true );
  1524. SetMacro( "_LINUX", "1", true );
  1525. SetMacro( "POSIX", "1", true );
  1526. SetMacro( "_POSIX", "1", true );
  1527. SetMacro( "_DLL_EXT", ".so", true );
  1528. SetMacro( "_IMPLIB_EXT", ".so", false );
  1529. SetMacro( "_IMPLIB_PREFIX", "lib", false );
  1530. SetMacro( "_IMPLIB_DLL_PREFIX", "lib", false );
  1531. SetMacro( "_STATICLIB_EXT", ".a", false );
  1532. SetMacro( "_EXE_EXT", "", false );
  1533. SetMacro( "_SYM_EXT", ".dbg", false );
  1534. }
  1535. else if ( V_stricmp( platformName.String(), "OSX32" ) == 0 || V_stricmp( platformName.String(), "OSX64" ) == 0 )
  1536. {
  1537. SetConditional( "OSXALL" );
  1538. if ( m_bDedicatedBuild )
  1539. {
  1540. SetConditional( "DEDICATED" );
  1541. }
  1542. SetConditional( "POSIX" );
  1543. SetMacro( "_POSIX", "1", true );
  1544. SetMacro( "_DLL_EXT", ".dylib", true );
  1545. SetMacro( "_IMPLIB_EXT", ".dylib", false );
  1546. SetMacro( "_IMPLIB_PREFIX", "lib", false );
  1547. SetMacro( "_IMPLIB_DLL_PREFIX", "lib", false );
  1548. SetMacro( "_STATICLIB_EXT", ".a", false );
  1549. SetMacro( "_EXE_EXT", "", false );
  1550. SetMacro( "_SYM_EXT", ".dSYM", false );
  1551. }
  1552. else if ( V_stricmp( platformName.String(), "CYGWIN" ) == 0 )
  1553. {
  1554. SetConditional( "CYGWIN" );
  1555. SetConditional( "CYGWIN_WINDOWS_TARGET" );
  1556. SetConditional( "DEDICATED" );
  1557. SetConditional( "POSIX" );
  1558. SetMacro( "CYGWIN", "1", true );
  1559. SetMacro( "_CYGWIN", "1", true );
  1560. SetMacro( "CYGWIN_WINDOWS_TARGET", "1", true );
  1561. SetMacro( "_CYGWIN_WINDOWS_TARGET", "1", true );
  1562. SetMacro( "POSIX", "1", true );
  1563. SetMacro( "_POSIX", "1", true );
  1564. SetMacro( "_DLL_EXT", ".dll", true );
  1565. SetMacro( "_IMPLIB_EXT", ".dll.a", false );
  1566. SetMacro( "_IMPLIB_DLL_PREFIX", "", false );
  1567. SetMacro( "_IMPLIB_PREFIX", "lib", false );
  1568. SetMacro( "_STATICLIB_EXT", ".a", false );
  1569. SetMacro( "_EXE_EXT", ".exe", false );
  1570. }
  1571. // Set VPCGAME macro based on target game
  1572. if ( m_bEnableVpcGameMacro )
  1573. {
  1574. int nGameDefineIndex = -1;
  1575. for ( int iOtherGameDefine = 0; iOtherGameDefine < m_Conditionals.Count(); ++ iOtherGameDefine )
  1576. {
  1577. if ( m_Conditionals[iOtherGameDefine].type == CONDITIONAL_GAME &&
  1578. m_Conditionals[iOtherGameDefine].m_bDefined )
  1579. {
  1580. if ( nGameDefineIndex == -1 )
  1581. {
  1582. nGameDefineIndex = iOtherGameDefine;
  1583. }
  1584. else
  1585. {
  1586. // uh-oh, multiple games defined for target build
  1587. // can't set VPCGAME accurately
  1588. nGameDefineIndex = -2;
  1589. }
  1590. }
  1591. }
  1592. SetMacro( "VPCGAME", ( nGameDefineIndex >= 0 ) ? m_Conditionals[nGameDefineIndex].name.Get() : "valve", true );
  1593. SetMacro( "VPCGAMECAPS", ( nGameDefineIndex >= 0 ) ? m_Conditionals[nGameDefineIndex].upperCaseName.Get() : "VALVE", true );
  1594. // force this into additional CRC string
  1595. m_ExtraOptionsCRCString += CFmtStr( "/vpcgame:%s", GetMacroValue( "VPCGAME" ) );
  1596. }
  1597. // Set defines for PS3 and XDK presence
  1598. if ( m_bPS3SDKPresent )
  1599. {
  1600. SetConditional( "PS3SDKINSTALLED" );
  1601. }
  1602. if ( m_bXDKPresent )
  1603. {
  1604. SetConditional( "XDKINSTALLED" );
  1605. }
  1606. }
  1607. //-----------------------------------------------------------------------------
  1608. // Checks for command line /params ( +/- used for projects, so ICommandLine() not suitable)
  1609. //-----------------------------------------------------------------------------
  1610. bool CVPC::HasCommandLineParameter( const char *pParamName )
  1611. {
  1612. for ( int i=1; i < m_nArgc; i++ )
  1613. {
  1614. if ( V_stricmp( m_ppArgv[i], pParamName ) == 0 )
  1615. return true;
  1616. }
  1617. return false;
  1618. }
  1619. //-----------------------------------------------------------------------------
  1620. //-----------------------------------------------------------------------------
  1621. bool CVPC::HasP4SLNCommand()
  1622. {
  1623. return HasCommandLineParameter( "/p4sln" );
  1624. }
  1625. //-----------------------------------------------------------------------------
  1626. //-----------------------------------------------------------------------------
  1627. bool CVPC::HandleP4SLN( IBaseSolutionGenerator *pSolutionGenerator )
  1628. {
  1629. #ifdef WIN32
  1630. // If they want to generate a solution based on a Perforce changelist, adjust m_targetProjects and set it up like /mksln had been passed in.
  1631. if ( m_iP4Changelists.Count() == 0 )
  1632. return false;
  1633. if ( !pSolutionGenerator )
  1634. {
  1635. VPCError( "No solution generator exists for this platform." );
  1636. }
  1637. // Figure out where to put the solution file.
  1638. char szFullSolutionPath[MAX_PATH];
  1639. if ( V_IsAbsolutePath( m_P4SolutionFilename.Get() ) )
  1640. {
  1641. V_strncpy( szFullSolutionPath, m_P4SolutionFilename.Get(), sizeof( szFullSolutionPath ) );
  1642. }
  1643. else
  1644. {
  1645. V_ComposeFileName( g_pVPC->GetStartDirectory(), m_P4SolutionFilename.Get(), szFullSolutionPath, sizeof( szFullSolutionPath ) );
  1646. }
  1647. CProjectDependencyGraph dependencyGraph;
  1648. GenerateSolutionForPerforceChangelist( dependencyGraph, m_iP4Changelists, pSolutionGenerator, szFullSolutionPath );
  1649. return true;
  1650. #else
  1651. return false;
  1652. #endif
  1653. }
  1654. //-----------------------------------------------------------------------------
  1655. //-----------------------------------------------------------------------------
  1656. void CVPC::HandleMKSLN( IBaseSolutionGenerator *pSolutionGenerator, CProjectDependencyGraph &dependencyGraph )
  1657. {
  1658. if ( m_MKSolutionFilename.IsEmpty() )
  1659. {
  1660. return;
  1661. }
  1662. if ( !pSolutionGenerator )
  1663. {
  1664. VPCError( "No solution generator exists for this platform." );
  1665. }
  1666. // Find out what depends on what.
  1667. if ( !dependencyGraph.HasGeneratedDependencies() )
  1668. {
  1669. dependencyGraph.BuildProjectDependencies( 0 );
  1670. }
  1671. // GenerateBuildSet basically generates what we want, except it uses projectIndex_t's, meaning that
  1672. // we don't know what subset of games we should use until we've called VPC_IterateTargetProjects.
  1673. CUtlVector<CDependency_Project*> referencedProjects;
  1674. dependencyGraph.TranslateProjectIndicesToDependencyProjects( m_TargetProjects, referencedProjects );
  1675. // Generate a solution file.
  1676. char szFullSolutionPath[MAX_PATH];
  1677. if ( V_IsAbsolutePath( m_MKSolutionFilename.Get() ) )
  1678. {
  1679. V_strncpy( szFullSolutionPath, m_MKSolutionFilename.Get(), sizeof( szFullSolutionPath ) );
  1680. }
  1681. else
  1682. {
  1683. V_ComposeFileName( g_pVPC->GetStartDirectory(), m_MKSolutionFilename.Get(), szFullSolutionPath, sizeof( szFullSolutionPath ) );
  1684. }
  1685. pSolutionGenerator->GenerateSolutionFile( szFullSolutionPath, referencedProjects );
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. //-----------------------------------------------------------------------------
  1689. void CVPC::SetupGenerators()
  1690. {
  1691. extern IBaseSolutionGenerator *GetSolutionGenerator_Win32();
  1692. extern IBaseProjectGenerator *GetWin32ProjectGenerator();
  1693. extern IBaseProjectGenerator *GetWin32ProjectGenerator_2010();
  1694. extern IBaseProjectGenerator *GetMakefileProjectGenerator();
  1695. extern IBaseProjectGenerator *GetPS3ProjectGenerator();
  1696. extern IBaseProjectGenerator *GetXbox360ProjectGenerator();
  1697. extern IBaseProjectGenerator *GetXbox360ProjectGenerator_2010();
  1698. #if defined( WIN32 )
  1699. // Under Windows we have the ability to generate makefiles so if they specified a linux config then use the makefile generator.
  1700. bool bUseMakefile = IsPlatformDefined( "LINUX" );
  1701. if ( bUseMakefile )
  1702. {
  1703. Log_Msg( LOG_VPC, "\n** Detected Linux platform. Using Makefile generator.\n" );
  1704. }
  1705. else
  1706. {
  1707. // Still use makefiles if they're building the (non-SRCDS) dedicated server.
  1708. conditional_t *pConditional = FindOrCreateConditional( "DEDICATED", false, CONDITIONAL_CUSTOM );
  1709. bUseMakefile = ( pConditional && pConditional->m_bDefined );
  1710. }
  1711. if ( bUseMakefile )
  1712. {
  1713. m_pProjectGenerator = GetMakefileProjectGenerator();
  1714. m_pSolutionGenerator = &g_PosixSolutionGenerator;
  1715. }
  1716. else
  1717. {
  1718. if ( IsPlatformDefined( "PS3" ) )
  1719. {
  1720. m_pProjectGenerator = GetPS3ProjectGenerator();
  1721. m_pSolutionGenerator = GetSolutionGenerator_Win32();
  1722. }
  1723. else if ( IsPlatformDefined( "X360" ) )
  1724. {
  1725. if ( m_bUse2010 )
  1726. {
  1727. Log_Msg( LOG_VPC, Color( 0, 255, 255, 255 ), "Generating for Visual Studio 2010.\n" );
  1728. m_pProjectGenerator = GetXbox360ProjectGenerator_2010();
  1729. }
  1730. else
  1731. {
  1732. m_pProjectGenerator = GetXbox360ProjectGenerator();
  1733. }
  1734. m_pSolutionGenerator = GetSolutionGenerator_Win32();
  1735. }
  1736. else
  1737. {
  1738. if ( m_bUse2013 )
  1739. {
  1740. Log_Msg( LOG_VPC, Color( 0, 255, 255, 255 ), "Generating for Visual Studio 2013.\n" );
  1741. m_pProjectGenerator = GetWin32ProjectGenerator_2010();
  1742. }
  1743. else if ( m_bUse2012 )
  1744. {
  1745. Log_Msg( LOG_VPC, Color( 0, 255, 255, 255 ), "Generating for Visual Studio 2012.\n" );
  1746. m_pProjectGenerator = GetWin32ProjectGenerator_2010();
  1747. }
  1748. else if ( m_bUse2010 )
  1749. {
  1750. Log_Msg( LOG_VPC, Color( 0, 255, 255, 255 ), "Generating for Visual Studio 2010.\n" );
  1751. m_pProjectGenerator = GetWin32ProjectGenerator_2010();
  1752. }
  1753. else
  1754. {
  1755. m_pProjectGenerator = GetWin32ProjectGenerator();
  1756. }
  1757. m_pSolutionGenerator = GetSolutionGenerator_Win32();
  1758. }
  1759. }
  1760. #else
  1761. // Linux always uses the makefile project generator.
  1762. m_pProjectGenerator = GetMakefileProjectGenerator();
  1763. m_pSolutionGenerator = &g_PosixSolutionGenerator;
  1764. #endif
  1765. }
  1766. //-----------------------------------------------------------------------------
  1767. // Since Steam's VPC builds tier0 and vstdlib directly in, Steam uses vpc.exe as the CRC checker.
  1768. // Source uses vpccrccheck.exe to do this.
  1769. //-----------------------------------------------------------------------------
  1770. void CVPC::InProcessCRCCheck()
  1771. {
  1772. for ( int i = 1; i<m_nArgc; i++ )
  1773. {
  1774. if ( !V_stricmp( m_ppArgv[i], "-crc" ) || !V_stricmp( m_ppArgv[i], "-crc2" ) )
  1775. {
  1776. // caller wants the crc check only
  1777. int ret = VPC_CommandLineCRCChecks( m_nArgc, m_ppArgv );
  1778. exit( ret );
  1779. }
  1780. }
  1781. }
  1782. //-----------------------------------------------------------------------------
  1783. //-----------------------------------------------------------------------------
  1784. const char *CVPC::BuildTempGroupScript( const char *pVPCScriptName )
  1785. {
  1786. char projectName[MAX_PATH];
  1787. V_StripExtension( pVPCScriptName, projectName, sizeof( projectName ) );
  1788. char szCurrentDirectory[MAX_PATH];
  1789. V_GetCurrentDirectory( szCurrentDirectory, sizeof( szCurrentDirectory ) );
  1790. // caller is specifying an explicit VPC, i.e. not a project from the default group
  1791. // create a temporary group file that mimics a VGC, that points to the current dir's VPC
  1792. char *pTmpName = tmpnam( NULL );
  1793. if ( !pTmpName || !pTmpName[0] )
  1794. {
  1795. VPCError( "Could not generate temp name. Tell a Programmer.\n" );
  1796. }
  1797. if ( pTmpName[0] == '\\' )
  1798. {
  1799. pTmpName++;
  1800. }
  1801. char tempGroupScriptFilename[MAX_PATH];
  1802. V_ComposeFileName( szCurrentDirectory, CFmtStr( "%svgc.tmp", pTmpName ), tempGroupScriptFilename, sizeof( tempGroupScriptFilename ) );
  1803. m_TempGroupScriptFilename = tempGroupScriptFilename;
  1804. // build the temp group script
  1805. FILE *fp = fopen( m_TempGroupScriptFilename.Get(), "w+t" );
  1806. if ( !fp )
  1807. {
  1808. VPCError( "Could not open temp file '%s'. Tell a Programmer.\n", m_TempGroupScriptFilename.Get() );
  1809. }
  1810. char vpcScriptFilename[MAX_PATH];
  1811. V_ComposeFileName( szCurrentDirectory, pVPCScriptName, vpcScriptFilename, sizeof( vpcScriptFilename ) );
  1812. // the actual vpc must be relative to the source path
  1813. const char *pVPCFilename = StringAfterPrefix( vpcScriptFilename, m_SourcePath.Get() );
  1814. if ( !pVPCFilename )
  1815. {
  1816. VPCError( "Script %s is not in source path %s\n", vpcScriptFilename, m_SourcePath.Get() );
  1817. }
  1818. if ( pVPCFilename[0] == '\\' )
  1819. {
  1820. pVPCFilename++;
  1821. }
  1822. fprintf( fp, "$Project \"%s\"\n", projectName );
  1823. fprintf( fp, "{\n" );
  1824. fprintf( fp, "\"%s\"\n", pVPCFilename );
  1825. fprintf( fp, "}\n" );
  1826. fclose( fp );
  1827. // fake a build command
  1828. char buildCommand[MAX_PATH];
  1829. V_snprintf( buildCommand, sizeof( buildCommand ), "+%s", projectName );
  1830. int index = m_BuildCommands.AddToTail();
  1831. m_BuildCommands[index] = buildCommand;
  1832. return m_TempGroupScriptFilename.Get();
  1833. }
  1834. //-----------------------------------------------------------------------------
  1835. //-----------------------------------------------------------------------------
  1836. int CVPC::ProcessCommandLine()
  1837. {
  1838. SetupDefaultConditionals();
  1839. DetermineSourcePath();
  1840. // possible extensions determine operation mode beyond expected normal user case
  1841. bool bScriptIsVGC = false;
  1842. bool bScriptIsVPC = false;
  1843. bool bScriptIsVCProj = false;
  1844. bool bHasBuildCommand = false;
  1845. const char *pScriptName = NULL;
  1846. const char *pScriptNameVCProj = NULL;
  1847. for ( int i = 1; i < m_nArgc; i++ )
  1848. {
  1849. const char *pArg = m_ppArgv[i];
  1850. if ( V_stristr( pArg, ".vgc" ) )
  1851. {
  1852. // caller explicitly providing group
  1853. pScriptName = pArg;
  1854. bScriptIsVGC = true;
  1855. bHasBuildCommand = true;
  1856. break;
  1857. }
  1858. else if ( V_stristr( pArg, ".vpc" ) )
  1859. {
  1860. // caller is using a local vpc, i.e. one that is not hooked into the groups
  1861. pScriptName = pArg;
  1862. bScriptIsVPC = true;
  1863. bHasBuildCommand = true;
  1864. break;
  1865. }
  1866. else
  1867. {
  1868. if ( V_stristr( pArg, ".vcproj" ) || V_stristr( pArg, ".vcxproj" ) )
  1869. {
  1870. // caller wants to re-gen the vcproj, this is commonly used by MSDEV to re-gen
  1871. pScriptNameVCProj = pArg;
  1872. bScriptIsVCProj = true;
  1873. bHasBuildCommand = true;
  1874. break;
  1875. }
  1876. }
  1877. }
  1878. for ( int i = 1; i < m_nArgc; i++ )
  1879. {
  1880. if ( m_ppArgv[i][0] == '-' || m_ppArgv[i][0] == '+' || m_ppArgv[i][0] == '*' || m_ppArgv[i][0] == '@' )
  1881. {
  1882. bHasBuildCommand = true;
  1883. break;
  1884. }
  1885. }
  1886. if ( bScriptIsVPC )
  1887. {
  1888. pScriptName = BuildTempGroupScript( pScriptName );
  1889. bScriptIsVPC = false;
  1890. bScriptIsVGC = true;
  1891. }
  1892. if ( !bScriptIsVGC )
  1893. {
  1894. // no script, use default group
  1895. pScriptName = "vpc_scripts\\default.vgc";
  1896. bScriptIsVGC = true;
  1897. }
  1898. // set the current directory, it is to be expected src, i.e. .\vpc_scripts\..
  1899. SetDefaultSourcePath();
  1900. char szCurrentDirectory[MAX_PATH];
  1901. V_GetCurrentDirectory( szCurrentDirectory, sizeof( szCurrentDirectory ) );
  1902. m_StartDirectory = szCurrentDirectory;
  1903. // parse and build tables from group script that options will reference
  1904. if ( bScriptIsVGC )
  1905. {
  1906. VPC_ParseGroupScript( pScriptName );
  1907. }
  1908. if ( bScriptIsVCProj )
  1909. {
  1910. // this is commonly used as an extern tool in MSDEV to re-vpc in place
  1911. // caller is msdev providing the vcproj name, solve to determine which project and generate
  1912. FindProjectFromVCPROJ( pScriptNameVCProj );
  1913. }
  1914. else
  1915. {
  1916. ParseBuildOptions( m_nArgc, m_ppArgv );
  1917. }
  1918. // set macros and conditionals derived from command-line options
  1919. SetMacrosAndConditionals();
  1920. // generate a CRC string derived from command-line options
  1921. GenerateOptionsCRCString();
  1922. SetupGenerators();
  1923. // filter user's build commands
  1924. // generate list of build targets
  1925. CProjectDependencyGraph dependencyGraph;
  1926. GenerateBuildSet( dependencyGraph );
  1927. if ( !bHasBuildCommand && !HasP4SLNCommand() )
  1928. {
  1929. // spew usage
  1930. m_bUsageOnly = true;
  1931. }
  1932. if ( m_bUsageOnly )
  1933. {
  1934. // spew only
  1935. SpewUsage();
  1936. return 0;
  1937. }
  1938. #ifdef WIN32
  1939. if ( HandleP4SLN( m_pSolutionGenerator ) )
  1940. {
  1941. return 0;
  1942. }
  1943. #endif
  1944. // iterate and build target projects
  1945. if ( !BuildTargetProjects() )
  1946. {
  1947. // build failure
  1948. return 0;
  1949. }
  1950. // now that we have valid project files, can generate solution
  1951. HandleMKSLN( m_pSolutionGenerator, dependencyGraph );
  1952. return 0;
  1953. }
  1954. //-----------------------------------------------------------------------------
  1955. // main
  1956. //
  1957. //-----------------------------------------------------------------------------
  1958. // VPC is a DLL in Source.
  1959. #if defined(STEAM) || defined(OSX) || defined(LINUX)
  1960. int main( int argc, char **argv )
  1961. #else
  1962. int vpcmain( int argc, char **argv )
  1963. #endif
  1964. {
  1965. g_pVPC = new CVPC();
  1966. if ( !g_pVPC->Init( argc, argv ) )
  1967. {
  1968. return 0;
  1969. }
  1970. int nRetVal = g_pVPC->ProcessCommandLine();
  1971. g_pVPC->Shutdown();
  1972. return nRetVal;
  1973. }
  1974. // VPC is a DLL in Source.
  1975. #if !(defined(STEAM) || defined(OSX) || defined(LINUX))
  1976. #include "ilaunchabledll.h"
  1977. // VPC is launched by vpc.exe, which is a copy of binlaunch.exe.
  1978. // All binlaunch does is setup the path to game\bin and load an ILaunchableDLL
  1979. // interface out of a DLL with the same name as the exe.
  1980. class CVPCLaunchableDLL : public ILaunchableDLL
  1981. {
  1982. public:
  1983. // All vpc.exe does is load the vpc DLL and run this.
  1984. virtual int main( int argc, char **argv )
  1985. {
  1986. return vpcmain( argc, argv );
  1987. }
  1988. };
  1989. EXPOSE_SINGLE_INTERFACE( CVPCLaunchableDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );
  1990. #endif