From 61075d2d7b0a77f53bc9a0d4c6644ab338f27886 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Tue, 22 Nov 2022 16:32:59 +0100 Subject: XCode: ensure LINK_LIBRARY genex is usable with XCODE_LINK_BUILD_PHASE_MODE Fixes: #24176 --- Source/cmGlobalXCodeGenerator.cxx | 74 ++++++++++++++++------ .../XcodeProject/LinkBinariesBuildPhase.cmake | 62 ++++++++++++++++-- .../LinkBinariesBuildPhase_BUILT_ONLY-check.cmake | 9 ++- ...nkBinariesBuildPhase_KNOWN_LOCATION-check.cmake | 9 ++- .../LinkBinariesBuildPhase_NONE-check.cmake | 2 +- 5 files changed, 127 insertions(+), 29 deletions(-) diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 0658021..116e510 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -3581,28 +3581,37 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) libItem.IsPath == cmComputeLinkInformation::ItemIsPath::Yes && forceLinkPhase))) { std::string libName; - bool canUseLinkPhase = true; - if (libItem.Target) { - if (libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) { - canUseLinkPhase = canUseLinkPhase && forceLinkPhase; + bool canUseLinkPhase = !libItem.HasFeature() || + libItem.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s || + libItem.GetFeatureName() == "FRAMEWORK"_s || + libItem.GetFeatureName() == "WEAK_FRAMEWORK"_s || + libItem.GetFeatureName() == "WEAK_LIBRARY"_s; + if (canUseLinkPhase) { + if (libItem.Target) { + if (libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) { + canUseLinkPhase = canUseLinkPhase && forceLinkPhase; + } else { + // If a library target uses custom build output directory Xcode + // won't pick it up so we have to resort back to linker flags, + // but that's OK as long as the custom output dir is absolute + // path. + for (auto const& libConfigName : + this->CurrentConfigurationTypes) { + canUseLinkPhase = canUseLinkPhase && + libItem.Target->UsesDefaultOutputDir( + libConfigName, cmStateEnums::RuntimeBinaryArtifact); + } + } + libName = libItem.Target->GetName(); } else { - // If a library target uses custom build output directory Xcode - // won't pick it up so we have to resort back to linker flags, but - // that's OK as long as the custom output dir is absolute path. - for (auto const& libConfigName : this->CurrentConfigurationTypes) { - canUseLinkPhase = canUseLinkPhase && - libItem.Target->UsesDefaultOutputDir( - libConfigName, cmStateEnums::RuntimeBinaryArtifact); + libName = cmSystemTools::GetFilenameName(libItem.Value.Value); + // We don't want all the possible files here, just standard + // libraries + const auto libExt = cmSystemTools::GetFilenameExtension(libName); + if (!IsLinkPhaseLibraryExtension(libExt)) { + canUseLinkPhase = false; } } - libName = libItem.Target->GetName(); - } else { - libName = cmSystemTools::GetFilenameName(libItem.Value.Value); - // We don't want all the possible files here, just standard libraries - const auto libExt = cmSystemTools::GetFilenameExtension(libName); - if (!IsLinkPhaseLibraryExtension(libExt)) { - canUseLinkPhase = false; - } } if (canUseLinkPhase) { // Add unique configuration name to target-config map for later @@ -3658,6 +3667,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) // separately. std::vector linkSearchPaths; std::vector frameworkSearchPaths; + std::set> linkBuildFileSet; for (auto const& libItem : linkPhaseTargetVector) { // Add target output directory as a library search path std::string linkDir; @@ -3760,8 +3770,30 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) cmSystemTools::Error("Missing files of PBXFrameworksBuildPhase"); continue; } - if (buildFile && !buildFiles->HasObject(buildFile)) { - buildFiles->AddObject(buildFile); + if (buildFile) { + if (cmHasPrefix(libItem->GetFeatureName(), "WEAK_"_s)) { + auto key = std::make_pair(buildFile->GetAttribute("fileRef"), + libItem->GetFeatureName()); + if (linkBuildFileSet.find(key) != linkBuildFileSet.end()) { + continue; + } + linkBuildFileSet.insert(key); + + cmXCodeObject* buildObject = + this->CreateObject(cmXCodeObject::PBXBuildFile); + buildObject->AddAttribute("fileRef", key.first); + // Add settings, ATTRIBUTES, Weak flag + cmXCodeObject* settings = + this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); + cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST); + attrs->AddObject(this->CreateString("Weak")); + settings->AddAttribute("ATTRIBUTES", attrs); + buildObject->AddAttribute("settings", settings); + buildFile = buildObject; + } + if (!buildFiles->HasObject(buildFile)) { + buildFiles->AddObject(buildFile); + } } } diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake index 7abc58b..a9ea717 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake @@ -65,8 +65,12 @@ add_custom_target(prebuildDependencies ALL COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build --target staticFrameworkExt sharedFrameworkExt --config Debug ) add_executable(app1 mainOuter.m) +add_executable(app2 mainOuter.m) +add_executable(app3 mainOuter.m) add_library(static1 STATIC funcOuter.c) add_library(shared1 SHARED funcOuter.c) +add_library(shared3 SHARED funcOuter.c) +add_library(shared4 SHARED funcOuter.c) add_library(module1 MODULE funcOuter.c) add_library(obj1 OBJECT funcOuter.c) add_library(staticFramework1 STATIC funcOuter.c) @@ -74,8 +78,12 @@ add_library(sharedFramework1 SHARED funcOuter.c) set_target_properties(staticFramework1 PROPERTIES FRAMEWORK TRUE) set_target_properties(sharedFramework1 PROPERTIES FRAMEWORK TRUE) add_dependencies(app1 prebuildDependencies) +add_dependencies(app2 prebuildDependencies) +add_dependencies(app3 prebuildDependencies) add_dependencies(static1 prebuildDependencies) add_dependencies(shared1 prebuildDependencies) +add_dependencies(shared3 prebuildDependencies) +add_dependencies(shared4 prebuildDependencies) add_dependencies(module1 prebuildDependencies) add_dependencies(obj1 prebuildDependencies) add_dependencies(staticFramework1 prebuildDependencies) @@ -103,6 +111,14 @@ set(libresolv \"${libresolv}\") set(CoreFoundation \"${CoreFoundation}\") ") +macro(SET_LINK_LIBRARIES) + foreach(mainTarget IN LISTS mainTargets) + foreach(linkTo IN LISTS linkToThings) + target_link_libraries(${mainTarget} PRIVATE ${linkTo}) + endforeach() + endforeach() +endmacro() + set(mainTargets app1 static1 @@ -125,8 +141,44 @@ set(linkToThings "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" ) -foreach(mainTarget IN LISTS mainTargets) - foreach(linkTo IN LISTS linkToThings) - target_link_libraries(${mainTarget} PRIVATE ${linkTo}) - endforeach() -endforeach() +set_link_libraries() + +set(mainTargets + app2 + shared3 +) + +set(linkToThings + static2 + "$" + obj2 + staticFramework2 + "$" + imported2 + ${libresolv} + ${CoreFoundation} + "$" + "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" +) + +set_link_libraries() + +set(mainTargets + app3 + shared4 +) + +set(linkToThings + static2 + "$" + obj2 + staticFramework2 + "$" + imported2 + ${libresolv} + ${CoreFoundation} + "$" + "${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/build/Debug/staticFrameworkExt.framework" +) + +set_link_libraries() diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake index d07a25b..75edc5c 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_BUILT_ONLY-check.cmake @@ -4,13 +4,20 @@ include(${RunCMake_TEST_BINARY_DIR}/foundLibs.cmake) # obj2 --> Embeds func3.o in the link flags, but obj2 is part of the path # ${libz} --> This is for imported2 -foreach(mainTarget IN ITEMS app1 shared1 module1 sharedFramework1) +foreach(mainTarget IN ITEMS app1 app2 shared1 shared3 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} "obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" "static2;shared2;staticFramework2;sharedFramework2" ) endforeach() +foreach(mainTarget IN ITEMS app3 shared4) + checkFlags(OTHER_LDFLAGS ${mainTarget} + "obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt;shared2;sharedFramework2" + "static2;staticFramework2" + ) +endforeach() + foreach(mainTarget IN ITEMS static1 staticFramework1) checkFlags(OTHER_LIBTOOLFLAGS ${mainTarget} "obj2" diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake index e1484e7..1044f78 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_KNOWN_LOCATION-check.cmake @@ -4,13 +4,20 @@ include(${RunCMake_TEST_BINARY_DIR}/foundLibs.cmake) # obj2 --> Embeds func3.o in the link flags, but obj2 is part of the path # ${libz} --> This is for imported2 -foreach(mainTarget IN ITEMS app1 shared1 module1 sharedFramework1) +foreach(mainTarget IN ITEMS app1 app2 shared1 shared3 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} "obj2" "static2;shared2;staticFramework2;sharedFramework2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" ) endforeach() +foreach(mainTarget IN ITEMS app3 shared4) + checkFlags(OTHER_LDFLAGS ${mainTarget} + "obj2;shared2;sharedFramework2;sharedFrameworkExt" + "static2;staticFramework2;${libz};${libresolv};CoreFoundation;staticFrameworkExt" + ) +endforeach() + foreach(mainTarget IN ITEMS static1 staticFramework1) checkFlags(OTHER_LIBTOOLFLAGS ${mainTarget} "obj2" diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake index 2601676..b67632b 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_NONE-check.cmake @@ -4,7 +4,7 @@ include(${RunCMake_TEST_BINARY_DIR}/foundLibs.cmake) # obj2 --> Embeds func3.o in the link flags, but obj2 is part of the path # ${libz} --> This is for imported2 -foreach(mainTarget IN ITEMS app1 shared1 module1 sharedFramework1) +foreach(mainTarget IN ITEMS app1 app2 app3 shared1 shared3 shared4 module1 sharedFramework1) checkFlags(OTHER_LDFLAGS ${mainTarget} "static2;shared2;staticFramework2;sharedFramework2;obj2;${libz};${libresolv};CoreFoundation;sharedFrameworkExt;staticFrameworkExt" "" -- cgit v0.12