|
|
//====== Copyright � 1996-2004, 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() { m_CompilationStep = NOT_COMPILING; m_hCompileProcess = NULL; 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( IProcess *hProcess ) { Assert( m_CompilationStep == PERFORMING_COMPILATION ); m_hCompileProcess = hProcess; if ( m_hCompileProcess == NULL ) { 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 == NULL ); ++m_nCurrentCompileTask; if ( m_nCurrentCompileTask == m_CompileTasks.Count() ) { PerformCompilationStep( AFTER_COMPILATION_SUCCEEDED ); m_nCurrentCompileTask = -1; m_CompileTasks.RemoveAll(); return; }
m_hCompileProcess = NULL;
// 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 == NULL ) ) { 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 != NULL ) { m_hCompileProcess->Abort(); m_hCompileProcess->Release(); m_hCompileProcess = NULL; }
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 == NULL ) return 0; return m_hCompileProcess->GetStdout()->GetNumBytesAvailable(); }
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 == NULL ) { if ( nBufLen > 0 ) { pOutputBuf[0] = 0; } return COMPILATION_SUCCESSFUL; }
if ( nBufLen > 0 ) { CUtlString sOutput; m_hCompileProcess->GetStdout()->ReadAvailable( sOutput ); V_strncpy( pOutputBuf, sOutput.String(), nBufLen ); V_TranslateLineFeedsToUnix( pOutputBuf ); }
if ( !m_hCompileProcess->IsComplete() ) return COMPILATION_NOT_COMPLETE;
m_nExitCode = m_hCompileProcess->GetExitCode(); bool bCompileSucceeded = ( m_nExitCode == 0 ); m_hCompileProcess->Release(); m_hCompileProcess = NULL;
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<CDmeMakefile*>( 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
IProcess *hProcess = g_pProcessUtils->StartProcess( pStudioMDLCmd, STARTPROCESS_CONNECTSTDPIPES ); 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<CDmeMakefile*>( 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() ); IProcess *hProcess = g_pProcessUtils->StartProcess( pMayaCommand, STARTPROCESS_CONNECTSTDPIPES ); 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, STARTPROCESS_CONNECTSTDPIPES ); }
|