Files
cstrike15_src/utils/vpc/vpc/projectscript.cpp
T
nephacks f12416cffd initial
2025-06-04 03:22:50 +02:00

2352 lines
68 KiB
C++

//========= Copyright 1996-2016, Valve Corporation, All rights reserved. ============//
//
// Purpose: VPC
//
//=====================================================================================//
#include "vpc.h"
#include "tier1/utldict.h"
#include "tier1/keyvalues.h"
#include "baseprojectdatacollector.h"
#include "tier1/fmtstr.h"
void VPC_Keyword_RemoveFile( void (*pFNNameTranslation)( CUtlStringBuilder *pStrBuf ) = nullptr );
static void VPC_AddLibraryDependencies( const char *pLibPath );
#ifndef STEAM
bool V_StrSubstInPlace( char *pchInOut, int cchInOut, const char *pMatch, const char *pReplaceWith, bool bCaseSensitive )
{
char *pchT = (char *)stackalloc( cchInOut );
if ( V_StrSubst( pchInOut, pMatch, pReplaceWith, pchT, cchInOut, bCaseSensitive ) )
{
V_strncpy( pchInOut, pchT, cchInOut );
return true;
}
return false;
}
#endif
void VPC_Keyword_FileBuildOrderModifier()
{
const char *pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
return;
if ( CharStrEq( pToken, '}' ) )
{
// end of section
return;
}
g_pVPC->GetProjectGenerator()->HandleProperty( "$BuildOrderModifier", pToken );
}
void VPC_ParseFileSection()
{
while ( 1 )
{
const char *pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
break;
if ( CharStrEq( pToken, '}' ) )
{
// end of section
break;
}
if ( !V_stricmp_fast( pToken, "$Configuration" ) )
{
VPC_Keyword_FileConfiguration();
}
else if ( !V_stricmp_fast( pToken, "$BuildOrderModifier" ) )
{
VPC_Keyword_FileBuildOrderModifier();
}
else
{
g_pVPC->VPCSyntaxError( "Unrecognized token '%s' in file section. Possibly missing a $Configuration scope?", pToken );
}
}
}
static const char *ResolveCandidate( const char *pszFile, const char *pszPlatform, CUtlPathStringHolder *pRetBuf )
{
CUtlStringBuilder* pStrBuf = g_pVPC->GetTempStringBuffer2();
V_ComposeFileName( g_pVPC->GetProjectPath(), pszFile, pStrBuf->Access(), pStrBuf->Capacity() );
pStrBuf->ReplaceFastCaseless( "$os", pszPlatform );
V_FixSlashes( pStrBuf->Access() );
V_RemoveDotSlashes( pStrBuf->Access() );
V_FixDoubleSlashes( pStrBuf->Access() );
if ( Sys_Exists( pStrBuf->Get() ) )
{
pStrBuf->Set( pszFile );
pStrBuf->ReplaceFastCaseless( "$os", pszPlatform );
pRetBuf->Set( pStrBuf->Get() );
return pRetBuf->Get();
}
return NULL;
}
// No particular order, but must not change without changing the arrPlatformChains matrix
const char *g_szArrPlatforms[] =
{
// raw platforms...
"win32", // 0
"win64", // 1
"osx32", // 2
"osx64", // 3
"linux64", // 4
"linuxserver64", // 5
"linuxsteamrtarm32hf", // 6
"linuxsteamrtarm64hf", // 7
"", // 8
"ps3", // 9
"x360", // 10
"androidarm32", // 11
"androidarm64", // 12
"androidmips32", // 13
"androidmips64", // 14
"androidx8632", // 15
"androidx8664", // 16
// fallbacks...
"win", // 17
"osx", // 18
"linux", // 19
"posix", // 20
"any", // 21
NULL
};
//fallback indices to match the array above
enum FallBacks_t
{
FB_WIN = 17,
FB_OSX = 18,
FB_LIN = 19,
FB_POS = 20,
FB_ANY = 21
};
//-----------------------------------------------------------------------------
// ResolveFilename
// Utility to expand $OS if present
// Returns expanded filename to a file that exists on disk, or NULL otherwise
// vecBonusFiles will contain a list of valid OS files that are on disk but did not
// match the current target platform.
//-----------------------------------------------------------------------------
static const char *ResolveFilename( const char *pszFile, CUtlPathStringHolder *pRetBuf, CUtlVector<CUtlString> &vecBonusFiles )
{
static const int k_lastRealPlatform = 16; // index, not count
static const int k_AnyPlatform = 21; // index
static const int arrPlatformChains[][k_lastRealPlatform + 1] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, // the raw platforms
{ FB_WIN, FB_WIN, FB_OSX, FB_OSX, FB_LIN, FB_LIN, FB_LIN, FB_LIN, FB_ANY, FB_POS, FB_WIN, FB_LIN, FB_LIN, FB_LIN, FB_LIN, FB_LIN, FB_LIN }, // first fallback
{ FB_ANY, FB_ANY, FB_POS, FB_POS, FB_POS, FB_POS, FB_POS, FB_POS, FB_ANY, FB_ANY, FB_ANY, FB_POS, FB_POS, FB_POS, FB_POS, FB_POS, FB_POS }, // 2nd fallback
{ FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY, FB_ANY }, // 3rd fallback
};
// Don't want the actual backing store to be const since we aren't compile-time init'ing,
// but it is single-instance initialized, so make a const ref to it that everyone except
// the init codes uses to make sure it doesn't get mucked with.
static bool bInited = false;
static char _szPlatformStore[128];
static const char (&szPlatform)[128] = _szPlatformStore;
if ( !bInited )
{
bInited = true;
const char *pszPlatform = g_pVPC->GetTargetPlatformName();
V_strncpy( _szPlatformStore, pszPlatform, sizeof( szPlatform ) );
V_strlower( _szPlatformStore );
}
vecBonusFiles.RemoveAll();
int nPlatformColumn = -1;
for ( int i = 0; i <= k_lastRealPlatform; i++ )
{
if ( g_szArrPlatforms[i][0] && V_strcmp( szPlatform, g_szArrPlatforms[i] ) == 0 )
{
nPlatformColumn = i;
break;
}
}
if ( nPlatformColumn < 0 )
{
g_pVPC->VPCWarning( "Internal Error: Target Platform: '%s' unrecognized while expanding $os!", szPlatform );
return NULL;
}
// Now walk the chain of potential platform matches from strongest to weakest
const char *pszResolved = NULL;
int nPlatformToCheck = -1;
int nCurrentPlatformRow = -1;
do
{
nCurrentPlatformRow++;
nPlatformToCheck = arrPlatformChains[nCurrentPlatformRow][nPlatformColumn];
pszResolved = ResolveCandidate( pszFile, g_szArrPlatforms[ nPlatformToCheck ], pRetBuf );
if ( pszResolved )
break;
}
while ( arrPlatformChains[nCurrentPlatformRow][nPlatformColumn] != k_AnyPlatform );
// Now go pickup the any files that exist, but were non-matches
CUtlPathStringHolder nonMatch;
for ( int i = 0; g_szArrPlatforms[i] != NULL; i++ )
{
// Don't pick up the actual found platform
if ( i != nPlatformToCheck )
{
if ( ResolveCandidate( pszFile, g_szArrPlatforms[ i ], &nonMatch ) )
vecBonusFiles.AddToTail( nonMatch.Get() );
}
}
return pszResolved;
}
//-----------------------------------------------------------------------------
// VPC_Keyword_AddFilesByPattern
//
//-----------------------------------------------------------------------------
void VPC_Keyword_AddFilesByPattern()
{
CUtlVector<CUtlString> files;
if ( !g_pVPC->IsFilePatternEnabled() )
{
g_pVPC->VPCSyntaxError( "$FilePattern support not enabled" );
}
// THIS FEATURE IS NOT COMPLIANT TO VPC CONDITIONAL SYNTAX.
while ( 1 )
{
const char *pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
break;
// Is this a conditional expression?
if ( pToken[0] == '[' )
{
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( false );
if ( pNextToken && pNextToken[0] == '[' )
{
g_pVPC->VPCSyntaxError( "Bad conditional syntax. Use C style boolean expression operators to express compound conditionals." );
}
if ( files.Count() == 0 )
{
g_pVPC->VPCSyntaxError( "Conditional specified on a $FilePattern without any pattern preceding it." );
}
if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
{
// we did all that work for no reason, time to bail out
return;
}
}
CUtlStringBuilder *pStrBuf = g_pVPC->GetMacroReplaceBuffer();
g_pVPC->ResolveMacrosInString( pToken, pStrBuf );
V_FixSlashes( pStrBuf->Access() );
CUtlVector< CUtlString > vecResults;
Sys_ExpandFilePattern( pStrBuf->Get(), vecResults );
for ( int i=0; i < vecResults.Count(); i++ )
{
g_pVPC->VPCStatus( false, "glob: adding '%s' to project", vecResults[i].String() );
g_pVPC->GetProjectGenerator()->StartFile( vecResults[i].String(), VPC_FILE_FLAGS_NONE, true );
g_pVPC->GetProjectGenerator()->EndFile();
}
}
}
class CNoNameTranslation
{
public:
void operator()( CUtlStringBuilder *pStrBuf ) {}
};
//-----------------------------------------------------------------------------
// Parse a list of filenames from the current token. Handles conditionals
// and line continuation (via '\' ). The presence of a line suffix conditional will
// cull ONLY that line's file.
//
// Valid syntax formats: (i.e. various combinations of conditionals and continuation)
//
// $PreQualifiedToken "file_a" [$CONDITION]
//
// $PreQualifiedToken "file_a" \
// "file_b" [$CONDITION] \
// "file_c" \
// "file_d" [$CONDITION]
//
//-----------------------------------------------------------------------------
static void VPC_ParseFileList( CUtlVector< CUtlString > &files, void (*pFNNameTranslation)( CUtlStringBuilder *pStrBuf ) = nullptr )
{
bool bFoundFilename = false;
bool bAllowNextLine = false;
while ( 1 )
{
const char *pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
if ( !pToken || !pToken[0] )
g_pVPC->VPCSyntaxError();
// enforcing per-line syntax (VPC's need to stay legible), and not some continuous-same-line cryptic compound sequence
bAllowNextLine = false;
if ( pToken && pToken[0] == '[' )
{
if ( !bFoundFilename )
{
g_pVPC->VPCSyntaxError( "Conditional '%s' specified on a file list without any file preceding it.", pToken );
}
bFoundFilename = false;
// token is an optional conditional
bool bResult = g_pVPC->EvaluateConditionalExpression( pToken );
if ( !bResult )
{
// conditional evaluated false
// cull the just-added file from list
files.Remove( files.Count() - 1 );
}
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( false );
if ( !pNextToken || !pNextToken[0] )
{
// end of tokens, finished
break;
}
else if ( CharStrEq( pNextToken, '\\' ) )
{
// the only allowed token after the conditional is the continuation marker
continue;
}
else
{
g_pVPC->VPCSyntaxError( "Unexpected token '%s' after the conditional '%s'", pNextToken, pToken );
}
}
if ( CharStrEq( pToken, '\\' ) )
{
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( false );
if ( pNextToken && pNextToken[0] )
{
g_pVPC->VPCSyntaxError( "Unexpected token '%s' on same line after the file continuation marker", pNextToken );
}
// parse on next line
bAllowNextLine = true;
continue;
}
CUtlStringBuilder *pStrBuf = g_pVPC->GetMacroReplaceBuffer();
g_pVPC->ResolveMacrosInString( pToken, pStrBuf );
V_FixSlashes( pStrBuf->Access() );
if ( pFNNameTranslation )
{
pFNNameTranslation( pStrBuf );
}
const char *pExtension = V_GetFileExtension( pStrBuf->Get() );
if ( IsCFileExtension( pExtension ) || IsHFileExtension( pExtension ) )
{
char szFilename[MAX_FIXED_PATH];
// Only need to fix c/h files as MSDEV is intolerant of these kinds of files with redundant pathing
// fixup filename with redundant pathing
Sys_ForceToMinimalRelativePath( g_pVPC->GetProjectPath(), pStrBuf->Get(), szFilename, sizeof( szFilename ) );
pStrBuf->Set( szFilename );
}
bFoundFilename = true;
files.AddToTail( pStrBuf->Get() );
// check for another optional token
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( false );
if ( !pNextToken || !pNextToken[0] )
break;
if ( pNextToken[0] == '[' || CharStrEq( pNextToken, '\\' ) )
continue;
g_pVPC->VPCSyntaxError( "Unexpected token '%s' after filename '%s'", pNextToken, pToken );
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_AddFile
//
//-----------------------------------------------------------------------------
void VPC_Keyword_AddFile( VpcFileFlags_t iFileFlags = VPC_FILE_FLAGS_NONE, void (*pFNNameTranslation)( CUtlStringBuilder *pStrBuf ) = nullptr )
{
bool bAddedAsLibrary = (iFileFlags & (VPC_FILE_FLAGS_STATIC_LIB | VPC_FILE_FLAGS_IMPORT_LIB | VPC_FILE_FLAGS_SHARED_LIB)) != 0;
CUtlVector< CUtlString > files;
VPC_ParseFileList( files, pFNNameTranslation );
if (!bAddedAsLibrary)
{
for (int i = 0; i < files.Count(); i++)
{
const char *pFilename = files[i].String();
if (IsLibraryFile(pFilename))
{
iFileFlags = VPC_FILE_FLAGS_STATIC_LIB;
bAddedAsLibrary = true;
break;
}
}
}
// check for optional section
bool bHasSection = false;
const char *pToken = g_pVPC->GetScript().PeekNextToken( true );
if ( pToken && pToken[0] && CharStrEq( pToken, '{' ) )
{
bHasSection = true;
}
//verify that libraries are tagged properly
if ( bAddedAsLibrary )
{
bool bFailLibs = false;
if ( g_pVPC->IsConditionalDefined( "COMPILING_LIB" ) && (iFileFlags & (VPC_FILE_FLAGS_STATIC_LIB | VPC_FILE_FLAGS_IMPORT_LIB)) )
{
bFailLibs = !g_pVPC->IsLibWithinLibEnabled();
}
for ( int i = 0; i < files.Count(); i++ )
{
const char *pFilename = files[i].String();
// Linking one static library into another causes many problems. It leads to binary bloat (the
// same code ends up checked in multiple times), it ends up causing duplicate symbol problems
// that are hard to track down, it makes it hard to figure out which libraries a DLL uses,
// and it is not supported on platforms other than Windows.
if ( bFailLibs )
{
g_pVPC->VPCSyntaxError( "Using $Lib or $ImpLib in a static library project is prohibited (lib used '%s').\nYou can use $LibDependsOn[Imp]Lib if you need something similar, look at lib_depends_source.vpc.\n", pFilename );
}
VPC_AddLibraryDependencies( pFilename );
}
}
else
{
for ( int i = 0; i < files.Count(); i++ )
{
const char *pFilename = files[i].String();
if ( IsLibraryFile( pFilename ) )
{
g_pVPC->VPCError( "Cannot add library \"%s\" using $File tag. Use $Lib, $ImpLib, or $SharedLib to specify how the library is used\n", pFilename );
}
}
}
// Check files is a guard requested to provide an early failure point as programmers were getting tagged later by compilation failures,
// or worse, run-time latent bugs, that could have been prevented at the moment of their sync/vpc.
// Check file existence early to handle a possible rejected section (NOTE: ignore dynamic files)
bool bDynamicFile = !!( iFileFlags & VPC_FILE_FLAGS_DYNAMIC );
if ( g_pVPC->IsCheckFiles() && !bDynamicFile )
{
for ( int i = 0; i < files.Count(); i++ )
{
const char *pFilename = files[i].String();
const char *pExtension = V_GetFileExtensionSafe( pFilename );
// Need to isolate files that have build steps and ignore script or other no-op injected project files.
bool bIsRelevantFile = IsSourceFile( pFilename ) || g_pVPC->m_CustomBuildSteps.HasElement( pExtension );
if ( bIsRelevantFile && !Sys_Exists( pFilename ) && !V_stristr( pFilename, "$os" ) )
{
g_pVPC->VPCWarning( "File '%s' does not exist for project '%s'. (Use $DynamicFile if it is created during the build process.)", pFilename, g_pVPC->GetProjectName() );
g_pVPC->IncrementFileMissing();
// need script stack to assist in tracking down missing file
g_pVPC->GetScript().SpewScriptStack( false );
files.Remove( i );
}
}
}
if ( g_pVPC->IsShowCaseIssues() && !bDynamicFile )
{
for ( int i = 0; i < files.Count(); i++ )
{
const char *pFilename = files[i].String();
char actualFilename[MAX_FIXED_PATH];
if ( !Sys_IsFilenameCaseConsistent( pFilename, actualFilename, sizeof( actualFilename ) ) )
{
g_pVPC->VPCWarning( "Case Consistency Issue! File '%s' specified in '%s' is inconsistent with OS version '%s'.", pFilename, g_pVPC->GetProjectName(), actualFilename );
// need script stack to assist in tracking down missing file
g_pVPC->GetScript().SpewScriptStack( true );
}
}
}
if ( !files.Count() && bHasSection )
{
// optional section has been conditionally removed
g_pVPC->GetScript().SkipBracedSection();
return;
}
if ( bHasSection )
{
// found optional section, parse opening brace
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
g_pVPC->VPCSyntaxError();
}
// Handle $OS expansion
// save parser state
CScriptSource startingScriptSource = g_pVPC->GetScript().GetCurrentScript();
CUtlVector< CUtlString > customFiles;
for ( int i = 0; i < files.Count(); i++ )
{
const char *pFilename = files[i].String();
CUtlPathStringHolder macroFilename;
if ( g_pVPC->IsOSMacroEnabled() )
{
if ( !g_pVPC->m_bIsDependencyPass && V_stristr( pFilename, "$os" ) )
{
CUtlVector< CUtlString > vecExcludedFiles;
const char *pResolvedFilename = ResolveFilename( pFilename, &macroFilename, vecExcludedFiles );
char rgchRejectList[4096];
rgchRejectList[0] = '\0';
if ( vecExcludedFiles.Count() )
{
for ( int j = 0; j < files.Count(); j++ )
{
V_strncat( rgchRejectList, files[j].String(), V_ARRAYSIZE( rgchRejectList ) );
V_strncat( rgchRejectList, ",", V_ARRAYSIZE( rgchRejectList ) );
}
}
g_pVPC->VPCStatus( false, "$OS: Resolved %s -> %s, rejected %s", pFilename, pResolvedFilename ? pResolvedFilename : "<nothing>", rgchRejectList );
if ( !pResolvedFilename )
{
g_pVPC->VPCWarning( "$File %s did not resolve to an existing file, skipping!", pFilename );
continue;
}
for ( i = 0; i < vecExcludedFiles.Count(); i++ )
{
const char *pExcludedFilename = vecExcludedFiles[i].String();
const char *pExcludedExtension = V_GetFileExtension( pExcludedFilename );
if ( !pExcludedExtension )
{
pExcludedExtension = "";
}
if ( pExcludedExtension && !V_stricmp_fast( pExcludedExtension, "cpp" ) )
{
g_pVPC->VPCStatus( false, "Excluding '%s' from build", pExcludedFilename );
g_pVPC->GetProjectGenerator()->StartFile( pExcludedFilename, iFileFlags, true );
CUtlVector< CUtlString > configurationNames;
g_pVPC->GetProjectGenerator()->GetAllConfigurationNames( configurationNames );
for ( int j = 0; j < configurationNames.Count(); j++ )
{
g_pVPC->GetProjectGenerator()->StartConfigurationBlock( configurationNames[j].String(), true );
g_pVPC->GetProjectGenerator()->FileExcludedFromBuild( true );
g_pVPC->GetProjectGenerator()->EndConfigurationBlock();
}
g_pVPC->GetProjectGenerator()->EndFile();
}
}
pFilename = pResolvedFilename;
}
}
bool bAdded = g_pVPC->GetProjectGenerator()->StartFile( pFilename, iFileFlags, !bAddedAsLibrary );
// Lookup extension for a custom build script
const char *pExtension = V_GetFileExtensionSafe( pFilename );
int nBuildStepIndex = g_pVPC->m_CustomBuildSteps.Find( pExtension );
if ( g_pVPC->m_CustomBuildSteps.IsValidIndex( nBuildStepIndex ) )
{
CVPC::CustomBuildStepForExtension_t &buildStepsForExtension = g_pVPC->m_CustomBuildSteps[nBuildStepIndex];
const char *pBuffer = buildStepsForExtension.m_BuildSteps.Get();
// save parser state
g_pVPC->GetScript().PushScript( buildStepsForExtension.m_DefinedInFile.Get(), pBuffer, buildStepsForExtension.m_nDefinitionStartLine, false, true );
// parse injected buildstep
VPC_ParseFileSection();
// restore parser state
g_pVPC->GetScript().PopScript();
g_pVPC->GetScript().RestoreScript( startingScriptSource );
}
const char *pCleanFilename = g_pVPC->GetProjectGenerator()->GetCurrentFileName();
// Apply PCH expansions.
if ( ( iFileFlags & ( VPC_FILE_FLAGS_CREATE_PCH |
VPC_FILE_FLAGS_NO_PCH ) ) != 0 )
{
const char *pScriptName = ( iFileFlags & VPC_FILE_FLAGS_CREATE_PCH ) != 0 ? "$CreatePCH" : "$NoPCH";
int nScriptIndex = g_pVPC->m_CustomAutoScripts.Find( pScriptName );
if ( g_pVPC->m_CustomAutoScripts.IsValidIndex( nScriptIndex ) )
{
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_FILENAME", pCleanFilename, false );
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_BASEFILENAME", V_UnqualifiedFileName( pCleanFilename ), false );
CUtlString scriptName;
scriptName.Format( "Custom Auto Script for '%s'", pScriptName );
g_pVPC->GetScript().PushScript( scriptName.Get(), g_pVPC->m_CustomAutoScripts[nScriptIndex].Get(), 1, false, false );
// We're going to use a per-file compiler option so allow that.
// This will get reverted on RestoreScript.
g_pVPC->GetScript().EnterPrivilegedScript();
// parse injected steps
VPC_ParseFileSection();
// restore parser state
g_pVPC->GetScript().PopScript();
g_pVPC->GetScript().RestoreScript( startingScriptSource );
}
}
// apply optional section to each file
if ( bHasSection && bAdded )
{
// restore parser state
g_pVPC->GetScript().RestoreScript( startingScriptSource );
VPC_ParseFileSection();
}
if ( bAdded )
{
int nAutoScriptIndex = g_pVPC->m_CustomAutoScripts.Find( pExtension );
if ( g_pVPC->m_CustomAutoScripts.IsValidIndex( nAutoScriptIndex ) )
{
// track each custom file that has a custom auto script
customFiles.AddToTail( pCleanFilename );
}
}
VPC_Schema_TrackFile( pCleanFilename, false, iFileFlags );
VPC_Qt_TrackFile( pCleanFilename, false, iFileFlags );
if ( bAdded )
{
g_pVPC->GetProjectGenerator()->EndFile();
}
}
if ( customFiles.Count() )
{
// save parser state
startingScriptSource = g_pVPC->GetScript().GetCurrentScript();
// emit the auto scripts
for ( int i = 0 ; i < customFiles.Count(); i++ )
{
const char *pExtension = V_GetFileExtensionSafe( customFiles[i].Get() );
int nAutoScriptIndex = g_pVPC->m_CustomAutoScripts.Find( pExtension );
if ( g_pVPC->m_CustomAutoScripts.IsValidIndex( nAutoScriptIndex ) )
{
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_FILENAME", customFiles[i].Get(), false );
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_BASEFILENAME", customFiles[i].GetBaseFilename().Get(), false );
CUtlString scriptName;
scriptName.Format( "Custom Auto Script for '%s'", pExtension );
// save parser state
g_pVPC->GetScript().PushScript( scriptName.Get(), g_pVPC->m_CustomAutoScripts[nAutoScriptIndex].Get(), 1, false, false );
// parse injected steps
VPC_HandleProjectCommands( NULL, 1, false );
// restore parser state
g_pVPC->GetScript().PopScript();
g_pVPC->GetScript().RestoreScript( startingScriptSource );
}
}
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_FILENAME", "", false );
g_pVPC->SetSystemMacro( "AUTO_SCRIPT_BASEFILENAME", "", false );
}
}
static const char *VPC_ExpandLibraryName( const char *pName, const char *pDefaultPath, char const *pFileNamePrefix, char const *pSuffix, CUtlPathStringHolder *pFullName )
{
if ( !pFileNamePrefix )
{
pFileNamePrefix = "";
}
// do not add the path prefix if the filename contains path information already, or if null was passed.
CUtlStringBuilder *pFixedName = g_pVPC->GetTempStringBuffer2();
pFixedName->Set( pName );
V_RemoveDotSlashes( pFixedName->Access() );
CUtlStringBuilder *pFormatBuf = g_pVPC->GetTempStringBuffer1();
char *pLastSlash = (char*)MAX( strrchr( pFixedName->Access(), '\\' ), strrchr( pFixedName->Access(), '/' ) );
if ( pLastSlash )
{
*pLastSlash = 0;
const char *pFilenamePart = pLastSlash + 1;
pFormatBuf->Format( "%s/%s%s%s", pFixedName->Get(), pFileNamePrefix, pFilenamePart, pSuffix );
}
else
{
pFormatBuf->Format( "%s%s%s%s", pDefaultPath, pFileNamePrefix, pName, pSuffix );
}
CUtlStringBuilder *pStrBuf = g_pVPC->GetMacroReplaceBuffer();
g_pVPC->ResolveMacrosInString( pFormatBuf->Get(), pStrBuf );
pFullName->Set( pStrBuf->Get() );
pFullName->FixSlashesAndDotSlashes();
return pFullName->Get();
}
static void nameTransformImpLib( CUtlStringBuilder *pStrBuf)
{
CUtlPathStringHolder szFilename1;
VPC_ExpandLibraryName( pStrBuf->Get(), "$LIBPUBLIC\\", "$_IMPLIB_PREFIX", "$_IMPLIB_EXT", &szFilename1 );
pStrBuf->Set( szFilename1.Get() );
}
static void nameTransformImpLibExternal( CUtlStringBuilder *pStrBuf)
{
CUtlPathStringHolder szFilename1;
VPC_ExpandLibraryName( pStrBuf->Get(), "$LIBPUBLIC\\", "$_IMPLIB_PREFIX", "$_EXTERNAL_IMPLIB_EXT", &szFilename1 );
pStrBuf->Set( szFilename1.Get() );
}
static void VPC_Keyword_ImportLibrary( bool bRemove = false, bool bExternal = false )
{
auto nameTransformFunction = bExternal ? nameTransformImpLibExternal : nameTransformImpLib;
if ( !bRemove )
{
VPC_Keyword_AddFile( VPC_FILE_FLAGS_IMPORT_LIB, nameTransformFunction );
}
else
{
VPC_Keyword_RemoveFile( nameTransformFunction );
}
}
static void nameTransformLinkLib( CUtlStringBuilder *pStrBuf )
{
CUtlPathStringHolder szFilename1;
VPC_ExpandLibraryName( pStrBuf->Get(), "$LIBPUBLIC\\", NULL, "$_STATICLIB_EXT", &szFilename1 );
pStrBuf->Set( szFilename1.Get() );
}
static void nameTransformLinkLibExternal( CUtlStringBuilder *pStrBuf )
{
CUtlPathStringHolder szFilename1;
VPC_ExpandLibraryName( pStrBuf->Get(), "$LIBPUBLIC\\", NULL, "$_EXTERNAL_STATICLIB_EXT", &szFilename1 );
pStrBuf->Set( szFilename1.Get() );
}
static void VPC_Keyword_LinkerLibrary( bool bRemove = false, bool bExternal = false )
{
auto nameTransformFunction = bExternal ? nameTransformLinkLibExternal : nameTransformLinkLib;
if ( !bRemove )
{
VPC_Keyword_AddFile( VPC_FILE_FLAGS_STATIC_LIB, nameTransformFunction );
}
else
{
VPC_Keyword_RemoveFile( nameTransformFunction );
}
}
static void VPC_Keyword_SharedLibrary( bool bRemove = false )
{
auto nameTransformFunction = []( CUtlStringBuilder *pStrBuf ) -> void
{
CUtlPathStringHolder szFilename1;
VPC_ExpandLibraryName( pStrBuf->Get(), "$LIBPUBLIC\\", "$_DLL_PREFIX", "$_DLL_EXT", &szFilename1 );
pStrBuf->Set( szFilename1.Get() );
};
if ( !bRemove )
{
VPC_Keyword_AddFile( VPC_FILE_FLAGS_SHARED_LIB, nameTransformFunction );
}
else
{
VPC_Keyword_RemoveFile( nameTransformFunction );
}
}
static void VPC_AddLibraryDependencies( const char *pLibPath )
{
int i = g_pVPC->m_LibraryDependencies.Find( pLibPath );
if ( i == g_pVPC->m_LibraryDependencies.InvalidIndex() )
{
return;
}
CUtlVector< CUtlString > &dependencies = g_pVPC->m_LibraryDependencies[i];
for ( i = 0; i < dependencies.Count(); i++ )
{
const char *pDependency = dependencies[i].Get();
if ( !g_pVPC->GetProjectGenerator()->HasFile( pDependency ) )
{
if ( !g_pVPC->m_bIsDependencyPass )
{
g_pVPC->VPCStatus( false, "$LibDependency: '%s' added for '%s'",
pDependency, pLibPath );
}
bool bAdded = g_pVPC->GetProjectGenerator()->StartFile( pDependency, VPC_FILE_FLAGS_STATIC_LIB, true );
if ( !bAdded )
{
g_pVPC->VPCError( "couldn't add %s", pDependency );
}
g_pVPC->GetProjectGenerator()->EndFile();
VPC_AddLibraryDependencies( pDependency );
}
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_$LibDepends
//-----------------------------------------------------------------------------
static void VPC_LibDepends( char const *pDefaultPath, char const *pFileNamePrefix, char const *pSuffix )
{
const char *pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
g_pVPC->VPCSyntaxError();
// The lib is always a static lib.
CUtlPathStringHolder libName;
VPC_ExpandLibraryName( pToken, "$LIBPUBLIC\\", NULL, "$_STATICLIB_EXT", &libName );
int i = g_pVPC->m_LibraryDependencies.Find( libName );
if ( i == g_pVPC->m_LibraryDependencies.InvalidIndex() )
{
i = g_pVPC->m_LibraryDependencies.Insert( libName );
}
CUtlVector< CUtlString > &dependencies = g_pVPC->m_LibraryDependencies[i];
CUtlVector< CUtlString > dependents;
VPC_ParseFileList( dependents );
if ( !dependents.Count() )
{
return;
}
for ( i = 0; i < dependents.Count(); i++ )
{
CUtlPathStringHolder depName;
VPC_ExpandLibraryName( dependents[i], pDefaultPath, pFileNamePrefix, pSuffix, &depName );
int j;
for ( j = 0; j < dependencies.Count(); j++ )
{
if ( !V_stricmp_fast( depName, dependencies[j] ) )
{
break;
}
}
if ( j >= dependencies.Count() )
{
dependencies.AddToTail( depName.Get() );
}
}
}
void VPC_Keyword_LibDependsOnLib()
{
VPC_LibDepends( "$LIBPUBLIC\\", NULL, "$_STATICLIB_EXT" );
}
void VPC_Keyword_LibDependsOnImpLib()
{
VPC_LibDepends( "$LIBPUBLIC\\", "$_IMPLIB_PREFIX", "$_IMPLIB_EXT" );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_RemoveFile
//
//-----------------------------------------------------------------------------
void VPC_Keyword_RemoveFile( void (*pFNNameTranslation)( CUtlStringBuilder *pStrBuf ) /*= nullptr*/ )
{
CUtlVector< CUtlString > filesToRemove;
VPC_ParseFileList( filesToRemove, pFNNameTranslation );
for ( int i = 0; i < filesToRemove.Count(); i++ )
{
bool bSucc = g_pVPC->GetProjectGenerator()->RemoveFile( filesToRemove[i].Get() );
if ( !bSucc )
{
// VPC_HandleLibraryExpansion() remove ./s so if we initially failed at
// killing this file, remove the ./ and retry.
CUtlString modifiedString = filesToRemove[i].Get();
V_RemoveDotSlashes( modifiedString.GetForModify() );
bSucc = g_pVPC->GetProjectGenerator()->RemoveFile( modifiedString.Get() );
if ( bSucc )
{
filesToRemove[i] = modifiedString;
}
}
if ( !bSucc )
{
g_pVPC->VPCWarning( "Failed to remove file \"%s\" from project \"%s\".",
filesToRemove[i].Get(), g_pVPC->GetProjectName() );
}
VPC_Schema_TrackFile( filesToRemove[i].Get(), true, VPC_FILE_FLAGS_NONE );
VPC_Qt_TrackFile( filesToRemove[i].Get(), true, VPC_FILE_FLAGS_NONE );
}
}
//-----------------------------------------------------------------------------
// VPC_Write_Shader_Folder
//
//-----------------------------------------------------------------------------
void VPC_Write_Shader_Folder( CUtlBuffer &vpcBuffer, const CUtlVector< CUtlString > &files, const char *pFolderName )
{
if ( files.Count() )
{
vpcBuffer.Printf( "$Folder \"%s\" \n", pFolderName );
vpcBuffer.Printf( "{\n" );
for ( int i=0; i<files.Count(); i++ )
{
vpcBuffer.Printf( "$file \"%s\"\n", files[i].String() );
}
vpcBuffer.Printf( "}\n" );
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_Shaders
//
//-----------------------------------------------------------------------------
void VPC_Keyword_Shaders( int depth )
{
// TODO: turn this into a 'feature' ala qt/schema
const char *pToken;
CUtlBuffer vpcBuffer;
CUtlVector< CUtlString > fxcList;
CUtlVector< CUtlString > vshList;
CUtlVector< CUtlString > pshList;
CUtlVector< CUtlString > vfxList;
CUtlVector< CUtlString > otherList;
bool bIgnoreRedundancyWarning;
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
return;
}
CUtlStringHolder<100> shadersName( pStrBuf->Get() );
g_pVPC->VPCStatus( false, "Parsing: %s", shadersName.Get() );
g_pVPC->GetScript().PushScript( shadersName, true );
// parse the shader list file into types (fxc,vsh,psh)
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
{
// end of file
break;
}
// Make token relative to location of script file by prepending the relative path of the script to the token
CUtlPathStringHolder relativePath( shadersName );
relativePath.StripLastDir();
relativePath.Append( pToken );
if ( V_stristr( relativePath.Get(), ".fxc" ) )
{
fxcList.AddToTail( relativePath.Get() );
}
else if ( V_stristr( relativePath.Get(), ".vsh" ) )
{
vshList.AddToTail( relativePath.Get() );
}
else if ( V_stristr( relativePath.Get(), ".psh" ) )
{
pshList.AddToTail( relativePath.Get() );
}
else if ( V_stristr( relativePath.Get(), ".vfx" ) )
{
vfxList.AddToTail( relativePath.Get() );
}
else
{
otherList.AddToTail( relativePath.Get() );
}
}
g_pVPC->GetScript().PopScript();
if ( !fxcList.Count() &&
!vshList.Count() &&
!pshList.Count() &&
!vfxList.Count() &&
!otherList.Count() )
{
//g_pVPC->VPCWarning( "No shaders found in %s", shadersName );
return;
}
// generate a vpc compatible file to generate the shader file hierarchy
vpcBuffer.SetBufferType( true, true );
vpcBuffer.Printf( "$Folder \"Shader Source\" \n" );
vpcBuffer.Printf( "{\n" );
{
// make one subfolder for each type of file:
VPC_Write_Shader_Folder( vpcBuffer, fxcList, "fxc" );
VPC_Write_Shader_Folder( vpcBuffer, vshList, "vsh" );
VPC_Write_Shader_Folder( vpcBuffer, pshList, "psh" );
VPC_Write_Shader_Folder( vpcBuffer, vfxList, "vfx" );
VPC_Write_Shader_Folder( vpcBuffer, otherList, "other" );
// add the shaders file as a convienence
vpcBuffer.Printf( "$file \"%s\"\n", shadersName.Get() );
}
vpcBuffer.Printf( "}\n" );
// save parser
bIgnoreRedundancyWarning = g_pVPC->IsIgnoreRedundancyWarning();
g_pVPC->SetIgnoreRedundancyWarning( true );
g_pVPC->GetScript().PushScript( "Internal List", (char*)vpcBuffer.Base(), 1, false, false );
pToken = g_pVPC->GetScript().GetToken( true );
if ( pToken && pToken[0] && !V_stricmp_fast( pToken, "$folder" ) )
{
VPC_Keyword_Folder( VPC_FOLDER_FLAGS_DYNAMIC );
}
// restore parser
g_pVPC->GetScript().PopScript();
g_pVPC->SetIgnoreRedundancyWarning( bIgnoreRedundancyWarning );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_Folder
//
//-----------------------------------------------------------------------------
void VPC_Keyword_Folder( VpcFolderFlags_t iFolderFlags )
{
const char *pToken;
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
g_pVPC->GetScript().SkipBracedSection();
return;
}
g_pVPC->GetProjectGenerator()->StartFolder( pStrBuf->Get(), iFolderFlags );
// Now parse all the files and subfolders..
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
g_pVPC->VPCSyntaxError();
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
break;
if ( CharStrEq( pToken, '}' ) )
{
// pop
break;
}
else if ( !V_stricmp_fast( pToken, "$file" ) )
{
// add file
VPC_Keyword_AddFile();
}
else if ( !V_stricmp_fast( pToken, "$File_CreatePCH" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_CREATE_PCH );
}
else if ( !V_stricmp_fast( pToken, "$File_NoPCH" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_NO_PCH );
}
else if ( !V_stricmp_fast( pToken, "$DynamicFile" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_DYNAMIC );
}
else if ( !V_stricmp_fast( pToken, "$DynamicFile_NoPCH" ) )
{
// add file
VPC_Keyword_AddFile( (VpcFileFlags_t)( VPC_FILE_FLAGS_DYNAMIC | VPC_FILE_FLAGS_NO_PCH ) );
}
else if ( !V_stricmp_fast( pToken, "$FilePattern" ) )
{
// glob the given pattern, add all files
VPC_Keyword_AddFilesByPattern();
}
else if ( !V_stricmp_fast( pToken, "$qtfile" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_QT );
}
else if ( !V_stricmp_fast( pToken, "$qtschemafile" ) )
{
// add file
VPC_Keyword_AddFile( (VpcFileFlags_t)( VPC_FILE_FLAGS_QT | VPC_FILE_FLAGS_SCHEMA ) );
}
else if ( !V_stricmp_fast( pToken, "$schemafile" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_SCHEMA );
}
else if ( !V_stricmp_fast( pToken, "$SchemaIncludeFile" ) )
{
// add file
VPC_Keyword_AddFile( VPC_FILE_FLAGS_SCHEMA_INCLUDE );
}
else if ( !V_stricmp_fast( pToken, "$implib" ) )
{
// add file
VPC_Keyword_ImportLibrary();
}
else if (!V_stricmp_fast(pToken, "$implibexternal"))
{
// add file
VPC_Keyword_ImportLibrary(false, true);
}
else if ( !V_stricmp_fast( pToken, "-$implib" ) )
{
// remove file
VPC_Keyword_ImportLibrary( true );
}
else if ( !V_stricmp_fast( pToken, "$libexternal" ) )
{
// add file
VPC_Keyword_LinkerLibrary(false, true);
}
else if ( !V_stricmp_fast( pToken, "$lib" ) )
{
// add file
VPC_Keyword_LinkerLibrary();
}
else if ( !V_stricmp_fast( pToken, "-$lib" ) )
{
// remove file
VPC_Keyword_LinkerLibrary( true );
}
else if ( !V_stricmp_fast( pToken, "$SharedLib" ) )
{
// add file
VPC_Keyword_SharedLibrary();
}
else if ( !V_stricmp_fast( pToken, "-$SharedLib" ) )
{
// remove file
VPC_Keyword_SharedLibrary( true );
}
else if ( !V_stricmp_fast( pToken, "-$file" ) )
{
// remove file
VPC_Keyword_RemoveFile();
}
else if ( !V_stricmp_fast( pToken, "$Shaders" ) )
{
// add contained shaders folder
VPC_Keyword_Shaders( 0 );
}
else if ( !V_stricmp_fast( pToken, "$folder" ) )
{
// descend into subdirectory
VPC_Keyword_Folder();
}
else if ( !V_stricmp_fast( pToken, "$unityfolder" ) )
{
// descend into subdirectory (and build unity files)
VPC_Keyword_Folder( VPC_FOLDER_FLAGS_UNITY );
}
else
{
g_pVPC->VPCSyntaxError("Unrecognized token: %s", pToken);
}
}
g_pVPC->GetProjectGenerator()->EndFolder();
}
//-----------------------------------------------------------------------------
// VPC_Keyword_Macro
//
//-----------------------------------------------------------------------------
enum MacroType_t { VPC_MACRO_VALUE, VPC_MACRO_EMPTY_STRING };
void VPC_Keyword_Macro( MacroType_t eMacroType )
{
const char *pToken;
pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
g_pVPC->VPCSyntaxError();
CUtlStringHolder<MAX_MACRO_NAME> macroName( pToken );
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
return;
}
CUtlStringHolder<100> value( pStrBuf->Get() );
const char *pValue = Sys_EvaluateEnvironmentExpression( value, "" );
if ( !pValue )
{
pValue = value;
}
g_pVPC->SetScriptMacro( macroName, ( eMacroType == VPC_MACRO_VALUE ) ? pValue : "" );
}
//-----------------------------------------------------------------------------
// $MacroRequired <Macro> [DefaultValue] [Condition]
// $MacroRequiredAllowEmpty <Macro> [DefaultValue] [Condition]
//
// Forces a script to error if a macro that it depends on was not set.
// The Default will be used if the macro was not defined, otherwise error.
// This is to allow a required macro in a base script to have a concept
// of a default initialization value.
//-----------------------------------------------------------------------------
enum MacroRequiredType_t { VPC_MACRO_REQUIRED_NOT_EMPTY, VPC_MACRO_REQUIRED_ALLOW_EMPTY };
void VPC_Keyword_MacroRequired( MacroRequiredType_t eMacroRequiredType )
{
const char *pToken;
CUtlStringHolder<100> macroDefaultValue;
pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
{
g_pVPC->VPCSyntaxError();
}
CUtlStringHolder<MAX_MACRO_NAME> macroName( pToken );
// optional default macro value or conditional
pToken = g_pVPC->GetScript().PeekNextToken( false );
if ( pToken && pToken[0] )
{
if ( pToken[0] == '[' )
{
pToken = g_pVPC->GetScript().GetToken( false );
// evaluate argument as conditional
if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
{
return;
}
}
else
{
// argument is a default macro value
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
return;
}
macroDefaultValue.Set( pStrBuf->Get() );
}
}
// find macro
CMacro *pMacro = g_pVPC->FindMacro( macroName );
if ( pMacro && pMacro->IsPropertyMacro() )
{
// property macros which are constrained to be within a configuration are not interchangeable with normal macros
g_pVPC->VPCError( "Macro '%s' already defined as a property macro.", macroName.Get() );
}
if ( !pMacro || ( eMacroRequiredType == VPC_MACRO_REQUIRED_NOT_EMPTY && !pMacro->HasValue() ) )
{
if ( macroDefaultValue[0] || ( eMacroRequiredType == VPC_MACRO_REQUIRED_ALLOW_EMPTY ) )
{
g_pVPC->SetScriptMacro( macroName, macroDefaultValue );
}
else
{
// In case we're in mksln showing a pacifier of dots. Make sure to show the error on a new line.
g_pVPC->VPCSyntaxError( "\n\nRequired Macro '%s', not defined or empty", macroName.Get() );
}
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_LoadAddressMacro
//
// $LoadAddressMacro <MacroName>
// {
// <ProjectName> <BaseAddress>
// }
//
// Specialized instruction to populate the load address macro based on a project
// name.
//-----------------------------------------------------------------------------
void VPC_Keyword_LoadAddressMacro( void )
{
const char *pToken;
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
g_pVPC->GetScript().SkipBracedSection();
return;
}
CUtlStringHolder<50> szMacroName( pStrBuf->Get() );
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
{
g_pVPC->VPCSyntaxError();
}
CUtlStringHolder<100> szProjectName;
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
{
break;
}
if ( CharStrEq( pToken, '}' ) )
{
break;
}
else
{
szProjectName.Set( pToken );
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
continue;
}
if ( !V_stricmp_fast( szProjectName, g_pVPC->GetLoadAddressName() ) )
{
// set Macro
g_pVPC->SetScriptMacro( szMacroName, pStrBuf->Get() );
}
}
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_LoadAddressMacroAlias
//
// $LoadAddressMacroAlias <Alias>
// {
// <ProjectName>
// }
//
// When evaluating $LoadAddressMacro/$LoadAddressMacroAuto, substitute all listed <ProjectName> entries with <Alias>
//-----------------------------------------------------------------------------
void VPC_Keyword_LoadAddressMacroAlias( void )
{
const char *pToken;
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
g_pVPC->GetScript().SkipBracedSection();
return;
}
CUtlStringHolder<50> szAlias( pStrBuf->Get() );
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
{
g_pVPC->VPCSyntaxError();
}
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
{
break;
}
if ( CharStrEq( pToken, '}' ) )
{
break;
}
else
{
if ( !V_stricmp_fast( pToken, g_pVPC->GetProjectName() ) )
{
// set Macro and alias
g_pVPC->SetScriptMacro( "LOADADDRESSNAME", szAlias );
g_pVPC->SetLoadAddressName( szAlias );
}
}
}
}
//-----------------------------------------------------------------------------
// Internal_LoadAddressMacroAuto
//
// bPad - Differentiate between $LoadAddressMacroAuto and $LoadAddressMacroAuto_Padded
// implementations
//
// Specialized instruction to populate the load address macro based on a project
// name.
//-----------------------------------------------------------------------------
void Internal_LoadAddressMacroAuto( bool bPad )
{
const char *pToken;
pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
{
g_pVPC->VPCSyntaxError();
}
CUtlStringHolder<MAX_MACRO_NAME> szMacroName( pToken );
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
g_pVPC->GetScript().SkipBracedSection();
return;
}
uint64 baseAddress = 0;
sscanf( pStrBuf->Get(), "%llx", &baseAddress );
uint64 iInitialBaseAddress = baseAddress;
int iSetEntryNum = 0;
uint64 iSetBaseAddress = 0;
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
{
g_pVPC->VPCSyntaxError();
}
int iEntryNum = 0;
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
{
break;
}
if ( !V_stricmp_fast( pToken, g_pVPC->GetLoadAddressName() ) )
{
// set Macro
char szMacroValue[100];
sprintf( szMacroValue, "0x%8.8llx", baseAddress );
iSetEntryNum = iEntryNum;
iSetBaseAddress = baseAddress;
g_pVPC->SetScriptMacro( szMacroName, szMacroValue );
}
if ( CharStrEq( pToken, '}' ) )
{
break;
}
else
{
unsigned int dllLength = 0;
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
continue;
}
if ( strstr( pStrBuf->Get(), "." ) )
{
// assume float format
float fLength = 0;
sscanf( pStrBuf->Get(), "%f", &fLength );
dllLength = fLength * 1024.0f * 1024.0f;
}
else
{
sscanf( pStrBuf->Get(), "%d", &dllLength );
}
if ( !bPad )
{
dllLength = AlignValue( dllLength, 64 * 1024 ); //will align later when we actually set the darn thing
}
if ( dllLength == 0 )
{
g_pVPC->VPCSyntaxError( "$LoadAddressMacroAuto no longer supports 0 size dlls. Use $LoadAddressMacroAlias to have two orthogonal projects load in the same space" );
}
baseAddress += dllLength;
}
++iEntryNum;
}
if ( bPad )
{
unsigned int iEndAddress;
if ( (iInitialBaseAddress >= 0x82000000) && (iInitialBaseAddress < 0x8C000000) )
{
iEndAddress = 0x8BFFFFFF;
}
else
{
iEndAddress = 0x9BFFFFFF;
}
// compute leftover unused address space
unsigned int iRemainingSpace = iEndAddress - baseAddress;
int iPadPerEntry = iRemainingSpace / iEntryNum;
//iPadPerEntry = iPadPerEntry & ~(64 * 1024); //align DOWN to 64k
if ( iPadPerEntry > 0 )
{
// set the base address again with padding added
iSetBaseAddress += iPadPerEntry * iSetEntryNum;
iSetBaseAddress = AlignValue( iSetBaseAddress, 64 * 1024 );
char szMacroValue[100];
sprintf( szMacroValue, "0x%8.8llx", iSetBaseAddress );
g_pVPC->SetScriptMacro( szMacroName, szMacroValue );
}
}
}
//-----------------------------------------------------------------------------
// VPC_Keyword_LoadAddressMacroAuto
//
// $LoadAddressMacroAuto <MacroName> <BaseAddress>
// {
// <ProjectName> <Length>
// }
//
// Specialized instruction to populate the load address macro based on a project
// name.
//-----------------------------------------------------------------------------
void VPC_Keyword_LoadAddressMacroAuto( void )
{
Internal_LoadAddressMacroAuto( false );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_LoadAddressMacroAuto_Padded
//
// $LoadAddressMacroAuto_Padded <MacroName> <BaseAddress>
// {
// <ProjectName> <Length>
// }
//
// Specialized instruction to populate the load address macro based on a project
// name. Assumes the contained list is minimally packed and has free reign of
// space up to the limit. Finds unused space spreads it out evenly between
// each project
//-----------------------------------------------------------------------------
void VPC_Keyword_LoadAddressMacroAuto_Padded( void )
{
Internal_LoadAddressMacroAuto( true );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void VPC_Keyword_Conditional( bool bOverrideReserved )
{
const char *pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
g_pVPC->VPCSyntaxError();
if ( pToken[0] == '$' )
{
// being nice to users, quietly remove the unwanted conditional prefix '$'
pToken++;
}
CUtlStringHolder<50> name( pToken );
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
return;
}
CUtlStringHolder<100> value( pStrBuf->Get() );
conditional_t *pConditional = g_pVPC->FindOrCreateConditional( name, true, CONDITIONAL_SCRIPT );
if ( !bOverrideReserved &&
( pConditional->m_Type != CONDITIONAL_CUSTOM && pConditional->m_Type != CONDITIONAL_SCRIPT ) )
{
// vpc scripts can only affect custom or script conditionals
// scripts cannot affect the more systemic global conditionals
g_pVPC->VPCSyntaxError( "$Conditional cannot be used on the reserved '$%s'", pConditional->m_UpperCaseName.Get() );
}
const char *pValue = Sys_EvaluateEnvironmentExpression( value, "0" );
if ( !pValue )
{
pValue = value;
}
// conditional has been pre-qualified, set accordingly
g_pVPC->SetConditional( name, Sys_StringToBool( pValue ), pConditional->m_Type );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_IgnoreRedundancyWarning
//
//-----------------------------------------------------------------------------
void VPC_Keyword_IgnoreRedundancyWarning( void )
{
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
return;
}
bool bVal = Sys_StringToBool( pStrBuf->Get() );
g_pVPC->SetIgnoreRedundancyWarning( bVal );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_Linux
//
//-----------------------------------------------------------------------------
void VPC_Keyword_Linux( void )
{
// always ignore everything in this block
// parsed and processed by a different tool
g_pVPC->GetScript().SkipBracedSection();
}
//-----------------------------------------------------------------------------
// VPC_PrepareToReadScript
//
//-----------------------------------------------------------------------------
void VPC_PrepareToReadScript( const char *pInputScriptName, int depth, bool bQuiet, char* &pScriptBuffer, CUtlString *pFixedScriptName )
{
pFixedScriptName->Set( pInputScriptName );
pFixedScriptName->FixSlashes();
// always spew the root script
if ( !bQuiet )
{
bool bSpew = ( depth == 0 );
g_pVPC->VPCStatus( bSpew, "Parsing: %s", pFixedScriptName->Get() );
}
// parse the text script
g_pVPC->GetScript().PushScript( pFixedScriptName->Get(), true );
}
void VPC_HandleIncludeStatement( int depth, bool bQuiet, void (*CallbackFn)( const char *pScriptName, int depth, bool bQuiet ) )
{
CUtlStringBuilder *pStrBuf = g_pVPC->GetPropertyValueBuffer();
if ( g_pVPC->GetScript().ParsePropertyValue( NULL, pStrBuf ) )
{
// recurse into and run
CUtlString pScriptName;
char *pScriptBuffer;
VPC_PrepareToReadScript( pStrBuf->Get(), depth+1, bQuiet, pScriptBuffer, &pScriptName );
g_pVPC->AddScriptToParsedList( pStrBuf->Get(), false );
CallbackFn( pScriptName, depth + 1, bQuiet );
// restore state
g_pVPC->GetScript().PopScript();
}
}
void VPC_HandleProjectCommands( const char *pUnusedScriptName, int depth, bool bQuiet )
{
const char *pToken;
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
break;
if ( CharStrEq( pToken, '}' ) )
{
break;
}
else if ( !V_stricmp_fast( pToken, "$include" ) )
{
VPC_HandleIncludeStatement( depth, bQuiet, VPC_HandleProjectCommands );
}
else if ( !V_stricmp_fast( pToken, "$Folder" ) ||
!V_stricmp_fast( pToken, "$ProjectOrFolder" ) )
{
// root level folder
VPC_Keyword_Folder();
}
else if ( !V_stricmp_fast( pToken, "$UnityFolder" ) )
{
// root level folder
VPC_Keyword_Folder( VPC_FOLDER_FLAGS_UNITY );
}
else if ( !V_stricmp_fast( pToken, "$File" ) )
{
// add root level file
VPC_Keyword_AddFile();
}
else if ( !V_stricmp_fast( pToken, "-$File" ) )
{
// remove root level file
VPC_Keyword_RemoveFile();
}
else if ( !V_stricmp_fast( pToken, "$Shaders" ) )
{
// add root level shaders folder
VPC_Keyword_Shaders( 0 );
}
else
{
g_pVPC->VPCSyntaxError();
}
}
}
const char *GetVPCExecuteableCRC( CRC32_t *pCRC )
{
static char vpcExePath[MAX_FIXED_PATH] = { 0 };
static CRC32_t nCRCFromFileContents = 0;
static bool bCalculated = false;
// only need to do this sill VPC exe CRC calc once, and re-use
if ( !bCalculated )
{
bCalculated = true;
if ( Sys_GetExecutablePath( vpcExePath, sizeof( vpcExePath ) ) )
{
char *pBuffer = NULL;
int cbVPCExe = Sys_LoadFile( vpcExePath, (void**)&pBuffer );
if ( cbVPCExe != - 1 )
{
// Calculate the CRC from the contents of the file.
nCRCFromFileContents = CRC32_ProcessSingleBuffer( pBuffer, cbVPCExe );
}
delete [] pBuffer;
}
}
*pCRC = nCRCFromFileContents;
return vpcExePath;
}
void WriteCRCCheckFile( const char *pVCProjFilename )
{
CUtlStringBuilder *pFormatBuf = g_pVPC->GetTempStringBuffer1();
pFormatBuf->Format( "%s." VPCCRCCHECK_FILE_EXTENSION, pVCProjFilename );
char szVCProjBasePath[MAX_PATH];
{
char szProjRelativeDir[MAX_PATH];
V_strncpy( szProjRelativeDir, pVCProjFilename, ARRAYSIZE( szProjRelativeDir ) );
V_StripFilename( szProjRelativeDir );
V_MakeAbsolutePath( szVCProjBasePath, ARRAYSIZE( szVCProjBasePath ), szProjRelativeDir, nullptr, k_bVPCForceLowerCase );
}
FILE *fp = fopen( pFormatBuf->Get(), "wt" );
if ( !fp )
{
g_pVPC->VPCError( "Unable to open %s to write CRCs into.", pFormatBuf->Get() );
}
fprintf( fp, "%s\n", VPCCRCCHECK_FILE_VERSION_STRING );
// add the supplemental string crc
fprintf( fp, "%s\n", g_pVPC->GetCRCString() );
// add metadata for the VS add-in
fprintf( fp, "%s\n", g_pVPC->GetVSAddInMetadataString() );
// Embedding the exe's CRC is not desired in branches that do rebuilds
if ( g_pVPC->AddExecuteableToCRCChecks() )
{
// add the executable crc
CRC32_t nCRCFromFileContents;
const char *pExePath = GetVPCExecuteableCRC( &nCRCFromFileContents );
fprintf( fp, "BIN %8.8x %s\n", ( unsigned int ) nCRCFromFileContents, pExePath );
}
CUtlDict<int, int> filenameDict( k_eDictCompareTypeFilenames );
for ( int i=0; i < g_pVPC->m_ScriptList.Count(); i++ )
{
scriptList_t *pScript = &g_pVPC->m_ScriptList[i];
if ( !pScript->m_bCRCCheck )
continue;
char szRelativeScriptPath[MAX_PATH];
if ( V_IsAbsolutePath( pScript->m_scriptName ) )
{
V_MakeRelativePath( pScript->m_scriptName, szVCProjBasePath, szRelativeScriptPath, ARRAYSIZE( szRelativeScriptPath ) );
}
else
{
V_strncpy( szRelativeScriptPath, pScript->m_scriptName, ARRAYSIZE( szRelativeScriptPath ) );
}
V_RemoveDotSlashes( szRelativeScriptPath );
// Use the dictionary to prevent duplicate file CRCs being written in here.
if ( filenameDict.Find( szRelativeScriptPath ) == filenameDict.InvalidIndex() )
{
filenameDict.Insert( szRelativeScriptPath, 1 );
// [crc] [filename]
fprintf( fp, "%8.8x %s\n", ( unsigned int ) pScript->m_crc, szRelativeScriptPath );
}
}
fclose( fp );
}
//-----------------------------------------------------------------------------
// VPC_Keyword_Project
//
//-----------------------------------------------------------------------------
void VPC_Keyword_Project( int depth, bool bQuiet )
{
//determine project generator before any generator-dependent configuration is allowed
g_pVPC->DetermineProjectGenerator();
CUtlString projectName;
g_pVPC->m_bInProjectSection = true;
// check for optional project name
const char *pToken = g_pVPC->GetScript().PeekNextToken( false );
if ( pToken && pToken[0] && !CharStrEq( pToken, '{' ) )
{
// get optional project name
pToken = g_pVPC->GetScript().GetToken( false );
if ( !pToken || !pToken[0] )
{
g_pVPC->VPCSyntaxError();
}
CUtlStringBuilder *pStrBuf = g_pVPC->GetMacroReplaceBuffer();
g_pVPC->ResolveMacrosInString( pToken, pStrBuf );
projectName = pStrBuf->Get();
g_pVPC->DecorateProjectName( projectName );
}
else
{
projectName = g_pVPC->GetProjectGenerator()->GetProjectName();
}
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || !CharStrEq( pToken, '{' ) )
g_pVPC->VPCSyntaxError();
VPC_HandleProjectCommands( NULL, depth, bQuiet );
// the unnamed project does not get written, once it is named it will be written on closing scope
if ( V_stricmp_fast( projectName.Get(), "UNNAMED" ) )
{
VPC_Schema_ForceAdditionalDependencies( projectName.Get() );
IBaseProjectGenerator *pProjGen = g_pVPC->GetProjectGenerator();
//write out the "VPC Scripts" folder
{
pProjGen->StartFolder( "VPC Scripts", VPC_FOLDER_FLAGS_DYNAMIC );
for ( int i = 0; i < g_pVPC->m_ScriptList.Count(); ++i )
{
if ( g_pVPC->m_ScriptList[i].m_bCRCCheck )
{
pProjGen->StartFile( g_pVPC->m_ScriptList[i].m_scriptName, VPC_FILE_FLAGS_NONE, false );
pProjGen->EndFile();
}
}
pProjGen->EndFolder();
}
// change name
pProjGen->SetProjectName( projectName.Get() );
pProjGen->EndProject( true );
g_pVPC->GetSolutionGenerator()->ProjectEnd( pProjGen );
g_pVPC->m_bGeneratedProject = true;
}
g_pVPC->m_bInProjectSection = false;
}
bool VPC_IsBuiltInFileType( const char *pExtension )
{
return ( IsCFileExtension( pExtension ) || IsHFileExtension( pExtension ) );
}
void VPC_Keyword_CustomBuildStep( void )
{
bool bAllowNextLine = false;
CUtlVector<CUtlString> extensions;
const char *pToken = NULL;
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
if ( !pToken || !pToken[0] )
break;
// Is this a conditional expression?
if ( pToken[0] == '[' )
{
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
if ( pNextToken && pNextToken[0] == '[' )
{
g_pVPC->VPCSyntaxError( "Bad conditional syntax. Use C style boolean expression operators to express compound conditionals." );
}
if ( extensions.Count() == 0 )
{
g_pVPC->VPCSyntaxError( "Conditional specified on a $CustomBuildStep without any extensions preceding it." );
}
if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
{
extensions.Remove( extensions.Count() - 1 );
}
continue;
}
if ( CharStrEq( pToken, '\\' ) )
{
bAllowNextLine = true;
continue;
}
else
{
bAllowNextLine = false;
}
if ( VPC_IsBuiltInFileType( pToken ) )
{
g_pVPC->VPCSyntaxError( "Cannot define a $CustomBuildStep for built in file type: %s", pToken);
}
CUtlString string = pToken;
extensions.AddToTail( string );
// check for another token
pToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
if ( !pToken || !pToken[0] )
break;
}
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || V_strcmp( pToken, "{" ) )
{
g_pVPC->VPCSyntaxError( "Missing section for $CustomBuildStep" );
}
else if ( extensions.Count() == 0 )
{
g_pVPC->GetScript().SkipBracedSection();
return;
}
else
{
int nLineSave = g_pVPC->GetScript().GetLine();
const char *pScriptSave = g_pVPC->GetScript().GetData();
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
break;
if ( CharStrEq( pToken, '}' ) )
{
// end of section
break;
}
}
CVPC::CustomBuildStepForExtension_t customBuildStep;
if ( g_pVPC->GetScript().GetData() > pScriptSave )
{
CUtlString tempString;
tempString.SetDirect( pScriptSave, int( g_pVPC->GetScript().GetData() - pScriptSave - 1 ) );
customBuildStep.m_BuildSteps = "$Configuration\n{\n$CustomBuildStep\n{";
customBuildStep.m_BuildSteps += tempString + "}\n}\n";
}
if ( !customBuildStep.m_BuildSteps.IsEmpty() )
{
FOR_EACH_VEC( extensions, i )
{
if ( g_pVPC->m_CustomBuildSteps.Find( extensions[i].Get() ) != g_pVPC->m_CustomBuildSteps.InvalidIndex() )
{
g_pVPC->VPCWarning( "Duplicate $CustomBuildStep For '%s' - Ignoring.", extensions[i].Get() );
}
else
{
customBuildStep.m_DefinedInFile = g_pVPC->GetScript().GetName();
customBuildStep.m_nDefinitionStartLine = nLineSave - 3; //-3 because we prepend 3 lines into customBuildStep.m_BuildSteps above
g_pVPC->m_CustomBuildSteps.Insert( extensions[i].Get(), customBuildStep );
}
}
}
}
}
void VPC_Keyword_CustomAutoScript()
{
bool bAllowNextLine = false;
CUtlVector<CUtlString> extensions;
const char *pToken = NULL;
while ( 1 )
{
pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
if ( !pToken || !pToken[0] )
break;
// Is this a conditional expression?
if ( pToken[0] == '[' )
{
const char *pNextToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
if ( pNextToken && pNextToken[0] == '[' )
{
g_pVPC->VPCSyntaxError( "Bad conditional syntax. Use C style boolean expression operators to express compound conditionals." );
}
if ( extensions.Count() == 0 )
{
g_pVPC->VPCSyntaxError( "Conditional specified on a $CustomAutoScript without any extensions preceding it." );
}
if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
{
extensions.Remove( extensions.Count() - 1 );
}
continue;
}
bAllowNextLine = CharStrEq( pToken, '\\' ) ;
if ( bAllowNextLine )
continue;
if ( VPC_IsBuiltInFileType( pToken ) )
{
g_pVPC->VPCSyntaxError( "Cannot define a $CustomAutoScript for built in file type: %s", pToken);
}
CUtlString string = pToken;
extensions.AddToTail( string );
// check for another token
pToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
if ( !pToken || !pToken[0] )
break;
}
pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] || V_strcmp( pToken, "{" ) )
{
g_pVPC->VPCSyntaxError( "Missing section for $CustomAutoScript" );
}
else if ( extensions.Count() == 0 )
{
g_pVPC->GetScript().SkipBracedSection( 1 );
return;
}
else
{
const char *pScriptSave = g_pVPC->GetScript().GetData();
g_pVPC->GetScript().SkipBracedSection( 1 );
CUtlString autoScriptString;
if ( g_pVPC->GetScript().GetData() > pScriptSave )
{
CUtlString tempString;
tempString.SetDirect( pScriptSave, int( g_pVPC->GetScript().GetData() - pScriptSave - 1 ) );
autoScriptString = tempString;
}
if ( !autoScriptString.IsEmpty() )
{
FOR_EACH_VEC( extensions, i )
{
if ( g_pVPC->m_CustomAutoScripts.Find( extensions[i].Get() ) != g_pVPC->m_CustomAutoScripts.InvalidIndex() )
{
g_pVPC->VPCWarning( "Duplicate $CustomAutoScript For '%s' - Ignoring.", extensions[i].Get() );
}
else
{
g_pVPC->m_CustomAutoScripts.Insert( extensions[i].Get(), autoScriptString );
}
}
}
}
}
void VPC_ParseProjectScriptParameters( const char *szScriptName, int depth, bool bQuiet )
{
while ( 1 )
{
const char *pToken = g_pVPC->GetScript().GetToken( true );
if ( !pToken || !pToken[0] )
{
// end of file
break;
}
if ( !V_stricmp_fast( pToken, "$Include" ) )
{
VPC_HandleIncludeStatement( depth, bQuiet, VPC_ParseProjectScriptParameters );
}
else if ( !V_stricmp_fast( pToken, "$Configuration" ) )
{
VPC_Keyword_Configuration();
}
else if ( !V_stricmp_fast( pToken, "$Project" ) )
{
Assert( depth || !g_pVPC->m_bProjectUsesUnity ); // Should already have been set up by CVPC::IsProjectUsingUnity()
VPC_Keyword_Project( depth, bQuiet );
}
else if ( !V_stricmp_fast( pToken, "$UnityProject" ) ||
!V_stricmp_fast( pToken, "$ProjectOrFolder" ) )
{
Assert( depth || g_pVPC->m_bProjectUsesUnity ); // Should already have been set up by CVPC::IsProjectUsingUnity()
VPC_Keyword_Project( depth, bQuiet );
}
else if ( !V_stricmp_fast( pToken, "$Macro" ) )
{
VPC_Keyword_Macro( VPC_MACRO_VALUE );
}
else if (!V_stricmp_fast(pToken, "$MacroEmptyString"))
{
VPC_Keyword_Macro(VPC_MACRO_EMPTY_STRING);
}
else if ( !V_stricmp_fast( pToken, "$MacroRequired" ) )
{
VPC_Keyword_MacroRequired( VPC_MACRO_REQUIRED_NOT_EMPTY );
}
else if ( !V_stricmp_fast( pToken, "$MacroRequiredAllowEmpty" ) )
{
VPC_Keyword_MacroRequired( VPC_MACRO_REQUIRED_ALLOW_EMPTY );
}
else if ( !V_stricmp_fast( pToken, "$LoadAddressMacro" ) )
{
VPC_Keyword_LoadAddressMacro();
}
else if ( !V_stricmp_fast( pToken, "$LoadAddressMacroAlias" ) )
{
VPC_Keyword_LoadAddressMacroAlias();
}
else if ( !V_stricmp_fast( pToken, "$LoadAddressMacroAuto" ) )
{
VPC_Keyword_LoadAddressMacroAuto();
}
else if ( !V_stricmp_fast( pToken, "$LoadAddressMacroAuto_Padded" ) )
{
VPC_Keyword_LoadAddressMacroAuto_Padded();
}
else if ( !V_stricmp_fast( pToken, "$IgnoreRedundancyWarning" ) )
{
VPC_Keyword_IgnoreRedundancyWarning();
}
else if ( !V_stricmp_fast( pToken, "$Linux" ) )
{
VPC_Keyword_Linux();
}
else if ( !V_stricmp_fast( pToken, "$CustomBuildStep" ) )
{
VPC_Keyword_CustomBuildStep();
}
else if ( !V_stricmp_fast( pToken, "$CustomAutoScript" ) )
{
VPC_Keyword_CustomAutoScript();
}
else if ( !V_stricmp_fast( pToken, "$Conditional" ) )
{
VPC_Keyword_Conditional( false );
}
else if ( !V_stricmp_fast( pToken, "$ConditionalOverrideReserved" ) )
{
VPC_Keyword_Conditional( true );
}
else if ( !V_stricmp_fast( pToken, "$LibDependsOnLib" ) )
{
VPC_Keyword_LibDependsOnLib();
}
else if ( !V_stricmp_fast( pToken, "$LibDependsOnImpLib" ) )
{
VPC_Keyword_LibDependsOnImpLib();
}
else
{
g_pVPC->VPCSyntaxError();
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CVPC::ParseProjectScript( const char *pScriptName, int depth, bool bQuiet, bool bWriteCRCCheckFile, CDependency_Project *pDependencyProject /*= nullptr*/ )
{
CDependency_Project *pOldDependencyProject = m_pDependencyProject;
if ( pDependencyProject )
{
m_pDependencyProject = pDependencyProject;
}
char *pScriptBuffer;
if ( !depth && !bQuiet )
{
// Emit a separator from the prior project in case of errors.
g_pVPC->VPCStatus( true, "\n" );
}
CUtlString szScriptName;
VPC_PrepareToReadScript( pScriptName, depth, bQuiet, pScriptBuffer, &szScriptName );
if ( !depth )
{
// create reserved $ROOTSCRIPT - tracks the root script
SetScriptMacro( "ROOTSCRIPT", szScriptName );
// create reserved $PROJECTNAME - tracks the undecorated pure project name
// $(ProjectName) can be auto-decorated, making it unuseable by scripts expecting a pure project name
SetScriptMacro( "PROJECTNAME", g_pVPC->GetProjectName() );
// An uppercase version of the project name for preprocessor macro standardization
CUtlString projectNameUpper( g_pVPC->GetProjectName() );
projectNameUpper.ToUpper();
SetScriptMacro( "UPPERCASEPROJECTNAME", projectNameUpper.Get() );
// create reserved $LOADADDRESSNAME - defaults to project name but can be aliased with $LoadAddressMacroAlias
SetScriptMacro( "LOADADDRESSNAME", g_pVPC->GetLoadAddressName() );
//#ifdef STEAM
// create reserved $PROJECTDIR
CUtlStringBuilder *pStrBuf = g_pVPC->GetTempStringBuffer1();
pStrBuf->Set( g_pVPC->GetProjectPath() );
V_RemoveDotSlashes( pStrBuf->Access() );
SetScriptMacro( "PROJECTDIR", pStrBuf->Get(), true );
//#endif
const CPUInformation &cpuInfo = GetCPUInformation();
SetScriptMacro( "PHYSICAL_PROCESSOR_COUNT", CFmtStr( "%d", cpuInfo.m_nPhysicalProcessors ).Get() );
SetScriptMacro( "PHYSICAL_PROCESSOR_COUNT_MINUS_ONE", CFmtStr( "%d", Max<int>( cpuInfo.m_nPhysicalProcessors - 1, 1 ) ).Get() );
SetScriptMacro( "LOGICAL_PROCESSOR_COUNT", CFmtStr( "%d", cpuInfo.m_nLogicalProcessors ).Get() );
SetScriptMacro( "LOGICAL_PROCESSOR_COUNT_MINUS_ONE", CFmtStr( "%d", Max<int>( cpuInfo.m_nLogicalProcessors - 1, 1 ) ).Get() );
g_pVPC->ResetMissingFilesCount();
// reset
g_pVPC->m_CustomBuildSteps.Purge();
g_pVPC->m_CustomAutoScripts.Purge();
VPC_Qt_OnParseProjectStart();
VPC_Schema_OnParseProjectStart();
VPC_Unity_OnParseProjectStart();
// save the conditional state before scripts can modify
g_pVPC->SaveConditionals();
}
VPC_ParseProjectScriptParameters( szScriptName, depth, bQuiet );
// for safety, force callers to restore to proper state
g_pVPC->GetScript().PopScript();
if ( !depth )
{
// at end of all processing
if ( bWriteCRCCheckFile )
{
// Finally write out the file with all the CRCs in it. This is referenced by the $CRCCHECK macro in the prebuild steps.
WriteCRCCheckFile( GetProjectGenerator()->GetOutputFileName() );
}
g_pVPC->UpdateCacheFile( szScriptName );
g_pVPC->m_ScriptList.Purge();
// Remove any macros that came from the script file.
g_pVPC->RemoveScriptCreatedMacros();
// Restore the state of conditionals (don't want script modifications to affect further processing)
g_pVPC->RestoreConditionals();
//done with this generator. We'll pick a new one the next time we parse a script's configuration or project section.
g_pVPC->SetProjectGenerator( nullptr );
}
if ( pDependencyProject )
{
m_pDependencyProject = pOldDependencyProject;
}
return true;
}
void CVPC::AddScriptToParsedList( const char *pScriptName, bool bAddToCRCCheck, CRC32_t crc /*= 0*/ )
{
for ( int i = 0; i < m_ScriptList.Count(); i++ )
{
if ( !V_stricmp_fast( m_ScriptList[i].m_scriptName.Get(), pScriptName ) )
{
if ( bAddToCRCCheck )
{
// update
m_ScriptList[i].m_crc = crc;
m_ScriptList[i].m_bCRCCheck = bAddToCRCCheck;
}
return;
}
}
int index = m_ScriptList.AddToTail();
m_ScriptList[index].m_scriptName = pScriptName;
m_ScriptList[index].m_crc = crc;
m_ScriptList[index].m_bCRCCheck = bAddToCRCCheck;
}