Merge topic 'LINK_LIBRARY-Feature-properties'

6e5e7968c3 GenEx $<LINK_LIBRARY>: Add the support of properties attached to features

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !9510
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 8f98f8b..b71e3d9 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -467,6 +467,7 @@
    /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE_SUPPORTED
+   /variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES
    /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE
@@ -485,6 +486,7 @@
    /variable/CMAKE_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
+   /variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG
    /variable/CMAKE_LINK_LIBRARY_USING_FEATURE
diff --git a/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst b/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst
new file mode 100644
index 0000000..a96eab9
--- /dev/null
+++ b/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst
@@ -0,0 +1,7 @@
+GenEx-LINK_LIBRARY-feature-properties
+-------------------------------------
+
+* Link features, as used with the :genex:`LINK_LIBRARY` generator expression,
+  gained the ability to have properties that describe their behavior by
+  specifying the :variable:`CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES` or
+  :variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` variables.
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst
new file mode 100644
index 0000000..d8efd0f
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst
@@ -0,0 +1,12 @@
+CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES
+----------------------------------------------
+
+.. versionadded:: 3.30
+
+This variable defines the semantics of the specified ``<FEATURE>`` for the
+language ``<LANG>`` (as described by the
+:variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` or
+:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variables) used for the link
+command generation.
+
+.. include:: CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst
new file mode 100644
index 0000000..86b4e77
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst
@@ -0,0 +1,14 @@
+CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES
+---------------------------------------
+
+.. versionadded:: 3.30
+
+This variable defines the semantics of the specified ``<FEATURE>`` (as
+described by the :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` or
+:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variables) used for the link
+command generation.
+
+This variable will be considered only if the
+ :variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` is not defined.
+
+.. include:: CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
new file mode 100644
index 0000000..202a7ec
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
@@ -0,0 +1,87 @@
+Feature Properties Definition
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A feature properties definition is a
+:ref:`semicolon-separated list <CMake Language Lists>` of ``property=value(s)``
+items. In the case of multiple values can be specified, they are separated by
+a comma.
+
+The following properties are supported:
+
+``LIBRARY_TYPE=<library_type-list>``
+  Specify which library types are supported by this feature. The possible
+  values are: ``STATIC``, ``SHARED``, ``MODULE`` or ``EXECUTABLE``.
+
+  If this property is not specified, the default is
+  ``LIBRARY_TYPE=STATIC,SHARED,MODULE,EXECUTABLE``.
+
+  If the feature is used with an unsupported library type, CMake will emit a
+  developer warning and the feature will be ignored.
+
+``OVERRIDE=<feature-list>``
+  Specify which features will be replaced by this one in the event of a
+  conflict. This override mechanism is superseded by any
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE` or
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties definitions.
+
+  If this property is not specified, the default is an empty list.
+
+``UNICITY=YES|NO|DEFAULT``
+  Manage the strategy of de-duplication for the libraries using this feature.
+
+  ``YES``
+    Libraries are de-duplicated regardless the default strategy applied by
+    CMake.
+
+  ``NO``
+    Libraries are not de-duplicated regardless the default strategy applied
+    by CMake.
+
+  ``DEFAULT``
+    Apply the default CMake strategy.
+
+  If this property is not specified, ``DEFAULT`` will be used.
+
+Example
+^^^^^^^
+
+A common need is the loading of a full archive as part of the creation of a
+shared library or an executable. For that purpose, the ``WHOLE_ARCHIVE``
+feature can be used.
+
+Currently, the associated properties with this feature are defined as follows:
+
+.. code-block:: cmake
+
+  set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC
+                                                  OVERRIDE=DEFAULT
+                                                  UNICITY=YES)
+
+``LIBRARY_TYPE=STATIC``
+  Obviously, this feature is only meaningful for static libraries.
+``OVERRIDE=DEFAULT``
+  The ``DEFAULT`` feature will be overridden by the ``WHOLE_ARCHIVE`` feature
+  because they are compatible and enhance the user's experience: standard
+  library specification and ``$<LINK_LIBRARY:WHOLE_ARCHIVE>`` can be used
+  freely.
+``UNICITY=YES``
+  When this feature is used, all symbols from the static library are loaded
+  by the linker, so there is no need to duplicate the library on the link
+  command.
+
+A typical usage of the ``WHOLE_ARCHIVE`` can be:
+
+.. code-block:: cmake
+
+  add_library(A STATIC ...)
+  add_library(B STATIC ...)
+
+  target_link_libraries(B PUBLIC A)
+  target_link_libraries(A PUBLIC B)
+
+  add_library(global SHARED ...)
+  target_link_libraries(global PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,A>)
+
+The resulting link command will only have one iteration of the ``A`` library
+specified with the needed linker flags to ensure the load of all the symbols
+of the library.
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
index 4b13b7c..0359f58 100644
--- a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
+++ b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
@@ -2,6 +2,9 @@
 and underscores.  Feature names defined in all uppercase are reserved for
 CMake's own built-in features (see `Predefined Features`_ further below).
 
+The feature behavior can be described using the
+:variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` or
+:variable:`CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES` variables.
 
 Feature Definitions
 ^^^^^^^^^^^^^^^^^^^
diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake
index bd5ba9a..31f4293 100644
--- a/Modules/Platform/Apple-Clang.cmake
+++ b/Modules/Platform/Apple-Clang.cmake
@@ -19,6 +19,7 @@
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+  set(CMAKE_${lang}_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
   # linker selection
   set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld")
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake
index 15f6a71..20b18ad 100644
--- a/Modules/Platform/Apple-GNU.cmake
+++ b/Modules/Platform/Apple-GNU.cmake
@@ -17,6 +17,7 @@
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+  set(CMAKE_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
   set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
   set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic")
diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake
index 070b24d..5e2ba41 100644
--- a/Modules/Platform/CYGWIN-GNU.cmake
+++ b/Modules/Platform/CYGWIN-GNU.cmake
@@ -40,6 +40,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 macro(__cygwin_compiler_gnu lang)
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index 533b9ce..1272baf 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -114,29 +114,37 @@
 # Defines LINK_LIBRARY features for frameworks
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_NEEDED_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_REEXPORT_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WEAK_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 # Defines LINK_LIBRARY features for libraries
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library,<LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_NEEDED_LIBRARY_PROPERTIES LIBRARY_TYPE=SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library,<LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_REEXPORT_LIBRARY_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,<LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WEAK_LIBRARY_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 # Defines LINK_LIBRARY feature to Force loading of all members of an archive
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,<LIB_ITEM>")
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # default to searching for frameworks first
 if(NOT DEFINED CMAKE_FIND_FRAMEWORK)
diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake
index bd5a786..7d5f951 100644
--- a/Modules/Platform/FreeBSD.cmake
+++ b/Modules/Platform/FreeBSD.cmake
@@ -51,6 +51,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake
index 97a116f..fba6ab8 100644
--- a/Modules/Platform/Linux.cmake
+++ b/Modules/Platform/Linux.cmake
@@ -44,6 +44,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # Features for LINK_GROUP generator expression
 ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake
index ab85923..af368cd 100644
--- a/Modules/Platform/NetBSD.cmake
+++ b/Modules/Platform/NetBSD.cmake
@@ -37,6 +37,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/SunOS.cmake b/Modules/Platform/SunOS.cmake
index b8a302c..73205c1 100644
--- a/Modules/Platform/SunOS.cmake
+++ b/Modules/Platform/SunOS.cmake
@@ -20,6 +20,7 @@
                                              "LINKER:-z,defaultextract")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index b9e6394..35055bc 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -140,6 +140,7 @@
     ## WHOLE_ARCHIVE: Force loading all members of an archive
     set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
     set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+    set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
   endif()
 
   enable_language(RC)
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index 9f81882..d2b25c6 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -88,6 +88,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # Features for LINK_GROUP generator expression
 ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/Windows-IntelLLVM.cmake b/Modules/Platform/Windows-IntelLLVM.cmake
index b1a336b..691c7ce 100644
--- a/Modules/Platform/Windows-IntelLLVM.cmake
+++ b/Modules/Platform/Windows-IntelLLVM.cmake
@@ -37,6 +37,7 @@
     ## WHOLE_ARCHIVE: Force loading all members of an archive
     set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
     set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+    set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
   endif()
 
   set(CMAKE_${lang}_LINK_EXECUTABLE
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index ef57031..c737b88 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -358,6 +358,7 @@
   ## WHOLE_ARCHIVE: Force loading all members of an archive
   set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "/WHOLEARCHIVE:<LIBRARY>")
   set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+  set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 endif()
 
 
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 8651436..db0d71a 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -30,6 +30,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
@@ -201,6 +202,113 @@
   return makefile->GetDefinition(featureSupported).IsOn();
 }
 
+// LINK_LIBRARY feature properties management
+struct LinkLibraryFeaturePropertySet
+{
+  std::set<cmStateEnums::TargetType> LibraryTypes = {
+    cmStateEnums::EXECUTABLE, cmStateEnums::STATIC_LIBRARY,
+    cmStateEnums::SHARED_LIBRARY, cmStateEnums::MODULE_LIBRARY,
+    cmStateEnums::UNKNOWN_LIBRARY
+  };
+  std::set<std::string> Override;
+
+  enum UnicityKind
+  {
+    Default,
+    Yes,
+    No
+  };
+  UnicityKind Unicity = Default;
+};
+std::map<std::string, LinkLibraryFeaturePropertySet>
+  LinkLibraryFeatureProperties;
+const LinkLibraryFeaturePropertySet& GetLinkLibraryFeatureProperties(
+  cmMakefile* makefile, std::string const& linkLanguage,
+  const std::string& feature)
+{
+  auto it = LinkLibraryFeatureProperties.find(feature);
+  if (it != LinkLibraryFeatureProperties.end()) {
+    return it->second;
+  }
+
+  auto featurePropertiesVariable =
+    cmStrCat("CMAKE_", linkLanguage, "_LINK_LIBRARY_", feature, "_PROPERTIES");
+  auto featurePropertiesValues =
+    makefile->GetDefinition(featurePropertiesVariable);
+  if (featurePropertiesValues.IsEmpty()) {
+    // try language agnostic definition
+    featurePropertiesVariable =
+      cmStrCat("CMAKE_LINK_LIBRARY_", feature, "_PROPERTIES");
+    featurePropertiesValues =
+      makefile->GetDefinition(featurePropertiesVariable);
+  }
+  if (!featurePropertiesValues.IsEmpty()) {
+    LinkLibraryFeaturePropertySet featureProperties;
+    cmsys::RegularExpression processingOption{
+      "^(LIBRARY_TYPE|UNICITY|OVERRIDE)=((STATIC|SHARED|MODULE|EXECUTABLE)(,("
+      "STATIC|"
+      "SHARED|MODULE|EXECUTABLE)"
+      ")*|YES|NO|DEFAULT|[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*)$"
+    };
+    std::string errorMessage;
+    for (auto const& option : cmList{ featurePropertiesValues }) {
+      if (processingOption.find(option)) {
+        if (processingOption.match(1) == "LIBRARY_TYPE") {
+          featureProperties.LibraryTypes.clear();
+          for (auto const& value :
+               cmTokenize(processingOption.match(2), ","_s)) {
+            if (value == "STATIC") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::STATIC_LIBRARY);
+            } else if (value == "SHARED") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::SHARED_LIBRARY);
+            } else if (value == "MODULE") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::MODULE_LIBRARY);
+            } else if (value == "EXECUTABLE") {
+              featureProperties.LibraryTypes.emplace(cmStateEnums::EXECUTABLE);
+            } else {
+              errorMessage += cmStrCat("  ", option, '\n');
+              break;
+            }
+          }
+          // Always add UNKNOWN type
+          featureProperties.LibraryTypes.emplace(
+            cmStateEnums::UNKNOWN_LIBRARY);
+        } else if (processingOption.match(1) == "UNICITY") {
+          if (processingOption.match(2) == "YES") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::Yes;
+          } else if (processingOption.match(2) == "NO") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::No;
+          } else if (processingOption.match(2) == "DEFAULT") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::Default;
+          } else {
+            errorMessage += cmStrCat("  ", option, '\n');
+          }
+        } else if (processingOption.match(1) == "OVERRIDE") {
+          featureProperties.Override.clear();
+          auto values = cmTokenize(processingOption.match(2), ","_s);
+          featureProperties.Override.insert(values.begin(), values.end());
+        }
+      } else {
+        errorMessage += cmStrCat("  ", option, '\n');
+      }
+    }
+    if (!errorMessage.empty()) {
+      makefile->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Erroneous option(s) for '", featurePropertiesVariable,
+                 "':\n", errorMessage));
+    }
+    return LinkLibraryFeatureProperties.emplace(feature, featureProperties)
+      .first->second;
+  }
+  return LinkLibraryFeatureProperties
+    .emplace(feature, LinkLibraryFeaturePropertySet{})
+    .first->second;
+}
+
 // LINK_GROUP helpers
 const auto LG_BEGIN = "<LINK_GROUP:"_s;
 const auto LG_END = "</LINK_GROUP:"_s;
@@ -235,7 +343,9 @@
   EntriesProcessing(const cmGeneratorTarget* target,
                     const std::string& linkLanguage, EntryVector& entries,
                     EntryVector& finalEntries)
-    : Entries(entries)
+    : Target(target)
+    , LinkLanguage(linkLanguage)
+    , Entries(entries)
     , FinalEntries(finalEntries)
   {
     const auto* makefile = target->Makefile;
@@ -398,6 +508,18 @@
 
   bool IncludeEntry(LinkEntry const& entry) const
   {
+    if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) {
+      auto const& featureProperties = GetLinkLibraryFeatureProperties(
+        this->Target->Makefile, this->LinkLanguage, entry.Feature);
+      if ((entry.Target == nullptr ||
+           featureProperties.LibraryTypes.find(entry.Target->GetType()) !=
+             featureProperties.LibraryTypes.end()) &&
+          featureProperties.Unicity !=
+            LinkLibraryFeaturePropertySet::Default) {
+        return featureProperties.Unicity == LinkLibraryFeaturePropertySet::No;
+      }
+    }
+
     return this->Unicity == None ||
       (this->Unicity == Shared &&
        (entry.Target == nullptr ||
@@ -418,6 +540,8 @@
 
   OrderKind Order = Reverse;
   UnicityKind Unicity = Shared;
+  const cmGeneratorTarget* Target;
+  const std::string& LinkLanguage;
   EntryVector& Entries;
   EntryVector& FinalEntries;
   std::set<size_t> Emitted;
@@ -973,12 +1097,14 @@
     auto ale = this->AddLinkEntry(item, groupIndex.first);
     dependee_index = ale.first;
     LinkEntry& entry = this->EntryList[dependee_index];
+    bool supportedItem = true;
     auto const& itemFeature =
       this->GetCurrentFeature(entry.Item.Value, item.Feature);
     if (inGroup && ale.second && entry.Target != nullptr &&
         (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
          entry.Target->GetType() ==
            cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+      supportedItem = false;
       const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
       this->CMakeInstance->IssueMessage(
         MessageType::AUTHOR_WARNING,
@@ -995,30 +1121,27 @@
     }
     if (ale.second) {
       // current item not yet defined
-      if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr &&
-          (entry.Target->GetType() ==
-             cmStateEnums::TargetType::OBJECT_LIBRARY ||
-           entry.Target->GetType() ==
-             cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
-        this->CMakeInstance->IssueMessage(
-          MessageType::AUTHOR_WARNING,
-          cmStrCat("The feature '", itemFeature,
-                   "', specified as part of a generator-expression "
-                   "'$<LINK_LIBRARY:",
-                   itemFeature, ">', will not be applied to the ",
-                   (entry.Target->GetType() ==
-                        cmStateEnums::TargetType::OBJECT_LIBRARY
-                      ? "OBJECT"
-                      : "INTERFACE"),
-                   " library '", entry.Item.Value, "'."),
-          this->Target->GetBacktrace());
-      }
       entry.Feature = itemFeature;
-    }
 
-    bool supportedItem = entry.Target == nullptr ||
-      (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
-       entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+      if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr) {
+        auto const& featureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, itemFeature);
+        if (featureProperties.LibraryTypes.find(entry.Target->GetType()) ==
+            featureProperties.LibraryTypes.end()) {
+          supportedItem = false;
+          entry.Feature = LinkEntry::DEFAULT;
+          this->CMakeInstance->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat("The feature '", itemFeature,
+                     "', specified as part of a generator-expression "
+                     "'$<LINK_LIBRARY:",
+                     itemFeature, ">', will not be applied to the ",
+                     cmState::GetTargetTypeName(entry.Target->GetType()), " '",
+                     entry.Item.Value, "'."),
+            this->Target->GetBacktrace());
+        }
+      }
+    }
 
     if (supportedItem) {
       if (inGroup) {
@@ -1043,21 +1166,41 @@
         }
       }
       if (entry.Feature != itemFeature) {
-        // incompatibles features occurred
-        this->CMakeInstance->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("Impossible to link target '", this->Target->GetName(),
-                   "' because the link item '", entry.Item.Value,
-                   "', specified ",
-                   (itemFeature == LinkEntry::DEFAULT
-                      ? "without any feature or 'DEFAULT' feature"
-                      : cmStrCat("with the feature '", itemFeature, '\'')),
-                   ", has already occurred ",
-                   (entry.Feature == LinkEntry::DEFAULT
-                      ? "without any feature or 'DEFAULT' feature"
-                      : cmStrCat("with the feature '", entry.Feature, '\'')),
-                   ", which is not allowed."),
-          this->Target->GetBacktrace());
+        bool incompatibleFeatures = true;
+        // check if an override is possible
+        auto const& entryFeatureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, entry.Feature);
+        auto const& itemFeatureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, itemFeature);
+        if (entryFeatureProperties.Override.empty() &&
+            !itemFeatureProperties.Override.empty() &&
+            itemFeatureProperties.Override.find(entry.Feature) !=
+              itemFeatureProperties.Override.end()) {
+          entry.Feature = itemFeature;
+          incompatibleFeatures = false;
+        } else if (!entryFeatureProperties.Override.empty() &&
+                   itemFeatureProperties.Override.empty() &&
+                   entryFeatureProperties.Override.find(itemFeature) !=
+                     entryFeatureProperties.Override.end()) {
+          incompatibleFeatures = false;
+        }
+        if (incompatibleFeatures) {
+          // incompatibles features occurred
+          this->CMakeInstance->IssueMessage(
+            MessageType::FATAL_ERROR,
+            cmStrCat("Impossible to link target '", this->Target->GetName(),
+                     "' because the link item '", entry.Item.Value,
+                     "', specified ",
+                     (itemFeature == LinkEntry::DEFAULT
+                        ? "without any feature or 'DEFAULT' feature"
+                        : cmStrCat("with the feature '", itemFeature, '\'')),
+                     ", has already occurred ",
+                     (entry.Feature == LinkEntry::DEFAULT
+                        ? "without any feature or 'DEFAULT' feature"
+                        : cmStrCat("with the feature '", entry.Feature, '\'')),
+                     ", which is not allowed."),
+            this->Target->GetBacktrace());
+        }
       }
     }
 
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
index 2ad45ba..a061127 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -35,3 +35,10 @@
 run_cmake(override-features3)
 run_cmake(override-features4)
 run_cmake(override-features5)
+
+# testing feature properties specification
+run_cmake(bad-feature-properties1)
+run_cmake(bad-feature-properties2)
+run_cmake(bad-feature-properties3)
+run_cmake(bad-feature-properties4)
+run_cmake(bad-feature-properties5)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt
new file mode 100644
index 0000000..ac07251
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    BAD_PROPERTY=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake
new file mode 100644
index 0000000..e5790a8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES BAD_PROPERTY=XXX)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt
new file mode 100644
index 0000000..ac07251
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    BAD_PROPERTY=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake
new file mode 100644
index 0000000..dea98d2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES LIBRARY_TYPE=STATIC BAD_PROPERTY=XXX UNICITY=YES)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt
new file mode 100644
index 0000000..29f5f66
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake
new file mode 100644
index 0000000..0a535db
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES LIBRARY_TYPE=STATIC,BAD_TYPE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt
new file mode 100644
index 0000000..29f5f66
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake
new file mode 100644
index 0000000..c106653
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES UNICITY=YES LIBRARY_TYPE=STATIC,BAD_TYPE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt
new file mode 100644
index 0000000..3e57782
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    UNICITY=YES,NO
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake
new file mode 100644
index 0000000..06efe7e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES UNICITY=YES,NO)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
index f9a99af..8f43a7f 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -1,6 +1,6 @@
 CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
   The feature 'feat', specified as part of a generator-expression
-  '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE library
+  '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE_LIBRARY
   'front'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
@@ -8,7 +8,14 @@
 
 CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
   The feature 'feat', specified as part of a generator-expression
-  '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT library 'dep'.
+  '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT_LIBRARY 'dep'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
+  The feature 'feat', specified as part of a generator-expression
+  '\$<LINK_LIBRARY:feat>', will not be applied to the SHARED_LIBRARY 'lib'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
index a888bb8..675b87d 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
@@ -2,13 +2,14 @@
 
 set(CMAKE_C_LINK_LIBRARY_USING_feat_SUPPORTED TRUE)
 set(CMAKE_C_LINK_LIBRARY_USING_feat "<LIBRARY>")
+set(CMAKE_C_LINK_LIBRARY_feat_PROPERTIES "LIBRARY_TYPE=STATIC")
 
 add_library(dep OBJECT empty.c)
 
 add_library(lib SHARED empty.c)
 
 add_library(front INTERFACE)
-target_link_libraries(front INTERFACE lib)
+target_link_libraries(front INTERFACE "$<LINK_LIBRARY:feat,lib>")
 
 
 add_library(lib2 SHARED empty.c)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
index 88a7e63..97a96b4 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -135,4 +135,5 @@
     OR CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|visionOS|watchOS|Linux|BSD|MSYS|CYGWIN")
   run_cmake(feature-WHOLE_ARCHIVE)
   run_cmake_target(feature-WHOLE_ARCHIVE link-exe main)
+  run_cmake_target(feature-WHOLE_ARCHIVE circular-exe main_circular)
 endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c
new file mode 100644
index 0000000..80ee413
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c
@@ -0,0 +1,6 @@
+void circular2(void);
+
+void circular1(void)
+{
+  circular2();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c
new file mode 100644
index 0000000..751bab5
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c
@@ -0,0 +1,7 @@
+
+void circular1(void);
+
+void circular2(void)
+{
+  circular1();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt
new file mode 100644
index 0000000..fb4871c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt
@@ -0,0 +1 @@
+(ld: warning: ignoring duplicate libraries:)?
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
index e525325..5c599c9 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
@@ -9,3 +9,13 @@
 
 add_executable(main main.c)
 target_link_libraries(main PRIVATE lib)
+
+
+add_library(circular1 STATIC circular1.c)
+add_library(circular2 STATIC circular2.c)
+
+target_link_libraries(circular1 PRIVATE circular2)
+target_link_libraries(circular2 PRIVATE circular1)
+
+add_executable(main_circular main_circular.c)
+target_link_libraries(main_circular PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,circular1>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c
new file mode 100644
index 0000000..16ebee6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c
@@ -0,0 +1,9 @@
+
+void circular1(void);
+
+int main(void)
+{
+  circular1();
+
+  return 0;
+}