//====== Copyright 1996-2005, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #include "vpc.h" #include "projectgenerator_xcode.h" #ifdef WIN32 #include #define mkdir(dir, mode) _mkdir(dir) #define getcwd _getcwd #endif static const char *k_pchSource = "Source Files"; static const char *k_pchHeaders = "Header Files"; static const char *k_pchResources = "Resources"; static const char *k_pchVPCFiles = "VPC Files"; static const char *k_pchFrameworksAndLibs = "VPC Files"; void CProjectGenerator_XCode::GenerateXCodeProject( CBaseProjectDataCollector *pCollector, const char *pOutFilename, const char *pMakefileFilename ) { char sPbxProjFile[MAX_PATH]; sprintf( sPbxProjFile, "%s.xcodeproj", pOutFilename ); mkdir( sPbxProjFile, 0777 ); g_pVPC->VPCStatus( true, "Saving XCode project for: '%s' File: '%s'", pCollector->GetProjectName().String(), sPbxProjFile ); sprintf( sPbxProjFile, "%s.xcodeproj/project.pbxproj", pOutFilename ); m_fp = fopen( sPbxProjFile, "wt" ); m_nIndent = 0; m_pCollector = pCollector; m_pMakefileFilename = pMakefileFilename; WriteHeader(); WriteFileReferences(); WriteFiles(); WriteProject( pMakefileFilename ); //WriteBuildFiles(); WriteBuildConfigurations(); // Write the files list. WriteTrailer(); fclose( m_fp ); m_fp = NULL; } void CProjectGenerator_XCode::Write( const char *pMsg, ... ) { char sOut[8192]; va_list marker; va_start( marker, pMsg ); V_vsnprintf( sOut, sizeof( sOut ), pMsg, marker ); va_end( marker ); for ( int i=0; i < m_nIndent; i++ ) fprintf( m_fp, "\t" ); fprintf( m_fp, "%s", sOut ); } void CProjectGenerator_XCode::WriteHeader() { // preamble Write( "// !$*UTF8*$!\n{\n" ); ++m_nIndent; Write( "archiveVersion = 1;\n" ); Write( "classes = {\n" ); Write( "};\n" ); Write( "objectVersion = 42;\n" ); Write( "objects = {\n" ); } void CProjectGenerator_XCode::WriteProject( const char *pMakefileFilename ) { ++m_nIndent; Write( "%024X = {\n", m_pCollector ); ++m_nIndent; Write( "isa = PBXGroup;\n" ); Write( "children = (\n" ); ++m_nIndent; Write( "%024X /* %s */,\n", k_pchSource, k_pchSource ); Write( "%024X /* %s */,\n", k_pchHeaders, k_pchHeaders ); Write( "%024X /* %s */,\n", k_pchResources, k_pchResources ); Write( "%024X /* %s */,\n", k_pchVPCFiles, k_pchVPCFiles ); --m_nIndent; Write( ");\n" ); Write( "sourceTree = \"\";\n" ); --m_nIndent; Write( "};" ); --m_nIndent; Write( "\n/* End PBXGroup section */\n" ); WriteLegacyTargets( pMakefileFilename ); Write( "\n/* Begin PBXProject section */\n" ); Write( "%024X /* Project object */ = {\n", this ); ++m_nIndent; Write( "isa = PBXProject;\n" ); Write( "buildConfigurationList = %024X /* Build configuration list for PBXProject \"%s\" */;\n", &(m_pCollector->m_BaseConfigData), m_pCollector->GetProjectName().String() ); Write( "compatibilityVersion = \"Xcode 2.4\";\n" ); Write( "hasScannedForEncodings = 0;\n" ); Write( "mainGroup = %024X;\n", m_pCollector ); Write( "projectDirPath = \"\";\n" ); char sSrcRootRelative[MAX_PATH]; g_pVPC->ResolveMacrosInString( "$SRCDIR", sSrcRootRelative, sizeof( sSrcRootRelative ) ); V_FixSlashes( sSrcRootRelative, '/' ); Write( "projectRoot = \"%s\";\n", sSrcRootRelative ); Write( "targets = (\n" ); ++m_nIndent; Write( "%024X /* build with make */, \n", &CProjectGenerator_XCode::WriteLegacyTargets ); --m_nIndent; Write( ");\n" ); --m_nIndent; Write( "};" ); Write( "\n/* End PBXProject section */\n" ); } void CProjectGenerator_XCode::WriteBuildFiles() { Write( "\n/* Begin PBXBuildFile section */\n" ); for ( int i=m_pCollector->m_Files.First(); i != m_pCollector->m_Files.InvalidIndex(); i=m_pCollector->m_Files.Next( i ) ) { const char *pFilename = m_pCollector->m_Files[i]->GetName(); Write( "%024X /* %s */ = {isa = PBXBuildFile; fileRef = %024X /* %s */;};\n", m_pCollector->m_Files[i], pFilename, m_pCollector->m_Files[i]->GetName(), pFilename, m_pCollector->m_Files[i]->GetName() ); } Write( "\n/* End PBXBuildFile section */\n" ); } void CProjectGenerator_XCode::WriteBuildConfigurations() { ++m_nIndent; CUtlVector< CUtlString > configNames; m_pCollector->GetAllConfigurationNames( configNames ); Write( "\n/* Begin XCBuildConfiguration section */\n" ); FOR_EACH_VEC( configNames, iConfig ) { Write( "%024X /* %s */ = {\n", &(configNames[iConfig]), configNames[iConfig].String() ); ++m_nIndent; Write( "isa = XCBuildConfiguration;\n" ); Write( "buildSettings = {\n" ); ++m_nIndent; Write( "COPY_PHASE_STRIP = %s;\n", ( V_strstr( configNames[iConfig].String(), "release" ) ? "YES" : "NO" ) ); --m_nIndent; Write( "};\n" ); Write( "name = %s;\n", configNames[iConfig].String() ); --m_nIndent; Write( "};\n" ); } FOR_EACH_VEC( configNames, iConfig ) { Write( "%024X /* %s */ = {\n", (byte*)&(configNames[iConfig])+1, configNames[iConfig].String() ); ++m_nIndent; Write( "isa = XCBuildConfiguration;\n" ); Write( "buildSettings = {\n" ); ++m_nIndent; Write( "PRODUCT_NAME = \"build with make\";\n" ); Write( "CFG = %s;\n", configNames[iConfig].String() ); --m_nIndent; Write( "};\n" ); Write( "name = %s;\n", configNames[iConfig].String() ); --m_nIndent; Write( "};\n" ); } Write( "\n/* End XCBuildConfiguration section */\n" ); Write( "\n/* Begin XCBuildConfigurationList section */\n" ); Write( "%024X /* Build configuration list for PBXProject \"%s\" */ = {\n", &(m_pCollector->m_BaseConfigData), m_pCollector->GetProjectName().String() ); ++m_nIndent; Write( "isa = XCConfigurationList;\n" ); Write( "buildConfigurations = (\n" ); ++m_nIndent; FOR_EACH_VEC( configNames, iConfig ) { Write( "%024X /* %s */,\n", &(configNames[iConfig]), configNames[iConfig].String() ); } --m_nIndent; Write( ");\n" ); Write( "defaultConfigurationIsVisible = 0;\n" ); Write( "defaultConfigurationName = %s;\n", configNames[0].String() ); --m_nIndent; Write( "};\n" ); Write( "%024X /* Build configuration list for PBXLegacyTarget \"build with make\" */ = {\n", (byte*)&(m_pCollector->m_BaseConfigData)+1 ); ++m_nIndent; Write( "isa = XCConfigurationList;\n" ); Write( "buildConfigurations = (\n" ); ++m_nIndent; FOR_EACH_VEC( configNames, iConfig ) { Write( "%024X /* %s */,\n", (byte*)&(configNames[iConfig])+1, configNames[iConfig].String() ); } --m_nIndent; Write( ");\n" ); Write( "defaultConfigurationIsVisible = 0;\n" ); Write( "defaultConfigurationName = %s;\n", configNames[0].String() ); --m_nIndent; Write( "};" ); Write( "\n/* End XCBuildConfigurationList section */\n" ); --m_nIndent; } void CProjectGenerator_XCode::WriteLegacyTargets( const char *pchMakefileName ) { Write( "\n/* Begin PBXLegacyTarget section */\n" ); ++m_nIndent; Write( "%024X /* build with make */ = {\n", &CProjectGenerator_XCode::WriteLegacyTargets ); ++m_nIndent; Write( "isa = PBXLegacyTarget;\n"); Write( "buildArgumentsString = \"-f %s $(ACTION) MAKE_VERBOSE=1\";\n", pchMakefileName ); Write( "buildConfigurationList = %024X /* Build configuration list for PBXLegacyTarget \"build with make\" */;\n", (byte*)&(m_pCollector->m_BaseConfigData)+1 ); Write( "buildPhases = (\n" ); Write( ");\n" ); Write( "buildToolPath = /usr/bin/make;\n" ); char rgchCurDir[MAX_PATH]; getcwd( rgchCurDir, MAX_PATH ); Write( "buildWorkingDirectory = %s;\n", rgchCurDir ); Write( "dependencies = (\n" ); Write( ");\n"); Write( "name = \"build with make\";\n" ); Write( "passBuildSettingsInEnvironment = 1;\n" ); Write( "productName = \"build with make\";\n" ); --m_nIndent; Write( "};\n" ); --m_nIndent; Write( "\n/* End PBXLegacyTarget section */\n" ); } void CProjectGenerator_XCode::WriteFileReferences() { Write( "\n/* Begin PBXFileReference section */\n" ); ++m_nIndent; for ( int i=m_pCollector->m_Files.First(); i != m_pCollector->m_Files.InvalidIndex(); i=m_pCollector->m_Files.Next( i ) ) { const char *pchExtension = V_GetFileExtension( m_pCollector->m_Files[i]->GetName() ); char rgchFileType[MAX_PATH]; if ( pchExtension && ( !Q_stricmp( pchExtension, "cpp" ) || !Q_stricmp( pchExtension, "h" ) || !Q_stricmp( pchExtension, "c" ) || !Q_stricmp( pchExtension, "cc" ) ) ) sprintf( rgchFileType, "sourcecode.cpp.%s", pchExtension ); else sprintf( rgchFileType, "text.plain" ); char rgchFilePath[MAX_PATH]; Q_strncpy( rgchFilePath, m_pCollector->m_Files[i]->GetName(), sizeof( rgchFilePath ) ); Q_RemoveDotSlashes( rgchFilePath ); Write( "%024X /* %s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = %s; path = \"%s\"; sourceTree = \"\"; };\n", m_pCollector->m_Files[i], rgchFilePath, rgchFileType, rgchFilePath ); } --m_nIndent; Write( "\n/* End PBXFileReference section */\n" ); } void CProjectGenerator_XCode::WriteTrailer() { Write( "};\n" ); Write( "rootObject = %024X /* project object */;\n", this ); --m_nIndent; Write( "}\n" ); } void CProjectGenerator_XCode::WriteConfig( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteTarget_Build( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteTarget_Compile( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteTarget_Rebuild( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteTarget_Link( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteTarget_Debug( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteIncludes( CSpecificConfig *pConfig ) { } void CProjectGenerator_XCode::WriteFilesFolder( const char *pFolderName, const char *pExtensions ) { CUtlVector extensions; V_SplitString( pExtensions, ";", extensions ); Write( "%024X /* %s */ = {\n", pFolderName, pFolderName ); ++m_nIndent; Write( "isa = PBXGroup;\n" ); Write( "children = (\n" ); ++m_nIndent; for ( int i=m_pCollector->m_Files.First(); i != m_pCollector->m_Files.InvalidIndex(); i=m_pCollector->m_Files.Next( i ) ) { const char *pFilename = m_pCollector->m_Files[i]->GetName(); // Make sure this file's extension is one of the extensions they're asking for. bool bValidExt = false; const char *pFileExtension = V_GetFileExtension( pFilename ); if ( pFileExtension ) { for ( int iExt=0; iExt < extensions.Count(); iExt++ ) { const char *pTestExt = extensions[iExt]; if ( pTestExt[0] == '*' && pTestExt[1] == '.' && V_stricmp( pTestExt+2, pFileExtension ) == 0 ) { bValidExt = true; break; } } } if ( bValidExt ) { char sFixedSlashes[MAX_PATH]; V_strncpy( sFixedSlashes, pFilename, sizeof( sFixedSlashes ) ); Write( "%024X /* %s */,\n", m_pCollector->m_Files[i], sFixedSlashes ); } } --m_nIndent; Write( ");\n" ); Write( "name = \"%s\";\n", pFolderName ); Write( "sourceTree = \"\";\n" ); --m_nIndent; Write( "};\n" ); } void CProjectGenerator_XCode::WriteFiles() { Write( "\n/* Begin PBXGroup section */\n" ); ++m_nIndent; WriteFilesFolder( k_pchSource, "*.c;*.C;*.cc;*.cpp;*.cp;*.cxx;*.c++;*.prg;*.pas;*.dpr;*.asm;*.s;*.bas;*.java;*.cs;*.sc;*.e;*.cob;*.html;*.rc;*.tcl;*.py;*.pl;*.m;*.mm" ); WriteFilesFolder( k_pchHeaders, "*.h;*.H;*.hh;*.hpp;*.hxx;*.inc;*.sh;*.cpy;*.if" ); WriteFilesFolder( k_pchResources, "*.plist;*.strings;*.xib" ); WriteFilesFolder( k_pchVPCFiles, "*.vpc" ); --m_nIndent; }