//====== 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 &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 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 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 &projects, CUtlVector &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 = "{"; } 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, "" ); 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; }