Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

273 lines
8.8 KiB

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "vpc.h"
#include "p4lib/ip4.h"
// fix filenames that have double backslashes at the start
// (Perforce will return this if the root of a clientspec is e.g. "D:\")
static const char* FixPerforceFilename( const char *filename )
{
if ( filename && V_strlen( filename ) > 2 && !V_strnicmp( filename + 1, ":\\\\", 3 ) )
{
// strip out the first backslash
static char newFilename[MAX_PATH];
V_snprintf( newFilename, sizeof(newFilename), "%c:%s",
filename[0],
&filename[3]
);
return newFilename;
}
return filename;
}
static void GetChangelistFilenames( CUtlVector<int> &changelists, CUtlVector<CUtlString> &changelistFilenames )
{
// P4 interface didn't initalize in main - abort
if ( !p4 )
{
g_pVPC->VPCWarning( "P4SLN: Perforce interface not available. Unable to generate solution from the given Perforce changelist." );
return;
}
CUtlVector<P4File_t> fileList;
int changeListIndex = 0;
if ( changelists.Count() )
{
changeListIndex = changelists[0];
}
if ( changeListIndex == -1 )
{
p4->GetOpenedFileList( fileList, false );
}
else if ( changeListIndex == 0 )
{
p4->GetOpenedFileList( fileList, true );
}
else
{
CUtlVector<P4File_t> partialFileList;
FOR_EACH_VEC( changelists, i )
{
p4->GetFileListInChangelist( changelists[i], partialFileList );
FOR_EACH_VEC( partialFileList, j )
{
fileList.AddToTail( partialFileList[j] );
}
}
}
// If -1 is in the changelist index, then include all.
bool bIncludeAllChangelists = ( changelists.Find( -1 ) != changelists.InvalidIndex() );
for ( int i=0; i < fileList.Count(); i++ )
{
if ( bIncludeAllChangelists || changelists.Find( fileList[i].m_iChangelist ) != changelists.InvalidIndex() )
{
const char *pFilename = p4->String( fileList[i].m_sLocalFile );
const char *pNewFilename = FixPerforceFilename( pFilename );
changelistFilenames.AddToTail( pNewFilename );
}
}
}
static void AddAdditionalDependencies( CUtlVector<CDependency_Project*> &projects, CUtlVector<CDependency_Project*> &allProjects )
{
for ( int nProject=0; nProject < projects.Count(); nProject++ )
{
CDependency_Project *pCurProject = projects[nProject];
// Look at all the $AdditionalProjectDependencies projects for this one.
for ( int nDependency=0; nDependency < pCurProject->m_AdditionalProjectDependencies.Count(); nDependency++ )
{
const char *pLookingFor = pCurProject->m_AdditionalProjectDependencies[nDependency].String();
// Search for a match in allProjects.
int nFound = CDependency_Project::FindByProjectName( allProjects, pLookingFor );
if ( nFound == -1 )
{
g_pVPC->VPCError( "P4SLN: Project %s lists '%s' in its $AdditionalProjectDependencies, but there is no project by that name.", pCurProject->GetName(), pLookingFor );
}
else
{
// Got a match.
CDependency_Project *pFound = allProjects[nFound];
int nTest = projects.Find( pFound );
if ( nTest == projects.InvalidIndex() )
{
projects.AddToTail( pFound );
}
}
}
}
}
static void GetProjectsDependingOnFiles( CProjectDependencyGraph &dependencyGraph, CUtlVector<CUtlString> &filenames, CUtlVector<CDependency_Project*> &projects )
{
// Now figure out the projects that depend on each of these files.
for ( int iFile=0; iFile < filenames.Count(); iFile++ )
{
CDependency *pFile = dependencyGraph.FindDependency( filenames[iFile].String() );
if ( !pFile )
{
char szRelative[MAX_PATH];
if ( !V_MakeRelativePath( filenames[iFile].String(), g_pVPC->GetSourcePath(), szRelative, sizeof( szRelative ) ) )
{
V_strncpy( szRelative, filenames[iFile].String(), sizeof( szRelative ) );
}
// This probably means their build commands on the command line didn't include
// any projects that included this file.
g_pVPC->VPCWarning( "%s is not found in the projects searched.", szRelative );
continue;
}
// Now see which projects depend on this file.
for ( int iProject=0; iProject < dependencyGraph.m_Projects.Count(); iProject++ )
{
CDependency_Project *pProject = dependencyGraph.m_Projects[iProject];
if ( pProject->DependsOn( pFile, k_EDependsOnFlagCheckNormalDependencies | k_EDependsOnFlagRecurse | k_EDependsOnFlagTraversePastLibs | k_EDependsOnFlagCheckAdditionalDependencies ) )
{
if ( projects.Find( pProject ) == -1 )
projects.AddToTail( pProject );
}
}
}
// generate the group restrictions
// the user can explicitly provide a set of groups to narrow the wide dependency target
CUtlVector< CUtlString > groupRestrictions;
groupRestrictions = g_pVPC->m_P4GroupRestrictions;
if ( !groupRestrictions.Count() )
{
// the default restriction is to the "everything" group
groupRestrictions.AddToTail( "everything" );
}
CUtlVector< projectIndex_t > allowedProjectIndices;
if ( groupRestrictions.Count() )
{
// get all of the allowed projects by iterating the restrict-to-groups
for ( int i = 0; i < groupRestrictions.Count(); i++ )
{
CUtlVector< projectIndex_t > projectIndices;
if ( !g_pVPC->GetProjectsInGroup( projectIndices, groupRestrictions[i].Get() ) )
{
g_pVPC->VPCError( "No projects found in group '%s'.", groupRestrictions[i].Get() );
}
// aggregate into wider list
for ( int j = 0; j < projectIndices.Count(); j++ )
{
allowedProjectIndices.AddToTail( projectIndices[j] );
}
}
}
// Make sure that each of the dependent projects are members of the restricted groups, otherwise prevent their inclusion.
CUtlVector< int > doomedProjectIndices;
if ( allowedProjectIndices.Count() )
{
for ( int j = 0; j < projects.Count(); j++ )
{
// find the target project in the allowed set
if ( allowedProjectIndices.Find( projects[j]->m_iProjectIndex ) == allowedProjectIndices.InvalidIndex() )
{
// the target project is not in the allowed set
// add in descending order so indices can be properly removed below from largest index to smallest
doomedProjectIndices.AddToHead( j );
}
}
// Remove the projects that are not part of the restrict-to-groups
// Indexes were added in descending order, so removal is actually from the end, truncating the set
for ( int j = 0; j < doomedProjectIndices.Count(); j++ )
{
projects.Remove( doomedProjectIndices[j] );
}
}
}
static void UpdateProjects( CUtlVector<CDependency_Project*> &projects )
{
for ( int iProject=0; iProject < projects.Count(); iProject++ )
{
Log_Msg( LOG_VPC, "\n" );
CDependency_Project *pDependency = projects[iProject];
pDependency->ExportProjectParameters();
if ( g_pVPC->IsForceGenerate() || !g_pVPC->IsProjectCurrent( g_pVPC->GetOutputFilename(), true ) )
{
project_t *pProject = &g_pVPC->m_Projects[ pDependency->m_iProjectIndex ];
g_pVPC->SetProjectName( pProject->name.String() );
g_pVPC->SetLoadAddressName( pProject->name.String() );
g_pVPC->ParseProjectScript( pDependency->m_szStoredScriptName, 0, false, true );
}
}
}
class CStringCaseLess
{
public:
bool Less( const char *lhs, const char *rhs, void *pCtx )
{
return ( V_stricmp( lhs, rhs ) < 0 ? true : false );
}
};
void GenerateSolutionForPerforceChangelist( CProjectDependencyGraph &dependencyGraph, CUtlVector<int> &changelists, IBaseSolutionGenerator *pGenerator, const char *pSolutionFilename )
{
// We want to check against ALL projects in projects.vgc.
int nDepFlags = BUILDPROJDEPS_FULL_DEPENDENCY_SET | BUILDPROJDEPS_CHECK_ALL_PROJECTS;
dependencyGraph.BuildProjectDependencies( nDepFlags );
// Get the list of files from Perforce.
CUtlVector<CUtlString> filenames;
GetChangelistFilenames( changelists, filenames );
// Get the list of projects that depend on these files.
CUtlVector<CDependency_Project*> projects;
GetProjectsDependingOnFiles( dependencyGraph, filenames, projects );
// Add g_targetProjects, which will include any other projects that they added on the command line with +tier0 *engine syntax.
CUtlVector<CDependency_Project*> commandLineProjects;
dependencyGraph.TranslateProjectIndicesToDependencyProjects( g_pVPC->m_TargetProjects, commandLineProjects );
for ( int i=0; i < commandLineProjects.Count(); i++ )
{
if ( projects.Find( commandLineProjects[i] ) == projects.InvalidIndex() )
projects.AddToTail( commandLineProjects[i] );
}
// Make sure the latest .vcproj files are generated.
UpdateProjects( projects );
// List the projects.
CUtlSortVector< CUtlString, CStringCaseLess > sortedProjectNames;
for ( int i=0; i < projects.Count(); i++ )
{
sortedProjectNames.InsertNoSort( projects[i]->GetName() );
}
sortedProjectNames.RedoSort();
Msg( "Dependent projects: \n\n" );
for ( int i=0; i < sortedProjectNames.Count(); i++ )
{
Msg( "%s\n", sortedProjectNames[i].Get() );
}
// Write the solution file.
pGenerator->GenerateSolutionFile( pSolutionFilename, projects );
}