Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

613 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Interface for makefiles to build differently depending on where they are run from
  4. //
  5. //===========================================================================//
  6. #include "movieobjects/dmemakefileutils.h"
  7. #include "movieobjects/dmemdlmakefile.h"
  8. #include "movieobjects/dmedccmakefile.h"
  9. #include "tier2/fileutils.h"
  10. #include "filesystem.h"
  11. //-----------------------------------------------------------------------------
  12. // Statics
  13. //-----------------------------------------------------------------------------
  14. IMPLEMENT_DMEMAKEFILE_UTIL_CLASS( CDmeMakefileUtils );
  15. //-----------------------------------------------------------------------------
  16. // Default implementation
  17. //-----------------------------------------------------------------------------
  18. static CDmeMakefileUtils s_MakefileUtils;
  19. IDmeMakefileUtils *GetDefaultDmeMakefileUtils()
  20. {
  21. return &s_MakefileUtils;
  22. }
  23. //-----------------------------------------------------------------------------
  24. // Constructor, destructor
  25. //-----------------------------------------------------------------------------
  26. CDmeMakefileUtils::CDmeMakefileUtils() : BaseClass( false )
  27. {
  28. m_CompilationStep = NOT_COMPILING;
  29. m_hCompileProcess = PROCESS_HANDLE_INVALID;
  30. m_nCurrentCompileTask = -1;
  31. m_nExitCode = 0;
  32. }
  33. CDmeMakefileUtils::~CDmeMakefileUtils()
  34. {
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Here's where systems can access other interfaces implemented by this object
  38. //-----------------------------------------------------------------------------
  39. void *CDmeMakefileUtils::QueryInterface( const char *pInterfaceName )
  40. {
  41. if ( !V_strcmp( pInterfaceName, DMEMAKEFILE_UTILS_INTERFACE_VERSION ) )
  42. return (IDmeMakefileUtils*)this;
  43. return NULL;
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Initialization.. set up messagemaps
  47. //-----------------------------------------------------------------------------
  48. InitReturnVal_t CDmeMakefileUtils::Init()
  49. {
  50. InitializeFuncMaps();
  51. return INIT_OK;
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Looks for an appropriate method to compile this element with
  55. //-----------------------------------------------------------------------------
  56. CCompileFuncAdapterBase *CDmeMakefileUtils::DetermineCompileAdapter( CDmElement *pElement )
  57. {
  58. int nBestInheritanceDepth = -1;
  59. CCompileFuncAdapterBase *pBestAdapter = NULL;
  60. CompileFuncTree_t *pTree = GetCompileTree();
  61. while ( pTree )
  62. {
  63. CCompileFuncAdapterBase *pCurr = pTree->m_pFirstAdapter;
  64. for ( ; pCurr; pCurr = pCurr->m_pNext )
  65. {
  66. // Choose this factory if it's more derived than the previous best
  67. int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType );
  68. if ( nInheritanceDepth < 0 )
  69. continue;
  70. if ( nInheritanceDepth == 0 )
  71. {
  72. // Found exact match.. do it!
  73. return pCurr;
  74. }
  75. // Don't look for the best thingy if we're not the root
  76. if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) )
  77. continue;
  78. nBestInheritanceDepth = nInheritanceDepth;
  79. pBestAdapter = pCurr;
  80. }
  81. pTree = pTree->m_pBaseAdapterTree;
  82. }
  83. // Return the closest match we could find
  84. return pBestAdapter;
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Looks for an appropriate method to open this element with
  88. //-----------------------------------------------------------------------------
  89. COpenEditorFuncAdapterBase *CDmeMakefileUtils::DetermineOpenEditorAdapter( CDmElement *pElement )
  90. {
  91. int nBestInheritanceDepth = -1;
  92. COpenEditorFuncAdapterBase *pBestAdapter = NULL;
  93. OpenEditorFuncTree_t *pTree = GetOpenEditorTree();
  94. while ( pTree )
  95. {
  96. COpenEditorFuncAdapterBase *pCurr = pTree->m_pFirstAdapter;
  97. for ( ; pCurr; pCurr = pCurr->m_pNext )
  98. {
  99. // Choose this factory if it's more derived than the previous best
  100. int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType );
  101. if ( nInheritanceDepth < 0 )
  102. continue;
  103. // Found exact match.. do it!
  104. if ( nInheritanceDepth == 0 )
  105. return pCurr;
  106. if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) )
  107. continue;
  108. nBestInheritanceDepth = nInheritanceDepth;
  109. pBestAdapter = pCurr;
  110. }
  111. pTree = pTree->m_pBaseAdapterTree;
  112. }
  113. return pBestAdapter;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Opens a element in an external editor
  117. //-----------------------------------------------------------------------------
  118. void CDmeMakefileUtils::PerformOpenEditor( CDmElement *pElement )
  119. {
  120. COpenEditorFuncAdapterBase *pAdapter = DetermineOpenEditorAdapter( pElement );
  121. if ( pAdapter )
  122. {
  123. pAdapter->OpenEditor( pElement );
  124. }
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Queues up a compilation task
  128. //-----------------------------------------------------------------------------
  129. void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement, CCompileFuncAdapterBase *pAdapter )
  130. {
  131. Assert( m_CompilationStep == BUILDING_STANDARD_DEPENDENCIES || m_CompilationStep == BUILDING_ALL_DEPENDENCIES );
  132. // Queue up the compilation task
  133. int j = m_CompileTasks.AddToTail();
  134. m_CompileTasks[j].m_hElement = pElement;
  135. m_CompileTasks[j].m_pAdapter = pAdapter;
  136. }
  137. void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement )
  138. {
  139. CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement );
  140. if ( pAdapter )
  141. {
  142. // Queue up the compilation task
  143. AddCompilationTask( pElement, pAdapter );
  144. }
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Sets the compile process
  148. //-----------------------------------------------------------------------------
  149. void CDmeMakefileUtils::SetCompileProcess( ProcessHandle_t hProcess )
  150. {
  151. Assert( m_CompilationStep == PERFORMING_COMPILATION );
  152. m_hCompileProcess = hProcess;
  153. if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
  154. {
  155. m_CompilationStep = AFTER_COMPILATION_FAILED;
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Default implementatations for compile dependencies
  160. //-----------------------------------------------------------------------------
  161. bool CDmeMakefileUtils::AddCompileDependencies( CDmeMakefile *pMakefile, bool bBuildAllDependencies )
  162. {
  163. if ( !pMakefile )
  164. return true;
  165. CUtlVector< CUtlString > outputs;
  166. int nCount = pMakefile->GetSourceCount();
  167. for ( int i = 0; i < nCount; ++i )
  168. {
  169. CDmeSource *pSource = pMakefile->GetSource( i );
  170. if ( !pSource )
  171. continue;
  172. CDmeMakefile *pDependentMakefile = pSource->GetDependentMakefile();
  173. if ( !pDependentMakefile )
  174. continue;
  175. bool bShouldBuildFile = bBuildAllDependencies;
  176. // Does the output files exist?
  177. int j = 0;
  178. if ( !bBuildAllDependencies )
  179. {
  180. pDependentMakefile->GetOutputs( outputs );
  181. int nOutputCount = outputs.Count();
  182. for ( j = 0; j < nOutputCount; ++j )
  183. {
  184. // If the file doesn't exist, we have to build it
  185. if ( !g_pFullFileSystem->FileExists( outputs[j] ) )
  186. break;
  187. bShouldBuildFile = true;
  188. break;
  189. }
  190. }
  191. if ( !bShouldBuildFile )
  192. continue;
  193. CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pDependentMakefile );
  194. if ( pAdapter )
  195. {
  196. // Add dependent makefiles first
  197. if ( !pAdapter->PerformCompilationStep( pDependentMakefile, bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES ) )
  198. return false;
  199. }
  200. // Queue up the compilation task
  201. AddCompilationTask( pDependentMakefile, pAdapter );
  202. }
  203. return true;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Default implementatations for precompilation step
  207. //-----------------------------------------------------------------------------
  208. bool CDmeMakefileUtils::PerformCompilationStep( CDmElement *pElement, CompilationStep_t step )
  209. {
  210. // Do nothing
  211. return true;
  212. }
  213. bool CDmeMakefileUtils::PerformCompilationStep( CDmeMakefile *pMakefile, CompilationStep_t step )
  214. {
  215. switch( step )
  216. {
  217. case BUILDING_ALL_DEPENDENCIES:
  218. return AddCompileDependencies( pMakefile, true );
  219. case BUILDING_STANDARD_DEPENDENCIES:
  220. return AddCompileDependencies( pMakefile, false );
  221. case BEFORE_COMPILATION:
  222. pMakefile->PreCompile();
  223. break;
  224. case AFTER_COMPILATION_SUCCEEDED:
  225. pMakefile->PostCompile();
  226. break;
  227. }
  228. return true;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Starts the next compile task
  232. //-----------------------------------------------------------------------------
  233. void CDmeMakefileUtils::StartNextCompileTask( )
  234. {
  235. Assert( m_hCompileProcess == PROCESS_HANDLE_INVALID );
  236. ++m_nCurrentCompileTask;
  237. if ( m_nCurrentCompileTask == m_CompileTasks.Count() )
  238. {
  239. PerformCompilationStep( AFTER_COMPILATION_SUCCEEDED );
  240. m_nCurrentCompileTask = -1;
  241. m_CompileTasks.RemoveAll();
  242. return;
  243. }
  244. m_hCompileProcess = PROCESS_HANDLE_INVALID;
  245. // NOTE: PerformCompilationStep is expected to call SetCompileProcess to set m_hCompileProcess
  246. CompileInfo_t &info = m_CompileTasks[m_nCurrentCompileTask];
  247. bool bOk = info.m_pAdapter->PerformCompilationStep( info.m_hElement, PERFORMING_COMPILATION );
  248. if ( !bOk || ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) )
  249. {
  250. AbortCurrentCompilation();
  251. return;
  252. }
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Performs the compilation step on all elements
  256. //-----------------------------------------------------------------------------
  257. bool CDmeMakefileUtils::PerformCompilationStep( CompilationStep_t step )
  258. {
  259. // Iterate through all elements and run a compilation step
  260. m_CompilationStep = step;
  261. int nCount = m_CompileTasks.Count();
  262. for ( int i = 0; i < nCount; ++i )
  263. {
  264. CompileInfo_t &info = m_CompileTasks[i];
  265. if ( info.m_hElement.Get() )
  266. {
  267. if ( !info.m_pAdapter->PerformCompilationStep( info.m_hElement, step ) )
  268. return false;
  269. }
  270. }
  271. return true;
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Main entry point for compilation
  275. //-----------------------------------------------------------------------------
  276. void CDmeMakefileUtils::PerformCompile( CDmElement *pElement, bool bBuildAllDependencies )
  277. {
  278. if ( IsCurrentlyCompiling() )
  279. {
  280. AbortCurrentCompilation();
  281. }
  282. CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement );
  283. if ( !pAdapter )
  284. {
  285. m_CompilationStep = AFTER_COMPILATION_FAILED;
  286. return;
  287. }
  288. // Add dependent makefiles first
  289. m_CompilationStep = bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES;
  290. if ( !pAdapter->PerformCompilationStep( pElement, m_CompilationStep ) )
  291. {
  292. AbortCurrentCompilation();
  293. return;
  294. }
  295. // Queue up the compilation task
  296. AddCompilationTask( pElement, pAdapter );
  297. // Iterate through all elements and run a precompilation step
  298. // NOTE: This is where perforce integration should go
  299. if ( !PerformCompilationStep( BEFORE_COMPILATION ) )
  300. {
  301. AbortCurrentCompilation();
  302. return;
  303. }
  304. // Dequeue the first compile task and start it up
  305. m_CompilationStep = PERFORMING_COMPILATION;
  306. StartNextCompileTask();
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Are we in the middle of compiling this makefile?
  310. //-----------------------------------------------------------------------------
  311. bool CDmeMakefileUtils::IsCurrentlyCompiling()
  312. {
  313. return ( m_CompilationStep != NOT_COMPILING );
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Aborts any current compilation
  317. //-----------------------------------------------------------------------------
  318. void CDmeMakefileUtils::AbortCurrentCompilation()
  319. {
  320. if ( m_hCompileProcess != PROCESS_HANDLE_INVALID )
  321. {
  322. g_pProcessUtils->AbortProcess( m_hCompileProcess );
  323. m_hCompileProcess = PROCESS_HANDLE_INVALID;
  324. }
  325. if ( IsCurrentlyCompiling() )
  326. {
  327. PerformCompilationStep( AFTER_COMPILATION_FAILED );
  328. m_nCurrentCompileTask = -1;
  329. m_CompileTasks.RemoveAll();
  330. }
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Returns the exit code of the failed compilation (if COMPILATION_FAILED occurred)
  334. //-----------------------------------------------------------------------------
  335. int CDmeMakefileUtils::GetExitCode()
  336. {
  337. return m_nExitCode;
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Returns output from the compilation
  341. //-----------------------------------------------------------------------------
  342. int CDmeMakefileUtils::GetCompileOutputSize()
  343. {
  344. if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
  345. return 0;
  346. return g_pProcessUtils->GetProcessOutputSize( m_hCompileProcess );
  347. }
  348. CompilationState_t CDmeMakefileUtils::UpdateCompilation( char *pOutputBuf, int nBufLen )
  349. {
  350. switch( m_CompilationStep )
  351. {
  352. case BUILDING_STANDARD_DEPENDENCIES:
  353. case BUILDING_ALL_DEPENDENCIES:
  354. case BEFORE_COMPILATION:
  355. return COMPILATION_NOT_COMPLETE;
  356. case AFTER_COMPILATION_FAILED:
  357. m_CompilationStep = NOT_COMPILING;
  358. return COMPILATION_FAILED;
  359. case AFTER_COMPILATION_SUCCEEDED:
  360. m_CompilationStep = NOT_COMPILING;
  361. return COMPILATION_SUCCESSFUL;
  362. }
  363. // This is the PERFORMING_COMPILATION case:
  364. // FIXME: Check return codes from compile process..
  365. // fail if compilation process had a problem
  366. if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
  367. {
  368. if ( nBufLen > 0 )
  369. {
  370. pOutputBuf[0] = 0;
  371. }
  372. return COMPILATION_SUCCESSFUL;
  373. }
  374. if ( nBufLen > 0 )
  375. {
  376. g_pProcessUtils->GetProcessOutput( m_hCompileProcess, pOutputBuf, nBufLen );
  377. }
  378. if ( !g_pProcessUtils->IsProcessComplete( m_hCompileProcess ) )
  379. return COMPILATION_NOT_COMPLETE;
  380. m_nExitCode = g_pProcessUtils->GetProcessExitCode( m_hCompileProcess );
  381. bool bCompileSucceeded = ( m_nExitCode == 0 );
  382. g_pProcessUtils->CloseProcess( m_hCompileProcess );
  383. m_hCompileProcess = PROCESS_HANDLE_INVALID;
  384. if ( !bCompileSucceeded )
  385. {
  386. AbortCurrentCompilation();
  387. return COMPILATION_NOT_COMPLETE;
  388. }
  389. StartNextCompileTask();
  390. if ( m_CompilationStep == PERFORMING_COMPILATION )
  391. return COMPILATION_NOT_COMPLETE;
  392. CompilationState_t retVal = ( m_CompilationStep == AFTER_COMPILATION_SUCCEEDED ) ? COMPILATION_SUCCESSFUL : COMPILATION_FAILED;
  393. m_CompilationStep = NOT_COMPILING;
  394. return retVal;
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Type-specific compilation functions
  398. //-----------------------------------------------------------------------------
  399. bool CDmeMakefileUtils::PerformCompilationStep( CDmeMDLMakefile *pMakeFile, CompilationStep_t step )
  400. {
  401. if ( step != PERFORMING_COMPILATION )
  402. return PerformCompilationStep( static_cast<CDmeMakefile*>( pMakeFile ), step );
  403. char pBinDirectory[MAX_PATH];
  404. GetModSubdirectory( "..\\bin", pBinDirectory, sizeof(pBinDirectory) );
  405. Q_RemoveDotSlashes( pBinDirectory );
  406. char pStudioMDLCmd[MAX_PATH];
  407. #ifdef _DEBUG
  408. Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe -allowdebug %s", pBinDirectory, pMakeFile->GetFileName() );
  409. #else
  410. Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe %s", pBinDirectory, pMakeFile->GetFileName() );
  411. #endif
  412. ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pStudioMDLCmd, true );
  413. SetCompileProcess( hProcess );
  414. return true;
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Exports a Maya file to a DMX file
  418. //-----------------------------------------------------------------------------
  419. bool CDmeMakefileUtils::PerformCompilationStep( CDmeMayaMakefile *pMakeFile, CompilationStep_t step )
  420. {
  421. if ( step != PERFORMING_COMPILATION )
  422. return PerformCompilationStep( static_cast<CDmeMakefile*>( pMakeFile ), step );
  423. // FIXME: Create batch export command here
  424. CUtlString mayaCommand;
  425. mayaCommand = "vsDmxIO -export";
  426. CUtlVector< CDmeHandle< CDmeSourceMayaFile > > sources;
  427. pMakeFile->GetSources( sources );
  428. if ( !sources.Count() )
  429. return false;
  430. CDmeSourceMayaFile *pDmeSourceDCCFile( sources[ 0 ].Get() );
  431. mayaCommand += " -selection";
  432. char pObjectId[128];
  433. UniqueIdToString( pMakeFile->GetId(), pObjectId, sizeof(pObjectId) );
  434. mayaCommand += " -makefileObjectId \\\"";
  435. mayaCommand += pObjectId;
  436. mayaCommand += "\\\"";
  437. mayaCommand += " -";
  438. mayaCommand += pDmeSourceDCCFile->m_ExportType.GetAttribute()->GetName();
  439. switch ( pDmeSourceDCCFile->m_ExportType.Get() )
  440. {
  441. case 1: // skeletal animation
  442. mayaCommand += " skeletalAnimation";
  443. mayaCommand += " -";
  444. mayaCommand += pDmeSourceDCCFile->m_FrameStart.GetAttribute()->GetName();
  445. mayaCommand += " ";
  446. mayaCommand += pDmeSourceDCCFile->m_FrameStart.Get();
  447. mayaCommand += " -";
  448. mayaCommand += pDmeSourceDCCFile->m_FrameEnd.GetAttribute()->GetName();
  449. mayaCommand += " ";
  450. mayaCommand += pDmeSourceDCCFile->m_FrameEnd.Get();
  451. mayaCommand += " -";
  452. mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.GetAttribute()->GetName();
  453. mayaCommand += " ";
  454. mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.Get();
  455. break;
  456. default: // Model
  457. mayaCommand += " model";
  458. break;
  459. }
  460. char pFileName[MAX_PATH];
  461. Q_strncpy( pFileName, pMakeFile->GetFileName(), sizeof( pFileName ) );
  462. Q_FixSlashes( pFileName, '/' );
  463. mayaCommand += " -filename \\\"";
  464. mayaCommand += pFileName;
  465. mayaCommand += "\\\"";
  466. const int rootObjectCount( pDmeSourceDCCFile->m_RootDCCObjects.Count() );
  467. for ( int rootObjectIndex( 0 ); rootObjectIndex < rootObjectCount; ++rootObjectIndex )
  468. {
  469. mayaCommand += " ";
  470. mayaCommand += pDmeSourceDCCFile->m_RootDCCObjects[ rootObjectIndex ];
  471. }
  472. char pSourcePath[MAX_PATH];
  473. pMakeFile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) );
  474. // Maya wants forward slashes
  475. Q_FixSlashes( pSourcePath, '/' );
  476. char pMayaCommand[1024];
  477. Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "mayabatch.exe -batch -file \"%s\" -command \"%s\"", pSourcePath, mayaCommand.Get() );
  478. ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pMayaCommand, true );
  479. SetCompileProcess( hProcess );
  480. return true;
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Opens Maya with a particular file
  484. //-----------------------------------------------------------------------------
  485. void CDmeMakefileUtils::OpenEditor( CDmeSourceMayaFile *pDmeSourceDCCFile )
  486. {
  487. CDmeMayaMakefile *pMakefile = FindReferringElement< CDmeMayaMakefile >( pDmeSourceDCCFile, "sources" );
  488. if ( !pMakefile )
  489. return;
  490. char pSourcePath[MAX_PATH];
  491. pMakefile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) );
  492. // Maya wants forward slashes
  493. Q_FixSlashes( pSourcePath, '/' );
  494. char pMayaCommand[1024];
  495. Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "maya.exe -file \"%s\"", pSourcePath );
  496. g_pProcessUtils->StartProcess( pMayaCommand, true );
  497. }