//========= Copyright Valve Corporation, All rights reserved. ============// // // Interface for makefiles to build differently depending on where they are run from // //===========================================================================// #include "movieobjects/dmemakefileutils.h" #include "movieobjects/dmemdlmakefile.h" #include "movieobjects/dmedccmakefile.h" #include "tier2/fileutils.h" #include "filesystem.h" //----------------------------------------------------------------------------- // Statics //----------------------------------------------------------------------------- IMPLEMENT_DMEMAKEFILE_UTIL_CLASS( CDmeMakefileUtils ); //----------------------------------------------------------------------------- // Default implementation //----------------------------------------------------------------------------- static CDmeMakefileUtils s_MakefileUtils; IDmeMakefileUtils *GetDefaultDmeMakefileUtils() { return &s_MakefileUtils; } //----------------------------------------------------------------------------- // Constructor, destructor //----------------------------------------------------------------------------- CDmeMakefileUtils::CDmeMakefileUtils() : BaseClass( false ) { m_CompilationStep = NOT_COMPILING; m_hCompileProcess = PROCESS_HANDLE_INVALID; m_nCurrentCompileTask = -1; m_nExitCode = 0; } CDmeMakefileUtils::~CDmeMakefileUtils() { } //----------------------------------------------------------------------------- // Here's where systems can access other interfaces implemented by this object //----------------------------------------------------------------------------- void *CDmeMakefileUtils::QueryInterface( const char *pInterfaceName ) { if ( !V_strcmp( pInterfaceName, DMEMAKEFILE_UTILS_INTERFACE_VERSION ) ) return (IDmeMakefileUtils*)this; return NULL; } //----------------------------------------------------------------------------- // Initialization.. set up messagemaps //----------------------------------------------------------------------------- InitReturnVal_t CDmeMakefileUtils::Init() { InitializeFuncMaps(); return INIT_OK; } //----------------------------------------------------------------------------- // Looks for an appropriate method to compile this element with //----------------------------------------------------------------------------- CCompileFuncAdapterBase *CDmeMakefileUtils::DetermineCompileAdapter( CDmElement *pElement ) { int nBestInheritanceDepth = -1; CCompileFuncAdapterBase *pBestAdapter = NULL; CompileFuncTree_t *pTree = GetCompileTree(); while ( pTree ) { CCompileFuncAdapterBase *pCurr = pTree->m_pFirstAdapter; for ( ; pCurr; pCurr = pCurr->m_pNext ) { // Choose this factory if it's more derived than the previous best int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType ); if ( nInheritanceDepth < 0 ) continue; if ( nInheritanceDepth == 0 ) { // Found exact match.. do it! return pCurr; } // Don't look for the best thingy if we're not the root if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) ) continue; nBestInheritanceDepth = nInheritanceDepth; pBestAdapter = pCurr; } pTree = pTree->m_pBaseAdapterTree; } // Return the closest match we could find return pBestAdapter; } //----------------------------------------------------------------------------- // Looks for an appropriate method to open this element with //----------------------------------------------------------------------------- COpenEditorFuncAdapterBase *CDmeMakefileUtils::DetermineOpenEditorAdapter( CDmElement *pElement ) { int nBestInheritanceDepth = -1; COpenEditorFuncAdapterBase *pBestAdapter = NULL; OpenEditorFuncTree_t *pTree = GetOpenEditorTree(); while ( pTree ) { COpenEditorFuncAdapterBase *pCurr = pTree->m_pFirstAdapter; for ( ; pCurr; pCurr = pCurr->m_pNext ) { // Choose this factory if it's more derived than the previous best int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType ); if ( nInheritanceDepth < 0 ) continue; // Found exact match.. do it! if ( nInheritanceDepth == 0 ) return pCurr; if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) ) continue; nBestInheritanceDepth = nInheritanceDepth; pBestAdapter = pCurr; } pTree = pTree->m_pBaseAdapterTree; } return pBestAdapter; } //----------------------------------------------------------------------------- // Opens a element in an external editor //----------------------------------------------------------------------------- void CDmeMakefileUtils::PerformOpenEditor( CDmElement *pElement ) { COpenEditorFuncAdapterBase *pAdapter = DetermineOpenEditorAdapter( pElement ); if ( pAdapter ) { pAdapter->OpenEditor( pElement ); } } //----------------------------------------------------------------------------- // Queues up a compilation task //----------------------------------------------------------------------------- void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement, CCompileFuncAdapterBase *pAdapter ) { Assert( m_CompilationStep == BUILDING_STANDARD_DEPENDENCIES || m_CompilationStep == BUILDING_ALL_DEPENDENCIES ); // Queue up the compilation task int j = m_CompileTasks.AddToTail(); m_CompileTasks[j].m_hElement = pElement; m_CompileTasks[j].m_pAdapter = pAdapter; } void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement ) { CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement ); if ( pAdapter ) { // Queue up the compilation task AddCompilationTask( pElement, pAdapter ); } } //----------------------------------------------------------------------------- // Sets the compile process //----------------------------------------------------------------------------- void CDmeMakefileUtils::SetCompileProcess( ProcessHandle_t hProcess ) { Assert( m_CompilationStep == PERFORMING_COMPILATION ); m_hCompileProcess = hProcess; if ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) { m_CompilationStep = AFTER_COMPILATION_FAILED; } } //----------------------------------------------------------------------------- // Default implementatations for compile dependencies //----------------------------------------------------------------------------- bool CDmeMakefileUtils::AddCompileDependencies( CDmeMakefile *pMakefile, bool bBuildAllDependencies ) { if ( !pMakefile ) return true; CUtlVector< CUtlString > outputs; int nCount = pMakefile->GetSourceCount(); for ( int i = 0; i < nCount; ++i ) { CDmeSource *pSource = pMakefile->GetSource( i ); if ( !pSource ) continue; CDmeMakefile *pDependentMakefile = pSource->GetDependentMakefile(); if ( !pDependentMakefile ) continue; bool bShouldBuildFile = bBuildAllDependencies; // Does the output files exist? int j = 0; if ( !bBuildAllDependencies ) { pDependentMakefile->GetOutputs( outputs ); int nOutputCount = outputs.Count(); for ( j = 0; j < nOutputCount; ++j ) { // If the file doesn't exist, we have to build it if ( !g_pFullFileSystem->FileExists( outputs[j] ) ) break; bShouldBuildFile = true; break; } } if ( !bShouldBuildFile ) continue; CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pDependentMakefile ); if ( pAdapter ) { // Add dependent makefiles first if ( !pAdapter->PerformCompilationStep( pDependentMakefile, bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES ) ) return false; } // Queue up the compilation task AddCompilationTask( pDependentMakefile, pAdapter ); } return true; } //----------------------------------------------------------------------------- // Default implementatations for precompilation step //----------------------------------------------------------------------------- bool CDmeMakefileUtils::PerformCompilationStep( CDmElement *pElement, CompilationStep_t step ) { // Do nothing return true; } bool CDmeMakefileUtils::PerformCompilationStep( CDmeMakefile *pMakefile, CompilationStep_t step ) { switch( step ) { case BUILDING_ALL_DEPENDENCIES: return AddCompileDependencies( pMakefile, true ); case BUILDING_STANDARD_DEPENDENCIES: return AddCompileDependencies( pMakefile, false ); case BEFORE_COMPILATION: pMakefile->PreCompile(); break; case AFTER_COMPILATION_SUCCEEDED: pMakefile->PostCompile(); break; } return true; } //----------------------------------------------------------------------------- // Starts the next compile task //----------------------------------------------------------------------------- void CDmeMakefileUtils::StartNextCompileTask( ) { Assert( m_hCompileProcess == PROCESS_HANDLE_INVALID ); ++m_nCurrentCompileTask; if ( m_nCurrentCompileTask == m_CompileTasks.Count() ) { PerformCompilationStep( AFTER_COMPILATION_SUCCEEDED ); m_nCurrentCompileTask = -1; m_CompileTasks.RemoveAll(); return; } m_hCompileProcess = PROCESS_HANDLE_INVALID; // NOTE: PerformCompilationStep is expected to call SetCompileProcess to set m_hCompileProcess CompileInfo_t &info = m_CompileTasks[m_nCurrentCompileTask]; bool bOk = info.m_pAdapter->PerformCompilationStep( info.m_hElement, PERFORMING_COMPILATION ); if ( !bOk || ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) ) { AbortCurrentCompilation(); return; } } //----------------------------------------------------------------------------- // Performs the compilation step on all elements //----------------------------------------------------------------------------- bool CDmeMakefileUtils::PerformCompilationStep( CompilationStep_t step ) { // Iterate through all elements and run a compilation step m_CompilationStep = step; int nCount = m_CompileTasks.Count(); for ( int i = 0; i < nCount; ++i ) { CompileInfo_t &info = m_CompileTasks[i]; if ( info.m_hElement.Get() ) { if ( !info.m_pAdapter->PerformCompilationStep( info.m_hElement, step ) ) return false; } } return true; } //----------------------------------------------------------------------------- // Main entry point for compilation //----------------------------------------------------------------------------- void CDmeMakefileUtils::PerformCompile( CDmElement *pElement, bool bBuildAllDependencies ) { if ( IsCurrentlyCompiling() ) { AbortCurrentCompilation(); } CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement ); if ( !pAdapter ) { m_CompilationStep = AFTER_COMPILATION_FAILED; return; } // Add dependent makefiles first m_CompilationStep = bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES; if ( !pAdapter->PerformCompilationStep( pElement, m_CompilationStep ) ) { AbortCurrentCompilation(); return; } // Queue up the compilation task AddCompilationTask( pElement, pAdapter ); // Iterate through all elements and run a precompilation step // NOTE: This is where perforce integration should go if ( !PerformCompilationStep( BEFORE_COMPILATION ) ) { AbortCurrentCompilation(); return; } // Dequeue the first compile task and start it up m_CompilationStep = PERFORMING_COMPILATION; StartNextCompileTask(); } //----------------------------------------------------------------------------- // Are we in the middle of compiling this makefile? //----------------------------------------------------------------------------- bool CDmeMakefileUtils::IsCurrentlyCompiling() { return ( m_CompilationStep != NOT_COMPILING ); } //----------------------------------------------------------------------------- // Aborts any current compilation //----------------------------------------------------------------------------- void CDmeMakefileUtils::AbortCurrentCompilation() { if ( m_hCompileProcess != PROCESS_HANDLE_INVALID ) { g_pProcessUtils->AbortProcess( m_hCompileProcess ); m_hCompileProcess = PROCESS_HANDLE_INVALID; } if ( IsCurrentlyCompiling() ) { PerformCompilationStep( AFTER_COMPILATION_FAILED ); m_nCurrentCompileTask = -1; m_CompileTasks.RemoveAll(); } } //----------------------------------------------------------------------------- // Returns the exit code of the failed compilation (if COMPILATION_FAILED occurred) //----------------------------------------------------------------------------- int CDmeMakefileUtils::GetExitCode() { return m_nExitCode; } //----------------------------------------------------------------------------- // Returns output from the compilation //----------------------------------------------------------------------------- int CDmeMakefileUtils::GetCompileOutputSize() { if ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) return 0; return g_pProcessUtils->GetProcessOutputSize( m_hCompileProcess ); } CompilationState_t CDmeMakefileUtils::UpdateCompilation( char *pOutputBuf, int nBufLen ) { switch( m_CompilationStep ) { case BUILDING_STANDARD_DEPENDENCIES: case BUILDING_ALL_DEPENDENCIES: case BEFORE_COMPILATION: return COMPILATION_NOT_COMPLETE; case AFTER_COMPILATION_FAILED: m_CompilationStep = NOT_COMPILING; return COMPILATION_FAILED; case AFTER_COMPILATION_SUCCEEDED: m_CompilationStep = NOT_COMPILING; return COMPILATION_SUCCESSFUL; } // This is the PERFORMING_COMPILATION case: // FIXME: Check return codes from compile process.. // fail if compilation process had a problem if ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) { if ( nBufLen > 0 ) { pOutputBuf[0] = 0; } return COMPILATION_SUCCESSFUL; } if ( nBufLen > 0 ) { g_pProcessUtils->GetProcessOutput( m_hCompileProcess, pOutputBuf, nBufLen ); } if ( !g_pProcessUtils->IsProcessComplete( m_hCompileProcess ) ) return COMPILATION_NOT_COMPLETE; m_nExitCode = g_pProcessUtils->GetProcessExitCode( m_hCompileProcess ); bool bCompileSucceeded = ( m_nExitCode == 0 ); g_pProcessUtils->CloseProcess( m_hCompileProcess ); m_hCompileProcess = PROCESS_HANDLE_INVALID; if ( !bCompileSucceeded ) { AbortCurrentCompilation(); return COMPILATION_NOT_COMPLETE; } StartNextCompileTask(); if ( m_CompilationStep == PERFORMING_COMPILATION ) return COMPILATION_NOT_COMPLETE; CompilationState_t retVal = ( m_CompilationStep == AFTER_COMPILATION_SUCCEEDED ) ? COMPILATION_SUCCESSFUL : COMPILATION_FAILED; m_CompilationStep = NOT_COMPILING; return retVal; } //----------------------------------------------------------------------------- // Type-specific compilation functions //----------------------------------------------------------------------------- bool CDmeMakefileUtils::PerformCompilationStep( CDmeMDLMakefile *pMakeFile, CompilationStep_t step ) { if ( step != PERFORMING_COMPILATION ) return PerformCompilationStep( static_cast( pMakeFile ), step ); char pBinDirectory[MAX_PATH]; GetModSubdirectory( "..\\bin", pBinDirectory, sizeof(pBinDirectory) ); Q_RemoveDotSlashes( pBinDirectory ); char pStudioMDLCmd[MAX_PATH]; #ifdef _DEBUG Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe -allowdebug %s", pBinDirectory, pMakeFile->GetFileName() ); #else Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe %s", pBinDirectory, pMakeFile->GetFileName() ); #endif ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pStudioMDLCmd, true ); SetCompileProcess( hProcess ); return true; } //----------------------------------------------------------------------------- // Exports a Maya file to a DMX file //----------------------------------------------------------------------------- bool CDmeMakefileUtils::PerformCompilationStep( CDmeMayaMakefile *pMakeFile, CompilationStep_t step ) { if ( step != PERFORMING_COMPILATION ) return PerformCompilationStep( static_cast( pMakeFile ), step ); // FIXME: Create batch export command here CUtlString mayaCommand; mayaCommand = "vsDmxIO -export"; CUtlVector< CDmeHandle< CDmeSourceMayaFile > > sources; pMakeFile->GetSources( sources ); if ( !sources.Count() ) return false; CDmeSourceMayaFile *pDmeSourceDCCFile( sources[ 0 ].Get() ); mayaCommand += " -selection"; char pObjectId[128]; UniqueIdToString( pMakeFile->GetId(), pObjectId, sizeof(pObjectId) ); mayaCommand += " -makefileObjectId \\\""; mayaCommand += pObjectId; mayaCommand += "\\\""; mayaCommand += " -"; mayaCommand += pDmeSourceDCCFile->m_ExportType.GetAttribute()->GetName(); switch ( pDmeSourceDCCFile->m_ExportType.Get() ) { case 1: // skeletal animation mayaCommand += " skeletalAnimation"; mayaCommand += " -"; mayaCommand += pDmeSourceDCCFile->m_FrameStart.GetAttribute()->GetName(); mayaCommand += " "; mayaCommand += pDmeSourceDCCFile->m_FrameStart.Get(); mayaCommand += " -"; mayaCommand += pDmeSourceDCCFile->m_FrameEnd.GetAttribute()->GetName(); mayaCommand += " "; mayaCommand += pDmeSourceDCCFile->m_FrameEnd.Get(); mayaCommand += " -"; mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.GetAttribute()->GetName(); mayaCommand += " "; mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.Get(); break; default: // Model mayaCommand += " model"; break; } char pFileName[MAX_PATH]; Q_strncpy( pFileName, pMakeFile->GetFileName(), sizeof( pFileName ) ); Q_FixSlashes( pFileName, '/' ); mayaCommand += " -filename \\\""; mayaCommand += pFileName; mayaCommand += "\\\""; const int rootObjectCount( pDmeSourceDCCFile->m_RootDCCObjects.Count() ); for ( int rootObjectIndex( 0 ); rootObjectIndex < rootObjectCount; ++rootObjectIndex ) { mayaCommand += " "; mayaCommand += pDmeSourceDCCFile->m_RootDCCObjects[ rootObjectIndex ]; } char pSourcePath[MAX_PATH]; pMakeFile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) ); // Maya wants forward slashes Q_FixSlashes( pSourcePath, '/' ); char pMayaCommand[1024]; Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "mayabatch.exe -batch -file \"%s\" -command \"%s\"", pSourcePath, mayaCommand.Get() ); ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pMayaCommand, true ); SetCompileProcess( hProcess ); return true; } //----------------------------------------------------------------------------- // Opens Maya with a particular file //----------------------------------------------------------------------------- void CDmeMakefileUtils::OpenEditor( CDmeSourceMayaFile *pDmeSourceDCCFile ) { CDmeMayaMakefile *pMakefile = FindReferringElement< CDmeMayaMakefile >( pDmeSourceDCCFile, "sources" ); if ( !pMakefile ) return; char pSourcePath[MAX_PATH]; pMakefile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) ); // Maya wants forward slashes Q_FixSlashes( pSourcePath, '/' ); char pMayaCommand[1024]; Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "maya.exe -file \"%s\"", pSourcePath ); g_pProcessUtils->StartProcess( pMayaCommand, true ); }