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).
This commit is contained in:
Gusts Kaksis 2019-07-10 12:32:16 +03:00 committed by Brad King
parent fd1df4995b
commit e637744c51
2 changed files with 148 additions and 58 deletions

View File

@ -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<cmXCodeObject> 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<std::string>& 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) ? "<absolute>" : "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<std::string> 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("<group>"));
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");

View File

@ -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<std::string> CurrentConfigurationTypes;
@ -294,6 +295,7 @@ private:
std::map<std::string, cmXCodeObject*> GroupNameMap;
std::map<std::string, cmXCodeObject*> TargetGroup;
std::map<std::string, cmXCodeObject*> FileRefs;
std::map<std::string, cmXCodeObject*> ExternalLibRefs;
std::map<cmGeneratorTarget const*, cmXCodeObject*> XCodeObjectMap;
std::map<cmXCodeObject*, cmXCodeObject*> FileRefToBuildFileMap;
std::vector<std::string> Architectures;