From e637744c515b1e61d58bfd3539a2ab5afc78f5bb Mon Sep 17 00:00:00 2001 From: Gusts Kaksis Date: Wed, 10 Jul 2019 12:32:16 +0300 Subject: Xcode: Use "Link Binary With Libraries" to link any library Add external libraries as fileRefs to Xcode project and add those references to Link Binary With Libraries build phase. This allows linking .a, .o, .dylib, .framework and .tbd libraries through "Link Binary With Libraries" build phase, as opposed to `OTHER_LINKER_FLAGS`. This improves on the approach added by commit 58c05e1c73 (Xcode: Use "Link Binary With Libraries" build phase when possible, 2020-06-12). --- Source/cmGlobalXCodeGenerator.cxx | 196 +++++++++++++++++++++++++++----------- Source/cmGlobalXCodeGenerator.h | 10 +- 2 files changed, 148 insertions(+), 58 deletions(-) diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 656a346..d0f2e24 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -172,6 +172,7 @@ cmGlobalXCodeGenerator::cmGlobalXCodeGenerator( this->RootObject = nullptr; this->MainGroupChildren = nullptr; + this->FrameworkGroup = nullptr; this->CurrentMakefile = nullptr; this->CurrentLocalGenerator = nullptr; this->XcodeBuildCommandInitialized = false; @@ -668,6 +669,7 @@ void cmGlobalXCodeGenerator::ClearXCodeObjects() this->GroupNameMap.clear(); this->TargetGroup.clear(); this->FileRefs.clear(); + this->ExternalLibRefs.clear(); } void cmGlobalXCodeGenerator::addObject(std::unique_ptr obj) @@ -736,7 +738,7 @@ std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target, return key; } -cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath( +cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeBuildFileFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) { @@ -872,7 +874,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile( lg->AppendFlags(flags, lg->GetIncludeFlags(includes, gtgt, lang, true)); cmXCodeObject* buildFile = - this->CreateXCodeSourceFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf); + this->CreateXCodeBuildFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf); cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); settings->AddAttributeIfNotEmpty("COMPILER_FLAGS", @@ -925,6 +927,31 @@ void cmGlobalXCodeGenerator::AddXCodeProjBuildRule( } } +bool IsLibraryExtension(const std::string& fileExt) +{ + return (fileExt == ".framework" || fileExt == ".a" || fileExt == ".o" || + fileExt == ".dylib" || fileExt == ".tbd"); +} +bool IsLibraryType(const std::string& fileType) +{ + return (fileType == "wrapper.framework" || fileType == "archive.ar" || + fileType == "compiled.mach-o.objfile" || + fileType == "compiled.mach-o.dylib" || + fileType == "sourcecode.text-based-dylib-definition"); +} + +std::string GetDirectoryValueFromFileExtension(const std::string& dirExt) +{ + std::string ext = cmSystemTools::LowerCase(dirExt); + if (ext == "framework") { + return "wrapper.framework"; + } + if (ext == "xcassets") { + return "folder.assetcatalog"; + } + return "folder"; +} + std::string GetSourcecodeValueFromFileExtension( const std::string& _ext, const std::string& lang, bool& keepLastKnownFileType, const std::vector& enabled_langs) @@ -933,6 +960,7 @@ std::string GetSourcecodeValueFromFileExtension( std::string sourcecode = "sourcecode"; if (ext == "o") { + keepLastKnownFileType = true; sourcecode = "compiled.mach-o.objfile"; } else if (ext == "xctest") { sourcecode = "wrapper.cfbundle"; @@ -976,6 +1004,14 @@ std::string GetSourcecodeValueFromFileExtension( sourcecode += ".metal"; } else if (ext == "mig") { sourcecode += ".mig"; + } else if (ext == "tbd") { + sourcecode += ".text-based-dylib-definition"; + } else if (ext == "a") { + keepLastKnownFileType = true; + sourcecode = "archive.ar"; + } else if (ext == "dylib") { + keepLastKnownFileType = true; + sourcecode = "compiled.mach-o.dylib"; } // else // { @@ -992,20 +1028,6 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) { - std::string key = GetGroupMapKeyFromPath(target, fullpath); - cmXCodeObject* fileRef = this->FileRefs[key]; - if (!fileRef) { - fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); - fileRef->SetComment(fullpath); - this->FileRefs[key] = fileRef; - } - cmXCodeObject* group = this->GroupMap[key]; - cmXCodeObject* children = group->GetObject("children"); - if (!children->HasObject(fileRef)) { - children->AddObject(fileRef); - } - fileRef->AddAttribute("fileEncoding", this->CreateString("4")); - bool useLastKnownFileType = false; std::string fileType; if (sf) { @@ -1016,19 +1038,18 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( fileType = *l; } } + // Compute the extension without leading '.'. + std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath); + if (!ext.empty()) { + ext = ext.substr(1); + } if (fileType.empty()) { - // Compute the extension without leading '.'. - std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath); - if (!ext.empty()) { - ext = ext.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)) { - fileType = (ext == "xcassets" ? "folder.assetcatalog" : "folder"); + fileType = GetDirectoryValueFromFileExtension(ext); useLastKnownFileType = true; } else { fileType = GetSourcecodeValueFromFileExtension( @@ -1036,18 +1057,38 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( } } + std::string key = GetGroupMapKeyFromPath(target, fullpath); + cmXCodeObject* fileRef = this->FileRefs[key]; + if (!fileRef) { + fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); + fileRef->SetComment(fullpath); + this->FileRefs[key] = fileRef; + } + fileRef->AddAttribute("fileEncoding", this->CreateString("4")); fileRef->AddAttribute(useLastKnownFileType ? "lastKnownFileType" : "explicitFileType", this->CreateString(fileType)); - // Store the file path relative to the top of the source tree. - std::string path = this->RelativeToSource(fullpath); + std::string path = fullpath; + if (!IsLibraryType(fileType)) { + path = this->RelativeToSource(fullpath); + } std::string name = cmSystemTools::GetFilenameName(path); const char* sourceTree = cmSystemTools::FileIsFullPath(path) ? "" : "SOURCE_ROOT"; fileRef->AddAttribute("name", this->CreateString(name)); fileRef->AddAttribute("path", this->CreateString(path)); fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree)); + + cmXCodeObject* group = this->GroupMap[key]; + if (!group && IsLibraryType(fileType)) { + group = this->FrameworkGroup; + this->GroupMap[key] = group; + } + cmXCodeObject* children = group->GetObject("children"); + if (!children->HasObject(fileRef)) { + children->AddObject(fileRef); + } return fileRef; } @@ -1182,11 +1223,14 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( this->CurrentLocalGenerator, sourceFile, gtgt); cmXCodeObject* fr = xsf->GetObject("fileRef"); cmXCodeObject* filetype = fr->GetObject()->GetObject("explicitFileType"); + if (!filetype) { + filetype = fr->GetObject()->GetObject("lastKnownFileType"); + } cmGeneratorTarget::SourceFileFlags tsFlags = gtgt->GetTargetSourceFileFlags(sourceFile); - if (filetype && filetype->GetString() == "compiled.mach-o.objfile") { + if (filetype && IsLibraryType(filetype->GetString())) { if (sourceFile->GetObjectLibrary().empty()) { externalObjFiles.push_back(xsf); } @@ -2800,21 +2844,30 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) continue; } for (auto const& libItem : cli->GetItems()) { - // TODO: Drop this check once we have option to add outside libraries to - // Xcode project - auto* libTarget = FindXCodeTarget(libItem.Target); if (gt->IsBundleOnApple() && (gt->GetType() == cmStateEnums::EXECUTABLE || gt->GetType() == cmStateEnums::SHARED_LIBRARY || gt->GetType() == cmStateEnums::MODULE_LIBRARY || gt->GetType() == cmStateEnums::UNKNOWN_LIBRARY) && - (libTarget && libItem.Target && - (libItem.Target->GetType() == cmStateEnums::STATIC_LIBRARY || - libItem.Target->GetType() == cmStateEnums::SHARED_LIBRARY || - libItem.Target->GetType() == cmStateEnums::MODULE_LIBRARY))) { + ((libItem.Target && + (libItem.Target->GetType() == cmStateEnums::STATIC_LIBRARY || + libItem.Target->GetType() == cmStateEnums::SHARED_LIBRARY || + libItem.Target->GetType() == cmStateEnums::MODULE_LIBRARY)) || + (!libItem.Target && libItem.IsPath))) { // Add unique configuration name to target-config map for later // checks - std::string libName = libItem.Target->GetName(); + std::string libName; + if (libItem.Target) { + libName = libItem.Target->GetName(); + } else { + libName = cmSystemTools::GetFilenameName(libItem.Value.Value); + const auto libExt = cmSystemTools::GetFilenameExtension(libName); + if (!IsLibraryExtension(libExt)) { + // Add this library item to a regular linker flag list + addToLinkerArguments(configName, &libItem); + continue; + } + } auto& configVector = targetConfigMap[libName]; if (std::find(configVector.begin(), configVector.end(), configName) == configVector.end()) { @@ -2865,38 +2918,63 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) std::vector linkSearchPaths; for (auto const& libItem : linkPhaseTargetVector) { // Add target output directory as a library search path - std::string linkDir = cmSystemTools::GetParentDirectory( - libItem->Target->GetLocationForBuild()); + std::string linkDir; + if (libItem->Target) { + linkDir = cmSystemTools::GetParentDirectory( + libItem->Target->GetLocationForBuild()); + } else { + linkDir = cmSystemTools::GetParentDirectory(libItem->Value.Value); + } if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) == linkSearchPaths.end()) { linkSearchPaths.push_back(linkDir); } // Add target dependency - auto const& libName = *libItem; - if (!libName.Target->IsImported()) { + if (libItem->Target && !libItem->Target->IsImported()) { for (auto const& configName : this->CurrentConfigurationTypes) { - target->AddDependTarget(configName, libName.Target->GetName()); + target->AddDependTarget(configName, libItem->Target->GetName()); } } // Get the library target auto* libTarget = FindXCodeTarget(libItem->Target); - if (!libTarget) { - continue; - } - // Add the target output file as a build reference for other targets - // to link against - auto* fileRefObject = libTarget->GetObject("productReference"); - if (!fileRefObject) { - continue; - } cmXCodeObject* buildFile; - auto it = FileRefToBuildFileMap.find(fileRefObject); - if (it == FileRefToBuildFileMap.end()) { - buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); - buildFile->AddAttribute("fileRef", fileRefObject); - FileRefToBuildFileMap[fileRefObject] = buildFile; + if (!libTarget) { + if (libItem->IsPath) { + // Get or create a direct file ref in the root project + auto it = this->ExternalLibRefs.find(libItem->Value.Value); + if (it == this->ExternalLibRefs.end()) { + buildFile = CreateXCodeBuildFileFromPath(libItem->Value.Value, gt, + "", nullptr); + this->ExternalLibRefs.emplace(libItem->Value.Value, buildFile); + } else { + buildFile = it->second; + } + } else { + // Add this library item back to a regular linker flag list + for (const auto& conf : configItemMap) { + addToLinkerArguments(conf.first, libItem); + } + continue; + } } else { - buildFile = it->second; + // Add the target output file as a build reference for other targets + // to link against + auto* fileRefObject = libTarget->GetObject("productReference"); + if (!fileRefObject) { + // Add this library item back to a regular linker flag list + for (const auto& conf : configItemMap) { + addToLinkerArguments(conf.first, libItem); + } + continue; + } + auto it = FileRefToBuildFileMap.find(fileRefObject); + if (it == FileRefToBuildFileMap.end()) { + buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); + buildFile->AddAttribute("fileRef", fileRefObject); + FileRefToBuildFileMap[fileRefObject] = buildFile; + } else { + buildFile = it->second; + } } // Add this reference to current target auto* buildPhases = target->GetObject("buildPhases"); @@ -3164,6 +3242,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( this->ClearXCodeObjects(); this->RootObject = nullptr; this->MainGroupChildren = nullptr; + this->FrameworkGroup = nullptr; cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO")); cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -3198,6 +3277,15 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( productGroup->AddAttribute("children", productGroupChildren); this->MainGroupChildren->AddObject(productGroup); + this->FrameworkGroup = this->CreateObject(cmXCodeObject::PBXGroup); + this->FrameworkGroup->AddAttribute("name", this->CreateString("Frameworks")); + this->FrameworkGroup->AddAttribute("sourceTree", + this->CreateString("")); + cmXCodeObject* frameworkGroupChildren = + this->CreateObject(cmXCodeObject::OBJECT_LIST); + this->FrameworkGroup->AddAttribute("children", frameworkGroupChildren); + this->MainGroupChildren->AddObject(this->FrameworkGroup); + this->RootObject = this->CreateObject(cmXCodeObject::PBXProject); this->RootObject->SetComment("Project object"); diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 11b9312..276a9ee 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -203,10 +203,10 @@ private: cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf); - cmXCodeObject* CreateXCodeSourceFileFromPath(const std::string& fullpath, - cmGeneratorTarget* target, - const std::string& lang, - cmSourceFile* sf); + cmXCodeObject* CreateXCodeBuildFileFromPath(const std::string& fullpath, + cmGeneratorTarget* target, + const std::string& lang, + cmSourceFile* sf); cmXCodeObject* CreateXCodeFileReference(cmSourceFile* sf, cmGeneratorTarget* target); cmXCodeObject* CreateXCodeSourceFile(cmLocalGenerator* gen, cmSourceFile* sf, @@ -281,6 +281,7 @@ private: std::string PostBuildMakeTarget(std::string const& tName, std::string const& configName); cmXCodeObject* MainGroupChildren; + cmXCodeObject* FrameworkGroup; cmMakefile* CurrentMakefile; cmLocalGenerator* CurrentLocalGenerator; std::vector CurrentConfigurationTypes; @@ -294,6 +295,7 @@ private: std::map GroupNameMap; std::map TargetGroup; std::map FileRefs; + std::map ExternalLibRefs; std::map XCodeObjectMap; std::map FileRefToBuildFileMap; std::vector Architectures; -- cgit v0.12 From 3603ca8e86c5bc41af7f2559ecd072e2df2caf83 Mon Sep 17 00:00:00 2001 From: Gusts Kaksis Date: Sat, 11 Jul 2020 15:21:47 +0300 Subject: Xcode: Special treatment for directly linked framework binaries Always refer to framework directory instead of binary directly. --- Source/cmGlobalXCodeGenerator.cxx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index d0f2e24..2d47d2c 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1038,17 +1038,31 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( fileType = *l; } } + // Make a copy so that we can override it later + std::string path = fullpath; // Compute the extension without leading '.'. - std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath); + std::string ext = cmSystemTools::GetFilenameLastExtension(path); if (!ext.empty()) { ext = ext.substr(1); } if (fileType.empty()) { + // If file has no extension it's either a raw executable or might + // be a direct reference to binary within a framework (bad practice!) + // so this is where we change the path to the point to framework + // directory. + if (ext.empty()) { + auto parentDir = cmSystemTools::GetParentDirectory(path); + auto parentExt = cmSystemTools::GetFilenameLastExtension(parentDir); + if (parentExt == ".framework") { + path = parentDir; + ext = parentExt.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)) { + if (cmSystemTools::FileIsDirectory(path)) { fileType = GetDirectoryValueFromFileExtension(ext); useLastKnownFileType = true; } else { @@ -1057,11 +1071,11 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( } } - std::string key = GetGroupMapKeyFromPath(target, fullpath); + std::string key = GetGroupMapKeyFromPath(target, path); cmXCodeObject* fileRef = this->FileRefs[key]; if (!fileRef) { fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); - fileRef->SetComment(fullpath); + fileRef->SetComment(path); this->FileRefs[key] = fileRef; } fileRef->AddAttribute("fileEncoding", this->CreateString("4")); @@ -1069,9 +1083,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( : "explicitFileType", this->CreateString(fileType)); // Store the file path relative to the top of the source tree. - std::string path = fullpath; if (!IsLibraryType(fileType)) { - path = this->RelativeToSource(fullpath); + path = this->RelativeToSource(path); } std::string name = cmSystemTools::GetFilenameName(path); const char* sourceTree = -- cgit v0.12