Преглед на файлове

Genex LINK_LIBRARY: Add support for framework with postfix

Marc Chevrier преди 3 години
родител
ревизия
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 ``[...]``):
   wildcard, and optional parts are shown as ``[...]``):
 
 
      * ``[/path/to/]FwName[.framework]``
      * ``[/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,
   Note that CMake recognizes and automatically handles framework targets,
   even without using the ``$<LINK_LIBRARY:FRAMEWORK,...>`` expression.
   even without using the ``$<LINK_LIBRARY:FRAMEWORK,...>`` expression.
@@ -59,6 +59,11 @@
   ``$<LINK_LIBRARY:FRAMEWORK,...>`` for file paths so that the expected
   ``$<LINK_LIBRARY:FRAMEWORK,...>`` for file paths so that the expected
   behavior is clear.
   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``
 ``NEEDED_FRAMEWORK``
   This is similar to the ``FRAMEWORK`` feature, except it forces the linker
   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
   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()) {
   if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) {
     // Add the framework directory and the framework item itself
     // 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(
       this->CMakeInstance->IssueMessage(
         MessageType::FATAL_ERROR,
         MessageType::FATAL_ERROR,
         cmStrCat("Could not parse framework path \"", item.Value,
         cmStrCat("Could not parse framework path \"", item.Value,
@@ -1575,12 +1576,13 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
         item.Backtrace);
         item.Backtrace);
       return;
       return;
     }
     }
-    if (!fwItems->first.empty()) {
+    if (!fwDescriptor->Directory.empty()) {
       // Add the directory portion to the framework search path.
       // Add the directory portion to the framework search path.
-      this->AddFrameworkPath(fwItems->first);
+      this->AddFrameworkPath(fwDescriptor->Directory);
     }
     }
     if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) {
     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));
                                this->FindLibraryFeature(entry.Feature));
     } else {
     } else {
       this->Items.emplace_back(
       this->Items.emplace_back(
@@ -1851,9 +1853,11 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
   std::string const& item = entry.Item.Value;
   std::string const& item = entry.Item.Value;
 
 
   // Try to separate the framework name and path.
   // 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;
     std::ostringstream e;
     e << "Could not parse framework path \"" << item << "\" "
     e << "Could not parse framework path \"" << item << "\" "
       << "linked by target " << this->Target->GetName() << ".";
       << "linked by target " << this->Target->GetName() << ".";
@@ -1861,18 +1865,14 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
     return;
     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()) {
   if (!fw_path.empty()) {
-    full_fw = cmStrCat(fw_path, '/', full_fw);
     // Add the directory portion to the framework search path.
     // Add the directory portion to the framework search path.
     this->AddFrameworkPath(fw_path);
     this->AddFrameworkPath(fw_path);
   }
   }
 
 
   // add runtime information
   // add runtime information
-  this->AddLibraryRuntimeInfo(full_fw);
+  this->AddLibraryRuntimeInfo(fwDescriptor->GetFullPath());
 
 
   if (entry.Feature == DEFAULT) {
   if (entry.Feature == DEFAULT) {
     // ensure FRAMEWORK feature is loaded
     // ensure FRAMEWORK feature is loaded
@@ -1887,9 +1887,9 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
                                                         ? "FRAMEWORK"
                                                         ? "FRAMEWORK"
                                                         : entry.Feature));
                                                         : entry.Feature));
   } else {
   } 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));
                                                         : entry.Feature));
   }
   }
 }
 }
@@ -2252,15 +2252,11 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo(
 
 
   // It could be an Apple framework
   // It could be an Apple framework
   if (!is_shared_library) {
   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) {
   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.
 // This is where we change the path to point to the framework directory.
 // .tbd files also can be located in SDK frameworks (they are
 // .tbd files also can be located in SDK frameworks (they are
 // placeholders for actual libraries shipped with the OS)
 // 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,
 cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
-                                      bool extendedFormat) const
+                                      FrameworkFormat format) const
 {
 {
   // Check for framework structure:
   // Check for framework structure:
   //    (/path/to/)?FwName.framework
   //    (/path/to/)?FwName.framework
@@ -2580,20 +2580,29 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
     auto name = frameworkPath.match(3);
     auto name = frameworkPath.match(3);
     auto libname =
     auto libname =
       cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6));
       cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6));
+    if (format == FrameworkFormat::Strict && libname.empty()) {
+      return cm::nullopt;
+    }
     if (!libname.empty() && !cmHasPrefix(libname, name)) {
     if (!libname.empty() && !cmHasPrefix(libname, name)) {
       return cm::nullopt;
       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)?
     // path format can be more flexible: (/path/to/)?fwName(.framework)?
     auto fwDir = cmSystemTools::GetParentDirectory(path);
     auto fwDir = cmSystemTools::GetParentDirectory(path);
     auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework"
     auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework"
       ? cmSystemTools::GetFilenameWithoutExtension(path)
       ? cmSystemTools::GetFilenameWithoutExtension(path)
       : cmSystemTools::GetFilenameName(path);
       : cmSystemTools::GetFilenameName(path);
 
 
-    return std::pair<std::string, std::string>{ fwDir, name };
+    return FrameworkDescriptor{ fwDir, name };
   }
   }
 
 
   return cm::nullopt;
   return cm::nullopt;

+ 53 - 4
Source/cmGlobalGenerator.h

@@ -17,6 +17,7 @@
 
 
 #include <cm/optional>
 #include <cm/optional>
 #include <cmext/algorithm>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 
 #include "cm_codecvt.hxx"
 #include "cm_codecvt.hxx"
 
 
@@ -367,13 +368,61 @@ public:
   /** Determine if a name resolves to a framework on disk or a built target
   /** Determine if a name resolves to a framework on disk or a built target
       that is a framework. */
       that is a framework. */
   bool NameResolvesToFramework(const std::string& libname) const;
   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
    * when extendedFormat is true, required format is relaxed (i.e. extension
    * `.framework' is optional). Used when FRAMEWORK link feature is
    * `.framework' is optional). Used when FRAMEWORK link feature is
    * specified */
    * 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;
   cmMakefile* FindMakefile(const std::string& start_dir) const;
   cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) 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(
 std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath(
   const std::string& path) const
   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;
   return path;
@@ -3641,9 +3637,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
       linkDir = libItem->Value.Value;
       linkDir = libItem->Value.Value;
     }
     }
     if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) {
     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(),
         if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(),
                       linkDir) == frameworkSearchPaths.end()) {
                       linkDir) == frameworkSearchPaths.end()) {
           frameworkSearchPaths.push_back(linkDir);
           frameworkSearchPaths.push_back(linkDir);
@@ -3839,13 +3836,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
           bool isFramework =
           bool isFramework =
             cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
             cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
           if (isFramework) {
           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
               // This is a search path we had not added before and it isn't
               // an implicit search path, so we need it
               // 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) {
             if (libName.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s) {
               // use the full path
               // use the full path
@@ -3853,10 +3851,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
                 libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
                 libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
                   .Value);
                   .Value);
             } else {
             } else {
-              libPaths.Add(
-                libName
-                  .GetFormattedItem(this->XCodeEscapePath(fwItems->second))
-                  .Value);
+              libPaths.Add(libName
+                             .GetFormattedItem(this->XCodeEscapePath(
+                               fwDescriptor->GetLinkName()))
+                             .Value);
             }
             }
           } else {
           } else {
             libPaths.Add(
             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_ID=${CMAKE_C_COMPILER_ID}
                                                      -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
                                                      -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
                                                      -DMSVC_VERSION=${MSVC_VERSION}
                                                      -DMSVC_VERSION=${MSVC_VERSION}
+                                                     -DXCODE=${XCODE}
+                                                     -DXCODE_VERSION=${XCODE_VERSION}
                                                      -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
                                                      -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
                                                      -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}
                                                      -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}
                                                      -DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX}
                                                      -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-framework main-target-framework)
   run_cmake_target(apple_framework target-reexport_framework main-target-reexport_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)
   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()
 endif()
 
 
 if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "12")
 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 needed_framework main-needed_framework)
 
 
   run_cmake_target(apple_framework target-needed_framework main-target-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()
 endif()
 
 
 # Apple library features
 # 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
 # feature WEAK_FRAMEWORK
 add_executable(main-target-weak_framework main.mm)
 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>")
 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()