//========= Copyright Valve Corporation, All rights reserved. ============// // // Describes an asset: something that is compiled from sources, // in potentially multiple steps, to a compiled resource // //============================================================================= #include "movieobjects/dmemdlmakefile.h" #include "movieobjects/idmemakefileutils.h" #include "datamodel/dmelementfactoryhelper.h" #include "tier2/fileutils.h" #include "tier3/tier3.h" #include "filesystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Hook into element factories //----------------------------------------------------------------------------- IMPLEMENT_ELEMENT_FACTORY( DmeSource, CDmeSource ); //----------------------------------------------------------------------------- // Construction/destruction //----------------------------------------------------------------------------- void CDmeSource::OnConstruction() { m_DependentMakefile = NULL; } void CDmeSource::OnDestruction() { } //----------------------------------------------------------------------------- // Sets/gets the makefile that was used to build this source //----------------------------------------------------------------------------- void CDmeSource::SetDependentMakefile( CDmeMakefile *pMakeFile ) { m_DependentMakefile = pMakeFile; } CDmeMakefile *CDmeSource::GetDependentMakefile() { return m_DependentMakefile.Get(); } //----------------------------------------------------------------------------- // Call this to open the source file in an editor //----------------------------------------------------------------------------- void CDmeSource::OpenEditor() { if ( g_pDmeMakefileUtils ) { g_pDmeMakefileUtils->PerformOpenEditor( this ); } } //----------------------------------------------------------------------------- // Hook into element factories //----------------------------------------------------------------------------- IMPLEMENT_ELEMENT_FACTORY( DmeMakefile, CDmeMakefile ); //----------------------------------------------------------------------------- // Construction/destruction //----------------------------------------------------------------------------- void CDmeMakefile::OnConstruction() { m_Sources.Init( this, "sources" ); m_hOutput = NULL; m_hCompileProcess = PROCESS_HANDLE_INVALID; m_bIsDirty = false; } void CDmeMakefile::OnDestruction() { DestroyOutputElement( m_hOutput.Get() ); m_hOutput = NULL; } //----------------------------------------------------------------------------- // Performs pre-compilation step //----------------------------------------------------------------------------- void CDmeMakefile::PreCompile( ) { // Make all outputs writeable MakeOutputsWriteable(); // Destroy the current output object; we'll need to reload it // NOTE: Don't check for m_hOutput == 0; we always need to call DestroyOutputElement // Sometimes makefiles have to do stuff even if m_hOutput == NULL DestroyOutputElement( m_hOutput ); m_hOutput = NULL; } void CDmeMakefile::PostCompile( ) { } //----------------------------------------------------------------------------- // Gets the output element created by compilation of this makefile //----------------------------------------------------------------------------- CDmElement *CDmeMakefile::GetOutputElement( bool bCreateIfNecessary ) { if ( m_hOutput.Get() ) return m_hOutput.Get(); if ( !bCreateIfNecessary ) return NULL; if ( !g_pDmeMakefileUtils || !g_pDmeMakefileUtils->IsCurrentlyCompiling() ) { m_hOutput = CreateOutputElement(); } return m_hOutput.Get(); } //----------------------------------------------------------------------------- // Gets the path of the makefile //----------------------------------------------------------------------------- void CDmeMakefile::GetMakefilePath( char *pFullPath, int nBufLen ) { DmFileId_t fileId = GetFileId(); const char *pFileName = ( fileId != DMFILEID_INVALID ) ? g_pDataModel->GetFileName( fileId ) : ""; Assert( !pFileName[0] || Q_IsAbsolutePath( pFileName ) ); Q_ExtractFilePath( pFileName, pFullPath, nBufLen ); } //----------------------------------------------------------------------------- // Returns the output directory we expect to compile files into //----------------------------------------------------------------------------- bool CDmeMakefile::GetOutputDirectory( char *pFullPath, int nBufLen ) { return GetDefaultDirectory( GetOutputDirectoryID(), pFullPath, nBufLen ); } //----------------------------------------------------------------------------- // Returns the output name (output directory + filename, no extension) //----------------------------------------------------------------------------- bool CDmeMakefile::GetOutputName( char *pFullPath, int nBufLen ) { pFullPath[0] = 0; char pOutputPath[MAX_PATH]; if ( !GetDefaultDirectory( GetOutputDirectoryID(), pOutputPath, sizeof(pOutputPath) ) ) return false; DmFileId_t fileId = GetFileId(); const char *pFileName = ( fileId != DMFILEID_INVALID ) ? g_pDataModel->GetFileName( fileId ) : ""; if ( !pFileName || !pFileName[0] ) return false; Q_ComposeFileName( pOutputPath, Q_UnqualifiedFileName(pFileName), pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } //----------------------------------------------------------------------------- // Converts the m_pDefaultDirectoryID field of the DmeMakefileType_t to a full path //----------------------------------------------------------------------------- bool CDmeMakefile::GetDefaultDirectory( const char *pDefaultDirectoryID, char *pFullPath, int nBufLen ) { if ( StringHasPrefix( pDefaultDirectoryID, "contentdir:" ) ) { pDefaultDirectoryID += 11; GetModContentSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } if ( StringHasPrefix( pDefaultDirectoryID, "gamedir:" ) ) { pDefaultDirectoryID += 8; GetModSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } if ( StringHasPrefix( pDefaultDirectoryID, "makefiledir:" ) ) { char pMakefilePath[MAX_PATH]; GetMakefilePath( pMakefilePath, sizeof(pMakefilePath) ); pDefaultDirectoryID += 12; Q_ComposeFileName( pMakefilePath, pDefaultDirectoryID, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } if ( StringHasPrefix( pDefaultDirectoryID, "makefilegamedir:" ) ) { char pMakefilePath[MAX_PATH]; GetMakefilePath( pMakefilePath, sizeof(pMakefilePath) ); char pModContentDirectory[MAX_PATH]; GetModContentSubdirectory( NULL, pModContentDirectory, sizeof(pModContentDirectory) ); char pRelativePath[MAX_PATH]; if ( !Q_MakeRelativePath( pMakefilePath, pModContentDirectory, pRelativePath, sizeof(pRelativePath) ) ) { pFullPath[0] = 0; return false; } char pModDirectory[MAX_PATH]; GetModSubdirectory( NULL, pModDirectory, sizeof(pModDirectory) ); char pMakefileGamePath[MAX_PATH]; Q_ComposeFileName( pModDirectory, pRelativePath, pMakefileGamePath, sizeof(pMakefileGamePath) ); pDefaultDirectoryID += 16; Q_ComposeFileName( pMakefileGamePath, pDefaultDirectoryID, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } // Assume it's a content subdir GetModContentSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); return true; } //----------------------------------------------------------------------------- // Relative path to full path //----------------------------------------------------------------------------- void CDmeMakefile::RelativePathToFullPath( const char *pRelativePath, char *pFullPath, int nBufLen ) { if ( !pRelativePath[0] ) { pFullPath[0] = 0; return; } char pRootDir[ MAX_PATH ]; GetMakefilePath( pRootDir, sizeof(pRootDir) ); Q_ComposeFileName( pRootDir, pRelativePath, pFullPath, nBufLen ); Q_RemoveDotSlashes( pFullPath ); } //----------------------------------------------------------------------------- // Fullpath to relative path //----------------------------------------------------------------------------- void CDmeMakefile::FullPathToRelativePath( const char *pFullPath, char *pRelativePath, int nBufLen ) { if ( !pFullPath[0] ) { pRelativePath[0] = 0; return; } char pRootDir[ MAX_PATH ]; GetMakefilePath( pRootDir, sizeof(pRootDir) ); if ( pRootDir[0] ) { Q_MakeRelativePath( pFullPath, pRootDir, pRelativePath, nBufLen ); } else { Q_strncpy( pRelativePath, pFullPath, nBufLen ); Q_FixSlashes( pRelativePath ); } } //----------------------------------------------------------------------------- // Adds a single source //----------------------------------------------------------------------------- CDmeSource *CDmeMakefile::AddSource( const char *pSourceType, const char *pFullPath ) { if ( pFullPath[0] && FindSource( pSourceType, pFullPath ) ) { Warning( "Attempted to add the same source twice %s!\n", pFullPath ); return NULL; } CDmElement *pElement = GetElement< CDmElement >( g_pDataModel->CreateElement( pSourceType, "", GetFileId() ) ); CDmeSource *pSource = CastElement< CDmeSource >( pElement ); Assert( pSource ); if ( !pSource ) { Warning( "Invalid source type name %s!\n", pSourceType ); if ( pElement ) { DestroyElement( pElement ); } return NULL; } char pRelativePath[MAX_PATH]; FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) ); pSource->SetRelativeFileName( pRelativePath ); m_Sources.AddToTail( pSource ); return pSource; } //----------------------------------------------------------------------------- // Removes a single source //----------------------------------------------------------------------------- CDmeSource *CDmeMakefile::FindSource( const char *pSourceType, const char *pFullPath ) { char pRelativePath[MAX_PATH]; FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) ); int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) ) continue; if ( !Q_stricmp( pRelativePath, m_Sources[i]->GetRelativeFileName() ) ) return m_Sources[i]; } return NULL; } //----------------------------------------------------------------------------- // Sets a source to be a single source //----------------------------------------------------------------------------- CDmeSource *CDmeMakefile::SetSingleSource( const char *pSourceType, const char *pFullPath ) { // FIXME: we maybe shouldn't remove everything if the source can't be created for some reason? RemoveAllSources( pSourceType ); return AddSource( pSourceType, pFullPath ); } //----------------------------------------------------------------------------- // Changes a source //----------------------------------------------------------------------------- void CDmeMakefile::SetSourceFullPath( CDmeSource *pSource, const char *pFullPath ) { char pRelativePath[MAX_PATH]; FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) ); if ( Q_stricmp( pRelativePath, pSource->GetRelativeFileName() ) ) { pSource->SetRelativeFileName( pRelativePath ); // FIXME: Should we delete the dependent makefile? pSource->SetDependentMakefile( NULL ); } } //----------------------------------------------------------------------------- // Returns the full path of a source //----------------------------------------------------------------------------- void CDmeMakefile::GetSourceFullPath( CDmeSource *pSource, char *pFullPath, int nBufLen ) { const char *pRelativePath = pSource->GetRelativeFileName( ); RelativePathToFullPath( pRelativePath, pFullPath, nBufLen ); } //----------------------------------------------------------------------------- // Returns a list of sources //----------------------------------------------------------------------------- void CDmeMakefile::GetSources( const char *pSourceType, CUtlVector< CDmeHandle< CDmeSource > > &sources ) { int nCount = m_Sources.Count(); sources.EnsureCapacity( nCount ); for ( int i = 0; i < nCount; ++i ) { if ( m_Sources[i]->IsA( pSourceType ) ) { int j = sources.AddToTail(); sources[j] = m_Sources[i]; } } } //----------------------------------------------------------------------------- // Gets a list of all sources, regardless of type //----------------------------------------------------------------------------- int CDmeMakefile::GetSourceCount() { return m_Sources.Count(); } CDmeSource *CDmeMakefile::GetSource( int nIndex ) { return m_Sources[nIndex]; } //----------------------------------------------------------------------------- // Removes a single source //----------------------------------------------------------------------------- void CDmeMakefile::RemoveSource( CDmeSource *pSource ) { int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( m_Sources[i] == pSource ) { m_Sources.Remove( i ); break; } } } void CDmeMakefile::RemoveSource( const char *pSourceType, const char *pFullPath ) { char pRelativePath[MAX_PATH]; FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) ); int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) ) continue; if ( !Q_stricmp( pRelativePath, m_Sources[i]->GetRelativeFileName() ) ) { m_Sources.Remove( i ); break; } } } //----------------------------------------------------------------------------- // Removes all sources of a particular type //----------------------------------------------------------------------------- void CDmeMakefile::RemoveAllSources( const char *pSourceType ) { int nCount = m_Sources.Count(); for ( int i = nCount; --i >= 0; ) { if ( !Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) ) { // NOTE: This works because we're iterating backward m_Sources.Remove( i ); } } } //----------------------------------------------------------------------------- // Source iteration //----------------------------------------------------------------------------- bool CDmeMakefile::HasSourceOfType( const char *pSourceType ) { int nCount = m_Sources.Count(); for ( int i = nCount; --i >= 0; ) { if ( !Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) ) return true; } return false; } //----------------------------------------------------------------------------- // Updates the source names to be relative to a particular path //----------------------------------------------------------------------------- bool CDmeMakefile::UpdateSourceNames( const char *pOldRootDir, const char *pNewRootDir, bool bApplyChanges ) { char pOldSourcePath[ MAX_PATH ]; char pNewSourcePath[ MAX_PATH ]; int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { const char *pOldRelativePath = m_Sources[i]->GetRelativeFileName(); if ( pOldRelativePath[0] ) { Q_ComposeFileName( pOldRootDir, pOldRelativePath, pOldSourcePath, sizeof(pOldSourcePath) ); Q_RemoveDotSlashes( pOldSourcePath ); if ( !Q_MakeRelativePath( pOldSourcePath, pNewRootDir, pNewSourcePath, sizeof(pNewSourcePath) ) ) { Assert( !bApplyChanges ); return false; } } else { pNewSourcePath[0] = 0; } if ( !bApplyChanges ) continue; m_Sources[i]->SetRelativeFileName( pNewSourcePath ); } return true; } //----------------------------------------------------------------------------- // Returns the filename //----------------------------------------------------------------------------- const char *CDmeMakefile::GetFileName() const { DmFileId_t fileId = GetFileId(); return g_pDataModel->GetFileName( fileId ); } //----------------------------------------------------------------------------- // Call this to change the file the makefile is stored in // Will make all sources be relative to this path //----------------------------------------------------------------------------- bool CDmeMakefile::SetFileName( const char *pFileName ) { if ( !Q_IsAbsolutePath( pFileName ) ) return false; char pOldRootDir[ MAX_PATH ]; char pNewRootDir[ MAX_PATH ]; GetMakefilePath( pOldRootDir, sizeof(pOldRootDir) ); Q_ExtractFilePath( pFileName, pNewRootDir, sizeof(pNewRootDir) ); // Gotta do this twice; once to check for validity, once to actually do it if ( !UpdateSourceNames( pOldRootDir, pNewRootDir, false ) ) return false; UpdateSourceNames( pOldRootDir, pNewRootDir, true ); DmFileId_t fileId = GetFileId(); if ( fileId == DMFILEID_INVALID ) { fileId = g_pDataModel->FindOrCreateFileId( pFileName ); SetFileId( fileId, TD_DEEP ); } else { g_pDataModel->SetFileName( fileId, pFileName ); } return true; } //----------------------------------------------------------------------------- // Make all outputs writeable //----------------------------------------------------------------------------- void CDmeMakefile::MakeOutputsWriteable( ) { // When we publish, we'll check them out. CUtlVector outputs; GetOutputs( outputs ); int nCount = outputs.Count(); for ( int i = 0; i < nCount; ++i ) { g_pFullFileSystem->SetFileWritable( outputs[i], true ); } } //----------------------------------------------------------------------------- // Sets a makefile/source association //----------------------------------------------------------------------------- void CDmeMakefile::SetAssociation( CDmeSource *pSource, CDmeMakefile *pSourceMakefile ) { if ( !pSource ) return; int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( m_Sources[i] != pSource ) continue; CDmeMakefile *pDependentMakeFile = m_Sources[i]->GetDependentMakefile(); if ( pSourceMakefile != pDependentMakeFile ) { // FIXME: Should I recursively delete pDependentMakeFile ? m_Sources[i]->SetDependentMakefile( pSourceMakefile ); } return; } } //----------------------------------------------------------------------------- // Finds a dependent makefile //----------------------------------------------------------------------------- CDmeMakefile *CDmeMakefile::FindDependentMakefile( CDmeSource *pSource ) { if ( !pSource ) return NULL; int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( m_Sources[i] == pSource ) return m_Sources[i]->GetDependentMakefile(); } return NULL; } //----------------------------------------------------------------------------- // Finds the associated source //----------------------------------------------------------------------------- CDmeSource *CDmeMakefile::FindAssociatedSource( CDmeMakefile *pChildMakefile ) { if ( !pChildMakefile ) return NULL; int nCount = m_Sources.Count(); for ( int i = 0; i < nCount; ++i ) { if ( m_Sources[i]->GetDependentMakefile() == pChildMakefile ) return m_Sources[i]; } return NULL; }