347 lines
11 KiB
C++
347 lines
11 KiB
C++
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
#include "vpc.h"
|
|
#include "dependencies.h"
|
|
#include "tier1/checksum_md5.h"
|
|
|
|
class CVCProjInfo
|
|
{
|
|
public:
|
|
CUtlString m_ProjectName;
|
|
CUtlString m_ProjectGUID;
|
|
};
|
|
|
|
|
|
class CSolutionGenerator_Win32 : public IBaseSolutionGenerator
|
|
{
|
|
public:
|
|
void GetVCPROJSolutionGUID( char (&szSolutionGUID)[256] )
|
|
{
|
|
HKEY hKey;
|
|
int firstVer = 8;
|
|
const int lastVer = 12; // Handle up to VS 12, AKA VS 2013
|
|
if ( g_pVPC->Is2010() )
|
|
{
|
|
firstVer = 10;
|
|
}
|
|
for ( int vsVer = firstVer; vsVer <= lastVer; ++vsVer )
|
|
{
|
|
// Handle both VisualStudio and VCExpress (used by some SourceSDK customers)
|
|
const char* productName[] =
|
|
{
|
|
"VisualStudio",
|
|
"VCExpress",
|
|
};
|
|
for ( int productNumber = 0; productNumber < ARRAYSIZE(productName); ++productNumber )
|
|
{
|
|
char pRegKeyName[1000];
|
|
V_snprintf( pRegKeyName, ARRAYSIZE(pRegKeyName), "Software\\Microsoft\\%s\\%d.0\\Projects", productName[ productNumber ], vsVer );
|
|
LONG ret = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pRegKeyName, 0, KEY_READ, &hKey );
|
|
//if ( ret != ERROR_SUCCESS )
|
|
// g_pVPC->VPCError( "Unable to open registry key %s.", pRegKeyName );
|
|
|
|
for ( int i=0; i < 200; i++ )
|
|
{
|
|
char szKeyName[MAX_PATH];
|
|
DWORD dwKeyNameSize = sizeof( szKeyName );
|
|
ret = RegEnumKeyEx( hKey, i, szKeyName, &dwKeyNameSize, NULL, NULL, NULL, NULL );
|
|
if ( ret == ERROR_NO_MORE_ITEMS )
|
|
break;
|
|
|
|
HKEY hSubKey;
|
|
LONG ret = RegOpenKeyEx( hKey, szKeyName, 0, KEY_READ, &hSubKey );
|
|
if ( ret == ERROR_SUCCESS )
|
|
{
|
|
DWORD dwType;
|
|
char ext[MAX_PATH];
|
|
DWORD dwExtLen = sizeof( ext );
|
|
ret = RegQueryValueEx( hSubKey, "DefaultProjectExtension", NULL, &dwType, (BYTE*)ext, &dwExtLen );
|
|
RegCloseKey( hSubKey );
|
|
|
|
// VS 2012 and beyond has the DefaultProjectExtension as vcxproj instead of vcproj
|
|
if ( ret == ERROR_SUCCESS && dwType == REG_SZ && ( V_stricmp( ext, "vcproj" ) == 0 || V_stricmp( ext, "vcxproj" ) == 0 ) )
|
|
{
|
|
V_strncpy( szSolutionGUID, szKeyName, ARRAYSIZE(szSolutionGUID) );
|
|
RegCloseKey( hKey );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
}
|
|
g_pVPC->VPCError( "Unable to find RegKey for .vcproj or .vcxproj files in solutions." );
|
|
}
|
|
|
|
virtual void GenerateSolutionFile( const char *pSolutionFilename, CUtlVector<CDependency_Project*> &projects )
|
|
{
|
|
// Default extension.
|
|
char szTmpSolutionFilename[MAX_PATH];
|
|
if ( !V_GetFileExtension( pSolutionFilename ) )
|
|
{
|
|
V_snprintf( szTmpSolutionFilename, sizeof( szTmpSolutionFilename ), "%s.sln", pSolutionFilename );
|
|
pSolutionFilename = szTmpSolutionFilename;
|
|
}
|
|
|
|
Msg( "\nWriting solution file %s.\n\n", pSolutionFilename );
|
|
|
|
char szSolutionGUID[256];
|
|
GetVCPROJSolutionGUID( szSolutionGUID );
|
|
|
|
CUtlVector<CVCProjInfo> vcprojInfos;
|
|
GetProjectInfos( projects, vcprojInfos );
|
|
|
|
// Write the file.
|
|
FILE *fp = fopen( pSolutionFilename, "wt" );
|
|
if ( !fp )
|
|
g_pVPC->VPCError( "Can't open %s for writing.", pSolutionFilename );
|
|
|
|
if ( g_pVPC->Is2013() )
|
|
{
|
|
fprintf( fp, "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 12.00\n" ); // Format didn't change from VS 2012 to VS 2013
|
|
fprintf( fp, "# Visual Studio 2013\n" );
|
|
}
|
|
else if ( g_pVPC->Is2012() )
|
|
{
|
|
fprintf( fp, "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 12.00\n" );
|
|
fprintf( fp, "# Visual Studio 2012\n" );
|
|
}
|
|
else if ( g_pVPC->Is2010() )
|
|
{
|
|
fprintf( fp, "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 11.00\n" );
|
|
fprintf( fp, "# Visual Studio 2010\n" );
|
|
}
|
|
else
|
|
{
|
|
fprintf( fp, "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 9.00\n" );
|
|
fprintf( fp, "# Visual Studio 2005\n" );
|
|
}
|
|
fprintf( fp, "#\n" );
|
|
fprintf( fp, "# Automatically generated solution:\n" );
|
|
fprintf( fp, "# devtools\\bin\\vpc " );
|
|
for ( int k = 1; k < __argc; ++ k )
|
|
fprintf( fp, "%s ", __argv[k] );
|
|
fprintf( fp, "\n" );
|
|
fprintf( fp, "#\n" );
|
|
fprintf( fp, "#\n" );
|
|
|
|
if ( !g_pVPC->Is2010() )
|
|
{
|
|
// if /slnItems <filename> is passed on the command line, build a Solution Items project
|
|
const char *pSolutionItemsFilename = g_pVPC->GetSolutionItemsFilename();
|
|
if ( pSolutionItemsFilename[0] != '\0' )
|
|
{
|
|
fprintf( fp, "Project(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{AAAAAAAA-8B4A-11D0-8D11-90A07D6D6F7D}\"\n" );
|
|
fprintf( fp, "\tProjectSection(SolutionItems) = preProject\n" );
|
|
WriteSolutionItems( fp );
|
|
fprintf( fp, "\tEndProjectSection\n" );
|
|
fprintf( fp, "EndProject\n" );
|
|
}
|
|
}
|
|
|
|
for ( int i=0; i < projects.Count(); i++ )
|
|
{
|
|
CDependency_Project *pCurProject = projects[i];
|
|
CVCProjInfo *pProjInfo = &vcprojInfos[i];
|
|
|
|
// Get a relative filename for the vcproj file.
|
|
const char *pFullProjectFilename = pCurProject->m_ProjectFilename.String();
|
|
char szRelativeFilename[MAX_PATH];
|
|
if ( !V_MakeRelativePath( pFullProjectFilename, g_pVPC->GetSourcePath(), szRelativeFilename, sizeof( szRelativeFilename ) ) )
|
|
g_pVPC->VPCError( "Can't make a relative path (to the base source directory) for %s.", pFullProjectFilename );
|
|
|
|
fprintf( fp, "Project(\"%s\") = \"%s\", \"%s\", \"{%s}\"\n", szSolutionGUID, pProjInfo->m_ProjectName.String(), szRelativeFilename, pProjInfo->m_ProjectGUID.String() );
|
|
bool bHasDependencies = false;
|
|
|
|
for ( int iTestProject=0; iTestProject < projects.Count(); iTestProject++ )
|
|
{
|
|
if ( i == iTestProject )
|
|
continue;
|
|
|
|
CDependency_Project *pTestProject = projects[iTestProject];
|
|
if ( pCurProject->DependsOn( pTestProject, k_EDependsOnFlagCheckNormalDependencies | k_EDependsOnFlagTraversePastLibs | k_EDependsOnFlagRecurse ) ||
|
|
pCurProject->DependsOn( pTestProject, k_EDependsOnFlagCheckAdditionalDependencies | k_EDependsOnFlagTraversePastLibs ) )
|
|
{
|
|
if ( !bHasDependencies )
|
|
{
|
|
fprintf( fp, "\tProjectSection(ProjectDependencies) = postProject\n" );
|
|
bHasDependencies = true;
|
|
}
|
|
fprintf( fp, "\t\t{%s} = {%s}\n", vcprojInfos[iTestProject].m_ProjectGUID.String(), vcprojInfos[iTestProject].m_ProjectGUID.String() );
|
|
}
|
|
}
|
|
if ( bHasDependencies )
|
|
fprintf( fp, "\tEndProjectSection\n" );
|
|
|
|
fprintf( fp, "EndProject\n" );
|
|
}
|
|
|
|
fclose( fp );
|
|
}
|
|
|
|
const char* FindInFile( const char *pFilename, const char *pFileData, const char *pSearchFor )
|
|
{
|
|
const char *pPos = V_stristr( pFileData, pSearchFor );
|
|
if ( !pPos )
|
|
{
|
|
g_pVPC->VPCError( "Can't find %s in %s.", pSearchFor, pFilename );
|
|
}
|
|
|
|
return pPos + V_strlen( pSearchFor );
|
|
}
|
|
|
|
void GetProjectInfos( CUtlVector<CDependency_Project*> &projects, CUtlVector<CVCProjInfo> &vcprojInfos )
|
|
{
|
|
for ( int i=0; i < projects.Count(); i++ )
|
|
{
|
|
CDependency_Project *pCurProject = projects[i];
|
|
const char *pFilename = pCurProject->m_ProjectFilename.String();
|
|
|
|
CVCProjInfo vcprojInfo;
|
|
|
|
char *pFileData;
|
|
int nResult = Sys_LoadFile( pFilename, (void**)&pFileData, false );
|
|
if ( nResult == -1 )
|
|
g_pVPC->VPCError( "Can't open %s to get ProjectGUID.", pFilename );
|
|
|
|
const char *pSearchFor;
|
|
if ( g_pVPC->Is2010() )
|
|
{
|
|
pSearchFor = "<ProjectGuid>{";
|
|
}
|
|
else
|
|
{
|
|
pSearchFor = "ProjectGUID=\"{";
|
|
}
|
|
|
|
const char *pPos = FindInFile( pFilename, pFileData, pSearchFor );
|
|
char szGuid[37];
|
|
const char *pGuid = pPos;
|
|
V_strncpy( szGuid, pGuid, sizeof( szGuid ) );
|
|
vcprojInfo.m_ProjectGUID = szGuid;
|
|
|
|
const char *pEnd;
|
|
if ( g_pVPC->Is2010() )
|
|
{
|
|
pPos = FindInFile( pFilename, pFileData, "<ProjectName>" );
|
|
pEnd = V_stristr( pPos, "<" );
|
|
}
|
|
else
|
|
{
|
|
pPos = FindInFile( pFilename, pFileData, "Name=\"" );
|
|
pEnd = V_stristr( pPos, "\"" );
|
|
}
|
|
|
|
if ( !pEnd || (pEnd - pPos) > 1024 || (pEnd - pPos) <= 0 )
|
|
g_pVPC->VPCError( "Can't find valid 'Name=' in %s.", pFilename );
|
|
|
|
char szName[256];
|
|
V_strncpy( szName, pPos, (pEnd - pPos) + 1 );
|
|
vcprojInfo.m_ProjectName = szName;
|
|
|
|
vcprojInfos.AddToTail( vcprojInfo );
|
|
|
|
free( pFileData );
|
|
}
|
|
}
|
|
|
|
// Parse g_SolutionItemsFilename, reading in filenames (including wildcards),
|
|
// and add them to the Solution Items project we're already writing.
|
|
void WriteSolutionItems( FILE *fp )
|
|
{
|
|
char szFullSolutionItemsPath[MAX_PATH];
|
|
if ( V_IsAbsolutePath( g_pVPC->GetSolutionItemsFilename() ) )
|
|
V_strncpy( szFullSolutionItemsPath, g_pVPC->GetSolutionItemsFilename(), sizeof( szFullSolutionItemsPath ) );
|
|
else
|
|
V_ComposeFileName( g_pVPC->GetStartDirectory(), g_pVPC->GetSolutionItemsFilename(), szFullSolutionItemsPath, sizeof( szFullSolutionItemsPath ) );
|
|
|
|
g_pVPC->GetScript().PushScript( szFullSolutionItemsPath );
|
|
|
|
int numSolutionItems = 0;
|
|
while ( g_pVPC->GetScript().GetData() )
|
|
{
|
|
// read a line
|
|
const char *pToken = g_pVPC->GetScript().GetToken( false );
|
|
|
|
// strip out \r\n chars
|
|
char *end = V_strstr( pToken, "\n" );
|
|
if ( end )
|
|
{
|
|
*end = '\0';
|
|
}
|
|
end = V_strstr( pToken, "\r" );
|
|
if ( end )
|
|
{
|
|
*end = '\0';
|
|
}
|
|
|
|
// bail on strings too small to be paths
|
|
if ( V_strlen( pToken ) < 3 )
|
|
continue;
|
|
|
|
// compose an absolute path w/o any ../
|
|
char szFullPath[MAX_PATH];
|
|
if ( V_IsAbsolutePath( pToken ) )
|
|
V_strncpy( szFullPath, pToken, sizeof( szFullPath ) );
|
|
else
|
|
V_ComposeFileName( g_pVPC->GetStartDirectory(), pToken, szFullPath, sizeof( szFullPath ) );
|
|
|
|
if ( !V_RemoveDotSlashes( szFullPath ) )
|
|
continue;
|
|
|
|
if ( V_strstr( szFullPath, "*" ) != NULL )
|
|
{
|
|
// wildcard!
|
|
char szWildcardPath[MAX_PATH];
|
|
V_strncpy( szWildcardPath, szFullPath, sizeof( szWildcardPath ) );
|
|
V_StripFilename( szWildcardPath );
|
|
|
|
struct _finddata32_t data;
|
|
intptr_t handle = _findfirst32( szFullPath, &data );
|
|
if ( handle != -1L )
|
|
{
|
|
do
|
|
{
|
|
if ( ( data.attrib & _A_SUBDIR ) == 0 )
|
|
{
|
|
// not a dir, just a filename - add it
|
|
V_ComposeFileName( szWildcardPath, data.name, szFullPath, sizeof( szFullPath ) );
|
|
|
|
if ( V_RemoveDotSlashes( szFullPath ) )
|
|
{
|
|
fprintf( fp, "\t\t%s = %s\n", szFullPath, szFullPath );
|
|
++numSolutionItems;
|
|
}
|
|
}
|
|
} while ( _findnext32( handle, &data ) == 0 );
|
|
|
|
_findclose( handle );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// just a file - add it
|
|
fprintf( fp, "\t\t%s = %s\n", szFullPath, szFullPath );
|
|
++numSolutionItems;
|
|
}
|
|
}
|
|
|
|
g_pVPC->GetScript().PopScript();
|
|
|
|
Msg( "Found %d solution files in %s\n", numSolutionItems, g_pVPC->GetSolutionItemsFilename() );
|
|
}
|
|
};
|
|
|
|
|
|
static CSolutionGenerator_Win32 g_SolutionGenerator_Win32;
|
|
IBaseSolutionGenerator* GetSolutionGenerator_Win32()
|
|
{
|
|
return &g_SolutionGenerator_Win32;
|
|
}
|
|
|