| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "cmGlobalXCodeGenerator.h" |
| #include "cmLocalXCodeGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmXCodeObject.h" |
| #include "cmXCode21Object.h" |
| #include "cmake.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmComputeLinkInformation.h" |
| #include "cmSourceFile.h" |
| #include "cmCustomCommandGenerator.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGlobalGeneratorFactory.h" |
| |
| #include <cmsys/auto_ptr.hxx> |
| |
| //---------------------------------------------------------------------------- |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| #include "cmXMLParser.h" |
| |
| // parse the xml file storing the installed version of Xcode on |
| // the machine |
| class cmXcodeVersionParser : public cmXMLParser |
| { |
| public: |
| cmXcodeVersionParser(): Version("1.5") {} |
| void StartElement(const char* , const char** ) |
| { |
| this->Data = ""; |
| } |
| void EndElement(const char* name) |
| { |
| if(strcmp(name, "key") == 0) |
| { |
| this->Key = this->Data; |
| } |
| else if(strcmp(name, "string") == 0) |
| { |
| if(this->Key == "CFBundleShortVersionString") |
| { |
| this->Version = this->Data; |
| } |
| } |
| } |
| void CharacterDataHandler(const char* data, int length) |
| { |
| this->Data.append(data, length); |
| } |
| std::string Version; |
| std::string Key; |
| std::string Data; |
| }; |
| #endif |
| |
| // Builds either an object list or a space-separated string from the |
| // given inputs. |
| class cmGlobalXCodeGenerator::BuildObjectListOrString |
| { |
| cmGlobalXCodeGenerator *Generator; |
| cmXCodeObject *Group; |
| bool Empty; |
| std::string String; |
| |
| public: |
| BuildObjectListOrString(cmGlobalXCodeGenerator *gen, bool buildObjectList) |
| : Generator(gen), Group(0), Empty(true) |
| { |
| if (buildObjectList) |
| { |
| this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST); |
| } |
| } |
| |
| bool IsEmpty() const { return this->Empty; } |
| |
| void Add(const char *newString) |
| { |
| this->Empty = false; |
| |
| if (this->Group) |
| { |
| this->Group->AddObject(this->Generator->CreateString(newString)); |
| } |
| else |
| { |
| this->String += newString; |
| this->String += ' '; |
| } |
| } |
| |
| const std::string &GetString() const { return this->String; } |
| |
| cmXCodeObject *CreateList() |
| { |
| if (this->Group) |
| { |
| return this->Group; |
| } |
| else |
| { |
| return this->Generator->CreateString(this->String.c_str()); |
| } |
| } |
| }; |
| |
| class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory |
| { |
| public: |
| virtual cmGlobalGenerator* CreateGlobalGenerator(const char* name) const; |
| |
| virtual void GetDocumentation(cmDocumentationEntry& entry) const { |
| cmGlobalXCodeGenerator::GetDocumentation(entry); } |
| |
| virtual void GetGenerators(std::vector<std::string>& names) const { |
| names.push_back(cmGlobalXCodeGenerator::GetActualName()); } |
| }; |
| |
| //---------------------------------------------------------------------------- |
| cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(std::string const& version) |
| { |
| this->VersionString = version; |
| |
| // Compute an integer form of the version number. |
| unsigned int v[2] = {0,0}; |
| sscanf(this->VersionString.c_str(), "%u.%u", &v[0], &v[1]); |
| this->XcodeVersion = 10*v[0] + v[1]; |
| |
| this->FindMakeProgramFile = "CMakeFindXCode.cmake"; |
| this->RootObject = 0; |
| this->MainGroupChildren = 0; |
| this->SourcesGroupChildren = 0; |
| this->ResourcesGroupChildren = 0; |
| this->CurrentMakefile = 0; |
| this->CurrentLocalGenerator = 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmGlobalGeneratorFactory* cmGlobalXCodeGenerator::NewFactory() |
| { |
| return new Factory; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmGlobalGenerator* cmGlobalXCodeGenerator::Factory |
| ::CreateGlobalGenerator(const char* name) const |
| { |
| if (strcmp(name, GetActualName())) |
| return 0; |
| #if defined(CMAKE_BUILD_WITH_CMAKE) |
| cmXcodeVersionParser parser; |
| std::string versionFile; |
| { |
| std::string out; |
| std::string::size_type pos; |
| if(cmSystemTools::RunSingleCommand("xcode-select --print-path", &out, 0, 0, |
| cmSystemTools::OUTPUT_NONE) && |
| (pos = out.find(".app/"), pos != out.npos)) |
| { |
| versionFile = out.substr(0, pos+5)+"Contents/version.plist"; |
| } |
| } |
| if(!versionFile.empty() && cmSystemTools::FileExists(versionFile.c_str())) |
| { |
| parser.ParseFile(versionFile.c_str()); |
| } |
| else if (cmSystemTools::FileExists( |
| "/Applications/Xcode.app/Contents/version.plist")) |
| { |
| parser.ParseFile |
| ("/Applications/Xcode.app/Contents/version.plist"); |
| } |
| else |
| { |
| parser.ParseFile |
| ("/Developer/Applications/Xcode.app/Contents/version.plist"); |
| } |
| cmsys::auto_ptr<cmGlobalXCodeGenerator> |
| gg(new cmGlobalXCodeGenerator(parser.Version)); |
| if (gg->XcodeVersion == 20) |
| { |
| cmSystemTools::Message("Xcode 2.0 not really supported by cmake, " |
| "using Xcode 15 generator\n"); |
| gg->XcodeVersion = 15; |
| } |
| return gg.release(); |
| #else |
| std::cerr << "CMake should be built with cmake to use Xcode, " |
| "default to Xcode 1.5\n"; |
| return new cmGlobalXCodeGenerator; |
| #endif |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts) |
| { |
| if(this->XcodeVersion >= 30) |
| { |
| this->PlatformToolset = ts; |
| return true; |
| } |
| else |
| { |
| return cmGlobalGenerator::SetGeneratorToolset(ts); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::EnableLanguage(std::vector<std::string>const& |
| lang, |
| cmMakefile * mf, bool optional) |
| { |
| mf->AddDefinition("XCODE","1"); |
| mf->AddDefinition("XCODE_VERSION", this->VersionString.c_str()); |
| if(this->XcodeVersion == 15) |
| { |
| } |
| else |
| { |
| if(!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) |
| { |
| mf->AddCacheDefinition( |
| "CMAKE_CONFIGURATION_TYPES", |
| "Debug;Release;MinSizeRel;RelWithDebInfo", |
| "Semicolon separated list of supported configuration types, " |
| "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, " |
| "anything else will be ignored.", |
| cmCacheManager::STRING); |
| } |
| } |
| mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1"); |
| if(!this->PlatformToolset.empty()) |
| { |
| mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", |
| this->PlatformToolset.c_str()); |
| } |
| this->cmGlobalGenerator::EnableLanguage(lang, mf, optional); |
| const char* osxArch = |
| mf->GetDefinition("CMAKE_OSX_ARCHITECTURES"); |
| const char* sysroot = |
| mf->GetDefinition("CMAKE_OSX_SYSROOT"); |
| if(osxArch && sysroot) |
| { |
| this->Architectures.clear(); |
| cmSystemTools::ExpandListArgument(std::string(osxArch), |
| this->Architectures); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::GenerateBuildCommand( |
| std::vector<std::string>& makeCommand, |
| const char* makeProgram, |
| const char* projectName, |
| const char* /*projectDir*/, |
| const char* targetName, |
| const char* config, |
| bool /*fast*/, |
| std::vector<std::string> const& makeOptions) |
| { |
| // now build the test |
| makeCommand.push_back( |
| this->SelectMakeProgram(makeProgram, "xcodebuild") |
| ); |
| |
| makeCommand.push_back("-project"); |
| std::string projectArg = projectName; |
| projectArg += ".xcode"; |
| if(this->XcodeVersion > 20) |
| { |
| projectArg += "proj"; |
| } |
| makeCommand.push_back(projectArg); |
| |
| bool clean = false; |
| if ( targetName && strcmp(targetName, "clean") == 0 ) |
| { |
| clean = true; |
| targetName = "ALL_BUILD"; |
| } |
| if(clean) |
| { |
| makeCommand.push_back("clean"); |
| } |
| else |
| { |
| makeCommand.push_back("build"); |
| } |
| makeCommand.push_back("-target"); |
| // if it is a null string for config don't use it |
| if(config && *config == 0) |
| { |
| config = 0; |
| } |
| if (targetName && strlen(targetName)) |
| { |
| makeCommand.push_back(targetName); |
| } |
| else |
| { |
| makeCommand.push_back("ALL_BUILD"); |
| } |
| if(this->XcodeVersion == 15) |
| { |
| makeCommand.push_back("-buildstyle"); |
| makeCommand.push_back("Development"); |
| } |
| else |
| { |
| makeCommand.push_back("-configuration"); |
| makeCommand.push_back(config?config:"Debug"); |
| } |
| makeCommand.insert(makeCommand.end(), |
| makeOptions.begin(), makeOptions.end()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| ///! Create a local generator appropriate to this Global Generator |
| cmLocalGenerator *cmGlobalXCodeGenerator::CreateLocalGenerator() |
| { |
| cmLocalGenerator *lg = new cmLocalXCodeGenerator; |
| lg->SetGlobalGenerator(this); |
| return lg; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::Generate() |
| { |
| std::map<cmStdString, std::vector<cmLocalGenerator*> >::iterator it; |
| // make sure extra targets are added before calling |
| // the parent generate which will call trace depends |
| for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it) |
| { |
| cmLocalGenerator* root = it->second[0]; |
| this->SetGenerationRoot(root); |
| // add ALL_BUILD, INSTALL, etc |
| this->AddExtraTargets(root, it->second); |
| } |
| this->ForceLinkerLanguages(); |
| this->cmGlobalGenerator::Generate(); |
| if(cmSystemTools::GetErrorOccuredFlag()) |
| { |
| return; |
| } |
| for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it) |
| { |
| cmLocalGenerator* root = it->second[0]; |
| this->SetGenerationRoot(root); |
| // now create the project |
| this->OutputXCodeProject(root, it->second); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root) |
| { |
| this->CurrentProject = root->GetMakefile()->GetProjectName(); |
| this->SetCurrentLocalGenerator(root); |
| cmSystemTools::SplitPath(this->CurrentMakefile->GetCurrentDirectory(), |
| this->ProjectSourceDirectoryComponents); |
| cmSystemTools::SplitPath(this->CurrentMakefile->GetCurrentOutputDirectory(), |
| this->ProjectOutputDirectoryComponents); |
| |
| this->CurrentXCodeHackMakefile = |
| root->GetMakefile()->GetCurrentOutputDirectory(); |
| this->CurrentXCodeHackMakefile += "/CMakeScripts"; |
| cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile.c_str()); |
| this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make"; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| cmGlobalXCodeGenerator::PostBuildMakeTarget(std::string const& tName, |
| std::string const& configName) |
| { |
| std::string target = tName; |
| cmSystemTools::ReplaceString(target, " ", "_"); |
| std::string out = "PostBuild." + target; |
| if(this->XcodeVersion > 20) |
| { |
| out += "." + configName; |
| } |
| return out; |
| } |
| |
| //---------------------------------------------------------------------------- |
| #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK" |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root, |
| std::vector<cmLocalGenerator*>& gens) |
| { |
| cmMakefile* mf = root->GetMakefile(); |
| |
| // Add ALL_BUILD |
| const char* no_working_directory = 0; |
| std::vector<std::string> no_depends; |
| mf->AddUtilityCommand("ALL_BUILD", true, no_depends, |
| no_working_directory, |
| "echo", "Build all projects"); |
| cmTarget* allbuild = mf->FindTarget("ALL_BUILD"); |
| |
| // Refer to the main build configuration file for easy editing. |
| std::string listfile = mf->GetStartDirectory(); |
| listfile += "/"; |
| listfile += "CMakeLists.txt"; |
| allbuild->AddSource(listfile.c_str()); |
| |
| // Add XCODE depend helper |
| std::string dir = mf->GetCurrentOutputDirectory(); |
| cmCustomCommandLine makeHelper; |
| if(this->XcodeVersion < 50) |
| { |
| makeHelper.push_back("make"); |
| makeHelper.push_back("-C"); |
| makeHelper.push_back(dir.c_str()); |
| makeHelper.push_back("-f"); |
| makeHelper.push_back(this->CurrentXCodeHackMakefile.c_str()); |
| makeHelper.push_back(""); // placeholder, see below |
| } |
| |
| // Add ZERO_CHECK |
| bool regenerate = !mf->IsOn("CMAKE_SUPPRESS_REGENERATION"); |
| if (regenerate) |
| { |
| this->CreateReRunCMakeFile(root, gens); |
| std::string file = this->ConvertToRelativeForMake( |
| this->CurrentReRunCMakeMakefile.c_str()); |
| cmSystemTools::ReplaceString(file, "\\ ", " "); |
| mf->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, true, no_depends, |
| no_working_directory, |
| "make", "-f", file.c_str()); |
| } |
| |
| // now make the allbuild depend on all the non-utility targets |
| // in the project |
| for(std::vector<cmLocalGenerator*>::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; |
| |
| if (regenerate && (l->first != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) |
| { |
| target.AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET); |
| } |
| |
| // make all exe, shared libs and modules |
| // run the depend check makefile as a post build rule |
| // this will make sure that when the next target is built |
| // things are up-to-date |
| if(!makeHelper.empty() && |
| (target.GetType() == cmTarget::EXECUTABLE || |
| // Nope - no post-build for OBJECT_LIRBRARY |
| // target.GetType() == cmTarget::OBJECT_LIBRARY || |
| target.GetType() == cmTarget::STATIC_LIBRARY || |
| target.GetType() == cmTarget::SHARED_LIBRARY || |
| target.GetType() == cmTarget::MODULE_LIBRARY)) |
| { |
| makeHelper[makeHelper.size()-1] = // fill placeholder |
| this->PostBuildMakeTarget(target.GetName(), "$(CONFIGURATION)"); |
| cmCustomCommandLines commandLines; |
| commandLines.push_back(makeHelper); |
| lg->GetMakefile()->AddCustomCommandToTarget(target.GetName(), |
| no_depends, |
| commandLines, |
| cmTarget::POST_BUILD, |
| "Depend check for xcode", |
| dir.c_str()); |
| } |
| |
| if(target.GetType() != cmTarget::INTERFACE_LIBRARY |
| && !target.GetPropertyAsBool("EXCLUDE_FROM_ALL")) |
| { |
| allbuild->AddUtility(target.GetName()); |
| } |
| |
| // Refer to the build configuration file for easy editing. |
| listfile = lg->GetMakefile()->GetStartDirectory(); |
| listfile += "/"; |
| listfile += "CMakeLists.txt"; |
| target.AddSource(listfile.c_str()); |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::CreateReRunCMakeFile( |
| cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens) |
| { |
| cmMakefile* mf = root->GetMakefile(); |
| std::vector<std::string> lfiles; |
| for(std::vector<cmLocalGenerator*>::const_iterator gi = gens.begin(); |
| gi != gens.end(); ++gi) |
| { |
| std::vector<std::string> const& lf = (*gi)->GetMakefile()->GetListFiles(); |
| lfiles.insert(lfiles.end(), lf.begin(), lf.end()); |
| } |
| |
| // sort the array |
| std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>()); |
| std::vector<std::string>::iterator new_end = |
| std::unique(lfiles.begin(), lfiles.end()); |
| lfiles.erase(new_end, lfiles.end()); |
| this->CurrentReRunCMakeMakefile = mf->GetStartOutputDirectory(); |
| this->CurrentReRunCMakeMakefile += "/CMakeScripts"; |
| cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile.c_str()); |
| this->CurrentReRunCMakeMakefile += "/ReRunCMake.make"; |
| cmGeneratedFileStream makefileStream |
| (this->CurrentReRunCMakeMakefile.c_str()); |
| makefileStream.SetCopyIfDifferent(true); |
| makefileStream << "# Generated by CMake, DO NOT EDIT\n"; |
| std::string checkCache = mf->GetHomeOutputDirectory(); |
| checkCache += "/"; |
| checkCache += cmake::GetCMakeFilesDirectoryPostSlash(); |
| checkCache += "cmake.check_cache"; |
| makefileStream << this->ConvertToRelativeForMake(checkCache.c_str()) |
| << ": "; |
| for(std::vector<std::string>::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() |
| { |
| this->TargetDoneSet.clear(); |
| for(unsigned int i = 0; i < this->XCodeObjects.size(); ++i) |
| { |
| delete this->XCodeObjects[i]; |
| } |
| this->XCodeObjects.clear(); |
| this->XCodeObjectIDs.clear(); |
| this->GroupMap.clear(); |
| this->GroupNameMap.clear(); |
| this->TargetGroup.clear(); |
| this->FileRefs.clear(); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::addObject(cmXCodeObject *obj) |
| { |
| if(obj->GetType() == cmXCodeObject::OBJECT) |
| { |
| cmStdString id = obj->GetId(); |
| |
| // If this is a duplicate id, it's an error: |
| // |
| if(this->XCodeObjectIDs.count(id)) |
| { |
| cmSystemTools::Error( |
| "Xcode generator: duplicate object ids not allowed"); |
| } |
| |
| this->XCodeObjectIDs.insert(id); |
| } |
| |
| this->XCodeObjects.push_back(obj); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::PBXType ptype) |
| { |
| cmXCodeObject* obj; |
| if(this->XcodeVersion == 15) |
| { |
| obj = new cmXCodeObject(ptype, cmXCodeObject::OBJECT); |
| } |
| else |
| { |
| obj = new cmXCode21Object(ptype, cmXCodeObject::OBJECT); |
| } |
| this->addObject(obj); |
| return obj; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type) |
| { |
| cmXCodeObject* obj = new cmXCodeObject(cmXCodeObject::None, type); |
| this->addObject(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; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmStdString |
| GetGroupMapKeyFromPath(cmTarget& cmtarget, const std::string& fullpath) |
| { |
| cmStdString key(cmtarget.GetName()); |
| key += "-"; |
| key += fullpath; |
| return key; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmStdString |
| GetGroupMapKey(cmTarget& cmtarget, cmSourceFile* sf) |
| { |
| return GetGroupMapKeyFromPath(cmtarget, sf->GetFullPath()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath( |
| const std::string &fullpath, |
| cmTarget& cmtarget, |
| const std::string &lang) |
| { |
| // Using a map and the full path guarantees that we will always get the same |
| // fileRef object for any given full path. |
| // |
| cmXCodeObject* fileRef = |
| this->CreateXCodeFileReferenceFromPath(fullpath, cmtarget, lang); |
| |
| cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); |
| buildFile->SetComment(fileRef->GetComment()); |
| buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef)); |
| |
| return buildFile; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg, |
| cmSourceFile* sf, |
| cmTarget& cmtarget) |
| { |
| // Add flags from target and source file properties. |
| std::string flags; |
| const char* srcfmt = sf->GetProperty("Fortran_FORMAT"); |
| switch(this->CurrentLocalGenerator->GetFortranFormat(srcfmt)) |
| { |
| case cmLocalGenerator::FortranFormatFixed: flags="-fixed "+flags; break; |
| case cmLocalGenerator::FortranFormatFree: flags="-free "+flags; break; |
| default: break; |
| } |
| lg->AppendFlags(flags, sf->GetProperty("COMPILE_FLAGS")); |
| |
| // Add per-source definitions. |
| BuildObjectListOrString flagsBuild(this, false); |
| this->AppendDefines(flagsBuild, |
| sf->GetProperty("COMPILE_DEFINITIONS"), true); |
| if (!flagsBuild.IsEmpty()) |
| { |
| if (flags.size()) |
| { |
| flags += ' '; |
| } |
| flags += flagsBuild.GetString(); |
| } |
| |
| const char* lang = |
| this->CurrentLocalGenerator->GetSourceFileLanguage(*sf); |
| if (!lang) |
| { |
| lang = ""; |
| } |
| |
| cmXCodeObject* buildFile = |
| this->CreateXCodeSourceFileFromPath(sf->GetFullPath(), cmtarget, lang); |
| cmXCodeObject* fileRef = buildFile->GetObject("fileRef")->GetObject(); |
| |
| cmXCodeObject* settings = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| settings->AddAttribute("COMPILER_FLAGS", this->CreateString(flags.c_str())); |
| |
| // Is this a resource file in this target? Add it to the resources group... |
| // |
| cmTarget::SourceFileFlags tsFlags = cmtarget.GetTargetSourceFileFlags(sf); |
| bool isResource = (tsFlags.Type == cmTarget::SourceFileTypeResource); |
| |
| // Is this a "private" or "public" framework header file? |
| // Set the ATTRIBUTES attribute appropriately... |
| // |
| if(cmtarget.IsFrameworkOnApple()) |
| { |
| if(tsFlags.Type == cmTarget::SourceFileTypePrivateHeader) |
| { |
| cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| attrs->AddObject(this->CreateString("Private")); |
| settings->AddAttribute("ATTRIBUTES", attrs); |
| isResource = true; |
| } |
| else if(tsFlags.Type == cmTarget::SourceFileTypePublicHeader) |
| { |
| cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| attrs->AddObject(this->CreateString("Public")); |
| settings->AddAttribute("ATTRIBUTES", attrs); |
| isResource = true; |
| } |
| } |
| |
| // Add the fileRef to the top level Resources group/folder if it is not |
| // already there. |
| // |
| if(isResource && this->ResourcesGroupChildren && |
| !this->ResourcesGroupChildren->HasObject(fileRef)) |
| { |
| this->ResourcesGroupChildren->AddObject(fileRef); |
| } |
| |
| buildFile->AddAttribute("settings", settings); |
| return buildFile; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| GetSourcecodeValueFromFileExtension(const std::string& _ext, |
| const std::string& lang, |
| bool& keepLastKnownFileType) |
| { |
| std::string ext = cmSystemTools::LowerCase(_ext); |
| std::string sourcecode = "sourcecode"; |
| |
| if(ext == "o") |
| { |
| sourcecode = "compiled.mach-o.objfile"; |
| } |
| else if(ext == "xib") |
| { |
| keepLastKnownFileType = true; |
| sourcecode = "file.xib"; |
| } |
| else if(ext == "storyboard") |
| { |
| keepLastKnownFileType = true; |
| sourcecode = "file.storyboard"; |
| } |
| else if(ext == "mm") |
| { |
| sourcecode += ".cpp.objcpp"; |
| } |
| else if(ext == "m") |
| { |
| sourcecode += ".c.objc"; |
| } |
| else if(ext == "plist") |
| { |
| sourcecode += ".text.plist"; |
| } |
| else if(ext == "h") |
| { |
| sourcecode += ".c.h"; |
| } |
| else if(ext == "hxx" || ext == "hpp" || ext == "txx" |
| || ext == "pch" || ext == "hh") |
| { |
| sourcecode += ".cpp.h"; |
| } |
| else if(ext == "png" || ext == "gif" || ext == "jpg") |
| { |
| keepLastKnownFileType = true; |
| sourcecode = "image"; |
| } |
| else if(ext == "txt") |
| { |
| sourcecode += ".text"; |
| } |
| else if(lang == "CXX") |
| { |
| sourcecode += ".cpp.cpp"; |
| } |
| else if(lang == "C") |
| { |
| sourcecode += ".c.c"; |
| } |
| else if(lang == "Fortran") |
| { |
| sourcecode += ".fortran.f90"; |
| } |
| else if(lang == "ASM") |
| { |
| sourcecode += ".asm"; |
| } |
| //else |
| // { |
| // // Already specialized above or we leave sourcecode == "sourcecode" |
| // // which is probably the most correct choice. Extensionless headers, |
| // // for example... Or file types unknown to Xcode that do not map to a |
| // // valid explicitFileType value. |
| // } |
| |
| return sourcecode; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( |
| const std::string &fullpath, |
| cmTarget& cmtarget, |
| const std::string &lang) |
| { |
| std::string fname = fullpath; |
| cmXCodeObject* fileRef = this->FileRefs[fname]; |
| if(!fileRef) |
| { |
| fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); |
| std::string comment = fname; |
| fileRef->SetComment(fname.c_str()); |
| this->FileRefs[fname] = fileRef; |
| } |
| cmStdString key = GetGroupMapKeyFromPath(cmtarget, fullpath); |
| cmXCodeObject* group = this->GroupMap[key]; |
| cmXCodeObject* children = group->GetObject("children"); |
| if (!children->HasObject(fileRef)) |
| { |
| children->AddObject(fileRef); |
| } |
| fileRef->AddAttribute("fileEncoding", this->CreateString("4")); |
| |
| // Compute the extension. |
| std::string ext; |
| std::string realExt = |
| cmSystemTools::GetFilenameLastExtension(fullpath); |
| if(!realExt.empty()) |
| { |
| // Extension without the leading '.'. |
| ext = realExt.substr(1); |
| } |
| |
| // If fullpath references a directory, then we need to specify |
| // lastKnownFileType as folder in order for Xcode to be able to open the |
| // contents of the folder (Xcode 4.6 does not like explicitFileType=folder). |
| if(cmSystemTools::FileIsDirectory(fullpath.c_str())) |
| { |
| fileRef->AddAttribute("lastKnownFileType", |
| this->CreateString("folder")); |
| } |
| else |
| { |
| bool keepLastKnownFileType = false; |
| std::string sourcecode = GetSourcecodeValueFromFileExtension(ext, |
| lang, keepLastKnownFileType); |
| const char* attribute = keepLastKnownFileType ? |
| "lastKnownFileType" : |
| "explicitFileType"; |
| fileRef->AddAttribute(attribute, |
| this->CreateString(sourcecode.c_str())); |
| } |
| |
| // Store the file path relative to the top of the source tree. |
| std::string path = this->RelativeToSource(fullpath.c_str()); |
| std::string name = cmSystemTools::GetFilenameName(path.c_str()); |
| const char* sourceTree = (cmSystemTools::FileIsFullPath(path.c_str())? |
| "<absolute>" : "SOURCE_ROOT"); |
| fileRef->AddAttribute("name", this->CreateString(name.c_str())); |
| fileRef->AddAttribute("path", this->CreateString(path.c_str())); |
| fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree)); |
| if(this->XcodeVersion == 15) |
| { |
| fileRef->AddAttribute("refType", this->CreateString("4")); |
| } |
| return fileRef; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateXCodeFileReference(cmSourceFile* sf, |
| cmTarget& cmtarget) |
| { |
| const char* lang = |
| this->CurrentLocalGenerator->GetSourceFileLanguage(*sf); |
| if (!lang) |
| { |
| lang = ""; |
| } |
| |
| return this->CreateXCodeFileReferenceFromPath( |
| sf->GetFullPath(), cmtarget, lang); |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname) |
| { |
| if(tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" || |
| tname == "install" || tname == "package" || tname == "RUN_TESTS" || |
| tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET ) |
| { |
| if(this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) |
| { |
| return true; |
| } |
| this->TargetDoneSet.insert(tname); |
| return false; |
| } |
| return false; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen) |
| { |
| this->CurrentLocalGenerator = gen; |
| this->CurrentMakefile = gen->GetMakefile(); |
| std::string outdir = |
| cmSystemTools::CollapseFullPath(this->CurrentMakefile-> |
| GetCurrentOutputDirectory()); |
| cmSystemTools::SplitPath(outdir.c_str(), |
| this->CurrentOutputDirectoryComponents); |
| |
| // Select the current set of configuration types. |
| this->CurrentConfigurationTypes.clear(); |
| this->CurrentMakefile->GetConfigurations(this->CurrentConfigurationTypes); |
| if(this->CurrentConfigurationTypes.empty()) |
| { |
| this->CurrentConfigurationTypes.push_back(""); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| struct cmSourceFilePathCompare |
| { |
| bool operator()(cmSourceFile* l, cmSourceFile* r) |
| { |
| return l->GetFullPath() < r->GetFullPath(); |
| } |
| }; |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen, |
| std::vector<cmXCodeObject*>& |
| targets) |
| { |
| this->SetCurrentLocalGenerator(gen); |
| cmTargets &tgts = this->CurrentMakefile->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::INTERFACE_LIBRARY) |
| { |
| continue; |
| } |
| |
| if(cmtarget.GetType() == cmTarget::UTILITY || |
| cmtarget.GetType() == cmTarget::GLOBAL_TARGET) |
| { |
| targets.push_back(this->CreateUtilityTarget(cmtarget)); |
| continue; |
| } |
| |
| // organize the sources |
| std::vector<cmSourceFile*> classes; |
| cmtarget.GetSourceFiles(classes); |
| std::sort(classes.begin(), classes.end(), cmSourceFilePathCompare()); |
| |
| std::vector<cmXCodeObject*> externalObjFiles; |
| std::vector<cmXCodeObject*> headerFiles; |
| std::vector<cmXCodeObject*> resourceFiles; |
| std::vector<cmXCodeObject*> sourceFiles; |
| for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); |
| i != classes.end(); ++i) |
| { |
| cmXCodeObject* xsf = |
| this->CreateXCodeSourceFile(this->CurrentLocalGenerator, |
| *i, cmtarget); |
| cmXCodeObject* fr = xsf->GetObject("fileRef"); |
| cmXCodeObject* filetype = |
| fr->GetObject()->GetObject("explicitFileType"); |
| |
| cmTarget::SourceFileFlags tsFlags = |
| cmtarget.GetTargetSourceFileFlags(*i); |
| |
| if(filetype && |
| strcmp(filetype->GetString(), "compiled.mach-o.objfile") == 0) |
| { |
| externalObjFiles.push_back(xsf); |
| } |
| else if(this->IsHeaderFile(*i) || |
| (tsFlags.Type == cmTarget::SourceFileTypePrivateHeader) || |
| (tsFlags.Type == cmTarget::SourceFileTypePublicHeader)) |
| { |
| headerFiles.push_back(xsf); |
| } |
| else if(tsFlags.Type == cmTarget::SourceFileTypeResource) |
| { |
| resourceFiles.push_back(xsf); |
| } |
| else if(!(*i)->GetPropertyAsBool("HEADER_FILE_ONLY")) |
| { |
| // Include this file in the build if it has a known language |
| // and has not been listed as an ignored extension for this |
| // generator. |
| if(this->CurrentLocalGenerator->GetSourceFileLanguage(**i) && |
| !this->IgnoreFile((*i)->GetExtension().c_str())) |
| { |
| sourceFiles.push_back(xsf); |
| } |
| } |
| } |
| |
| if(this->XcodeVersion < 50) |
| { |
| // Add object library contents as external objects. (Equivalent to |
| // the externalObjFiles above, except each one is not a cmSourceFile |
| // within the target.) |
| std::vector<std::string> objs; |
| this->GetGeneratorTarget(&cmtarget)->UseObjectLibraries(objs); |
| for(std::vector<std::string>::const_iterator |
| oi = objs.begin(); oi != objs.end(); ++oi) |
| { |
| std::string obj = *oi; |
| cmXCodeObject* xsf = |
| this->CreateXCodeSourceFileFromPath(obj, cmtarget, ""); |
| externalObjFiles.push_back(xsf); |
| } |
| } |
| |
| // some build phases only apply to bundles and/or frameworks |
| bool isFrameworkTarget = cmtarget.IsFrameworkOnApple(); |
| bool isBundleTarget = cmtarget.GetPropertyAsBool("MACOSX_BUNDLE"); |
| bool isCFBundleTarget = cmtarget.IsCFBundleOnApple(); |
| |
| cmXCodeObject* buildFiles = 0; |
| |
| // create source build phase |
| cmXCodeObject* sourceBuildPhase = 0; |
| if (!sourceFiles.empty()) |
| { |
| sourceBuildPhase = |
| this->CreateObject(cmXCodeObject::PBXSourcesBuildPhase); |
| sourceBuildPhase->SetComment("Sources"); |
| sourceBuildPhase->AddAttribute("buildActionMask", |
| this->CreateString("2147483647")); |
| buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| for(std::vector<cmXCodeObject*>::iterator i = sourceFiles.begin(); |
| i != sourceFiles.end(); ++i) |
| { |
| buildFiles->AddObject(*i); |
| } |
| sourceBuildPhase->AddAttribute("files", buildFiles); |
| sourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", |
| this->CreateString("0")); |
| } |
| |
| // create header build phase - only for framework targets |
| cmXCodeObject* headerBuildPhase = 0; |
| if (!headerFiles.empty() && isFrameworkTarget) |
| { |
| headerBuildPhase = |
| this->CreateObject(cmXCodeObject::PBXHeadersBuildPhase); |
| headerBuildPhase->SetComment("Headers"); |
| headerBuildPhase->AddAttribute("buildActionMask", |
| this->CreateString("2147483647")); |
| buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| for(std::vector<cmXCodeObject*>::iterator i = headerFiles.begin(); |
| i != headerFiles.end(); ++i) |
| { |
| buildFiles->AddObject(*i); |
| } |
| headerBuildPhase->AddAttribute("files", buildFiles); |
| headerBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", |
| this->CreateString("0")); |
| } |
| |
| // create resource build phase - only for framework or bundle targets |
| cmXCodeObject* resourceBuildPhase = 0; |
| if (!resourceFiles.empty() && |
| (isFrameworkTarget || isBundleTarget || isCFBundleTarget)) |
| { |
| resourceBuildPhase = |
| this->CreateObject(cmXCodeObject::PBXResourcesBuildPhase); |
| resourceBuildPhase->SetComment("Resources"); |
| resourceBuildPhase->AddAttribute("buildActionMask", |
| this->CreateString("2147483647")); |
| buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| for(std::vector<cmXCodeObject*>::iterator i = resourceFiles.begin(); |
| i != resourceFiles.end(); ++i) |
| { |
| buildFiles->AddObject(*i); |
| } |
| resourceBuildPhase->AddAttribute("files", buildFiles); |
| resourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", |
| this->CreateString("0")); |
| } |
| |
| // create vector of "non-resource content file" build phases - only for |
| // framework or bundle targets |
| std::vector<cmXCodeObject*> contentBuildPhases; |
| if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) |
| { |
| typedef std::map<cmStdString, std::vector<cmSourceFile*> > |
| mapOfVectorOfSourceFiles; |
| mapOfVectorOfSourceFiles bundleFiles; |
| for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); |
| i != classes.end(); ++i) |
| { |
| cmTarget::SourceFileFlags tsFlags = |
| cmtarget.GetTargetSourceFileFlags(*i); |
| if(tsFlags.Type == cmTarget::SourceFileTypeMacContent) |
| { |
| bundleFiles[tsFlags.MacFolder].push_back(*i); |
| } |
| } |
| mapOfVectorOfSourceFiles::iterator mit; |
| for ( mit = bundleFiles.begin(); mit != bundleFiles.end(); ++ mit ) |
| { |
| cmXCodeObject* copyFilesBuildPhase = |
| this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase); |
| copyFilesBuildPhase->SetComment("Copy files"); |
| copyFilesBuildPhase->AddAttribute("buildActionMask", |
| this->CreateString("2147483647")); |
| copyFilesBuildPhase->AddAttribute("dstSubfolderSpec", |
| this->CreateString("6")); |
| cmOStringStream ostr; |
| if (cmtarget.IsFrameworkOnApple()) |
| { |
| // dstPath in frameworks is relative to Versions/<version> |
| ostr << mit->first; |
| } |
| else if ( mit->first != "MacOS" ) |
| { |
| // dstPath in bundles is relative to Contents/MacOS |
| ostr << "../" << mit->first.c_str(); |
| } |
| copyFilesBuildPhase->AddAttribute("dstPath", |
| this->CreateString(ostr.str().c_str())); |
| copyFilesBuildPhase->AddAttribute( |
| "runOnlyForDeploymentPostprocessing", this->CreateString("0")); |
| buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| copyFilesBuildPhase->AddAttribute("files", buildFiles); |
| std::vector<cmSourceFile*>::iterator sfIt; |
| for ( sfIt = mit->second.begin(); sfIt != mit->second.end(); ++ sfIt ) |
| { |
| cmXCodeObject* xsf = |
| this->CreateXCodeSourceFile(this->CurrentLocalGenerator, |
| *sfIt, cmtarget); |
| buildFiles->AddObject(xsf); |
| } |
| contentBuildPhases.push_back(copyFilesBuildPhase); |
| } |
| } |
| |
| // create framework build phase |
| cmXCodeObject* frameworkBuildPhase = 0; |
| if (!externalObjFiles.empty()) |
| { |
| frameworkBuildPhase = |
| this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase); |
| frameworkBuildPhase->SetComment("Frameworks"); |
| frameworkBuildPhase->AddAttribute("buildActionMask", |
| this->CreateString("2147483647")); |
| buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| frameworkBuildPhase->AddAttribute("files", buildFiles); |
| for(std::vector<cmXCodeObject*>::iterator i = externalObjFiles.begin(); |
| i != externalObjFiles.end(); ++i) |
| { |
| buildFiles->AddObject(*i); |
| } |
| frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", |
| this->CreateString("0")); |
| } |
| |
| // create list of build phases and create the Xcode target |
| cmXCodeObject* buildPhases = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| |
| this->CreateCustomCommands(buildPhases, sourceBuildPhase, |
| headerBuildPhase, resourceBuildPhase, |
| contentBuildPhases, |
| frameworkBuildPhase, cmtarget); |
| |
| targets.push_back(this->CreateXCodeTarget(cmtarget, buildPhases)); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::ForceLinkerLanguages() |
| { |
| // This makes sure all targets link using the proper language. |
| for(std::map<cmStdString, cmTarget*>::const_iterator |
| ti = this->TotalTargets.begin(); ti != this->TotalTargets.end(); ++ti) |
| { |
| this->ForceLinkerLanguage(*ti->second); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::ForceLinkerLanguage(cmTarget& cmtarget) |
| { |
| // This matters only for targets that link. |
| if(cmtarget.GetType() != cmTarget::EXECUTABLE && |
| cmtarget.GetType() != cmTarget::SHARED_LIBRARY && |
| cmtarget.GetType() != cmTarget::MODULE_LIBRARY) |
| { |
| return; |
| } |
| |
| const char* llang = cmtarget.GetLinkerLanguage("NOCONFIG"); |
| if(!llang) { return; } |
| |
| // If the language is compiled as a source trust Xcode to link with it. |
| cmTarget::LinkImplementation const* impl = |
| cmtarget.GetLinkImplementation("NOCONFIG", &cmtarget); |
| for(std::vector<std::string>::const_iterator li = impl->Languages.begin(); |
| li != impl->Languages.end(); ++li) |
| { |
| if(*li == llang) { return; } |
| } |
| |
| // Add an empty source file to the target that compiles with the |
| // linker language. This should convince Xcode to choose the proper |
| // language. |
| cmMakefile* mf = cmtarget.GetMakefile(); |
| std::string fname = mf->GetCurrentOutputDirectory(); |
| fname += cmake::GetCMakeFilesDirectory(); |
| fname += "/"; |
| fname += cmtarget.GetName(); |
| fname += "-CMakeForceLinker"; |
| fname += "."; |
| fname += cmSystemTools::LowerCase(llang); |
| { |
| cmGeneratedFileStream fout(fname.c_str()); |
| fout << "\n"; |
| } |
| if(cmSourceFile* sf = mf->GetOrCreateSource(fname.c_str())) |
| { |
| sf->SetProperty("LANGUAGE", llang); |
| cmtarget.AddSourceFile(sf); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf) |
| { |
| const std::vector<std::string>& hdrExts = |
| this->CurrentMakefile->GetHeaderExtensions(); |
| return (std::find(hdrExts.begin(), hdrExts.end(), sf->GetExtension()) != |
| hdrExts.end()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateBuildPhase(const char* name, |
| const char* name2, |
| cmTarget& cmtarget, |
| const std::vector<cmCustomCommand>& |
| 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* |
| resourceBuildPhase, |
| std::vector<cmXCodeObject*> |
| contentBuildPhases, |
| cmXCodeObject* |
| frameworkBuildPhase, |
| cmTarget& cmtarget) |
| { |
| std::vector<cmCustomCommand> const & prebuild |
| = cmtarget.GetPreBuildCommands(); |
| std::vector<cmCustomCommand> const & prelink |
| = cmtarget.GetPreLinkCommands(); |
| std::vector<cmCustomCommand> postbuild |
| = cmtarget.GetPostBuildCommands(); |
| |
| if(cmtarget.GetType() == cmTarget::SHARED_LIBRARY && |
| !cmtarget.IsFrameworkOnApple()) |
| { |
| cmCustomCommandLines cmd; |
| cmd.resize(1); |
| cmd[0].push_back(this->CurrentMakefile->GetDefinition("CMAKE_COMMAND")); |
| cmd[0].push_back("-E"); |
| cmd[0].push_back("cmake_symlink_library"); |
| std::string str_file = "$<TARGET_FILE:"; |
| str_file += cmtarget.GetName(); |
| str_file += ">"; |
| std::string str_so_file = "$<TARGET_SONAME_FILE:"; |
| str_so_file += cmtarget.GetName(); |
| str_so_file += ">"; |
| std::string str_link_file = "$<TARGET_LINKER_FILE:"; |
| str_link_file += cmtarget.GetName(); |
| str_link_file += ">"; |
| cmd[0].push_back(str_file); |
| cmd[0].push_back(str_so_file); |
| cmd[0].push_back(str_link_file); |
| |
| cmCustomCommand command(this->CurrentMakefile, |
| std::vector<std::string>(), |
| std::vector<std::string>(), |
| cmd, |
| "Creating symlinks", |
| ""); |
| |
| postbuild.push_back(command); |
| } |
| |
| std::vector<cmSourceFile*> classes; |
| cmtarget.GetSourceFiles(classes); |
| // add all the sources |
| std::vector<cmCustomCommand> commands; |
| for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); |
| i != classes.end(); ++i) |
| { |
| if((*i)->GetCustomCommand()) |
| { |
| commands.push_back(*(*i)->GetCustomCommand()); |
| } |
| } |
| // 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 prelink phase |
| cmXCodeObject* preLinkPhase = |
| this->CreateBuildPhase("CMake PreLink Rules", "preLinkCommands", |
| cmtarget, prelink); |
| // create postbuild phase |
| cmXCodeObject* postBuildPhase = |
| this->CreateBuildPhase("CMake PostBuild Rules", "postBuildPhase", |
| cmtarget, postbuild); |
| |
| // The order here is the order they will be built in. |
| // The order "headers, resources, sources" mimics a native project generated |
| // from an xcode template... |
| // |
| if(preBuildPhase) |
| { |
| buildPhases->AddObject(preBuildPhase); |
| } |
| if(cmakeRulesBuildPhase) |
| { |
| buildPhases->AddObject(cmakeRulesBuildPhase); |
| } |
| if(headerBuildPhase) |
| { |
| buildPhases->AddObject(headerBuildPhase); |
| } |
| if(resourceBuildPhase) |
| { |
| buildPhases->AddObject(resourceBuildPhase); |
| } |
| std::vector<cmXCodeObject*>::iterator cit; |
| for (cit = contentBuildPhases.begin(); cit != contentBuildPhases.end(); |
| ++cit) |
| { |
| buildPhases->AddObject(*cit); |
| } |
| if(sourceBuildPhase) |
| { |
| buildPhases->AddObject(sourceBuildPhase); |
| } |
| if(preLinkPhase) |
| { |
| buildPhases->AddObject(preLinkPhase); |
| } |
| if(frameworkBuildPhase) |
| { |
| buildPhases->AddObject(frameworkBuildPhase); |
| } |
| if(postBuildPhase) |
| { |
| buildPhases->AddObject(postBuildPhase); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| // This function removes each occurrence of the flag and returns the last one |
| // (i.e., the dominant flag in GCC) |
| std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag, |
| std::string& flags) |
| { |
| std::string retFlag; |
| std::string::size_type lastOccurancePos = flags.rfind(flag); |
| bool saved = false; |
| while(lastOccurancePos != flags.npos) |
| { |
| //increment pos, we use lastOccurancePos to reduce search space on next inc |
| std::string::size_type pos = lastOccurancePos; |
| if(pos == 0 || flags[pos-1]==' ') |
| { |
| while(pos < flags.size() && flags[pos] != ' ') |
| { |
| if(!saved) |
| { |
| retFlag += flags[pos]; |
| } |
| flags[pos] = ' '; |
| pos++; |
| } |
| saved = true; |
| } |
| //decrement lastOccurancePos while making sure we don't loop around |
| //and become a very large positive number since size_type is unsigned |
| lastOccurancePos = lastOccurancePos == 0 ? 0 : lastOccurancePos-1; |
| lastOccurancePos = flags.rfind(flag,lastOccurancePos); |
| } |
| return retFlag; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::AddCommandsToBuildPhase(cmXCodeObject* buildphase, |
| cmTarget& target, |
| std::vector<cmCustomCommand> |
| const & commands, |
| const char* name) |
| { |
| |
| // collect multiple outputs of custom commands into a set |
| // which will be used for every configuration |
| std::map<cmStdString, cmStdString> multipleOutputPairs; |
| for(std::vector<cmCustomCommand>::const_iterator i = commands.begin(); |
| i != commands.end(); ++i) |
| { |
| cmCustomCommand const& cc = *i; |
| if(!cc.GetCommandLines().empty()) |
| { |
| const std::vector<std::string>& outputs = cc.GetOutputs(); |
| if(!outputs.empty()) |
| { |
| // If there are more than one outputs treat the |
| // first as the primary output and make the rest depend on it. |
| std::vector<std::string>::const_iterator o = outputs.begin(); |
| std::string primaryOutput = this->ConvertToRelativeForMake(o->c_str()); |
| for(++o; o != outputs.end(); ++o) |
| { |
| std::string currentOutput=this->ConvertToRelativeForMake(o->c_str()); |
| multipleOutputPairs[currentOutput] = primaryOutput; |
| } |
| } |
| } |
| } |
| |
| std::string dir = this->CurrentMakefile->GetCurrentOutputDirectory(); |
| dir += "/CMakeScripts"; |
| cmSystemTools::MakeDirectory(dir.c_str()); |
| std::string makefile = dir; |
| makefile += "/"; |
| makefile += target.GetName(); |
| makefile += "_"; |
| makefile += name; |
| makefile += ".make"; |
| |
| for (std::vector<std::string>::const_iterator currentConfig= |
| this->CurrentConfigurationTypes.begin(); |
| currentConfig!=this->CurrentConfigurationTypes.end(); |
| currentConfig++ ) |
| { |
| this->CreateCustomRulesMakefile(makefile.c_str(), |
| target, |
| commands, |
| currentConfig->c_str(), |
| multipleOutputPairs); |
| } |
| |
| std::string cdir = this->CurrentMakefile->GetCurrentOutputDirectory(); |
| cdir = this->ConvertToRelativeForXCode(cdir.c_str()); |
| std::string makecmd = "make -C "; |
| makecmd += cdir; |
| makecmd += " -f "; |
| makecmd += this->ConvertToRelativeForMake( |
| (makefile+"$CONFIGURATION").c_str()); |
| if(!multipleOutputPairs.empty()) |
| { |
| makecmd += " cmake_check_multiple_outputs"; |
| } |
| makecmd += " all"; |
| cmSystemTools::ReplaceString(makecmd, "\\ ", "\\\\ "); |
| buildphase->AddAttribute("shellScript", |
| this->CreateString(makecmd.c_str())); |
| buildphase->AddAttribute("showEnvVarsInLog", |
| this->CreateString("0")); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator |
| ::CreateCustomRulesMakefile(const char* makefileBasename, |
| cmTarget& target, |
| std::vector<cmCustomCommand> |
| const & commands, |
| const char* configName, |
| const std::map<cmStdString, |
| cmStdString>& multipleOutputPairs |
| ) |
| { |
| std::string makefileName=makefileBasename; |
| if(this->XcodeVersion > 20) |
| { |
| makefileName+=configName; |
| } |
| cmGeneratedFileStream makefileStream(makefileName.c_str()); |
| if(!makefileStream) |
| { |
| return; |
| } |
| makefileStream.SetCopyIfDifferent(true); |
| makefileStream << "# Generated by CMake, DO NOT EDIT\n"; |
| makefileStream << "# Custom rules for " << target.GetName() << "\n"; |
| |
| // disable the implicit rules |
| makefileStream << ".SUFFIXES: " << "\n"; |
| |
| // have all depend on all outputs |
| makefileStream << "all: "; |
| std::map<const cmCustomCommand*, cmStdString> tname; |
| int count = 0; |
| for(std::vector<cmCustomCommand>::const_iterator i = commands.begin(); |
| i != commands.end(); ++i) |
| { |
| cmCustomCommand const& cc = *i; |
| if(!cc.GetCommandLines().empty()) |
| { |
| const std::vector<std::string>& outputs = cc.GetOutputs(); |
| if(!outputs.empty()) |
| { |
| for(std::vector<std::string>::const_iterator o = outputs.begin(); |
| o != outputs.end(); ++o) |
| { |
| makefileStream |
| << "\\\n\t" << this->ConvertToRelativeForMake(o->c_str()); |
| } |
| } |
| else |
| { |
| cmOStringStream str; |
| str << "_buildpart_" << count++ ; |
| tname[&cc] = std::string(target.GetName()) + str.str(); |
| makefileStream << "\\\n\t" << tname[&cc]; |
| } |
| } |
| } |
| makefileStream << "\n\n"; |
| for(std::vector<cmCustomCommand>::const_iterator i = commands.begin(); |
| i != commands.end(); ++i) |
| { |
| cmCustomCommand const& cc = *i; |
| if(!cc.GetCommandLines().empty()) |
| { |
| cmCustomCommandGenerator ccg(cc, configName, this->CurrentMakefile); |
| makefileStream << "\n"; |
| const std::vector<std::string>& outputs = cc.GetOutputs(); |
| if(!outputs.empty()) |
| { |
| // There is at least one output, start the rule for it |
| std::string primary_output = |
| this->ConvertToRelativeForMake(outputs.begin()->c_str()); |
| makefileStream << primary_output << ": "; |
| } |
| else |
| { |
| // There are no outputs. Use the generated force rule name. |
| makefileStream << tname[&cc] << ": "; |
| } |
| for(std::vector<std::string>::const_iterator d = |
| cc.GetDepends().begin(); |
| d != cc.GetDepends().end(); ++d) |
| { |
| std::string dep; |
| if(this->CurrentLocalGenerator |
| ->GetRealDependency(d->c_str(), configName, dep)) |
| { |
| makefileStream << "\\\n" << |
| this->ConvertToRelativeForMake(dep.c_str()); |
| } |
| } |
| makefileStream << "\n"; |
| |
| if(const char* comment = cc.GetComment()) |
| { |
| std::string echo_cmd = "echo "; |
| echo_cmd += (this->CurrentLocalGenerator-> |
| EscapeForShell(comment, cc.GetEscapeAllowMakeVars())); |
| makefileStream << "\t" << echo_cmd.c_str() << "\n"; |
| } |
| |
| // Add each command line to the set of commands. |
| for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) |
| { |
| // Build the command line in a single string. |
| std::string cmd2 = ccg.GetCommand(c); |
| cmSystemTools::ReplaceString(cmd2, "/./", "/"); |
| cmd2 = this->ConvertToRelativeForMake(cmd2.c_str()); |
| std::string cmd; |
| if(cc.GetWorkingDirectory()) |
| { |
| cmd += "cd "; |
| cmd += this->ConvertToRelativeForMake(cc.GetWorkingDirectory()); |
| cmd += " && "; |
| } |
| cmd += cmd2; |
| ccg.AppendArguments(c, cmd); |
| makefileStream << "\t" << cmd.c_str() << "\n"; |
| } |
| } |
| } |
| |
| // Add rules to deal with multiple outputs of custom commands. |
| if(!multipleOutputPairs.empty()) |
| { |
| makefileStream << |
| "\n# Dependencies of multiple outputs to their primary outputs \n"; |
| |
| for(std::map<cmStdString, cmStdString>::const_iterator o = |
| multipleOutputPairs.begin(); o != multipleOutputPairs.end(); ++o) |
| { |
| makefileStream << o->first << ": " << o->second << "\n"; |
| } |
| |
| makefileStream << |
| "\n" |
| "cmake_check_multiple_outputs:\n"; |
| for(std::map<cmStdString, cmStdString>::const_iterator o = |
| multipleOutputPairs.begin(); o != multipleOutputPairs.end(); ++o) |
| { |
| makefileStream << "\t@if [ ! -f " |
| << o->first << " ]; then rm -f " |
| << o->second << "; fi\n"; |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target, |
| cmXCodeObject* buildSettings, |
| const char* configName) |
| { |
| if(target.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| return; |
| } |
| |
| std::string flags; |
| std::string defFlags; |
| bool shared = ((target.GetType() == cmTarget::SHARED_LIBRARY) || |
| (target.GetType() == cmTarget::MODULE_LIBRARY)); |
| bool binary = ((target.GetType() == cmTarget::OBJECT_LIBRARY) || |
| (target.GetType() == cmTarget::STATIC_LIBRARY) || |
| (target.GetType() == cmTarget::EXECUTABLE) || |
| shared); |
| |
| const char* lang = target.GetLinkerLanguage(configName); |
| std::string cflags; |
| if(lang) |
| { |
| // for c++ projects get the c flags as well |
| if(strcmp(lang, "CXX") == 0) |
| { |
| this->CurrentLocalGenerator->AddLanguageFlags(cflags, "C", configName); |
| this->CurrentLocalGenerator->AddCMP0018Flags(cflags, &target, |
| "C", configName); |
| this->CurrentLocalGenerator-> |
| AddCompileOptions(cflags, &target, "C", configName); |
| } |
| |
| // Add language-specific flags. |
| this->CurrentLocalGenerator->AddLanguageFlags(flags, lang, configName); |
| |
| // Add shared-library flags if needed. |
| this->CurrentLocalGenerator->AddCMP0018Flags(flags, &target, |
| lang, configName); |
| |
| this->CurrentLocalGenerator->AddVisibilityPresetFlags(flags, &target, |
| lang); |
| |
| this->CurrentLocalGenerator-> |
| AddCompileOptions(flags, &target, lang, configName); |
| } |
| else if(binary) |
| { |
| cmSystemTools::Error |
| ("CMake can not determine linker language for target: ", |
| target.GetName()); |
| return; |
| } |
| |
| // Add define flags |
| this->CurrentLocalGenerator-> |
| AppendFlags(defFlags, |
| this->CurrentMakefile->GetDefineFlags()); |
| |
| // Add preprocessor definitions for this target and configuration. |
| BuildObjectListOrString ppDefs(this, this->XcodeVersion >= 30); |
| if(this->XcodeVersion > 15) |
| { |
| this->AppendDefines(ppDefs, |
| "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\""); |
| } |
| if(const char* exportMacro = target.GetExportMacro()) |
| { |
| // Add the export symbol definition for shared library objects. |
| this->AppendDefines(ppDefs, exportMacro); |
| } |
| cmGeneratorTarget *gtgt = this->GetGeneratorTarget(&target); |
| std::vector<std::string> targetDefines; |
| target.GetCompileDefinitions(targetDefines, configName); |
| this->AppendDefines(ppDefs, targetDefines); |
| buildSettings->AddAttribute |
| ("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); |
| |
| std::string extraLinkOptionsVar; |
| std::string extraLinkOptions; |
| if(target.GetType() == cmTarget::EXECUTABLE) |
| { |
| extraLinkOptionsVar = "CMAKE_EXE_LINKER_FLAGS"; |
| } |
| else if(target.GetType() == cmTarget::SHARED_LIBRARY) |
| { |
| extraLinkOptionsVar = "CMAKE_SHARED_LINKER_FLAGS"; |
| } |
| else if(target.GetType() == cmTarget::MODULE_LIBRARY) |
| { |
| extraLinkOptionsVar = "CMAKE_MODULE_LINKER_FLAGS"; |
| } |
| if(extraLinkOptionsVar.size()) |
| { |
| this->CurrentLocalGenerator |
| ->AddConfigVariableFlags(extraLinkOptions, |
| extraLinkOptionsVar.c_str(), |
| configName); |
| } |
| |
| if(target.GetType() == cmTarget::OBJECT_LIBRARY || |
| target.GetType() == cmTarget::STATIC_LIBRARY) |
| { |
| this->CurrentLocalGenerator |
| ->GetStaticLibraryFlags(extraLinkOptions, |
| cmSystemTools::UpperCase(configName), |
| &target); |
| } |
| else |
| { |
| const char* targetLinkFlags = target.GetProperty("LINK_FLAGS"); |
| if(targetLinkFlags) |
| { |
| this->CurrentLocalGenerator-> |
| AppendFlags(extraLinkOptions, targetLinkFlags); |
| } |
| if(configName && *configName) |
| { |
| std::string linkFlagsVar = "LINK_FLAGS_"; |
| linkFlagsVar += cmSystemTools::UpperCase(configName); |
| if(const char* linkFlags = target.GetProperty(linkFlagsVar.c_str())) |
| { |
| this->CurrentLocalGenerator-> |
| AppendFlags(extraLinkOptions, linkFlags); |
| } |
| } |
| } |
| |
| // Set target-specific architectures. |
| std::vector<std::string> archs; |
| gtgt->GetAppleArchs(configName, archs); |
| |
| if(!archs.empty()) |
| { |
| // Enable ARCHS attribute. |
| buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", |
| this->CreateString("NO")); |
| |
| // Store ARCHS value. |
| if(archs.size() == 1) |
| { |
| buildSettings->AddAttribute("ARCHS", |
| this->CreateString(archs[0].c_str())); |
| } |
| else |
| { |
| cmXCodeObject* archObjects = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| for(std::vector<std::string>::iterator i = archs.begin(); |
| i != archs.end(); i++) |
| { |
| archObjects->AddObject(this->CreateString((*i).c_str())); |
| } |
| buildSettings->AddAttribute("ARCHS", archObjects); |
| } |
| } |
| |
| // Get the product name components. |
| std::string pnprefix; |
| std::string pnbase; |
| std::string pnsuffix; |
| target.GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName); |
| |
| const char* version = target.GetProperty("VERSION"); |
| const char* soversion = target.GetProperty("SOVERSION"); |
| if(!target.HasSOName(configName) || target.IsFrameworkOnApple()) |
| { |
| version = 0; |
| soversion = 0; |
| } |
| if(version && !soversion) |
| { |
| soversion = version; |
| } |
| if(!version && soversion) |
| { |
| version = soversion; |
| } |
| |
| std::string realName = pnbase; |
| std::string soName = pnbase; |
| if(version && soversion) |
| { |
| realName += "."; |
| realName += version; |
| soName += "."; |
| soName += soversion; |
| } |
| |
| // Set attributes to specify the proper name for the target. |
| std::string pndir = this->CurrentMakefile->GetCurrentOutputDirectory(); |
| if(target.GetType() == cmTarget::STATIC_LIBRARY || |
| target.GetType() == cmTarget::SHARED_LIBRARY || |
| target.GetType() == cmTarget::MODULE_LIBRARY || |
| target.GetType() == cmTarget::EXECUTABLE) |
| { |
| if(this->XcodeVersion >= 21) |
| { |
| if(!target.UsesDefaultOutputDir(configName, false)) |
| { |
| std::string pncdir = target.GetDirectory(configName); |
| buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR", |
| this->CreateString(pncdir.c_str())); |
| } |
| } |
| else |
| { |
| buildSettings->AddAttribute("OBJROOT", |
| this->CreateString(pndir.c_str())); |
| pndir = target.GetDirectory(configName); |
| } |
| |
| if(target.IsFrameworkOnApple() || target.IsCFBundleOnApple()) |
| { |
| pnprefix = ""; |
| } |
| |
| buildSettings->AddAttribute("EXECUTABLE_PREFIX", |
| this->CreateString(pnprefix.c_str())); |
| buildSettings->AddAttribute("EXECUTABLE_SUFFIX", |
| this->CreateString(pnsuffix.c_str())); |
| } |
| else if(target.GetType() == cmTarget::OBJECT_LIBRARY) |
| { |
| pnprefix = "lib"; |
| pnbase = target.GetName(); |
| pnsuffix = ".a"; |
| |
| if(this->XcodeVersion >= 21) |
| { |
| std::string pncdir = this->GetObjectsNormalDirectory( |
| this->CurrentProject, configName, &target); |
| buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR", |
| this->CreateString(pncdir.c_str())); |
| } |
| else |
| { |
| buildSettings->AddAttribute("OBJROOT", |
| this->CreateString(pndir.c_str())); |
| pndir = this->GetObjectsNormalDirectory( |
| this->CurrentProject, configName, &target); |
| } |
| } |
| |
| // Store the product name for all target types. |
| buildSettings->AddAttribute("PRODUCT_NAME", |
| this->CreateString(realName.c_str())); |
| buildSettings->AddAttribute("SYMROOT", |
| this->CreateString(pndir.c_str())); |
| |
| // Handle settings for each target type. |
| switch(target.GetType()) |
| { |
| case cmTarget::OBJECT_LIBRARY: |
| case cmTarget::STATIC_LIBRARY: |
| { |
| buildSettings->AddAttribute("LIBRARY_STYLE", |
| this->CreateString("STATIC")); |
| break; |
| } |
| |
| case cmTarget::MODULE_LIBRARY: |
| { |
| buildSettings->AddAttribute("LIBRARY_STYLE", |
| this->CreateString("BUNDLE")); |
| if (target.GetPropertyAsBool("BUNDLE")) |
| { |
| // It turns out that a BUNDLE is basically the same |
| // in many ways as an application bundle, as far as |
| // link flags go |
| std::string createFlags = |
| this->LookupFlags("CMAKE_SHARED_MODULE_CREATE_", lang, "_FLAGS", |
| "-bundle"); |
| if(!createFlags.empty()) |
| { |
| extraLinkOptions += " "; |
| extraLinkOptions += createFlags; |
| } |
| std::string plist = this->ComputeInfoPListLocation(target); |
| // Xcode will create the final version of Info.plist at build time, |
| // so let it replace the cfbundle name. This avoids creating |
| // a per-configuration Info.plist file. The cfbundle plist |
| // is very similar to the application bundle plist |
| this->CurrentLocalGenerator |
| ->GenerateAppleInfoPList(&target, "$(EXECUTABLE_NAME)", |
| plist.c_str()); |
| std::string path = |
| this->ConvertToRelativeForXCode(plist.c_str()); |
| buildSettings->AddAttribute("INFOPLIST_FILE", |
| this->CreateString(path.c_str())); |
| } |
| else if(this->XcodeVersion >= 22) |
| { |
| buildSettings->AddAttribute("MACH_O_TYPE", |
| this->CreateString("mh_bundle")); |
| buildSettings->AddAttribute("GCC_DYNAMIC_NO_PIC", |
| this->CreateString("NO")); |
| // Add the flags to create an executable. |
| std::string createFlags = |
| this->LookupFlags("CMAKE_", lang, "_LINK_FLAGS", ""); |
| if(!createFlags.empty()) |
| { |
| extraLinkOptions += " "; |
| extraLinkOptions += createFlags; |
| } |
| } |
| else |
| { |
| // Add the flags to create a module. |
| std::string createFlags = |
| this->LookupFlags("CMAKE_SHARED_MODULE_CREATE_", lang, "_FLAGS", |
| "-bundle"); |
| if(!createFlags.empty()) |
| { |
| extraLinkOptions += " "; |
| extraLinkOptions += createFlags; |
| } |
| } |
| break; |
| } |
| case cmTarget::SHARED_LIBRARY: |
| { |
| if(target.GetPropertyAsBool("FRAMEWORK")) |
| { |
| std::string fw_version = target.GetFrameworkVersion(); |
| buildSettings->AddAttribute("FRAMEWORK_VERSION", |
| this->CreateString(fw_version.c_str())); |
| |
| std::string plist = this->ComputeInfoPListLocation(target); |
| // Xcode will create the final version of Info.plist at build time, |
| // so let it replace the framework name. This avoids creating |
| // a per-configuration Info.plist file. |
| this->CurrentLocalGenerator |
| ->GenerateFrameworkInfoPList(&target, "$(EXECUTABLE_NAME)", |
| plist.c_str()); |
| std::string path = |
| this->ConvertToRelativeForXCode(plist.c_str()); |
| buildSettings->AddAttribute("INFOPLIST_FILE", |
| this->CreateString(path.c_str())); |
| } |
| else |
| { |
| // Add the flags to create a shared library. |
| std::string createFlags = |
| this->LookupFlags("CMAKE_SHARED_LIBRARY_CREATE_", lang, "_FLAGS", |
| "-dynamiclib"); |
| if(!createFlags.empty()) |
| { |
| extraLinkOptions += " "; |
| extraLinkOptions += createFlags; |
| } |
| } |
| |
| buildSettings->AddAttribute("LIBRARY_STYLE", |
| this->CreateString("DYNAMIC")); |
| break; |
| } |
| case cmTarget::EXECUTABLE: |
| { |
| // Add the flags to create an executable. |
| std::string createFlags = |
| this->LookupFlags("CMAKE_", lang, "_LINK_FLAGS", ""); |
| if(!createFlags.empty()) |
| { |
| extraLinkOptions += " "; |
| extraLinkOptions += createFlags; |
| } |
| |
| // Handle bundles and normal executables separately. |
| if(target.GetPropertyAsBool("MACOSX_BUNDLE")) |
| { |
| std::string plist = this->ComputeInfoPListLocation(target); |
| // Xcode will create the final version of Info.plist at build time, |
| // so let it replace the executable name. This avoids creating |
| // a per-configuration Info.plist file. |
| this->CurrentLocalGenerator |
| ->GenerateAppleInfoPList(&target, "$(EXECUTABLE_NAME)", |
| plist.c_str()); |
| std::string path = |
| this->ConvertToRelativeForXCode(plist.c_str()); |
| buildSettings->AddAttribute("INFOPLIST_FILE", |
| this->CreateString(path.c_str())); |
| |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| if(this->XcodeVersion >= 22 && this->XcodeVersion < 40) |
| { |
| buildSettings->AddAttribute("PREBINDING", |
| this->CreateString("NO")); |
| } |
| |
| BuildObjectListOrString dirs(this, this->XcodeVersion >= 30); |
| BuildObjectListOrString fdirs(this, this->XcodeVersion >= 30); |
| std::vector<std::string> includes; |
| this->CurrentLocalGenerator->GetIncludeDirectories(includes, gtgt, |
| "C", configName); |
| std::set<cmStdString> emitted; |
| emitted.insert("/System/Library/Frameworks"); |
| for(std::vector<std::string>::iterator i = includes.begin(); |
| i != includes.end(); ++i) |
| { |
| if(this->NameResolvesToFramework(i->c_str())) |
| { |
| std::string frameworkDir = *i; |
| frameworkDir += "/../"; |
| frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir.c_str()); |
| if(emitted.insert(frameworkDir).second) |
| { |
| fdirs.Add(this->XCodeEscapePath(frameworkDir.c_str()).c_str()); |
| } |
| } |
| else |
| { |
| std::string incpath = |
| this->XCodeEscapePath(i->c_str()); |
| dirs.Add(incpath.c_str()); |
| } |
| } |
| // Add framework search paths needed for linking. |
| if(cmComputeLinkInformation* cli = target.GetLinkInformation(configName)) |
| { |
| std::vector<std::string> const& fwDirs = cli->GetFrameworkPaths(); |
| for(std::vector<std::string>::const_iterator fdi = fwDirs.begin(); |
| fdi != fwDirs.end(); ++fdi) |
| { |
| if(emitted.insert(*fdi).second) |
| { |
| fdirs.Add(this->XCodeEscapePath(fdi->c_str()).c_str()); |
| } |
| } |
| } |
| if(!fdirs.IsEmpty()) |
| { |
| buildSettings->AddAttribute("FRAMEWORK_SEARCH_PATHS", |
| fdirs.CreateList()); |
| } |
| if(!dirs.IsEmpty()) |
| { |
| buildSettings->AddAttribute("HEADER_SEARCH_PATHS", |
| dirs.CreateList()); |
| } |
| std::string oflagc = this->ExtractFlag("-O", cflags); |
| char optLevel[2]; |
| optLevel[0] = '0'; |
| optLevel[1] = 0; |
| if(oflagc.size() == 3) |
| { |
| optLevel[0] = oflagc[2]; |
| } |
| if(oflagc.size() == 2) |
| { |
| optLevel[0] = '1'; |
| } |
| std::string oflag = this->ExtractFlag("-O", flags); |
| if(oflag.size() == 3) |
| { |
| optLevel[0] = oflag[2]; |
| } |
| if(oflag.size() == 2) |
| { |
| optLevel[0] = '1'; |
| } |
| std::string gflagc = this->ExtractFlag("-g", cflags); |
| // put back gdwarf-2 if used since there is no way |
| // to represent it in the gui, but we still want debug yes |
| if(gflagc == "-gdwarf-2") |
| { |
| cflags += " "; |
| cflags += gflagc; |
| } |
| std::string gflag = this->ExtractFlag("-g", flags); |
| if(gflag == "-gdwarf-2") |
| { |
| flags += " "; |
| flags += gflag; |
| } |
| const char* debugStr = "YES"; |
| // We can't set the Xcode flag differently depending on the language, |
| // so put them back in this case. |
| if( (lang && strcmp(lang, "CXX") == 0) && gflag != gflagc ) |
| { |
| cflags += " "; |
| cflags += gflagc; |
| flags += " "; |
| flags += gflag; |
| debugStr = "NO"; |
| } |
| if( gflag == "-g0" || gflag.size() == 0 ) |
| { |
| debugStr = "NO"; |
| } |
| |
| buildSettings->AddAttribute("COMBINE_HIDPI_IMAGES", |
| this->CreateString("YES")); |
| buildSettings->AddAttribute("GCC_GENERATE_DEBUGGING_SYMBOLS", |
| this->CreateString(debugStr)); |
| buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL", |
| this->CreateString(optLevel)); |
| buildSettings->AddAttribute("GCC_SYMBOLS_PRIVATE_EXTERN", |
| this->CreateString("NO")); |
| buildSettings->AddAttribute("GCC_INLINES_ARE_PRIVATE_EXTERN", |
| this->CreateString("NO")); |
| if(lang && strcmp(lang, "CXX") == 0) |
| { |
| flags += " "; |
| flags += defFlags; |
| buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS", |
| this->CreateString(flags.c_str())); |
| cflags += " "; |
| cflags += defFlags; |
| buildSettings->AddAttribute("OTHER_CFLAGS", |
| this->CreateString(cflags.c_str())); |
| |
| } |
| else |
| { |
| flags += " "; |
| flags += defFlags; |
| buildSettings->AddAttribute("OTHER_CFLAGS", |
| this->CreateString(flags.c_str())); |
| } |
| |
| // Add Fortran source format attribute if property is set. |
| const char* format = 0; |
| const char* tgtfmt = target.GetProperty("Fortran_FORMAT"); |
| switch(this->CurrentLocalGenerator->GetFortranFormat(tgtfmt)) |
| { |
| case cmLocalGenerator::FortranFormatFixed: format = "fixed"; break; |
| case cmLocalGenerator::FortranFormatFree: format = "free"; break; |
| default: break; |
| } |
| if(format) |
| { |
| buildSettings->AddAttribute("IFORT_LANG_SRCFMT", |
| this->CreateString(format)); |
| } |
| |
| // Create the INSTALL_PATH attribute. |
| std::string install_name_dir; |
| if(target.GetType() == cmTarget::SHARED_LIBRARY) |
| { |
| // Get the install_name directory for the build tree. |
| install_name_dir = target.GetInstallNameDirForBuildTree(configName); |
| // Xcode doesn't create the correct install_name in some cases. |
| // That is, if the INSTALL_PATH is empty, or if we have versioning |
| // of dylib libraries, we want to specify the install_name. |
| // This is done by adding a link flag to create an install_name |
| // with just the library soname. |
| std::string install_name; |
| if(!install_name_dir.empty()) |
| { |
| // Convert to a path for the native build tool. |
| cmSystemTools::ConvertToUnixSlashes(install_name_dir); |
| install_name += install_name_dir; |
| install_name += "/"; |
| } |
| install_name += target.GetSOName(configName); |
| |
| if((realName != soName) || install_name_dir.empty()) |
| { |
| install_name_dir = ""; |
| extraLinkOptions += " -install_name "; |
| extraLinkOptions += XCodeEscapePath(install_name.c_str()); |
| } |
| } |
| buildSettings->AddAttribute("INSTALL_PATH", |
| this->CreateString(install_name_dir.c_str())); |
| |
| // Create the LD_RUNPATH_SEARCH_PATHS |
| cmComputeLinkInformation* pcli = target.GetLinkInformation(configName); |
| if(pcli) |
| { |
| std::string search_paths; |
| std::vector<std::string> runtimeDirs; |
| pcli->GetRPath(runtimeDirs, false); |
| // runpath dirs needs to be unique to prevent corruption |
| std::set<std::string> unique_dirs; |
| |
| for(std::vector<std::string>::const_iterator i = runtimeDirs.begin(); |
| i != runtimeDirs.end(); ++i) |
| { |
| std::string runpath = *i; |
| runpath = this->ExpandCFGIntDir(runpath, configName); |
| |
| if(unique_dirs.find(runpath) == unique_dirs.end()) |
| { |
| unique_dirs.insert(runpath); |
| if(!search_paths.empty()) |
| { |
| search_paths += " "; |
| } |
| search_paths += this->XCodeEscapePath(runpath.c_str()); |
| } |
| } |
| if(!search_paths.empty()) |
| { |
| buildSettings->AddAttribute("LD_RUNPATH_SEARCH_PATHS", |
| this->CreateString(search_paths.c_str())); |
| } |
| } |
| |
| buildSettings->AddAttribute("OTHER_LDFLAGS", |
| this->CreateString(extraLinkOptions.c_str())); |
| buildSettings->AddAttribute("OTHER_REZFLAGS", |
| this->CreateString("")); |
| buildSettings->AddAttribute("SECTORDER_FLAGS", |
| this->CreateString("")); |
| buildSettings->AddAttribute("USE_HEADERMAP", |
| this->CreateString("NO")); |
| if (this->XcodeVersion >= 30) |
| { |
| cmXCodeObject *group = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| group->AddObject(this->CreateString("-Wmost")); |
| group->AddObject(this->CreateString("-Wno-four-char-constants")); |
| group->AddObject(this->CreateString("-Wno-unknown-pragmas")); |
| buildSettings->AddAttribute("WARNING_CFLAGS", group); |
| } |
| else |
| { |
| buildSettings->AddAttribute("WARNING_CFLAGS", |
| this->CreateString( |
| "-Wmost -Wno-four-char-constants" |
| " -Wno-unknown-pragmas")); |
| } |
| |
| // Runtime version information. |
| if(target.GetType() == cmTarget::SHARED_LIBRARY) |
| { |
| int major; |
| int minor; |
| int patch; |
| |
| // VERSION -> current_version |
| target.GetTargetVersion(false, major, minor, patch); |
| cmOStringStream v; |
| |
| // Xcode always wants at least 1.0.0 or nothing |
| if(!(major == 0 && minor == 0 && patch == 0)) |
| { |
| v << major << "." << minor << "." << patch; |
| } |
| buildSettings->AddAttribute("DYLIB_CURRENT_VERSION", |
| this->CreateString(v.str().c_str())); |
| |
| // SOVERSION -> compatibility_version |
| target.GetTargetVersion(true, major, minor, patch); |
| cmOStringStream vso; |
| |
| // Xcode always wants at least 1.0.0 or nothing |
| if(!(major == 0 && minor == 0 && patch == 0)) |
| { |
| vso << major << "." << minor << "." << patch; |
| } |
| buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION", |
| this->CreateString(vso.str().c_str())); |
| } |
| // put this last so it can override existing settings |
| // Convert "XCODE_ATTRIBUTE_*" properties directly. |
| { |
| cmPropertyMap const& props = target.GetProperties(); |
| for(cmPropertyMap::const_iterator i = props.begin(); |
| i != props.end(); ++i) |
| { |
| if(i->first.find("XCODE_ATTRIBUTE_") == 0) |
| { |
| cmStdString attribute = i->first.substr(16); |
| // Handle [variant=<config>] condition explicitly here. |
| cmStdString::size_type beginVariant = |
| attribute.find("[variant="); |
| if (beginVariant != cmStdString::npos) |
| { |
| cmStdString::size_type endVariant = |
| attribute.find("]", beginVariant+9); |
| if (endVariant != cmStdString::npos) |
| { |
| // Compare the variant to the configuration. |
| cmStdString variant = |
| attribute.substr(beginVariant+9, endVariant-beginVariant-9); |
| if (variant == configName) |
| { |
| // The variant matches the configuration so use this |
| // attribute but drop the [variant=<config>] condition. |
| attribute.erase(beginVariant, endVariant-beginVariant+1); |
| } |
| else |
| { |
| // The variant does not match the configuration so |
| // do not use this attribute. |
| attribute.clear(); |
| } |
| } |
| } |
| |
| if (!attribute.empty()) |
| { |
| buildSettings->AddAttribute(attribute.c_str(), |
| this->CreateString(i->second.GetValue())); |
| } |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| 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")); |
| shellBuildPhase->AddAttribute("showEnvVarsInLog", |
| this->CreateString("0")); |
| |
| cmXCodeObject* target = |
| this->CreateObject(cmXCodeObject::PBXAggregateTarget); |
| target->SetComment(cmtarget.GetName()); |
| cmXCodeObject* buildPhases = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| std::vector<cmXCodeObject*> emptyContentVector; |
| this->CreateCustomCommands(buildPhases, 0, 0, 0, emptyContentVector, 0, |
| cmtarget); |
| target->AddAttribute("buildPhases", buildPhases); |
| if(this->XcodeVersion > 20) |
| { |
| this->AddConfigurations(target, cmtarget); |
| } |
| else |
| { |
| const char* theConfig = |
| this->CurrentMakefile->GetDefinition("CMAKE_BUILD_TYPE"); |
| cmXCodeObject* buildSettings = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| this->CreateBuildSettings(cmtarget, buildSettings, theConfig); |
| 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->SetTarget(&cmtarget); |
| |
| // Add source files without build rules for editing convenience. |
| if(cmtarget.GetType() == cmTarget::UTILITY) |
| { |
| std::vector<cmSourceFile*> sources; |
| cmtarget.GetSourceFiles(sources); |
| for(std::vector<cmSourceFile*>::const_iterator i = sources.begin(); |
| i != sources.end(); ++i) |
| { |
| if(!(*i)->GetPropertyAsBool("GENERATED")) |
| { |
| this->CreateXCodeFileReference(*i, cmtarget); |
| } |
| } |
| } |
| |
| target->SetId(this->GetOrCreateId( |
| cmtarget.GetName(), target->GetId()).c_str()); |
| |
| return target; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::AddConfigurations(cmXCodeObject* target, |
| cmTarget& cmtarget) |
| { |
| std::string configTypes = |
| this->CurrentMakefile->GetRequiredDefinition("CMAKE_CONFIGURATION_TYPES"); |
| std::vector<std::string> configVectorIn; |
| std::vector<std::string> configVector; |
| configVectorIn.push_back(configTypes); |
| cmSystemTools::ExpandList(configVectorIn, configVector); |
| cmXCodeObject* configlist = |
| this->CreateObject(cmXCodeObject::XCConfigurationList); |
| cmXCodeObject* buildConfigurations = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| configlist->AddAttribute("buildConfigurations", buildConfigurations); |
| std::string comment = "Build configuration list for "; |
| comment += cmXCodeObject::PBXTypeNames[target->GetIsA()]; |
| comment += " \""; |
| comment += cmtarget.GetName(); |
| comment += "\""; |
| configlist->SetComment(comment.c_str()); |
| target->AddAttribute("buildConfigurationList", |
| this->CreateObjectReference(configlist)); |
| for(unsigned int i = 0; i < configVector.size(); ++i) |
| { |
| cmXCodeObject* config = |
| this->CreateObject(cmXCodeObject::XCBuildConfiguration); |
| buildConfigurations->AddObject(config); |
| cmXCodeObject* buildSettings = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| this->CreateBuildSettings(cmtarget, buildSettings, |
| configVector[i].c_str()); |
| config->AddAttribute("name", this->CreateString(configVector[i].c_str())); |
| config->SetComment(configVector[i].c_str()); |
| config->AddAttribute("buildSettings", buildSettings); |
| } |
| if(configVector.size()) |
| { |
| configlist->AddAttribute("defaultConfigurationName", |
| this->CreateString(configVector[0].c_str())); |
| configlist->AddAttribute("defaultConfigurationIsVisible", |
| this->CreateString("0")); |
| return configVector[0]; |
| } |
| return ""; |
| } |
| |
| //---------------------------------------------------------------------------- |
| const char* cmGlobalXCodeGenerator::GetTargetFileType(cmTarget& cmtarget) |
| { |
| switch(cmtarget.GetType()) |
| { |
| case cmTarget::OBJECT_LIBRARY: |
| case cmTarget::STATIC_LIBRARY: |
| return "archive.ar"; |
| case cmTarget::MODULE_LIBRARY: |
| if (cmtarget.IsCFBundleOnApple()) |
| return "wrapper.plug-in"; |
| else |
| return ((this->XcodeVersion >= 22)? |
| "compiled.mach-o.executable" : "compiled.mach-o.dylib"); |
| case cmTarget::SHARED_LIBRARY: |
| return (cmtarget.GetPropertyAsBool("FRAMEWORK")? |
| "wrapper.framework" : "compiled.mach-o.dylib"); |
| case cmTarget::EXECUTABLE: |
| return "compiled.mach-o.executable"; |
| default: break; |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| const char* cmGlobalXCodeGenerator::GetTargetProductType(cmTarget& cmtarget) |
| { |
| switch(cmtarget.GetType()) |
| { |
| case cmTarget::OBJECT_LIBRARY: |
| case cmTarget::STATIC_LIBRARY: |
| return "com.apple.product-type.library.static"; |
| case cmTarget::MODULE_LIBRARY: |
| if (cmtarget.IsCFBundleOnApple()) |
| return "com.apple.product-type.bundle"; |
| else |
| return ((this->XcodeVersion >= 22)? |
| "com.apple.product-type.tool" : |
| "com.apple.product-type.library.dynamic"); |
| case cmTarget::SHARED_LIBRARY: |
| return (cmtarget.GetPropertyAsBool("FRAMEWORK")? |
| "com.apple.product-type.framework" : |
| "com.apple.product-type.library.dynamic"); |
| case cmTarget::EXECUTABLE: |
| return (cmtarget.GetPropertyAsBool("MACOSX_BUNDLE")? |
| "com.apple.product-type.application" : |
| "com.apple.product-type.tool"); |
| default: break; |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* |
| cmGlobalXCodeGenerator::CreateXCodeTarget(cmTarget& cmtarget, |
| cmXCodeObject* buildPhases) |
| { |
| if(cmtarget.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| return 0; |
| } |
| cmXCodeObject* target = |
| this->CreateObject(cmXCodeObject::PBXNativeTarget); |
| target->AddAttribute("buildPhases", buildPhases); |
| cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| target->AddAttribute("buildRules", buildRules); |
| std::string defConfig; |
| if(this->XcodeVersion > 20) |
| { |
| defConfig = this->AddConfigurations(target, cmtarget); |
| } |
| else |
| { |
| cmXCodeObject* buildSettings = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| defConfig = this->CurrentMakefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
| this->CreateBuildSettings(cmtarget, buildSettings, defConfig.c_str()); |
| 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); |
| if(const char* fileType = this->GetTargetFileType(cmtarget)) |
| { |
| fileRef->AddAttribute("explicitFileType", this->CreateString(fileType)); |
| } |
| std::string fullName; |
| if(cmtarget.GetType() == cmTarget::OBJECT_LIBRARY) |
| { |
| fullName = "lib"; |
| fullName += cmtarget.GetName(); |
| fullName += ".a"; |
| } |
| else |
| { |
| fullName = cmtarget.GetFullName(defConfig.c_str()); |
| } |
| fileRef->AddAttribute("path", this->CreateString(fullName.c_str())); |
| fileRef->AddAttribute("refType", this->CreateString("0")); |
| fileRef->AddAttribute("sourceTree", |
| this->CreateString("BUILT_PRODUCTS_DIR")); |
| fileRef->SetComment(cmtarget.GetName()); |
| target->AddAttribute("productReference", |
| this->CreateObjectReference(fileRef)); |
| if(const char* productType = this->GetTargetProductType(cmtarget)) |
| { |
| target->AddAttribute("productType", this->CreateString(productType)); |
| } |
| target->SetTarget(&cmtarget); |
| target->SetId(this->GetOrCreateId( |
| cmtarget.GetName(), target->GetId()).c_str()); |
| return target; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget(cmTarget const* t) |
| { |
| if(!t) |
| { |
| return 0; |
| } |
| for(std::vector<cmXCodeObject*>::iterator i = this->XCodeObjects.begin(); |
| i != this->XCodeObjects.end(); ++i) |
| { |
| cmXCodeObject* o = *i; |
| if(o->GetTarget() == t) |
| { |
| return o; |
| } |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::GetOrCreateId(const char* name, |
| const char* id) |
| { |
| std::string guidStoreName = name; |
| guidStoreName += "_GUID_CMAKE"; |
| const char* storedGUID = |
| this->CMakeInstance->GetCacheDefinition(guidStoreName.c_str()); |
| |
| if(storedGUID) |
| { |
| return storedGUID; |
| } |
| |
| this->CMakeInstance->AddCacheEntry(guidStoreName.c_str(), |
| id, "Stored Xcode object GUID", cmCacheManager::INTERNAL); |
| |
| return id; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target, |
| cmXCodeObject* dependTarget) |
| { |
| // This is called once for every edge in the target dependency graph. |
| cmXCodeObject* container = |
| this->CreateObject(cmXCodeObject::PBXContainerItemProxy); |
| container->SetComment("PBXContainerItemProxy"); |
| container->AddAttribute("containerPortal", |
| this->CreateObjectReference(this->RootObject)); |
| container->AddAttribute("proxyType", this->CreateString("1")); |
| container->AddAttribute("remoteGlobalIDString", |
| this->CreateObjectReference(dependTarget)); |
| container->AddAttribute("remoteInfo", |
| this->CreateString( |
| dependTarget->GetTarget()->GetName())); |
| cmXCodeObject* targetdep = |
| this->CreateObject(cmXCodeObject::PBXTargetDependency); |
| targetdep->SetComment("PBXTargetDependency"); |
| targetdep->AddAttribute("target", |
| this->CreateObjectReference(dependTarget)); |
| targetdep->AddAttribute("targetProxy", |
| this->CreateObjectReference(container)); |
| |
| cmXCodeObject* depends = target->GetObject("dependencies"); |
| if(!depends) |
| { |
| cmSystemTools:: |
| Error("target does not have dependencies attribute error.."); |
| |
| } |
| else |
| { |
| depends->AddUniqueObject(targetdep); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::AppendOrAddBuildSetting(cmXCodeObject* settings, |
| const char* attribute, |
| const char* value) |
| { |
| if(settings) |
| { |
| cmXCodeObject* attr = settings->GetObject(attribute); |
| if(!attr) |
| { |
| settings->AddAttribute(attribute, this->CreateString(value)); |
| } |
| else |
| { |
| std::string oldValue = attr->GetString(); |
| oldValue += " "; |
| oldValue += value; |
| attr->SetString(oldValue.c_str()); |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator |
| ::AppendBuildSettingAttribute(cmXCodeObject* target, |
| const char* attribute, |
| const char* value, |
| const char* configName) |
| { |
| if(this->XcodeVersion < 21) |
| { |
| // There is only one configuration. Add the setting to the buildSettings |
| // of the target. |
| this->AppendOrAddBuildSetting(target->GetObject("buildSettings"), |
| attribute, value); |
| } |
| else |
| { |
| // There are multiple configurations. Add the setting to the |
| // buildSettings of the configuration name given. |
| cmXCodeObject* configurationList = |
| target->GetObject("buildConfigurationList")->GetObject(); |
| cmXCodeObject* buildConfigs = |
| configurationList->GetObject("buildConfigurations"); |
| std::vector<cmXCodeObject*> list = buildConfigs->GetObjectList(); |
| // each configuration and the target itself has a buildSettings in it |
| //list.push_back(target); |
| for(std::vector<cmXCodeObject*>::iterator i = list.begin(); |
| i != list.end(); ++i) |
| { |
| if(configName) |
| { |
| if(strcmp((*i)->GetObject("name")->GetString(), configName) == 0) |
| { |
| cmXCodeObject* settings = (*i)->GetObject("buildSettings"); |
| this->AppendOrAddBuildSetting(settings, attribute, value); |
| } |
| } |
| else |
| { |
| cmXCodeObject* settings = (*i)->GetObject("buildSettings"); |
| this->AppendOrAddBuildSetting(settings, attribute, value); |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator |
| ::AddDependAndLinkInformation(cmXCodeObject* target) |
| { |
| cmTarget* cmtarget = target->GetTarget(); |
| if(cmtarget->GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| return; |
| } |
| if(!cmtarget) |
| { |
| cmSystemTools::Error("Error no target on xobject\n"); |
| return; |
| } |
| |
| // Add dependencies on other CMake targets. |
| TargetDependSet const& deps = this->GetTargetDirectDepends(*cmtarget); |
| for(TargetDependSet::const_iterator i = deps.begin(); i != deps.end(); ++i) |
| { |
| if(cmXCodeObject* dptarget = this->FindXCodeTarget(*i)) |
| { |
| this->AddDependTarget(target, dptarget); |
| } |
| } |
| |
| // Loop over configuration types and set per-configuration info. |
| for(std::vector<std::string>::iterator i = |
| this->CurrentConfigurationTypes.begin(); |
| i != this->CurrentConfigurationTypes.end(); ++i) |
| { |
| // Get the current configuration name. |
| const char* configName = i->c_str(); |
| if(!*configName) |
| { |
| configName = 0; |
| } |
| |
| if(this->XcodeVersion >= 50) |
| { |
| // Add object library contents as link flags. |
| std::string linkObjs; |
| const char* sep = ""; |
| std::vector<std::string> objs; |
| this->GetGeneratorTarget(cmtarget)->UseObjectLibraries(objs); |
| for(std::vector<std::string>::const_iterator |
| oi = objs.begin(); oi != objs.end(); ++oi) |
| { |
| linkObjs += sep; |
| sep = " "; |
| linkObjs += this->XCodeEscapePath(oi->c_str()); |
| } |
| this->AppendBuildSettingAttribute(target, "OTHER_LDFLAGS", |
| linkObjs.c_str(), configName); |
| } |
| |
| // Skip link information for object libraries. |
| if(cmtarget->GetType() == cmTarget::OBJECT_LIBRARY || |
| cmtarget->GetType() == cmTarget::STATIC_LIBRARY) |
| { |
| continue; |
| } |
| |
| // Compute the link library and directory information. |
| cmComputeLinkInformation* pcli = cmtarget->GetLinkInformation(configName); |
| if(!pcli) |
| { |
| continue; |
| } |
| cmComputeLinkInformation& cli = *pcli; |
| |
| // Add dependencies directly on library files. |
| { |
| std::vector<std::string> const& libDeps = cli.GetDepends(); |
| for(std::vector<std::string>::const_iterator j = libDeps.begin(); |
| j != libDeps.end(); ++j) |
| { |
| target->AddDependLibrary(configName, j->c_str()); |
| } |
| } |
| |
| // add the library search paths |
| { |
| std::vector<std::string> const& libDirs = cli.GetDirectories(); |
| std::string linkDirs; |
| for(std::vector<std::string>::const_iterator libDir = libDirs.begin(); |
| libDir != libDirs.end(); ++libDir) |
| { |
| if(libDir->size() && *libDir != "/usr/lib") |
| { |
| if(this->XcodeVersion > 15) |
| { |
| // Now add the same one but append |
| // $(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) to it: |
| linkDirs += " "; |
| linkDirs += this->XCodeEscapePath( |
| (*libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)").c_str()); |
| } |
| linkDirs += " "; |
| linkDirs += this->XCodeEscapePath(libDir->c_str()); |
| } |
| } |
| this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS", |
| linkDirs.c_str(), configName); |
| } |
| |
| // now add the link libraries |
| { |
| std::string linkLibs; |
| const char* sep = ""; |
| typedef cmComputeLinkInformation::ItemVector ItemVector; |
| ItemVector const& libNames = cli.GetItems(); |
| for(ItemVector::const_iterator li = libNames.begin(); |
| li != libNames.end(); ++li) |
| { |
| linkLibs += sep; |
| sep = " "; |
| if(li->IsPath) |
| { |
| linkLibs += this->XCodeEscapePath(li->Value.c_str()); |
| } |
| else if (!li->Target |
| || li->Target->GetType() != cmTarget::INTERFACE_LIBRARY) |
| { |
| linkLibs += li->Value; |
| } |
| if(li->Target && !li->Target->IsImported()) |
| { |
| target->AddDependTarget(configName, li->Target->GetName()); |
| } |
| } |
| this->AppendBuildSettingAttribute(target, "OTHER_LDFLAGS", |
| linkLibs.c_str(), configName); |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::CreateGroups(cmLocalGenerator* root, |
| std::vector<cmLocalGenerator*>& |
| generators) |
| { |
| for(std::vector<cmLocalGenerator*>::iterator i = generators.begin(); |
| i != generators.end(); ++i) |
| { |
| if(this->IsExcluded(root, *i)) |
| { |
| continue; |
| } |
| cmMakefile* mf = (*i)->GetMakefile(); |
| std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups(); |
| cmTargets &tgts = mf->GetTargets(); |
| for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++) |
| { |
| cmTarget& cmtarget = l->second; |
| |
| // Same skipping logic here as in CreateXCodeTargets so that we do not |
| // end up with (empty anyhow) ALL_BUILD and XCODE_DEPEND_HELPER source |
| // groups: |
| // |
| if(cmtarget.GetType() == cmTarget::GLOBAL_TARGET) |
| { |
| continue; |
| } |
| if(cmtarget.GetType() == cmTarget::INTERFACE_LIBRARY) |
| { |
| continue; |
| } |
| |
| // add the soon to be generated Info.plist file as a source for a |
| // MACOSX_BUNDLE file |
| if(cmtarget.GetPropertyAsBool("MACOSX_BUNDLE")) |
| { |
| std::string plist = this->ComputeInfoPListLocation(cmtarget); |
| cmSourceFile* sf = mf->GetOrCreateSource(plist.c_str(), true); |
| cmtarget.AddSourceFile(sf); |
| } |
| |
| std::vector<cmSourceFile*> classes; |
| cmtarget.GetSourceFiles(classes); |
| |
| // Put cmSourceFile instances in proper groups: |
| for(std::vector<cmSourceFile*>::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); |
| cmStdString key = GetGroupMapKey(cmtarget, sf); |
| this->GroupMap[key] = pbxgroup; |
| } |
| |
| // Put OBJECT_LIBRARY objects in proper groups: |
| std::vector<std::string> objs; |
| this->GetGeneratorTarget(&cmtarget)->UseObjectLibraries(objs); |
| for(std::vector<std::string>::const_iterator |
| oi = objs.begin(); oi != objs.end(); ++oi) |
| { |
| std::string const& source = *oi; |
| cmSourceGroup* sourceGroup = |
| mf->FindSourceGroup(source.c_str(), sourceGroups); |
| cmXCodeObject* pbxgroup = |
| this->CreateOrGetPBXGroup(cmtarget, sourceGroup); |
| cmStdString key = GetGroupMapKeyFromPath(cmtarget, source); |
| this->GroupMap[key] = pbxgroup; |
| } |
| } |
| } |
| } |
| |
| cmXCodeObject *cmGlobalXCodeGenerator |
| ::CreatePBXGroup(cmXCodeObject *parent, cmStdString name) |
| { |
| cmXCodeObject* parentChildren = NULL; |
| if(parent) |
| parentChildren = parent->GetObject("children"); |
| cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup); |
| cmXCodeObject* groupChildren = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| group->AddAttribute("name", this->CreateString(name.c_str())); |
| group->AddAttribute("children", groupChildren); |
| if(this->XcodeVersion == 15) |
| { |
| group->AddAttribute("refType", this->CreateString("4")); |
| } |
| group->AddAttribute("sourceTree", this->CreateString("<group>")); |
| if(parentChildren) |
| parentChildren->AddObject(group); |
| return group; |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmXCodeObject* cmGlobalXCodeGenerator |
| ::CreateOrGetPBXGroup(cmTarget& cmtarget, cmSourceGroup* sg) |
| { |
| cmStdString s; |
| cmStdString target; |
| const char *targetFolder= cmtarget.GetProperty("FOLDER"); |
| if(targetFolder) { |
| target = targetFolder; |
| target += "/"; |
| } |
| target += cmtarget.GetName(); |
| s = target + "/"; |
| s += sg->GetFullName(); |
| std::map<cmStdString, cmXCodeObject* >::iterator it = |
| this->GroupNameMap.find(s); |
| if(it != this->GroupNameMap.end()) |
| { |
| return it->second; |
| } |
| |
| it = this->TargetGroup.find(target); |
| cmXCodeObject* tgroup = 0; |
| if(it != this->TargetGroup.end()) |
| { |
| tgroup = it->second; |
| } |
| else |
| { |
| std::vector<std::string> tgt_folders = |
| cmSystemTools::tokenize(target, "/"); |
| cmStdString curr_tgt_folder; |
| for(std::vector<std::string>::size_type i = 0; i < tgt_folders.size();i++) |
| { |
| if (i != 0) |
| { |
| curr_tgt_folder += "/"; |
| } |
| curr_tgt_folder += tgt_folders[i]; |
| it = this->TargetGroup.find(curr_tgt_folder); |
| if(it != this->TargetGroup.end()) |
| { |
| tgroup = it->second; |
| continue; |
| } |
| tgroup = this->CreatePBXGroup(tgroup,tgt_folders[i]); |
| this->TargetGroup[curr_tgt_folder] = tgroup; |
| if(i == 0) |
| { |
| this->SourcesGroupChildren->AddObject(tgroup); |
| } |
| } |
| } |
| this->TargetGroup[target] = tgroup; |
| |
| // If it's the default source group (empty name) then put the source file |
| // directly in the tgroup... |
| // |
| if (cmStdString(sg->GetFullName()) == "") |
| { |
| this->GroupNameMap[s] = tgroup; |
| return tgroup; |
| } |
| |
| //It's a recursive folder structure, let's find the real parent group |
| if(std::string(sg->GetFullName()) != std::string(sg->GetName())) |
| { |
| std::vector<std::string> folders = |
| cmSystemTools::tokenize(sg->GetFullName(), "\\"); |
| cmStdString curr_folder = target; |
| curr_folder += "/"; |
| for(std::vector<std::string>::size_type i = 0; i < folders.size();i++) |
| { |
| curr_folder += folders[i]; |
| std::map<cmStdString, cmXCodeObject* >::iterator i_folder = |
| this->GroupNameMap.find(curr_folder); |
| //Create new folder |
| if(i_folder == this->GroupNameMap.end()) |
| { |
| cmXCodeObject *group = this->CreatePBXGroup(tgroup,folders[i]); |
| this->GroupNameMap[curr_folder] = group; |
| tgroup = group; |
| } |
| else |
| { |
| tgroup = i_folder->second; |
| } |
| curr_folder = curr_folder + "\\"; |
| } |
| return tgroup; |
| } |
| cmXCodeObject *group = this->CreatePBXGroup(tgroup,sg->GetName()); |
| this->GroupNameMap[s] = group; |
| return group; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator |
| ::CreateXCodeObjects(cmLocalGenerator* root, |
| std::vector<cmLocalGenerator*>& |
| generators) |
| { |
| this->ClearXCodeObjects(); |
| this->RootObject = 0; |
| this->SourcesGroupChildren = 0; |
| this->ResourcesGroupChildren = 0; |
| this->MainGroupChildren = 0; |
| cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO")); |
| cmXCodeObject* developBuildStyle = |
| this->CreateObject(cmXCodeObject::PBXBuildStyle); |
| cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| if(this->XcodeVersion == 15) |
| { |
| developBuildStyle->AddAttribute("name", |
| this->CreateString("Development")); |
| developBuildStyle->AddAttribute("buildSettings", group); |
| listObjs->AddObject(developBuildStyle); |
| 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); |
| listObjs->AddObject(deployBuildStyle); |
| } |
| else |
| { |
| for(unsigned int i = 0; i < this->CurrentConfigurationTypes.size(); ++i) |
| { |
| cmXCodeObject* buildStyle = |
| this->CreateObject(cmXCodeObject::PBXBuildStyle); |
| const char* name = this->CurrentConfigurationTypes[i].c_str(); |
| buildStyle->AddAttribute("name", this->CreateString(name)); |
| buildStyle->SetComment(name); |
| cmXCodeObject* sgroup = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| sgroup->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO")); |
| buildStyle->AddAttribute("buildSettings", sgroup); |
| listObjs->AddObject(buildStyle); |
| } |
| } |
| |
| cmXCodeObject* mainGroup = this->CreateObject(cmXCodeObject::PBXGroup); |
| this->MainGroupChildren = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| mainGroup->AddAttribute("children", this->MainGroupChildren); |
| if(this->XcodeVersion == 15) |
| { |
| mainGroup->AddAttribute("refType", this->CreateString("4")); |
| } |
| mainGroup->AddAttribute("sourceTree", this->CreateString("<group>")); |
| |
| cmXCodeObject* sourcesGroup = this->CreateObject(cmXCodeObject::PBXGroup); |
| this->SourcesGroupChildren = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| sourcesGroup->AddAttribute("name", this->CreateString("Sources")); |
| sourcesGroup->AddAttribute("children", this->SourcesGroupChildren); |
| if(this->XcodeVersion == 15) |
| { |
| sourcesGroup->AddAttribute("refType", this->CreateString("4")); |
| } |
| sourcesGroup->AddAttribute("sourceTree", this->CreateString("<group>")); |
| this->MainGroupChildren->AddObject(sourcesGroup); |
| |
| cmXCodeObject* resourcesGroup = this->CreateObject(cmXCodeObject::PBXGroup); |
| this->ResourcesGroupChildren = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| resourcesGroup->AddAttribute("name", this->CreateString("Resources")); |
| resourcesGroup->AddAttribute("children", this->ResourcesGroupChildren); |
| if(this->XcodeVersion == 15) |
| { |
| resourcesGroup->AddAttribute("refType", this->CreateString("4")); |
| } |
| resourcesGroup->AddAttribute("sourceTree", this->CreateString("<group>")); |
| this->MainGroupChildren->AddObject(resourcesGroup); |
| |
| // now create the cmake groups |
| this->CreateGroups(root, generators); |
| |
| cmXCodeObject* productGroup = this->CreateObject(cmXCodeObject::PBXGroup); |
| productGroup->AddAttribute("name", this->CreateString("Products")); |
| if(this->XcodeVersion == 15) |
| { |
| productGroup->AddAttribute("refType", this->CreateString("4")); |
| } |
| productGroup->AddAttribute("sourceTree", this->CreateString("<group>")); |
| cmXCodeObject* productGroupChildren = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| productGroup->AddAttribute("children", productGroupChildren); |
| this->MainGroupChildren->AddObject(productGroup); |
| |
| |
| this->RootObject = this->CreateObject(cmXCodeObject::PBXProject); |
| this->RootObject->SetComment("Project object"); |
| |
| std::string project_id = "PROJECT_"; |
| project_id += root->GetMakefile()->GetProjectName(); |
| this->RootObject->SetId(this->GetOrCreateId( |
| project_id.c_str(), this->RootObject->GetId()).c_str()); |
| |
| group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| this->RootObject->AddAttribute("mainGroup", |
| this->CreateObjectReference(mainGroup)); |
| this->RootObject->AddAttribute("buildSettings", group); |
| this->RootObject->AddAttribute("buildStyles", listObjs); |
| this->RootObject->AddAttribute("hasScannedForEncodings", |
| this->CreateString("0")); |
| if (this->XcodeVersion >= 30) |
| { |
| group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| group->AddAttribute("BuildIndependentTargetsInParallel", |
| this->CreateString("YES")); |
| this->RootObject->AddAttribute("attributes", group); |
| if (this->XcodeVersion >= 32) |
| this->RootObject->AddAttribute("compatibilityVersion", |
| this->CreateString("Xcode 3.2")); |
| else if (this->XcodeVersion >= 31) |
| this->RootObject->AddAttribute("compatibilityVersion", |
| this->CreateString("Xcode 3.1")); |
| else |
| this->RootObject->AddAttribute("compatibilityVersion", |
| this->CreateString("Xcode 3.0")); |
| } |
| // Point Xcode at the top of the source tree. |
| { |
| std::string pdir = |
| this->RelativeToBinary(root->GetMakefile()->GetCurrentDirectory()); |
| this->RootObject->AddAttribute("projectDirPath", |
| this->CreateString(pdir.c_str())); |
| this->RootObject->AddAttribute("projectRoot", this->CreateString("")); |
| } |
| cmXCodeObject* configlist = |
| this->CreateObject(cmXCodeObject::XCConfigurationList); |
| cmXCodeObject* buildConfigurations = |
| this->CreateObject(cmXCodeObject::OBJECT_LIST); |
| std::vector<cmXCodeObject*> configs; |
| const char *defaultConfigName = "Debug"; |
| if(this->XcodeVersion == 15) |
| { |
| cmXCodeObject* configDebug = |
| this->CreateObject(cmXCodeObject::XCBuildConfiguration); |
| configDebug->AddAttribute("name", this->CreateString("Debug")); |
| configs.push_back(configDebug); |
| cmXCodeObject* configRelease = |
| this->CreateObject(cmXCodeObject::XCBuildConfiguration); |
| configRelease->AddAttribute("name", this->CreateString("Release")); |
| configs.push_back(configRelease); |
| } |
| else |
| { |
| for(unsigned int i = 0; i < this->CurrentConfigurationTypes.size(); ++i) |
| { |
| const char* name = this->CurrentConfigurationTypes[i].c_str(); |
| if (0 == i) |
| { |
| defaultConfigName = name; |
| } |
| cmXCodeObject* config = |
| this->CreateObject(cmXCodeObject::XCBuildConfiguration); |
| config->AddAttribute("name", this->CreateString(name)); |
| configs.push_back(config); |
| } |
| } |
| for(std::vector<cmXCodeObject*>::iterator c = configs.begin(); |
| c != configs.end(); ++c) |
| { |
| buildConfigurations->AddObject(*c); |
| } |
| configlist->AddAttribute("buildConfigurations", buildConfigurations); |
| |
| std::string comment = "Build configuration list for PBXProject "; |
| comment += " \""; |
| comment += this->CurrentProject; |
| comment += "\""; |
| configlist->SetComment(comment.c_str()); |
| configlist->AddAttribute("defaultConfigurationIsVisible", |
| this->CreateString("0")); |
| configlist->AddAttribute("defaultConfigurationName", |
| this->CreateString(defaultConfigName)); |
| cmXCodeObject* buildSettings = |
| this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); |
| const char* osxArch = |
| this->CurrentMakefile->GetDefinition("CMAKE_OSX_ARCHITECTURES"); |
| if(!osxArch || strlen(osxArch) == 0) |
| { |
| if(this->XcodeVersion >= 32) |
| { |
| osxArch = "$(ARCHS_STANDARD_32_64_BIT)"; |
| } |
| else if(this->XcodeVersion == 31) |
| { |
| osxArch = "$(ARCHS_STANDARD_32_BIT)"; |
| } |
| else if(this->XcodeVersion <= 30) |
| { |
| #ifdef __ppc__ |
| osxArch = "ppc"; |
| #endif |
| #ifdef __i386 |
| osxArch = "i386"; |
| #endif |
| } |
| buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", |
| this->CreateString("YES")); |
| } |
| |
| const char* sysroot = |
| this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT"); |
| const char* deploymentTarget = |
| this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); |
| if(osxArch && sysroot) |
| { |
| // recompute this as it may have been changed since enable language |
| this->Architectures.clear(); |
| cmSystemTools::ExpandListArgument(std::string(osxArch), |
| this->Architectures); |
| buildSettings->AddAttribute("SDKROOT", |
| this->CreateString(sysroot)); |
| std::string archString; |
| const char* sep = ""; |
| for( std::vector<std::string>::iterator i = |
| this->Architectures.begin(); |
| i != this->Architectures.end(); ++i) |
| { |
| archString += sep; |
| archString += *i; |
| sep = " "; |
| } |
| buildSettings->AddAttribute("ARCHS", |
| this->CreateString(archString.c_str())); |
| } |
| if(deploymentTarget && *deploymentTarget) |
| { |
| buildSettings->AddAttribute("MACOSX_DEPLOYMENT_TARGET", |
| this->CreateString(deploymentTarget)); |
| } |
| if(!this->PlatformToolset.empty()) |
| { |
| buildSettings->AddAttribute("GCC_VERSION", |
| this->CreateString(this->PlatformToolset.c_str())); |
| } |
| |
| // Put this last so it can override existing settings |
| // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly. |
| { |
| std::vector<std::string> vars = this->CurrentMakefile->GetDefinitions(); |
| for(std::vector<std::string>::const_iterator i = vars.begin(); |
| i != vars.end(); ++i) |
| { |
| if(i->find("CMAKE_XCODE_ATTRIBUTE_") == 0) |
| { |
| buildSettings->AddAttribute(i->substr(22).c_str(), |
| this->CreateString( |
| this->CurrentMakefile->GetDefinition(i->c_str()))); |
| } |
| } |
| } |
| |
| std::string symroot = root->GetMakefile()->GetCurrentOutputDirectory(); |
| symroot += "/build"; |
| buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot.c_str())); |
| |
| for( std::vector<cmXCodeObject*>::iterator i = configs.begin(); |
| i != configs.end(); ++i) |
| { |
| (*i)->AddAttribute("buildSettings", buildSettings); |
| } |
| |
| this->RootObject->AddAttribute("buildConfigurationList", |
| this->CreateObjectReference(configlist)); |
| |
| std::vector<cmXCodeObject*> targets; |
| for(std::vector<cmLocalGenerator*>::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<cmXCodeObject*>::iterator i = targets.begin(); |
| i != targets.end(); ++i) |
| { |
| cmXCodeObject* t = *i; |
| this->AddDependAndLinkInformation(t); |
| } |
| if(this->XcodeVersion < 50) |
| { |
| // 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<cmXCodeObject*>::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()); |
| } |
| } |
| this->RootObject->AddAttribute("targets", allTargets); |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| cmGlobalXCodeGenerator::GetObjectsNormalDirectory( |
| const std::string &projName, |
| const std::string &configName, |
| const cmTarget *t) const |
| { |
| std::string dir = |
| t->GetMakefile()->GetCurrentOutputDirectory(); |
| dir += "/"; |
| dir += projName; |
| dir += ".build/"; |
| dir += configName; |
| dir += "/"; |
| dir += t->GetName(); |
| dir += ".build/Objects-normal/"; |
| |
| return dir; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( |
| std::vector<cmXCodeObject*>& targets) |
| { |
| cmGeneratedFileStream |
| makefileStream(this->CurrentXCodeHackMakefile.c_str()); |
| if(!makefileStream) |
| { |
| cmSystemTools::Error("Could not create", |
| this->CurrentXCodeHackMakefile.c_str()); |
| return; |
| } |
| makefileStream.SetCopyIfDifferent(true); |
| // 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\n" |
| "default:\n" |
| "\techo \"Do not invoke directly\"\n" |
| "\n"; |
| makefileStream |
| << "# For each target create a dummy rule " |
| "so the target does not have to exist\n"; |
| std::set<cmStdString> emitted; |
| for(std::vector<cmXCodeObject*>::iterator i = targets.begin(); |
| i != targets.end(); ++i) |
| { |
| cmXCodeObject* target = *i; |
| std::map<cmStdString, cmXCodeObject::StringVec> const& deplibs = |
| target->GetDependLibraries(); |
| for(std::map<cmStdString, cmXCodeObject::StringVec>::const_iterator ci |
| = deplibs.begin(); ci != deplibs.end(); ++ci) |
| { |
| for(cmXCodeObject::StringVec::const_iterator d = ci->second.begin(); |
| d != ci->second.end(); ++d) |
| { |
| if(emitted.insert(*d).second) |
| { |
| makefileStream << |
| this->ConvertToRelativeForMake(d->c_str()) << ":\n"; |
| } |
| } |
| } |
| } |
| makefileStream << "\n\n"; |
| |
| // Write rules to help Xcode relink things at the right time. |
| makefileStream << |
| "# Rules to remove targets that are older than anything to which they\n" |
| "# link. This forces Xcode to relink the targets from scratch. It\n" |
| "# does not seem to check these dependencies itself.\n"; |
| for(std::vector<std::string>::const_iterator |
| ct = this->CurrentConfigurationTypes.begin(); |
| ct != this->CurrentConfigurationTypes.end(); ++ct) |
| { |
| const char* configName = 0; |
| if(!ct->empty()) |
| { |
| configName = ct->c_str(); |
| } |
| for(std::vector<cmXCodeObject*>::iterator i = targets.begin(); |
| i != targets.end(); ++i) |
| { |
| cmXCodeObject* target = *i; |
| cmTarget* t =target->GetTarget(); |
| |
| if(t->GetType() == cmTarget::EXECUTABLE || |
| // Nope - no post-build for OBJECT_LIRBRARY |
| // t->GetType() == cmTarget::OBJECT_LIBRARY || |
| t->GetType() == cmTarget::STATIC_LIBRARY || |
| t->GetType() == cmTarget::SHARED_LIBRARY || |
| t->GetType() == cmTarget::MODULE_LIBRARY) |
| { |
| // Declare an entry point for the target post-build phase. |
| makefileStream << this->PostBuildMakeTarget(t->GetName(), *ct) |
| << ":\n"; |
| } |
| |
| if(t->GetType() == cmTarget::EXECUTABLE || |
| t->GetType() == cmTarget::SHARED_LIBRARY || |
| t->GetType() == cmTarget::MODULE_LIBRARY) |
| { |
| std::string tfull = t->GetFullPath(configName); |
| std::string trel = this->ConvertToRelativeForMake(tfull.c_str()); |
| |
| // Add this target to the post-build phases of its dependencies. |
| std::map<cmStdString, cmXCodeObject::StringVec>::const_iterator |
| y = target->GetDependTargets().find(*ct); |
| if(y != target->GetDependTargets().end()) |
| { |
| std::vector<cmStdString> const& deptgts = y->second; |
| for(std::vector<cmStdString>::const_iterator d = deptgts.begin(); |
| d != deptgts.end(); ++d) |
| { |
| makefileStream << this->PostBuildMakeTarget(*d, *ct) << ": " |
| << trel << "\n"; |
| } |
| } |
| |
| // Create a rule for this target. |
| makefileStream << trel << ":"; |
| |
| // List dependencies if any exist. |
| std::map<cmStdString, cmXCodeObject::StringVec>::const_iterator |
| x = target->GetDependLibraries().find(*ct); |
| if(x != target->GetDependLibraries().end()) |
| { |
| std::vector<cmStdString> const& deplibs = x->second; |
| for(std::vector<cmStdString>::const_iterator d = deplibs.begin(); |
| d != deplibs.end(); ++d) |
| { |
| makefileStream << "\\\n\t" << |
| this->ConvertToRelativeForMake(d->c_str()); |
| } |
| } |
| // Write the action to remove the target if it is out of date. |
| makefileStream << "\n"; |
| makefileStream << "\t/bin/rm -f " |
| << this->ConvertToRelativeForMake(tfull.c_str()) |
| << "\n"; |
| // if building for more than one architecture |
| // then remove those exectuables as well |
| if(this->Architectures.size() > 1) |
| { |
| std::string universal = this->GetObjectsNormalDirectory( |
| this->CurrentProject, configName, t); |
| for( std::vector<std::string>::iterator arch = |
| this->Architectures.begin(); |
| arch != this->Architectures.end(); ++arch) |
| { |
| std::string universalFile = universal; |
| universalFile += *arch; |
| universalFile += "/"; |
| universalFile += t->GetFullName(configName); |
| makefileStream << "\t/bin/rm -f " |
| << |
| this->ConvertToRelativeForMake(universalFile.c_str()) |
| << "\n"; |
| } |
| } |
| makefileStream << "\n\n"; |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::OutputXCodeProject(cmLocalGenerator* root, |
| std::vector<cmLocalGenerator*>& |
| generators) |
| { |
| if(generators.size() == 0) |
| { |
| return; |
| } |
| // Skip local generators that are excluded from this project. |
| for(std::vector<cmLocalGenerator*>::iterator g = generators.begin(); |
| g != generators.end(); ++g) |
| { |
| if(this->IsExcluded(root, *g)) |
| { |
| continue; |
| } |
| } |
| |
| this->CreateXCodeObjects(root, |
| generators); |
| std::string xcodeDir = root->GetMakefile()->GetStartOutputDirectory(); |
| xcodeDir += "/"; |
| xcodeDir += root->GetMakefile()->GetProjectName(); |
| xcodeDir += ".xcode"; |
| if(this->XcodeVersion > 20) |
| { |
| xcodeDir += "proj"; |
| } |
| cmSystemTools::MakeDirectory(xcodeDir.c_str()); |
| std::string xcodeProjFile = xcodeDir + "/project.pbxproj"; |
| cmGeneratedFileStream fout(xcodeProjFile.c_str()); |
| fout.SetCopyIfDifferent(true); |
| if(!fout) |
| { |
| return; |
| } |
| this->WriteXCodePBXProj(fout, root, generators); |
| this->ClearXCodeObjects(); |
| |
| // Since this call may have created new cache entries, save the cache: |
| // |
| root->GetMakefile()->GetCacheManager()->SaveCache( |
| root->GetMakefile()->GetHomeOutputDirectory()); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout, |
| cmLocalGenerator* , |
| std::vector<cmLocalGenerator*>& ) |
| { |
| 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); |
| if(this->XcodeVersion >= 21) |
| { |
| if (this->XcodeVersion >= 32) |
| fout << "objectVersion = 46;\n"; |
| else if (this->XcodeVersion >= 31) |
| fout << "objectVersion = 45;\n"; |
| else if (this->XcodeVersion >= 30) |
| fout << "objectVersion = 44;\n"; |
| else |
| fout << "objectVersion = 42;\n"; |
| cmXCode21Object::PrintList(this->XCodeObjects, fout); |
| } |
| else |
| { |
| fout << "objectVersion = 39;\n"; |
| cmXCodeObject::PrintList(this->XCodeObjects, fout); |
| } |
| cmXCodeObject::Indent(1, fout); |
| fout << "rootObject = " << this->RootObject->GetId() << ";\n"; |
| fout << "}\n"; |
| } |
| |
| //---------------------------------------------------------------------------- |
| const char* cmGlobalXCodeGenerator::GetCMakeCFGIntDir() const |
| { |
| return this->XcodeVersion >= 21 ? |
| "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" : "."; |
| } |
| |
| std::string cmGlobalXCodeGenerator::ExpandCFGIntDir(const std::string& str, |
| const std::string& config) const |
| { |
| std::string replace1 = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; |
| std::string replace2 = "$(CONFIGURATION)"; |
| |
| std::string tmp = str; |
| for(std::string::size_type i = tmp.find(replace1); |
| i != std::string::npos; |
| i = tmp.find(replace1, i)) |
| { |
| tmp.replace(i, replace1.size(), config); |
| i += config.size(); |
| } |
| for(std::string::size_type i = tmp.find(replace2); |
| i != std::string::npos; |
| i = tmp.find(replace2, i)) |
| { |
| tmp.replace(i, replace2.size(), config); |
| i += config.size(); |
| } |
| return tmp; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry) |
| { |
| entry.Name = cmGlobalXCodeGenerator::GetActualName(); |
| entry.Brief = "Generate Xcode project files."; |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(const char* p) |
| { |
| if ( !this->CurrentMakefile->IsOn("CMAKE_USE_RELATIVE_PATHS") ) |
| { |
| return cmSystemTools::ConvertToOutputPath(p); |
| } |
| else |
| { |
| std::string ret = |
| this->CurrentLocalGenerator-> |
| ConvertToRelativePath(this->CurrentOutputDirectoryComponents, p); |
| return cmSystemTools::ConvertToOutputPath(ret.c_str()); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::ConvertToRelativeForXCode(const char* p) |
| { |
| if ( !this->CurrentMakefile->IsOn("CMAKE_USE_RELATIVE_PATHS") ) |
| { |
| return cmSystemTools::ConvertToOutputPath(p); |
| } |
| else |
| { |
| std::string ret = |
| this->CurrentLocalGenerator-> |
| ConvertToRelativePath(this->ProjectOutputDirectoryComponents, p); |
| return cmSystemTools::ConvertToOutputPath(ret.c_str()); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::RelativeToSource(const char* p) |
| { |
| // We force conversion because Xcode breakpoints do not work unless |
| // they are in a file named relative to the source tree. |
| return this->CurrentLocalGenerator-> |
| ConvertToRelativePath(this->ProjectSourceDirectoryComponents, p, true); |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::RelativeToBinary(const char* p) |
| { |
| return this->CurrentLocalGenerator-> |
| ConvertToRelativePath(this->ProjectOutputDirectoryComponents, p); |
| } |
| |
| //---------------------------------------------------------------------------- |
| 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; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator |
| ::AppendDirectoryForConfig(const char* prefix, |
| const char* config, |
| const char* suffix, |
| std::string& dir) |
| { |
| if(this->XcodeVersion > 20) |
| { |
| if(config) |
| { |
| dir += prefix; |
| dir += config; |
| dir += suffix; |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmGlobalXCodeGenerator::LookupFlags(const char* varNamePrefix, |
| const char* varNameLang, |
| const char* varNameSuffix, |
| const char* default_flags) |
| { |
| if(varNameLang) |
| { |
| std::string varName = varNamePrefix; |
| varName += varNameLang; |
| varName += varNameSuffix; |
| if(const char* varValue = |
| this->CurrentMakefile->GetDefinition(varName.c_str())) |
| { |
| if(*varValue) |
| { |
| return varValue; |
| } |
| } |
| } |
| return default_flags; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::AppendDefines(BuildObjectListOrString& defs, |
| const char* defines_list, |
| bool dflag) |
| { |
| // Skip this if there are no definitions. |
| if(!defines_list) |
| { |
| return; |
| } |
| |
| // Expand the list of definitions. |
| std::vector<std::string> defines; |
| cmSystemTools::ExpandListArgument(defines_list, defines); |
| |
| // Store the definitions in the string. |
| this->AppendDefines(defs, defines, dflag); |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator::AppendDefines(BuildObjectListOrString& defs, |
| std::vector<std::string> const& defines, |
| bool dflag) |
| { |
| // GCC_PREPROCESSOR_DEFINITIONS is a space-separated list of definitions. |
| std::string def; |
| for(std::vector<std::string>::const_iterator di = defines.begin(); |
| di != defines.end(); ++di) |
| { |
| // Start with -D if requested. |
| def = dflag? "-D": ""; |
| def += *di; |
| |
| // Append the flag with needed escapes. |
| std::string tmp; |
| this->AppendFlag(tmp, def); |
| defs.Add(tmp.c_str()); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmGlobalXCodeGenerator::AppendFlag(std::string& flags, |
| std::string const& flag) |
| { |
| // Short-circuit for an empty flag. |
| if(flag.empty()) |
| { |
| return; |
| } |
| |
| // Separate from previous flags. |
| if(!flags.empty()) |
| { |
| flags += " "; |
| } |
| |
| // Check if the flag needs quoting. |
| bool quoteFlag = |
| flag.find_first_of("`~!@#$%^&*()+={}[]|:;\"'<>,.? ") != flag.npos; |
| |
| // We escape a flag as follows: |
| // - Place each flag in single quotes '' |
| // - Escape a single quote as \\' |
| // - Escape a backslash as \\\\ since it itself is an escape |
| // Note that in the code below we need one more level of escapes for |
| // C string syntax in this source file. |
| // |
| // The final level of escaping is done when the string is stored |
| // into the project file by cmXCodeObject::PrintString. |
| |
| if(quoteFlag) |
| { |
| // Open single quote. |
| flags += "'"; |
| } |
| |
| // Flag value with escaped quotes and backslashes. |
| for(const char* c = flag.c_str(); *c; ++c) |
| { |
| if(*c == '\'') |
| { |
| if (this->XcodeVersion >= 40) |
| { |
| flags += "'\\\\''"; |
| } |
| else |
| { |
| flags += "\\\\'"; |
| } |
| } |
| else if(*c == '\\') |
| { |
| flags += "\\\\\\\\"; |
| } |
| else |
| { |
| flags += *c; |
| } |
| } |
| |
| if(quoteFlag) |
| { |
| // Close single quote. |
| flags += "'"; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| cmGlobalXCodeGenerator::ComputeInfoPListLocation(cmTarget& target) |
| { |
| std::string plist = target.GetMakefile()->GetCurrentOutputDirectory(); |
| plist += cmake::GetCMakeFilesDirectory(); |
| plist += "/"; |
| plist += target.GetName(); |
| plist += ".dir/Info.plist"; |
| return plist; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Return true if the generated build tree may contain multiple builds. |
| // i.e. "Can I build Debug and Release in the same tree?" |
| bool cmGlobalXCodeGenerator::IsMultiConfig() |
| { |
| // Old Xcode 1.5 is single config: |
| if(this->XcodeVersion == 15) |
| { |
| return false; |
| } |
| |
| // Newer Xcode versions are multi config: |
| return true; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmGlobalXCodeGenerator |
| ::ComputeTargetObjects(cmGeneratorTarget* gt) const |
| { |
| // Count the number of object files with each name. Warn about duplicate |
| // names since Xcode names them uniquely automatically with a numeric suffix |
| // to avoid exact duplicate file names. Note that Mac file names are not |
| // typically case sensitive, hence the LowerCase. |
| std::map<cmStdString, int> counts; |
| std::vector<cmSourceFile*> objectSources; |
| gt->GetObjectSources(objectSources); |
| for(std::vector<cmSourceFile*>::const_iterator |
| si = objectSources.begin(); |
| si != objectSources.end(); ++si) |
| { |
| cmSourceFile* sf = *si; |
| std::string objectName = |
| cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()); |
| objectName += ".o"; |
| |
| std::string objectNameLower = cmSystemTools::LowerCase(objectName); |
| counts[objectNameLower] += 1; |
| if (2 == counts[objectNameLower]) |
| { |
| // TODO: emit warning about duplicate name? |
| } |
| |
| gt->AddObject(sf, objectName); |
| } |
| |
| const char* configName = this->GetCMakeCFGIntDir(); |
| std::string dir = this->GetObjectsNormalDirectory( |
| "$(PROJECT_NAME)", configName, gt->Target); |
| if(this->XcodeVersion >= 21) |
| { |
| dir += "$(CURRENT_ARCH)/"; |
| } |
| else |
| { |
| #ifdef __ppc__ |
| dir += "ppc/"; |
| #endif |
| #ifdef __i386 |
| dir += "i386/"; |
| #endif |
| } |
| gt->ObjectDirectory = dir; |
| } |