From 0bd3efffbc97783bf8b1a6dcf3132a1bca84dce9 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Thu, 15 Sep 2022 17:09:07 +0200 Subject: Genex LINK_LIBRARY: Add support for framework with postfix --- ...enex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst | 5 ++ Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt | 9 +++- Source/cmComputeLinkInformation.cxx | 48 +++++++++--------- Source/cmGlobalGenerator.cxx | 19 ++++++-- Source/cmGlobalGenerator.h | 57 ++++++++++++++++++++-- Source/cmGlobalXCodeGenerator.cxx | 36 +++++++------- Tests/RunCMake/CMakeLists.txt | 2 + .../RunCMakeTest.cmake | 10 ++++ .../apple_framework.cmake | 30 ++++++++++++ 9 files changed, 160 insertions(+), 56 deletions(-) create mode 100644 Help/release/dev/genex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst diff --git a/Help/release/dev/genex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst b/Help/release/dev/genex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst new file mode 100644 index 0000000..e4d82ee --- /dev/null +++ b/Help/release/dev/genex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst @@ -0,0 +1,5 @@ +genex-LINK_LIBRARY-FRAMEWORK-supports-suffix +-------------------------------------------- + +The :genex:`$` generator expression gains the capability, for the +``FRAMEWORK`` features, to handle the suffix of the framework library name. diff --git a/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt index 8ae6c57..aea1be8 100644 --- a/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt +++ b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt @@ -45,8 +45,8 @@ wildcard, and optional parts are shown as ``[...]``): * ``[/path/to/]FwName[.framework]`` - * ``[/path/to/]FwName.framework/FwName`` - * ``[/path/to/]FwName.framework/Versions/*/FwName`` + * ``[/path/to/]FwName.framework/FwName[suffix]`` + * ``[/path/to/]FwName.framework/Versions/*/FwName[suffix]`` Note that CMake recognizes and automatically handles framework targets, even without using the ``$`` expression. @@ -59,6 +59,11 @@ ``$`` for file paths so that the expected behavior is clear. + .. versionadded:: 3.25 + The :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_` target property as + well as the ``suffix`` of the framework library name are now supported by + the ``FRAMEWORK`` features. + ``NEEDED_FRAMEWORK`` This is similar to the ``FRAMEWORK`` feature, except it forces the linker to link with the framework even if no symbols are used from it. It uses diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index cda70fc..044f69f 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -1566,8 +1566,9 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) { // Add the framework directory and the framework item itself - auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true); - if (!fwItems) { + auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( + item.Value, cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor) { this->CMakeInstance->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Could not parse framework path \"", item.Value, @@ -1575,12 +1576,13 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) item.Backtrace); return; } - if (!fwItems->first.empty()) { + if (!fwDescriptor->Directory.empty()) { // Add the directory portion to the framework search path. - this->AddFrameworkPath(fwItems->first); + this->AddFrameworkPath(fwDescriptor->Directory); } if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) { - this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target, + this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, + target, this->FindLibraryFeature(entry.Feature)); } else { this->Items.emplace_back( @@ -1851,9 +1853,11 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) std::string const& item = entry.Item.Value; // Try to separate the framework name and path. - auto fwItems = - this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT); - if (!fwItems) { + auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( + item, + entry.Feature == DEFAULT ? cmGlobalGenerator::FrameworkFormat::Relaxed + : cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor) { std::ostringstream e; e << "Could not parse framework path \"" << item << "\" " << "linked by target " << this->Target->GetName() << "."; @@ -1861,18 +1865,14 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) return; } - std::string fw_path = std::move(fwItems->first); - std::string fw = std::move(fwItems->second); - std::string full_fw = cmStrCat(fw, ".framework/", fw); - + const std::string& fw_path = fwDescriptor->Directory; if (!fw_path.empty()) { - full_fw = cmStrCat(fw_path, '/', full_fw); // Add the directory portion to the framework search path. this->AddFrameworkPath(fw_path); } // add runtime information - this->AddLibraryRuntimeInfo(full_fw); + this->AddLibraryRuntimeInfo(fwDescriptor->GetFullPath()); if (entry.Feature == DEFAULT) { // ensure FRAMEWORK feature is loaded @@ -1887,9 +1887,9 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) ? "FRAMEWORK" : entry.Feature)); } else { - this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr, - this->FindLibraryFeature(entry.Feature == DEFAULT - ? "FRAMEWORK" + this->Items.emplace_back( + fwDescriptor->GetLinkName(), ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT ? "FRAMEWORK" : entry.Feature)); } } @@ -2252,15 +2252,11 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo( // It could be an Apple framework if (!is_shared_library) { - if (fullPath.find(".framework") != std::string::npos) { - static cmsys::RegularExpression splitFramework( - "^(.*)/(.*).framework/(.*)$"); - if (splitFramework.find(fullPath) && - (std::string::npos != - splitFramework.match(3).find(splitFramework.match(2)))) { - is_shared_library = true; - } - } + is_shared_library = + this->GlobalGenerator + ->SplitFrameworkPath(fullPath, + cmGlobalGenerator::FrameworkFormat::Strict) + .has_value(); } if (!is_shared_library) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 8130521..6962b52 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2563,9 +2563,9 @@ bool cmGlobalGenerator::NameResolvesToFramework( // This is where we change the path to point to the framework directory. // .tbd files also can be located in SDK frameworks (they are // placeholders for actual libraries shipped with the OS) -cm::optional> +cm::optional cmGlobalGenerator::SplitFrameworkPath(const std::string& path, - bool extendedFormat) const + FrameworkFormat format) const { // Check for framework structure: // (/path/to/)?FwName.framework @@ -2580,20 +2580,29 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path, auto name = frameworkPath.match(3); auto libname = cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6)); + if (format == FrameworkFormat::Strict && libname.empty()) { + return cm::nullopt; + } if (!libname.empty() && !cmHasPrefix(libname, name)) { return cm::nullopt; } - return std::pair{ frameworkPath.match(2), name }; + + if (libname.empty() || name.size() == libname.size()) { + return FrameworkDescriptor{ frameworkPath.match(2), name }; + } + + return FrameworkDescriptor{ frameworkPath.match(2), name, + libname.substr(name.size()) }; } - if (extendedFormat) { + if (format == FrameworkFormat::Extended) { // path format can be more flexible: (/path/to/)?fwName(.framework)? auto fwDir = cmSystemTools::GetParentDirectory(path); auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework" ? cmSystemTools::GetFilenameWithoutExtension(path) : cmSystemTools::GetFilenameName(path); - return std::pair{ fwDir, name }; + return FrameworkDescriptor{ fwDir, name }; } return cm::nullopt; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 6e3072b..89b2ea8 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -17,6 +17,7 @@ #include #include +#include #include "cm_codecvt.hxx" @@ -367,13 +368,61 @@ public: /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ bool NameResolvesToFramework(const std::string& libname) const; - /** Split a framework path to the directory and name of the framework - * returns std::nullopt if the path does not match with framework format + /** Split a framework path to the directory and name of the framework as well + * as optiona; suffix. + * Returns std::nullopt if the path does not match with framework format * when extendedFormat is true, required format is relaxed (i.e. extension * `.framework' is optional). Used when FRAMEWORK link feature is * specified */ - cm::optional> SplitFrameworkPath( - const std::string& path, bool extendedFormat = false) const; + struct FrameworkDescriptor + { + FrameworkDescriptor(std::string directory, std::string name) + : Directory(std::move(directory)) + , Name(std::move(name)) + { + } + FrameworkDescriptor(std::string directory, std::string name, + std::string suffix) + : Directory(std::move(directory)) + , Name(std::move(name)) + , Suffix(std::move(suffix)) + { + } + std::string GetLinkName() const + { + return this->Suffix.empty() ? this->Name + : cmStrCat(this->Name, ',', this->Suffix); + } + std::string GetFullName() const + { + return cmStrCat(this->Name, ".framework/"_s, this->Name, this->Suffix); + } + std::string GetFrameworkPath() const + { + return this->Directory.empty() + ? cmStrCat(this->Name, ".framework"_s) + : cmStrCat(this->Directory, '/', this->Name, ".framework"_s); + } + std::string GetFullPath() const + { + return this->Directory.empty() + ? this->GetFullName() + : cmStrCat(this->Directory, '/', this->GetFullName()); + } + + const std::string Directory; + const std::string Name; + const std::string Suffix; + }; + enum class FrameworkFormat + { + Strict, + Relaxed, + Extended + }; + cm::optional SplitFrameworkPath( + const std::string& path, + FrameworkFormat format = FrameworkFormat::Relaxed) const; cmMakefile* FindMakefile(const std::string& start_dir) const; cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 2c455cf..9739a4e 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1192,13 +1192,9 @@ std::string GetTargetObjectDirArch(T const& target, std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath( const std::string& path) const { - auto fwItems = this->SplitFrameworkPath(path); - if (fwItems) { - if (fwItems->first.empty()) { - return cmStrCat(fwItems->second, ".framework"); - } else { - return cmStrCat(fwItems->first, '/', fwItems->second, ".framework"); - } + auto fwDescriptor = this->SplitFrameworkPath(path); + if (fwDescriptor) { + return fwDescriptor->GetFrameworkPath(); } return path; @@ -3641,9 +3637,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) linkDir = libItem->Value.Value; } if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) { - auto fwItems = this->SplitFrameworkPath(linkDir, true); - if (fwItems && !fwItems->first.empty()) { - linkDir = std::move(fwItems->first); + auto fwDescriptor = this->SplitFrameworkPath( + linkDir, cmGlobalGenerator::FrameworkFormat::Extended); + if (fwDescriptor && !fwDescriptor->Directory.empty()) { + linkDir = fwDescriptor->Directory; if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), linkDir) == frameworkSearchPaths.end()) { frameworkSearchPaths.push_back(linkDir); @@ -3839,13 +3836,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) bool isFramework = cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s); if (isFramework) { - const auto fwItems = - this->SplitFrameworkPath(cleanPath, isFramework); - if (!fwItems->first.empty() && - emitted.insert(fwItems->first).second) { + const auto fwDescriptor = this->SplitFrameworkPath( + cleanPath, cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor->Directory.empty() && + emitted.insert(fwDescriptor->Directory).second) { // This is a search path we had not added before and it isn't // an implicit search path, so we need it - libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first)); + libPaths.Add("-F " + + this->XCodeEscapePath(fwDescriptor->Directory)); } if (libName.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s) { // use the full path @@ -3853,10 +3851,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) libName.GetFormattedItem(this->XCodeEscapePath(cleanPath)) .Value); } else { - libPaths.Add( - libName - .GetFormattedItem(this->XCodeEscapePath(fwItems->second)) - .Value); + libPaths.Add(libName + .GetFormattedItem(this->XCodeEscapePath( + fwDescriptor->GetLinkName())) + .Value); } } else { libPaths.Add( diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index db4200b..d802e1f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -697,6 +697,8 @@ add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION} -DMSVC_VERSION=${MSVC_VERSION} + -DXCODE=${XCODE} + -DXCODE_VERSION=${XCODE_VERSION} -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX} -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX} -DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX} diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 021de41..9b6581c 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake @@ -90,12 +90,22 @@ if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES run_cmake_target(apple_framework target-framework main-target-framework) run_cmake_target(apple_framework target-reexport_framework main-target-reexport_framework) run_cmake_target(apple_framework target-weak_framework main-target-weak_framework) + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND (NOT XCODE OR XCODE_VERSION GREATER_EQUAL 13)) + run_cmake_target(apple_framework target-framework-postfix main-target-framework-postfix) + run_cmake_target(apple_framework target-reexport_framework-postfix main-target-reexport_framework-postfix) + run_cmake_target(apple_framework target-weak_framework-postfix main-target-weak_framework-postfix) + endif() endif() if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "12") run_cmake_target(apple_framework needed_framework main-needed_framework) run_cmake_target(apple_framework target-needed_framework main-target-needed_framework) + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND (NOT XCODE OR XCODE_VERSION GREATER_EQUAL 13)) + run_cmake_target(apple_framework target-needed_framework-postfix main-target-needed_framework-postfix) + endif() endif() # Apple library features diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake index e9a93e9..ca0e72d 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake @@ -59,3 +59,33 @@ target_link_libraries(main-target-reexport_framework PRIVATE "$" "$") + + + +get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTI_CONFIG) + add_library(target-framework-postfix SHARED foo.mm) + set_target_properties(target-framework-postfix PROPERTIES FRAMEWORK TRUE + FRAMEWORK_MULTI_CONFIG_POSTFIX_RELEASE "_release") + target_link_libraries(target-framework-postfix PRIVATE "$") + + + # feature FRAMEWORK + add_executable(main-target-framework-postfix main.mm) + target_link_libraries(main-target-framework-postfix PRIVATE "$" "$") + + + # feature NEEDED_FRAMEWORK + add_executable(main-target-needed_framework-postfix main.mm) + target_link_libraries(main-target-needed_framework-postfix PRIVATE "$" "$") + + + # feature REEXPORT_FRAMEWORK + add_executable(main-target-reexport_framework-postfix main.mm) + target_link_libraries(main-target-reexport_framework-postfix PRIVATE "$" "$") + + + # feature WEAK_FRAMEWORK + add_executable(main-target-weak_framework-postfix main.mm) + target_link_libraries(main-target-weak_framework-postfix PRIVATE "$" "$") +endif() -- cgit v0.12