Browse Source

Genex LINK_LIBRARY: Add support for framework with postfix

Marc Chevrier 3 years ago
parent
commit
0bd3efffbc

+ 5 - 0
Help/release/dev/genex-LINK_LIBRARY-FRAMEWORK-supports-suffix.rst

@@ -0,0 +1,5 @@
+genex-LINK_LIBRARY-FRAMEWORK-supports-suffix
+--------------------------------------------
+
+The :genex:`$<LINK_LIBRARY>` generator expression gains the capability, for the
+``FRAMEWORK`` features, to handle the suffix of the framework library name.

+ 7 - 2
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 ``$<LINK_LIBRARY:FRAMEWORK,...>`` expression.
@@ -59,6 +59,11 @@
   ``$<LINK_LIBRARY:FRAMEWORK,...>`` for file paths so that the expected
   behavior is clear.
 
+  .. versionadded:: 3.25
+    The :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>` 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

+ 22 - 26
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) {

+ 14 - 5
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<std::pair<std::string, std::string>>
+cm::optional<cmGlobalGenerator::FrameworkDescriptor>
 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<std::string, std::string>{ 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<std::string, std::string>{ fwDir, name };
+    return FrameworkDescriptor{ fwDir, name };
   }
 
   return cm::nullopt;

+ 53 - 4
Source/cmGlobalGenerator.h

@@ -17,6 +17,7 @@
 
 #include <cm/optional>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #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<std::pair<std::string, std::string>> 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<FrameworkDescriptor> SplitFrameworkPath(
+    const std::string& path,
+    FrameworkFormat format = FrameworkFormat::Relaxed) const;
 
   cmMakefile* FindMakefile(const std::string& start_dir) const;
   cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;

+ 17 - 19
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(

+ 2 - 0
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}

+ 10 - 0
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

+ 30 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake

@@ -59,3 +59,33 @@ target_link_libraries(main-target-reexport_framework PRIVATE "$<LINK_LIBRARY:FRA
 # feature WEAK_FRAMEWORK
 add_executable(main-target-weak_framework main.mm)
 target_link_libraries(main-target-weak_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework>")
+
+
+
+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 "$<LINK_LIBRARY:FRAMEWORK,Foundation>")
+
+
+  # feature FRAMEWORK
+  add_executable(main-target-framework-postfix main.mm)
+  target_link_libraries(main-target-framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:FRAMEWORK,target-framework-postfix>")
+
+
+  # feature NEEDED_FRAMEWORK
+  add_executable(main-target-needed_framework-postfix main.mm)
+  target_link_libraries(main-target-needed_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:NEEDED_FRAMEWORK,target-framework-postfix>")
+
+
+  # feature REEXPORT_FRAMEWORK
+  add_executable(main-target-reexport_framework-postfix main.mm)
+  target_link_libraries(main-target-reexport_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework-postfix>")
+
+
+  # feature WEAK_FRAMEWORK
+  add_executable(main-target-weak_framework-postfix main.mm)
+  target_link_libraries(main-target-weak_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework-postfix>")
+endif()