/*========================================================================= Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmGlobalXCodeGenerator.h" #include "cmGlobalXCode21Generator.h" #include "cmLocalXCodeGenerator.h" #include "cmMakefile.h" #include "cmXCodeObject.h" #include "cmake.h" #include "cmGeneratedFileStream.h" #include "cmSourceFile.h" #include "cmOrderLinkDirectories.h" #include "cmXMLParser.h" // parse the xml file storing the installed version of Xcode on // the machine class cmXcodeVersionParser : public cmXMLParser { public: void StartElement(const char* name, const char** atts) { m_Data = ""; } void EndElement(const char* name) { if(strcmp(name, "key") == 0) { m_Key = m_Data; } else if(strcmp(name, "string") == 0) { if(m_Key == "CFBundleShortVersionString") { m_Version = (int)(10.0 * atof(m_Data.c_str())); } } } void CharacterDataHandler(const char* data, int length) { m_Data.append(data, length); } int m_Version; std::string m_Key; std::string m_Data; }; //TODO // add OSX application stuff //---------------------------------------------------------------------------- cmGlobalXCodeGenerator::cmGlobalXCodeGenerator() { m_FindMakeProgramFile = "CMakeFindXCode.cmake"; m_RootObject = 0; m_MainGroupChildren = 0; m_SourcesGroupChildren = 0; m_CurrentMakefile = 0; m_CurrentLocalGenerator = 0; m_XcodeVersion = 15; } //---------------------------------------------------------------------------- cmGlobalGenerator* cmGlobalXCodeGenerator::New() { cmXcodeVersionParser parser; parser.ParseFile("/Developer/Applications/Xcode.app/Contents/version.plist"); if(parser.m_Version == 15) { return new cmGlobalXCodeGenerator; } else if (parser.m_Version == 20) { cmSystemTools::Message("Xcode 2.0 not really supported by cmake, " "using Xcode 15 generator\n"); return new cmGlobalXCodeGenerator; } return new cmGlobalXCode21Generator; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::EnableLanguage(std::vectorconst& lang, cmMakefile * mf) { mf->AddDefinition("XCODE","1"); if(m_XcodeVersion == 15) { mf->AddDefinition("CMAKE_CFG_INTDIR","."); } else { mf->AddDefinition("CMAKE_CFG_INTDIR","$(CONFIGURATION)"); } mf->AddDefinition("CMAKE_GENERATOR_CC", "gcc"); mf->AddDefinition("CMAKE_GENERATOR_CXX", "g++"); mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1"); this->cmGlobalGenerator::EnableLanguage(lang, mf); } //---------------------------------------------------------------------------- std::string cmGlobalXCodeGenerator::GenerateBuildCommand(const char* makeProgram, const char *projectName, const char *targetName, const char* config, bool ignoreErrors) { // Config is not used yet (void) config; (void) ignoreErrors; // now build the test if(makeProgram == 0 || !strlen(makeProgram)) { cmSystemTools::Error( "Generator cannot find the appropriate make command."); return ""; } std::string makeCommand = cmSystemTools::ConvertToOutputPath(makeProgram); std::string lowerCaseCommand = makeCommand; cmSystemTools::LowerCase(lowerCaseCommand); makeCommand += " -project "; makeCommand += projectName; makeCommand += ".xcode"; bool clean = false; if ( targetName && strcmp(targetName, "clean") == 0 ) { clean = true; targetName = "ALL_BUILD"; } if(clean) { makeCommand += " clean"; } else { makeCommand += " build"; } makeCommand += " -target "; if (targetName && strlen(targetName)) { makeCommand += targetName; } else { makeCommand += "ALL_BUILD"; } if(m_XcodeVersion == 15) { makeCommand += " -buildstyle Development "; } else { makeCommand += " -configuration Debug"; } return makeCommand; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::ConfigureOutputPaths() { // Format the library and executable output paths. m_LibraryOutputPath = m_CurrentMakefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH"); if(m_LibraryOutputPath.size() == 0) { m_LibraryOutputPath = m_CurrentMakefile->GetCurrentOutputDirectory(); } // make sure there is a trailing slash if(m_LibraryOutputPath.size() && m_LibraryOutputPath[m_LibraryOutputPath.size()-1] != '/') { m_LibraryOutputPath += "/"; if(!cmSystemTools::MakeDirectory(m_LibraryOutputPath.c_str())) { cmSystemTools::Error("Error creating directory ", m_LibraryOutputPath.c_str()); } } m_CurrentMakefile->AddLinkDirectory(m_LibraryOutputPath.c_str()); m_ExecutableOutputPath = m_CurrentMakefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); if(m_ExecutableOutputPath.size() == 0) { m_ExecutableOutputPath = m_CurrentMakefile->GetCurrentOutputDirectory(); } // make sure there is a trailing slash if(m_ExecutableOutputPath.size() && m_ExecutableOutputPath[m_ExecutableOutputPath.size()-1] != '/') { m_ExecutableOutputPath += "/"; if(!cmSystemTools::MakeDirectory(m_ExecutableOutputPath.c_str())) { cmSystemTools::Error("Error creating directory ", m_ExecutableOutputPath.c_str()); } } } //---------------------------------------------------------------------------- ///! Create a local generator appropriate to this Global Generator cmLocalGenerator *cmGlobalXCodeGenerator::CreateLocalGenerator() { cmLocalGenerator *lg = new cmLocalXCodeGenerator; lg->SetGlobalGenerator(this); return lg; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::Generate() { this->cmGlobalGenerator::Generate(); std::map >::iterator it; for(it = m_ProjectMap.begin(); it!= m_ProjectMap.end(); ++it) { cmLocalGenerator* root = it->second[0]; m_CurrentProject = root->GetMakefile()->GetProjectName(); this->SetCurrentLocalGenerator(root); m_OutputDir = m_CurrentMakefile->GetHomeOutputDirectory(); m_OutputDir = cmSystemTools::CollapseFullPath(m_OutputDir.c_str()); cmSystemTools::SplitPath(m_OutputDir.c_str(), m_ProjectOutputDirectoryComponents); m_CurrentLocalGenerator = root; // add ALL_BUILD, INSTALL, etc this->AddExtraTargets(root, it->second); // now create the project this->OutputXCodeProject(root, it->second); } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root, std::vector& gens) { cmMakefile* mf = root->GetMakefile(); // Add ALL_BUILD const char* no_output = 0; std::vector no_depends; mf->AddUtilityCommand("ALL_BUILD", false, no_output, no_depends, "echo", "Build all projects"); cmTarget* allbuild = mf->FindTarget("ALL_BUILD"); // ADD install std::string cmake_command = mf->GetRequiredDefinition("CMAKE_COMMAND"); if(m_XcodeVersion == 15) { mf->AddUtilityCommand("install", false, no_output, no_depends, cmake_command.c_str(), "-P", "cmake_install.cmake"); } else { mf->AddUtilityCommand("install", false, no_output, no_depends, cmake_command.c_str(), "-DBUILD_TYPE=$(CONFIGURATION)", "-P", "cmake_install.cmake"); } const char* noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); if(!noall || cmSystemTools::IsOff(noall)) { cmTarget* install = mf->FindTarget("install"); install->AddUtility("ALL_BUILD"); } // Add RUN_TESTS target if testing has been enabled std::string fname; fname = mf->GetStartOutputDirectory(); fname += "/"; fname += "DartTestfile.txt"; if (cmSystemTools::FileExists(fname.c_str())) { std::string ctest_command = mf->GetRequiredDefinition("CMAKE_CTEST_COMMAND"); mf->AddUtilityCommand("RUN_TESTS", false, no_output, no_depends, ctest_command.c_str()); } // Add XCODE depend helper std::string dir = mf->GetCurrentOutputDirectory(); m_CurrentXCodeHackMakefile = dir; m_CurrentXCodeHackMakefile += "/CMakeScripts"; cmSystemTools::MakeDirectory(m_CurrentXCodeHackMakefile.c_str()); m_CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make"; cmCustomCommandLine makecommand; makecommand.push_back("make"); makecommand.push_back("-C"); makecommand.push_back(dir.c_str()); makecommand.push_back("-f"); makecommand.push_back(m_CurrentXCodeHackMakefile.c_str()); cmCustomCommandLines commandLines; commandLines.push_back(makecommand); mf->AddUtilityCommand("XCODE_DEPEND_HELPER", false, no_output, no_depends, commandLines); // Add Re-Run CMake rules this->CreateReRunCMakeFile(root); // now make the allbuild depend on all the non-utility targets // in the project for(std::vector::iterator i = gens.begin(); i != gens.end(); ++i) { cmLocalGenerator* lg = *i; if(this->IsExcluded(root, *i)) { continue; } cmTargets& tgts = lg->GetMakefile()->GetTargets(); for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++) { cmTarget& target = l->second; // make all exe, shared libs and modules depend // on the XCODE_DEPEND_HELPER target if((target.GetType() == cmTarget::EXECUTABLE || target.GetType() == cmTarget::SHARED_LIBRARY || target.GetType() == cmTarget::MODULE_LIBRARY)) { target.AddUtility("XCODE_DEPEND_HELPER"); } if(target.IsInAll()) { allbuild->AddUtility(target.GetName()); } } } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateReRunCMakeFile(cmLocalGenerator* root) { cmMakefile* mf = root->GetMakefile(); std::vector lfiles = mf->GetListFiles(); // sort the array std::sort(lfiles.begin(), lfiles.end(), std::less()); std::vector::iterator new_end = std::unique(lfiles.begin(), lfiles.end()); lfiles.erase(new_end, lfiles.end()); std::string dir = mf->GetHomeOutputDirectory(); m_CurrentReRunCMakeMakefile = dir; m_CurrentReRunCMakeMakefile += "/CMakeScripts"; cmSystemTools::MakeDirectory(m_CurrentReRunCMakeMakefile.c_str()); m_CurrentReRunCMakeMakefile += "/ReRunCMake.make"; cmGeneratedFileStream makefileStream(m_CurrentReRunCMakeMakefile.c_str()); makefileStream << "# Generated by CMake, DO NOT EDIT\n"; makefileStream << "cmake.check_cache: "; for(std::vector::const_iterator i = lfiles.begin(); i != lfiles.end(); ++i) { makefileStream << "\\\n" << this->ConvertToRelativeForMake(i->c_str()); } std::string cmake = mf->GetRequiredDefinition("CMAKE_COMMAND"); makefileStream << "\n\t" << this->ConvertToRelativeForMake(cmake.c_str()) << " -H" << this->ConvertToRelativeForMake( mf->GetHomeDirectory()) << " -B" << this->ConvertToRelativeForMake( mf->GetHomeOutputDirectory()) << "\n"; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::ClearXCodeObjects() { m_TargetDoneSet.clear(); for(unsigned int i = 0; i < m_XCodeObjects.size(); ++i) { delete m_XCodeObjects[i]; } m_XCodeObjects.clear(); m_GroupMap.clear(); m_GroupNameMap.clear(); m_TargetGroup.clear(); } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::PBXType ptype) { cmXCodeObject* obj = new cmXCodeObject(ptype, cmXCodeObject::OBJECT); m_XCodeObjects.push_back(obj); return obj; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type) { cmXCodeObject* obj = new cmXCodeObject(cmXCodeObject::None, type); m_XCodeObjects.push_back(obj); return obj; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateString(const char* s) { cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING); obj->SetString(s); return obj; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateObjectReference(cmXCodeObject* ref) { cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF); obj->SetObject(ref); return obj; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg, cmSourceFile* sf) { std::string flags; // Add flags from source file properties. lg->AppendFlags(flags, sf->GetProperty("COMPILE_FLAGS")); cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); cmXCodeObject* group = m_GroupMap[sf]; cmXCodeObject* children = group->GetObject("children"); children->AddObject(fileRef); // m_SourcesGroupChildren->AddObject(fileRef); cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef)); cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); settings->AddAttribute("COMPILER_FLAGS", this->CreateString(flags.c_str())); buildFile->AddAttribute("settings", settings); fileRef->AddAttribute("fileEncoding", this->CreateString("4")); const char* lang = this->GetLanguageFromExtension(sf->GetSourceExtension().c_str()); std::string sourcecode = "sourcecode"; std::string ext = sf->GetSourceExtension(); ext = cmSystemTools::LowerCase(ext); if(ext == "o") { sourcecode = "compiled.mach-o.objfile"; } else if(ext == "mm") { sourcecode += ".cpp.objcpp"; } else if(ext == "m") { sourcecode += ".cpp.objc"; } else if(!lang) { sourcecode += ext; sourcecode += "."; sourcecode += ext; } else if(strcmp(lang, "C") == 0) { sourcecode += ".c.c"; } else { sourcecode += ".cpp.cpp"; } fileRef->AddAttribute("lastKnownFileType", this->CreateString(sourcecode.c_str())); std::string path = this->ConvertToRelativeForXCode(sf->GetFullPath().c_str()); // std::string file = // cmSystemTools::RelativePath(m_CurrentMakefile->GetHomeDirectory(), // sf->GetFullPath().c_str()); std::string dir; std::string file; cmSystemTools::SplitProgramPath(sf->GetFullPath().c_str(), dir, file); fileRef->AddAttribute("name", this->CreateString(file.c_str())); fileRef->AddAttribute("path", this->CreateString(path.c_str())); fileRef->AddAttribute("refType", this->CreateString("4")); if(path.size() > 1 && path[0] == '.' && path[1] == '.') { fileRef->AddAttribute("sourceTree", this->CreateString("")); } else { fileRef->AddAttribute("sourceTree", this->CreateString("")); } return buildFile; } //---------------------------------------------------------------------------- bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname) { if(tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" || tname == "install" || tname == "RUN_TESTS" ) { if(m_TargetDoneSet.find(tname) != m_TargetDoneSet.end()) { return true; } m_TargetDoneSet.insert(tname); return false; } return false; } void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen) { m_CurrentLocalGenerator = gen; m_CurrentMakefile = gen->GetMakefile(); std::string outdir = cmSystemTools::CollapseFullPath(m_CurrentMakefile-> GetCurrentOutputDirectory()); cmSystemTools::SplitPath(outdir.c_str(), m_CurrentOutputDirectoryComponents); } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen, std::vector& targets) { this->SetCurrentLocalGenerator(gen); cmTargets &tgts = gen->GetMakefile()->GetTargets(); for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++) { cmTarget& cmtarget = l->second; // make sure ALL_BUILD, INSTALL, etc are only done once if(this->SpecialTargetEmitted(l->first.c_str())) { continue; } if(cmtarget.GetType() == cmTarget::UTILITY || cmtarget.GetType() == cmTarget::INSTALL_FILES || cmtarget.GetType() == cmTarget::INSTALL_PROGRAMS) { if(cmtarget.GetType() == cmTarget::UTILITY) { targets.push_back(this->CreateUtilityTarget(cmtarget)); } continue; } // create source build phase cmXCodeObject* sourceBuildPhase = this->CreateObject(cmXCodeObject::PBXSourcesBuildPhase); sourceBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); sourceBuildPhase->AddAttribute("files", buildFiles); sourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", this->CreateString("0")); std::vector &classes = l->second.GetSourceFiles(); // add all the sources std::vector externalObjFiles; std::vector headerFiles; for(std::vector::iterator i = classes.begin(); i != classes.end(); ++i) { cmXCodeObject* xsf = this->CreateXCodeSourceFile(gen, *i); cmXCodeObject* fr = xsf->GetObject("fileRef"); cmXCodeObject* filetype = fr->GetObject()->GetObject("lastKnownFileType"); if(strcmp(filetype->GetString(), "\"compiled.mach-o.objfile\"") == 0) { externalObjFiles.push_back(xsf); } else if((*i)->GetPropertyAsBool("HEADER_FILE_ONLY")) { headerFiles.push_back(xsf); } else { buildFiles->AddObject(xsf); } } // create header build phase cmXCodeObject* headerBuildPhase = this->CreateObject(cmXCodeObject::PBXHeadersBuildPhase); headerBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); for(std::vector::iterator i = headerFiles.begin(); i != headerFiles.end(); ++i) { buildFiles->AddObject(*i); } headerBuildPhase->AddAttribute("files", buildFiles); headerBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", this->CreateString("0")); // create framework build phase cmXCodeObject* frameworkBuildPhase = this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase); frameworkBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); frameworkBuildPhase->AddAttribute("files", buildFiles); for(std::vector::iterator i = externalObjFiles.begin(); i != externalObjFiles.end(); ++i) { buildFiles->AddObject(*i); } frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", this->CreateString("0")); cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST); this->CreateCustomCommands(buildPhases, sourceBuildPhase, headerBuildPhase, frameworkBuildPhase, cmtarget); targets.push_back(this->CreateXCodeTarget(l->second, buildPhases)); } } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateBuildPhase(const char* name, const char* name2, cmTarget& cmtarget, const std::vector& commands) { if(commands.size() == 0 && strcmp(name, "CMake ReRun") != 0) { return 0; } cmXCodeObject* buildPhase = this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); buildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); buildPhase->AddAttribute("files", buildFiles); buildPhase->AddAttribute("name", this->CreateString(name)); buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", this->CreateString("0")); buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh")); this->AddCommandsToBuildPhase(buildPhase, cmtarget, commands, name2); return buildPhase; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateCustomCommands(cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase, cmXCodeObject* headerBuildPhase, cmXCodeObject* frameworkBuildPhase, cmTarget& cmtarget) { std::vector const & prebuild = cmtarget.GetPreBuildCommands(); std::vector const & prelink = cmtarget.GetPreLinkCommands(); std::vector const & postbuild = cmtarget.GetPostBuildCommands(); cmtarget.TraceVSDependencies(cmtarget.GetName(), m_CurrentMakefile); std::vector &classes = cmtarget.GetSourceFiles(); // add all the sources std::vector commands; for(std::vector::iterator i = classes.begin(); i != classes.end(); ++i) { if((*i)->GetCustomCommand()) { commands.push_back(*(*i)->GetCustomCommand()); } } std::vector reruncom; cmXCodeObject* cmakeReRunPhase = this->CreateBuildPhase("CMake ReRun", "cmakeReRunPhase", cmtarget, reruncom); buildPhases->AddObject(cmakeReRunPhase); // create prebuild phase cmXCodeObject* cmakeRulesBuildPhase = this->CreateBuildPhase("CMake Rules", "cmakeRulesBuildPhase", cmtarget, commands); // create prebuild phase cmXCodeObject* preBuildPhase = this->CreateBuildPhase("CMake PreBuild Rules", "preBuildCommands", cmtarget, prebuild); // create prebuild phase cmXCodeObject* preLinkPhase = this->CreateBuildPhase("CMake PreLink Rules", "preLinkCommands", cmtarget, prelink); // create prebuild phase cmXCodeObject* postBuildPhase = this->CreateBuildPhase("CMake PostBuild Rules", "postBuildPhase", cmtarget, postbuild); // the order here is the order they will be built in if(preBuildPhase) { buildPhases->AddObject(preBuildPhase); } if(cmakeRulesBuildPhase) { buildPhases->AddObject(cmakeRulesBuildPhase); } if(sourceBuildPhase) { buildPhases->AddObject(sourceBuildPhase); } if(headerBuildPhase) { buildPhases->AddObject(headerBuildPhase); } if(preLinkPhase) { buildPhases->AddObject(preLinkPhase); } if(frameworkBuildPhase) { buildPhases->AddObject(frameworkBuildPhase); } if(postBuildPhase) { buildPhases->AddObject(postBuildPhase); } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase, cmTarget& target, std::vector const & commands, const char* name) { if(strcmp(name, "cmakeReRunPhase") == 0) { std::string cdir = m_CurrentMakefile->GetHomeOutputDirectory(); cdir = this->ConvertToRelativeForMake(cdir.c_str()); std::string makecmd = "make -C "; makecmd += cdir; makecmd += " -f "; makecmd += this->ConvertToRelativeForMake(m_CurrentReRunCMakeMakefile.c_str()); cmSystemTools::ReplaceString(makecmd, "\\ ", "\\\\ "); buildphase->AddAttribute("shellScript", this->CreateString(makecmd.c_str())); return; } std::string dir = m_CurrentMakefile->GetCurrentOutputDirectory(); dir += "/CMakeScripts"; cmSystemTools::MakeDirectory(dir.c_str()); std::string makefile = dir; makefile += "/"; makefile += target.GetName(); makefile += "_"; makefile += name; makefile += ".make"; cmGeneratedFileStream makefileStream(makefile.c_str()); if(!makefileStream) { return; } makefileStream << "# Generated by CMake, DO NOT EDIT\n"; makefileStream << "# Custom rules for " << target.GetName() << "\n"; // have all depend on all outputs makefileStream << "all: "; std::map tname; int count = 0; for(std::vector::const_iterator i = commands.begin(); i != commands.end(); ++i) { cmCustomCommand const& cc = *i; if(!cc.GetCommandLines().empty()) { if(cc.GetOutput()[0]) { makefileStream << "\\\n\t" << this-> ConvertToRelativeForMake(cc.GetOutput()); } else { char c = '1' + count++; tname[&cc] = std::string(target.GetName()) + c; makefileStream << "\\\n\t" << tname[&cc]; } } } makefileStream << "\n\n"; for(std::vector::const_iterator i = commands.begin(); i != commands.end(); ++i) { cmCustomCommand const& cc = *i; if(!cc.GetCommandLines().empty()) { makefileStream << "\n#" << "Custom command rule: " << cc.GetComment() << "\n"; if(cc.GetOutput()[0]) { makefileStream << this ->ConvertToRelativeForMake(cc.GetOutput()) << ": "; } else { makefileStream << tname[&cc] << ": "; } for(std::vector::const_iterator d = cc.GetDepends().begin(); d != cc.GetDepends().end(); ++d) { if(!this->FindTarget(m_CurrentProject.c_str(), d->c_str())) { makefileStream << "\\\n" << this ->ConvertToRelativeForMake(d->c_str()); } else { // if the depend is a target then make // the target with the source that is a custom command // depend on the that target via a AddUtility call target.AddUtility(d->c_str()); } } makefileStream << "\n"; // Add each command line to the set of commands. for(cmCustomCommandLines::const_iterator cl = cc.GetCommandLines().begin(); cl != cc.GetCommandLines().end(); ++cl) { // Build the command line in a single string. const cmCustomCommandLine& commandLine = *cl; std::string cmd = commandLine[0]; cmSystemTools::ReplaceString(cmd, "/./", "/"); cmd = this->ConvertToRelativeForMake(cmd.c_str()); for(unsigned int j=1; j < commandLine.size(); ++j) { cmd += " "; cmd += cmSystemTools::EscapeSpaces(commandLine[j].c_str()); } makefileStream << "\t" << cmd.c_str() << "\n"; } } } std::string cdir = m_CurrentMakefile->GetCurrentOutputDirectory(); cdir = this->ConvertToRelativeForXCode(cdir.c_str()); std::string makecmd = "make -C "; makecmd += cdir; makecmd += " -f "; makecmd += this->ConvertToRelativeForMake(makefile.c_str()); cmSystemTools::ReplaceString(makecmd, "\\ ", "\\\\ "); buildphase->AddAttribute("shellScript", this->CreateString(makecmd.c_str())); } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target, cmXCodeObject* buildSettings, std::string& fileType, std::string& productType, std::string& productName) { this->ConfigureOutputPaths(); std::string flags; bool shared = ((target.GetType() == cmTarget::SHARED_LIBRARY) || (target.GetType() == cmTarget::MODULE_LIBRARY)); if(shared) { flags += "-D"; if(const char* custom_export_name = target.GetProperty("DEFINE_SYMBOL")) { flags += custom_export_name; } else { std::string in = target.GetName(); in += "_EXPORTS"; flags += cmSystemTools::MakeCindentifier(in.c_str()); } } const char* lang = target.GetLinkerLanguage(this); if(lang) { // Add language-specific flags. m_CurrentLocalGenerator->AddLanguageFlags(flags, lang); // Add shared-library flags if needed. m_CurrentLocalGenerator->AddSharedFlags(flags, lang, shared); } // Add define flags m_CurrentLocalGenerator->AppendFlags(flags, m_CurrentMakefile->GetDefineFlags()); cmSystemTools::ReplaceString(flags, "\"", "\\\""); if(m_XcodeVersion == 21) { flags += " -DCMAKE_INTDIR=\\\\\\\"$(CONFIGURATION)\\\\\\\" "; } productName = target.GetName(); switch(target.GetType()) { case cmTarget::STATIC_LIBRARY: { if(m_LibraryOutputPath.size()) { buildSettings->AddAttribute("SYMROOT", this->CreateString (m_LibraryOutputPath.c_str())); } productName += ".a"; std::string t = "lib"; t += productName; productName = t; productType = "com.apple.product-type.library.static"; fileType = "archive.ar"; buildSettings->AddAttribute("LIBRARY_STYLE", this->CreateString("STATIC")); break; } case cmTarget::MODULE_LIBRARY: { if(m_LibraryOutputPath.size()) { buildSettings->AddAttribute("SYMROOT", this->CreateString (m_LibraryOutputPath.c_str())); } buildSettings->AddAttribute("EXECUTABLE_PREFIX", this->CreateString("lib")); buildSettings->AddAttribute("EXECUTABLE_EXTENSION", this->CreateString("so")); buildSettings->AddAttribute("LIBRARY_STYLE", this->CreateString("BUNDLE")); productName += ".so"; std::string t = "lib"; t += productName; productName = t; buildSettings->AddAttribute("OTHER_LDFLAGS", this->CreateString("-bundle")); productType = "com.apple.product-type.library.dynamic"; fileType = "compiled.mach-o.dylib"; break; } case cmTarget::SHARED_LIBRARY: { if(m_LibraryOutputPath.size()) { buildSettings->AddAttribute("SYMROOT", this->CreateString (m_LibraryOutputPath.c_str())); } buildSettings->AddAttribute("LIBRARY_STYLE", this->CreateString("DYNAMIC")); productName += ".dylib"; std::string t = "lib"; t += productName; productName = t; buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION", this->CreateString("1")); buildSettings->AddAttribute("DYLIB_CURRENT_VERSION", this->CreateString("1")); buildSettings->AddAttribute("OTHER_LDFLAGS", this->CreateString("-dynamiclib")); productType = "com.apple.product-type.library.dynamic"; fileType = "compiled.mach-o.dylib"; break; } case cmTarget::EXECUTABLE: { std::string symRoot; if(m_ExecutableOutputPath.size()) { std::string path = m_ExecutableOutputPath; if(target.GetPropertyAsBool("MACOSX_BUNDLE")) { path += target.GetName(); path += ".app/Contents/MacOS/"; } symRoot = path; } fileType = "compiled.mach-o.executable"; if(target.GetPropertyAsBool("MACOSX_BUNDLE")) { if(symRoot.size() == 0) { symRoot = target.GetName(); symRoot += ".app/Contents/MacOS/"; } productType = "com.apple.product-type.tool"; } else { productType = "com.apple.product-type.tool"; } if(symRoot.size()) { buildSettings->AddAttribute("SYMROOT", this->CreateString (symRoot.c_str())); } } break; case cmTarget::UTILITY: break; case cmTarget::INSTALL_FILES: break; case cmTarget::INSTALL_PROGRAMS: break; } std::string dirs; std::vector& includes = m_CurrentMakefile->GetIncludeDirectories(); std::vector::iterator i = includes.begin(); for(;i != includes.end(); ++i) { std::string incpath = this->XCodeEscapePath(i->c_str()); dirs += incpath + " "; } if(dirs.size()) { buildSettings->AddAttribute("HEADER_SEARCH_PATHS", this->CreateString(dirs.c_str())); } buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL", this->CreateString("0")); buildSettings->AddAttribute("INSTALL_PATH", this->CreateString("")); buildSettings->AddAttribute("OPTIMIZATION_CFLAGS", this->CreateString("")); buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags.c_str())); buildSettings->AddAttribute("OTHER_LDFLAGS", this->CreateString("")); buildSettings->AddAttribute("OTHER_REZFLAGS", this->CreateString("")); buildSettings->AddAttribute("SECTORDER_FLAGS", this->CreateString("")); buildSettings->AddAttribute("USE_HEADERMAP", this->CreateString("NO")); buildSettings->AddAttribute("WARNING_CFLAGS", this->CreateString( "-Wmost -Wno-four-char-constants" " -Wno-unknown-pragmas")); std::string pname; if(target.GetType() == cmTarget::SHARED_LIBRARY) { pname = "lib"; } pname += target.GetName(); buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(pname.c_str())); } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(cmTarget& cmtarget) { cmXCodeObject* shellBuildPhase = this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); shellBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); shellBuildPhase->AddAttribute("files", buildFiles); cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST); shellBuildPhase->AddAttribute("inputPaths", inputPaths); cmXCodeObject* outputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST); shellBuildPhase->AddAttribute("outputPaths", outputPaths); shellBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", this->CreateString("0")); shellBuildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh")); shellBuildPhase->AddAttribute("shellScript", this->CreateString( "# shell script goes here\nexit 0")); cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXAggregateTarget); cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST); this->CreateCustomCommands(buildPhases, 0, 0, 0, cmtarget); target->AddAttribute("buildPhases", buildPhases); cmXCodeObject* buildSettings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); std::string fileTypeString; std::string productTypeString; std::string productName; this->CreateBuildSettings(cmtarget, buildSettings, fileTypeString, productTypeString, productName); target->AddAttribute("buildSettings", buildSettings); cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST); target->AddAttribute("dependencies", dependencies); target->AddAttribute("name", this->CreateString(cmtarget.GetName())); target->AddAttribute("productName",this->CreateString(cmtarget.GetName())); target->SetcmTarget(&cmtarget); return target; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget(cmTarget& cmtarget, cmXCodeObject* buildPhases) { cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget); target->AddAttribute("buildPhases", buildPhases); cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST); target->AddAttribute("buildRules", buildRules); cmXCodeObject* buildSettings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); std::string fileTypeString; std::string productTypeString; std::string productName; this->CreateBuildSettings(cmtarget, buildSettings, fileTypeString, productTypeString, productName); target->AddAttribute("buildSettings", buildSettings); cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST); target->AddAttribute("dependencies", dependencies); target->AddAttribute("name", this->CreateString(cmtarget.GetName())); target->AddAttribute("productName",this->CreateString(cmtarget.GetName())); cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); fileRef->AddAttribute("explicitFileType", this->CreateString(fileTypeString.c_str())); fileRef->AddAttribute("path", this->CreateString(productName.c_str())); fileRef->AddAttribute("refType", this->CreateString("0")); fileRef->AddAttribute("sourceTree", this->CreateString("BUILT_PRODUCTS_DIR")); target->AddAttribute("productReference", this->CreateObjectReference(fileRef)); target->AddAttribute("productType", this->CreateString(productTypeString.c_str())); target->SetcmTarget(&cmtarget); return target; } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget(cmTarget* t) { if(!t) { return 0; } for(std::vector::iterator i = m_XCodeObjects.begin(); i != m_XCodeObjects.end(); ++i) { cmXCodeObject* o = *i; if(o->GetcmTarget() == t) { return o; } } return 0; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target, cmXCodeObject* dependTarget) { // make sure a target does not depend on itself if(target == dependTarget) { return; } // now avoid circular references if dependTarget already // depends on target then skip it. Circular references crashes // xcode cmXCodeObject* dependTargetDepends = dependTarget->GetObject("dependencies"); if(dependTargetDepends) { if(dependTargetDepends->HasObject(target->GetPBXTargetDependency())) { return; } } cmXCodeObject* targetdep = dependTarget->GetPBXTargetDependency(); if(!targetdep) { cmXCodeObject* container = this->CreateObject(cmXCodeObject::PBXContainerItemProxy); container->AddAttribute("containerPortal", this->CreateObjectReference(m_RootObject)); container->AddAttribute("proxyType", this->CreateString("1")); container->AddAttribute("remoteGlobalIDString", this->CreateObjectReference(dependTarget)); container->AddAttribute("remoteInfo", this->CreateString( dependTarget->GetcmTarget()->GetName())); targetdep = this->CreateObject(cmXCodeObject::PBXTargetDependency); targetdep->AddAttribute("target", this->CreateObjectReference(dependTarget)); targetdep->AddAttribute("targetProxy", this->CreateObjectReference(container)); dependTarget->SetPBXTargetDependency(targetdep); } cmXCodeObject* depends = target->GetObject("dependencies"); if(!depends) { cmSystemTools:: Error("target does not have dependencies attribute error.."); } else { depends->AddUniqueObject(targetdep); } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::AddLinkLibrary(cmXCodeObject* target, const char* library, cmTarget* dtarget) { if(dtarget) { target->AddDependLibrary(this->GetTargetFullPath(dtarget).c_str()); } // if the library is not a full path then add it with a -l flag // to the settings of the target cmXCodeObject* settings = target->GetObject("buildSettings"); cmXCodeObject* ldflags = settings->GetObject("OTHER_LDFLAGS"); std::string link = ldflags->GetString(); cmSystemTools::ReplaceString(link, "\"", ""); cmsys::RegularExpression reg("^([ \t]*\\-[lLWRB])|([ \t]*\\-framework)|(\\${)|([ \t]*\\-pthread)|([ \t]*`)"); // if the library is not already in the form required by the compiler // add a -l infront of the name link += " "; if(!reg.find(library)) { link += "-l"; } link += library; ldflags->SetString(link.c_str()); } //---------------------------------------------------------------------------- std::string cmGlobalXCodeGenerator::GetTargetFullPath(cmTarget* target) { std::string libPath; cmXCodeObject* xtarget = this->FindXCodeTarget(target); cmXCodeObject* bset = xtarget->GetObject("buildSettings"); cmXCodeObject* spath = bset->GetObject("SYMROOT"); if(m_XcodeVersion == 21) { libPath += "$(CONFIGURATION)/"; } libPath = spath->GetString(); libPath = libPath.substr(1, libPath.size()-2); if(target->GetType() == cmTarget::STATIC_LIBRARY) { libPath += "lib"; libPath += target->GetName(); libPath += ".a"; } else if(target->GetType() == cmTarget::SHARED_LIBRARY) { libPath += "lib"; libPath += target->GetName(); libPath += ".dylib"; } else { libPath += target->GetName(); } return libPath; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) { cmTarget* cmtarget = target->GetcmTarget(); if(!cmtarget) { cmSystemTools::Error("Error no target on xobject\n"); return; } // compute the correct order for link libraries cmOrderLinkDirectories orderLibs; std::string ext = m_CurrentMakefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"); if(ext.size()) { orderLibs.AddLinkExtension(ext.c_str()); } ext = m_CurrentMakefile->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"); if(ext.size()) { orderLibs.AddLinkExtension(ext.c_str()); } ext = m_CurrentMakefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); if(ext.size()) { orderLibs.AddLinkExtension(ext.c_str()); } const char* targetLibrary = cmtarget->GetName(); if(cmtarget->GetType() == cmTarget::EXECUTABLE) { targetLibrary = 0; } orderLibs.SetLinkInformation(*cmtarget, cmTarget::GENERAL, targetLibrary); orderLibs.DetermineLibraryPathOrder(); std::vector libdirs; std::vector linkItems; orderLibs.GetLinkerInformation(libdirs, linkItems); std::string linkDirs; // add the library search paths for(std::vector::const_iterator libDir = libdirs.begin(); libDir != libdirs.end(); ++libDir) { if(libDir->size() && *libDir != "/usr/lib") { if(m_XcodeVersion == 21) { // now add the same one but append $(CONFIGURATION) to it: linkDirs += " "; linkDirs += this->XCodeEscapePath(libDir->c_str()); linkDirs += "/$(CONFIGURATION)"; } linkDirs += " "; linkDirs += this->XCodeEscapePath(libDir->c_str()); } } cmXCodeObject* bset = target->GetObject("buildSettings"); if(bset) { bset->AddAttribute("LIBRARY_SEARCH_PATHS", this->CreateString(linkDirs.c_str())); } // now add the link libraries for(std::vector::iterator lib = linkItems.begin(); lib != linkItems.end(); ++lib) { cmTarget* t = this->FindTarget(m_CurrentProject.c_str(), lib->c_str()); cmXCodeObject* dptarget = this->FindXCodeTarget(t); if(dptarget) { this->AddDependTarget(target, dptarget); if(cmtarget->GetType() != cmTarget::STATIC_LIBRARY) { this->AddLinkLibrary(target, t->GetName(), t); } } else { if(cmtarget->GetType() != cmTarget::STATIC_LIBRARY) { this->AddLinkLibrary(target, lib->c_str()); } } } // write utility dependencies. for(std::set::const_iterator i = cmtarget->GetUtilities().begin(); i != cmtarget->GetUtilities().end(); ++i) { cmTarget* t = this->FindTarget(m_CurrentProject.c_str(), i->c_str()); // if the target is in this project then make target depend // on it. It may not be in this project if this is a sub // project from the top. if(t) { cmXCodeObject* dptarget = this->FindXCodeTarget(t); if(dptarget) { this->AddDependTarget(target, dptarget); } else { std::string m = "Error Utility: "; m += i->c_str(); m += "\n"; m += "cmtarget "; if(t) { m += t->GetName(); } m += "\n"; m += "Is on the target "; m += cmtarget->GetName(); m += "\n"; m += "But it has no xcode target created yet??\n"; m += "Current project is "; m += m_CurrentProject.c_str(); cmSystemTools::Error(m.c_str()); } } } std::vector fullPathLibs; orderLibs.GetFullPathLibraries(fullPathLibs); for(std::vector::iterator i = fullPathLibs.begin(); i != fullPathLibs.end(); ++i) { target->AddDependLibrary(i->c_str()); } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateGroups(cmLocalGenerator* root, std::vector& generators) { for(std::vector::iterator i = generators.begin(); i != generators.end(); ++i) { if(this->IsExcluded(root, *i)) { continue; } cmMakefile* mf = (*i)->GetMakefile(); std::vector sourceGroups = mf->GetSourceGroups(); cmTargets &tgts = mf->GetTargets(); for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++) { cmTarget& cmtarget = l->second; std::vector & classes = cmtarget.GetSourceFiles(); for(std::vector::const_iterator s = classes.begin(); s != classes.end(); s++) { cmSourceFile* sf = *s; // Add the file to the list of sources. std::string const& source = sf->GetFullPath(); cmSourceGroup& sourceGroup = mf->FindSourceGroup(source.c_str(), sourceGroups); cmXCodeObject* pbxgroup = this->CreateOrGetPBXGroup(cmtarget, &sourceGroup); m_GroupMap[sf] = pbxgroup; } } } } //---------------------------------------------------------------------------- cmXCodeObject* cmGlobalXCodeGenerator::CreateOrGetPBXGroup(cmTarget& cmtarget, cmSourceGroup* sg) { cmStdString s = cmtarget.GetName(); s += "/"; s += sg->GetName(); std::map::iterator i = m_GroupNameMap.find(s); if(i != m_GroupNameMap.end()) { return i->second; } i = m_TargetGroup.find(cmtarget.GetName()); cmXCodeObject* tgroup = 0; if(i != m_TargetGroup.end()) { tgroup = i->second; } else { tgroup = this->CreateObject(cmXCodeObject::PBXGroup); m_TargetGroup[cmtarget.GetName()] = tgroup; cmXCodeObject* tgroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST); tgroup->AddAttribute("name", this->CreateString(cmtarget.GetName())); tgroup->AddAttribute("children", tgroupChildren); tgroup->AddAttribute("refType", this->CreateString("4")); tgroup->AddAttribute("sourceTree", this->CreateString("")); m_SourcesGroupChildren->AddObject(tgroup); } cmXCodeObject* tgroupChildren = tgroup->GetObject("children"); cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup); cmXCodeObject* groupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST); group->AddAttribute("name", this->CreateString(sg->GetName())); group->AddAttribute("children", groupChildren); group->AddAttribute("refType", this->CreateString("4")); group->AddAttribute("sourceTree", this->CreateString("")); tgroupChildren->AddObject(group); m_GroupNameMap[s] = group; return group; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateXCodeObjects(cmLocalGenerator* root, std::vector& generators ) { this->ClearXCodeObjects(); m_RootObject = 0; m_SourcesGroupChildren = 0; m_MainGroupChildren = 0; cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO")); cmXCodeObject* developBuildStyle = this->CreateObject(cmXCodeObject::PBXBuildStyle); developBuildStyle->AddAttribute("name", this->CreateString("Development")); developBuildStyle->AddAttribute("buildSettings", group); group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("YES")); cmXCodeObject* deployBuildStyle = this->CreateObject(cmXCodeObject::PBXBuildStyle); deployBuildStyle->AddAttribute("name", this->CreateString("Deployment")); deployBuildStyle->AddAttribute("buildSettings", group); cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST); listObjs->AddObject(developBuildStyle); listObjs->AddObject(deployBuildStyle); cmXCodeObject* mainGroup = this->CreateObject(cmXCodeObject::PBXGroup); m_MainGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST); mainGroup->AddAttribute("children", m_MainGroupChildren); mainGroup->AddAttribute("refType", this->CreateString("4")); mainGroup->AddAttribute("sourceTree", this->CreateString("")); cmXCodeObject* sourcesGroup = this->CreateObject(cmXCodeObject::PBXGroup); m_SourcesGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST); sourcesGroup->AddAttribute("name", this->CreateString("Sources")); sourcesGroup->AddAttribute("children", m_SourcesGroupChildren); sourcesGroup->AddAttribute("refType", this->CreateString("4")); sourcesGroup->AddAttribute("sourceTree", this->CreateString("")); m_MainGroupChildren->AddObject(sourcesGroup); // now create the cmake groups this->CreateGroups(root, generators); cmXCodeObject* productGroup = this->CreateObject(cmXCodeObject::PBXGroup); productGroup->AddAttribute("name", this->CreateString("Products")); productGroup->AddAttribute("refType", this->CreateString("4")); productGroup->AddAttribute("sourceTree", this->CreateString("")); cmXCodeObject* productGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST); productGroup->AddAttribute("children", productGroupChildren); m_MainGroupChildren->AddObject(productGroup); m_RootObject = this->CreateObject(cmXCodeObject::PBXProject); group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); m_RootObject->AddAttribute("mainGroup", this->CreateObjectReference(mainGroup)); m_RootObject->AddAttribute("buildSettings", group); m_RootObject->AddAttribute("buildSyles", listObjs); m_RootObject->AddAttribute("hasScannedForEncodings", this->CreateString("0")); std::vector targets; for(std::vector::iterator i = generators.begin(); i != generators.end(); ++i) { if(!this->IsExcluded(root, *i)) { this->CreateXCodeTargets(*i, targets); } } // loop over all targets and add link and depend info for(std::vector::iterator i = targets.begin(); i != targets.end(); ++i) { cmXCodeObject* t = *i; this->AddDependAndLinkInformation(t); } // now create xcode depend hack makefile this->CreateXCodeDependHackTarget(targets); // now add all targets to the root object cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST); for(std::vector::iterator i = targets.begin(); i != targets.end(); ++i) { cmXCodeObject* t = *i; allTargets->AddObject(t); cmXCodeObject* productRef = t->GetObject("productReference"); if(productRef) { productGroupChildren->AddObject(productRef->GetObject()); } } m_RootObject->AddAttribute("targets", allTargets); } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( std::vector& targets) { cmGeneratedFileStream makefileStream(m_CurrentXCodeHackMakefile.c_str()); if(!makefileStream) { cmSystemTools::Error("Could not create", m_CurrentXCodeHackMakefile.c_str()); return; } // one more pass for external depend information not handled // correctly by xcode makefileStream << "# DO NOT EDIT\n"; makefileStream << "# This makefile makes sure all linkable targets are \n"; makefileStream << "# up-to-date with anything they link to,avoiding a bug in XCode 1.5\n"; makefileStream << "all: "; for(std::vector::iterator i = targets.begin(); i != targets.end(); ++i) { cmXCodeObject* target = *i; cmTarget* t =target->GetcmTarget(); if(t->GetType() == cmTarget::EXECUTABLE || t->GetType() == cmTarget::SHARED_LIBRARY || t->GetType() == cmTarget::MODULE_LIBRARY) { makefileStream << "\\\n\t" << this-> ConvertToRelativeForMake(this->GetTargetFullPath(target->GetcmTarget()).c_str()); } } makefileStream << "\n\n"; makefileStream << "# For each target create a dummy rule " "so the target does not have to exist\n"; std::set emitted; for(std::vector::iterator i = targets.begin(); i != targets.end(); ++i) { cmXCodeObject* target = *i; std::vector const& deplibs = target->GetDependLibraries(); for(std::vector::const_iterator d = deplibs.begin(); d != deplibs.end(); ++d) { if(emitted.insert(*d).second) { makefileStream << this->ConvertToRelativeForMake(d->c_str()) << ":\n"; } } } makefileStream << "\n\n"; makefileStream << "# Each linkable target depends on everything it links to.\n"; makefileStream << "#And the target is removed if it is older than what it linkes to\n"; for(std::vector::iterator i = targets.begin(); i != targets.end(); ++i) { cmXCodeObject* target = *i; cmTarget* t =target->GetcmTarget(); if(t->GetType() == cmTarget::EXECUTABLE || t->GetType() == cmTarget::SHARED_LIBRARY || t->GetType() == cmTarget::MODULE_LIBRARY) { std::vector const& deplibs = target->GetDependLibraries(); std::string tfull = this->GetTargetFullPath(target->GetcmTarget()); makefileStream << this->ConvertToRelativeForMake(tfull.c_str()) << ": "; for(std::vector::const_iterator d = deplibs.begin(); d != deplibs.end(); ++d) { makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(d->c_str()); } makefileStream << "\n"; makefileStream << "\t/bin/rm -f " << this->ConvertToRelativeForMake(tfull.c_str()) << "\n"; makefileStream << "\n\n"; } } } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::OutputXCodeProject(cmLocalGenerator* root, std::vector& generators) { if(generators.size() == 0) { return; } this->CreateXCodeObjects(root, generators); std::string xcodeDir = root->GetMakefile()->GetStartOutputDirectory(); xcodeDir += "/"; xcodeDir += root->GetMakefile()->GetProjectName(); xcodeDir += ".xcode"; cmSystemTools::MakeDirectory(xcodeDir.c_str()); xcodeDir += "/project.pbxproj"; cmGeneratedFileStream fout(xcodeDir.c_str()); fout.SetCopyIfDifferent(true); if(!fout) { return; } this->WriteXCodePBXProj(fout, root, generators); this->ClearXCodeObjects(); } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout, cmLocalGenerator* , std::vector& ) { fout << "// !$*UTF8*$!\n"; fout << "{\n"; cmXCodeObject::Indent(1, fout); fout << "archiveVersion = 1;\n"; cmXCodeObject::Indent(1, fout); fout << "classes = {\n"; cmXCodeObject::Indent(1, fout); fout << "};\n"; cmXCodeObject::Indent(1, fout); fout << "objectVersion = 39;\n"; cmXCodeObject::PrintList(m_XCodeObjects, fout); cmXCodeObject::Indent(1, fout); fout << "rootObject = " << m_RootObject->GetId() << ";\n"; fout << "}\n"; } //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry) const { entry.name = this->GetName(); entry.brief = "Generate XCode project files."; entry.full = ""; } //---------------------------------------------------------------------------- std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(const char* p) { if ( !m_CurrentMakefile->IsOn("CMAKE_USE_RELATIVE_PATHS") ) { return cmSystemTools::ConvertToOutputPath(p); } else { std::string ret = this->ConvertToRelativePath(m_CurrentOutputDirectoryComponents, p); return cmSystemTools::ConvertToOutputPath(ret.c_str()); } } //---------------------------------------------------------------------------- std::string cmGlobalXCodeGenerator::ConvertToRelativeForXCode(const char* p) { if ( !m_CurrentMakefile->IsOn("CMAKE_USE_RELATIVE_PATHS") ) { return cmSystemTools::ConvertToOutputPath(p); } else { std::string ret = this->ConvertToRelativePath(m_ProjectOutputDirectoryComponents, p); return cmSystemTools::ConvertToOutputPath(ret.c_str()); } } std::string cmGlobalXCodeGenerator::XCodeEscapePath(const char* p) { std::string ret = p; if(ret.find(' ') != ret.npos) { std::string t = ret; ret = "\\\""; ret += t; ret += "\\\""; } return ret; }