From 28d7432468932ea3a7425579e909d51fe36a70c7 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Mon, 1 Nov 2021 15:01:22 +0100
Subject: cmComputeLinkInformation: use cmComputeLinkDepends::LinkEntry

In preparation of support of genex $<LINK_LIBRARY:...>, propagate
cmComputeLinkDepends::LinkEntry instances to ensure to have, when needed,
all attributes attached to the link item.
---
 Source/cmComputeLinkDepends.h       |   8 +++
 Source/cmComputeLinkInformation.cxx | 111 ++++++++++++++++++++++--------------
 Source/cmComputeLinkInformation.h   |  26 ++++-----
 3 files changed, 88 insertions(+), 57 deletions(-)

diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 72316f1..727c666 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -9,6 +9,7 @@
 #include <queue>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "cmGraphAdjacencyList.h"
@@ -38,6 +39,13 @@ public:
   // Basic information about each link item.
   struct LinkEntry
   {
+    LinkEntry() = default;
+    LinkEntry(BT<std::string> item, cmGeneratorTarget const* target = nullptr)
+      : Item(std::move(item))
+      , Target(target)
+    {
+    }
+
     BT<std::string> Item;
     cmGeneratorTarget const* Target = nullptr;
     bool IsSharedDep = false;
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 2ff91fe..62a96dd 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -517,10 +517,9 @@ bool cmComputeLinkInformation::Compute()
   // Add the link line items.
   for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
     if (linkEntry.IsSharedDep) {
-      this->AddSharedDepItem(linkEntry.Item, linkEntry.Target);
+      this->AddSharedDepItem(linkEntry);
     } else {
-      this->AddItem(linkEntry.Item, linkEntry.Target,
-                    linkEntry.IsObject ? ItemIsObject::Yes : ItemIsObject::No);
+      this->AddItem(linkEntry);
     }
   }
 
@@ -610,7 +609,7 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang)
     std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
     for (std::string const& i : libsVec) {
       if (!cm::contains(this->ImplicitLinkLibs, i)) {
-        this->AddItem(i, nullptr);
+        this->AddItem({ i });
       }
     }
   }
@@ -625,7 +624,7 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
     std::vector<std::string> libsVec = cmExpandedList(*libs);
     for (std::string const& i : libsVec) {
       if (!cm::contains(this->ImplicitLinkLibs, i)) {
-        this->AddItem(i, nullptr);
+        this->AddItem({ i });
       }
     }
   }
@@ -639,10 +638,11 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
   }
 }
 
-void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
-                                       cmGeneratorTarget const* tgt,
-                                       ItemIsObject isObject)
+void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
 {
+  cmGeneratorTarget const* tgt = entry.Target;
+  BT<std::string> const& item = entry.Item;
+
   // Compute the proper name to use to link this library.
   const std::string& config = this->Config;
   bool impexe = (tgt && tgt->IsExecutableWithExports());
@@ -678,7 +678,7 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
       // Also add the item the interface specifies to be used in its place.
       std::string const& libName = tgt->GetImportedLibName(config);
       if (!libName.empty()) {
-        this->AddItem(BT<std::string>(libName, item.Backtrace), nullptr);
+        this->AddItem(BT<std::string>(libName, item.Backtrace));
       }
     } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
       // Ignore object library!
@@ -706,7 +706,9 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
         this->Depends.push_back(lib.Value);
       }
 
-      this->AddTargetItem(lib, tgt);
+      LinkEntry libEntry{ entry };
+      libEntry.Item = lib;
+      this->AddTargetItem(libEntry);
       this->AddLibraryRuntimeInfo(lib.Value, tgt);
       if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
           this->Target->IsDLLPlatform()) {
@@ -719,26 +721,28 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
       if (cmSystemTools::IsPathToFramework(item.Value) &&
           this->Makefile->IsOn("APPLE")) {
         // This is a framework.
-        this->AddFrameworkItem(item.Value);
+        this->AddFrameworkItem(entry);
       } else if (cmSystemTools::FileIsDirectory(item.Value)) {
         // This is a directory.
         this->DropDirectoryItem(item);
       } else {
         // Use the full path given to the library file.
         this->Depends.push_back(item.Value);
-        this->AddFullItem(item, isObject);
+        this->AddFullItem(entry);
         this->AddLibraryRuntimeInfo(item.Value);
       }
     } else {
       // This is a library or option specified by the user.
-      this->AddUserItem(item, true);
+      this->AddUserItem(entry, true);
     }
   }
 }
 
-void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
-                                                const cmGeneratorTarget* tgt)
+void cmComputeLinkInformation::AddSharedDepItem(LinkEntry const& entry)
 {
+  BT<std::string> const& item = entry.Item;
+  const cmGeneratorTarget* tgt = entry.Target;
+
   // Record dependencies on DLLs.
   if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
       this->Target->IsDLLPlatform() &&
@@ -776,7 +780,7 @@ void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
 
   // If in linking mode, just link to the shared library.
   if (this->SharedDependencyMode == SharedDepModeLink) {
-    this->AddItem(item, tgt);
+    this->AddItem(entry);
     return;
   }
 
@@ -1058,8 +1062,7 @@ void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt)
   }
 }
 
-void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
-                                             cmGeneratorTarget const* target)
+void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
 {
   // This is called to handle a link item that is a full path to a target.
   // If the target is not a static library make sure the link type is
@@ -1067,6 +1070,9 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
   // shared and static libraries but static-mode can handle only
   // static libraries.  If a previous user item changed the link type
   // to static we need to make sure it is back to shared.
+  BT<std::string> const& item = entry.Item;
+  cmGeneratorTarget const* target = entry.Target;
+
   if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
     this->SetCurrentLinkType(LinkShared);
   }
@@ -1079,7 +1085,7 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
   // Handle case of an imported shared library with no soname.
   if (this->NoSONameUsesPath &&
       target->IsImportedSharedLibWithoutSOName(this->Config)) {
-    this->AddSharedLibNoSOName(item.Value);
+    this->AddSharedLibNoSOName(entry);
     return;
   }
 
@@ -1095,16 +1101,17 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
   this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
 }
 
-void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
-                                           ItemIsObject isObject)
+void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
 {
+  BT<std::string> const& item = entry.Item;
+
   // Check for the implicit link directory special case.
-  if (this->CheckImplicitDirItem(item.Value)) {
+  if (this->CheckImplicitDirItem(entry)) {
     return;
   }
 
   // Check for case of shared library with no builtin soname.
-  if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item.Value)) {
+  if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(entry)) {
     return;
   }
 
@@ -1116,7 +1123,7 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
        generator.find("Xcode") != std::string::npos)) {
     std::string file = cmSystemTools::GetFilenameName(item.Value);
     if (!this->ExtractAnyLibraryName.find(file)) {
-      this->HandleBadFullItem(item.Value, file);
+      this->HandleBadFullItem(entry, file);
       return;
     }
   }
@@ -1147,11 +1154,15 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
   }
 
   // Now add the full path to the library.
-  this->Items.emplace_back(item, ItemIsPath::Yes, isObject);
+  this->Items.emplace_back(item, ItemIsPath::Yes,
+                           entry.IsObject ? ItemIsObject::Yes
+                                          : ItemIsObject::No);
 }
 
-bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
+bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
 {
+  BT<std::string> const& item = entry.Item;
+
   // We only switch to a pathless item if the link type may be
   // enforced.  Fortunately only platforms that support link types
   // seem to have magic per-architecture implicit link directories.
@@ -1160,7 +1171,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
   }
 
   // Check if this item is in an implicit link directory.
-  std::string dir = cmSystemTools::GetFilenamePath(item);
+  std::string dir = cmSystemTools::GetFilenamePath(item.Value);
   if (!cm::contains(this->ImplicitLinkDirs, dir)) {
     // Only libraries in implicit link directories are converted to
     // pathless items.
@@ -1169,7 +1180,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
 
   // Only apply the policy below if the library file is one that can
   // be found by the linker.
-  std::string file = cmSystemTools::GetFilenameName(item);
+  std::string file = cmSystemTools::GetFilenameName(item.Value);
   if (!this->ExtractAnyLibraryName.find(file)) {
     return false;
   }
@@ -1179,10 +1190,10 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
     case cmPolicies::WARN:
       if (this->CMP0060Warn) {
         // Print the warning at most once for this item.
-        std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
+        std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value;
         if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
           this->CMakeInstance->SetProperty(wid, "1");
-          this->CMP0060WarnItems.insert(item);
+          this->CMP0060WarnItems.insert(item.Value);
         }
       }
       CM_FALLTHROUGH;
@@ -1200,15 +1211,19 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
   // directory then just report the file name without the directory
   // portion.  This will allow the system linker to locate the proper
   // library for the architecture at link time.
-  this->AddUserItem(file, false);
+  LinkEntry fileEntry{ entry };
+  fileEntry.Item = file;
+  this->AddUserItem(fileEntry, false);
 
   // Make sure the link directory ordering will find the library.
-  this->OrderLinkerSearchPath->AddLinkLibrary(item);
+  this->OrderLinkerSearchPath->AddLinkLibrary(item.Value);
 
   return true;
 }
 
-void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
+// void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
+//                                            bool pathNotKnown)
+void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
                                            bool pathNotKnown)
 {
   // This is called to handle a link item that does not match a CMake
@@ -1219,6 +1234,8 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
   //   foo       ==>  -lfoo
   //   libfoo.a  ==>  -Wl,-Bstatic -lfoo
 
+  BT<std::string> const& item = entry.Item;
+
   // Pass flags through untouched.
   if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
     // if this is a -l option then we might need to warn about
@@ -1315,8 +1332,10 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
   // specification.
 }
 
-void cmComputeLinkInformation::AddFrameworkItem(std::string const& item)
+void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
 {
+  std::string const& item = entry.Item.Value;
+
   // Try to separate the framework name and path.
   if (!this->SplitFramework.find(item)) {
     std::ostringstream e;
@@ -1390,42 +1409,44 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
   }
 }
 
-bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item)
+bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry)
 {
   // This platform will use the path to a library as its soname if the
   // library is given via path and was not built with an soname.  If
   // this is a shared library that might be the case.
-  std::string file = cmSystemTools::GetFilenameName(item);
+  std::string file = cmSystemTools::GetFilenameName(entry.Item.Value);
   if (this->ExtractSharedLibraryName.find(file)) {
     // If we can guess the soname fairly reliably then assume the
     // library has one.  Otherwise assume the library has no builtin
     // soname.
     std::string soname;
-    if (!cmSystemTools::GuessLibrarySOName(item, soname)) {
-      this->AddSharedLibNoSOName(item);
+    if (!cmSystemTools::GuessLibrarySOName(entry.Item.Value, soname)) {
+      this->AddSharedLibNoSOName(entry);
       return true;
     }
   }
   return false;
 }
 
-void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
+void cmComputeLinkInformation::AddSharedLibNoSOName(LinkEntry const& entry)
 {
   // We have a full path to a shared library with no soname.  We need
   // to ask the linker to locate the item because otherwise the path
   // we give to it will be embedded in the target linked.  Then at
   // runtime the dynamic linker will search for the library using the
   // path instead of just the name.
-  std::string file = cmSystemTools::GetFilenameName(item);
-  this->AddUserItem(file, false);
+  LinkEntry fileEntry{ entry };
+  fileEntry.Item = cmSystemTools::GetFilenameName(entry.Item.Value);
+  this->AddUserItem(fileEntry, false);
 
   // Make sure the link directory ordering will find the library.
-  this->OrderLinkerSearchPath->AddLinkLibrary(item);
+  this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value);
 }
 
-void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
+void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry,
                                                  std::string const& file)
 {
+  std::string const& item = entry.Item.Value;
   // Do not depend on things that do not exist.
   auto i = std::find(this->Depends.begin(), this->Depends.end(), item);
   if (i != this->Depends.end()) {
@@ -1435,7 +1456,9 @@ void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
   // Tell the linker to search for the item and provide the proper
   // path for it.  Do not contribute to any CMP0003 warning (do not
   // put in OldLinkDirItems or OldUserFlagItems).
-  this->AddUserItem(file, false);
+  LinkEntry fileEntry{ entry };
+  fileEntry.Item = file;
+  this->AddUserItem(fileEntry, false);
   this->OrderLinkerSearchPath->AddLinkLibrary(item);
 
   // Produce any needed message.
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 0315540..2edcd5f 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -13,6 +13,7 @@
 
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmComputeLinkDepends.h"
 #include "cmListFileCache.h"
 #include "cmValue.h"
 
@@ -104,10 +105,10 @@ public:
   const cmGeneratorTarget* GetTarget() { return this->Target; }
 
 private:
-  void AddItem(BT<std::string> const& item, const cmGeneratorTarget* tgt,
-               ItemIsObject isObject = ItemIsObject::No);
-  void AddSharedDepItem(BT<std::string> const& item,
-                        cmGeneratorTarget const* tgt);
+  using LinkEntry = cmComputeLinkDepends::LinkEntry;
+
+  void AddItem(LinkEntry const& entry);
+  void AddSharedDepItem(LinkEntry const& entry);
   void AddRuntimeDLL(cmGeneratorTarget const* tgt);
 
   // Output information.
@@ -181,16 +182,15 @@ private:
   std::string NoCaseExpression(std::string const& str);
 
   // Handling of link items.
-  void AddTargetItem(BT<std::string> const& item,
-                     const cmGeneratorTarget* target);
-  void AddFullItem(BT<std::string> const& item, ItemIsObject isObject);
-  bool CheckImplicitDirItem(std::string const& item);
-  void AddUserItem(BT<std::string> const& item, bool pathNotKnown);
-  void AddFrameworkItem(std::string const& item);
+  void AddTargetItem(LinkEntry const& entry);
+  void AddFullItem(LinkEntry const& entry);
+  bool CheckImplicitDirItem(LinkEntry const& entry);
+  void AddUserItem(LinkEntry const& entry, bool pathNotKnown);
+  void AddFrameworkItem(LinkEntry const& entry);
   void DropDirectoryItem(BT<std::string> const& item);
-  bool CheckSharedLibNoSOName(std::string const& item);
-  void AddSharedLibNoSOName(std::string const& item);
-  void HandleBadFullItem(std::string const& item, std::string const& file);
+  bool CheckSharedLibNoSOName(LinkEntry const& entry);
+  void AddSharedLibNoSOName(LinkEntry const& entry);
+  void HandleBadFullItem(LinkEntry const& entry, std::string const& file);
 
   // Framework info.
   void ComputeFrameworkInfo();
-- 
cgit v0.12


From 4b55828a9f2afbc6c37e53f7cb0d4adec0fa73b5 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Mon, 15 Nov 2021 14:27:34 +0100
Subject: cmExpandListWithBacktrace: add handling of empty elements.

---
 Source/cmListFileCache.cxx  | 6 +++---
 Source/cmListFileCache.h    | 5 +++--
 Source/cmLocalGenerator.cxx | 4 ++--
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index 3da266d..b28b282 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -539,11 +539,11 @@ std::ostream& operator<<(std::ostream& os, BT<std::string> const& s)
   return os << s.Value;
 }
 
-std::vector<BT<std::string>> ExpandListWithBacktrace(
-  std::string const& list, cmListFileBacktrace const& bt)
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
+  std::string const& list, cmListFileBacktrace const& bt, bool emptyArgs)
 {
   std::vector<BT<std::string>> result;
-  std::vector<std::string> tmp = cmExpandedList(list);
+  std::vector<std::string> tmp = cmExpandedList(list, emptyArgs);
   result.reserve(tmp.size());
   for (std::string& i : tmp) {
     result.emplace_back(std::move(i), bt);
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h
index 5d45027..58bc57e 100644
--- a/Source/cmListFileCache.h
+++ b/Source/cmListFileCache.h
@@ -230,9 +230,10 @@ public:
   friend bool operator==(T const& l, BTs<T> const& r) { return l == r.Value; }
 };
 
-std::vector<BT<std::string>> ExpandListWithBacktrace(
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
   std::string const& list,
-  cmListFileBacktrace const& bt = cmListFileBacktrace());
+  cmListFileBacktrace const& bt = cmListFileBacktrace(),
+  bool emptyArgs = false);
 
 struct cmListFile
 {
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 2adb232..3976c42 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -3199,7 +3199,7 @@ void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
                                      std::string const& defines_list) const
 {
   std::set<BT<std::string>> tmp;
-  this->AppendDefines(tmp, ExpandListWithBacktrace(defines_list));
+  this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list));
   for (BT<std::string> const& i : tmp) {
     defines.emplace(i.Value);
   }
@@ -3214,7 +3214,7 @@ void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines,
   }
 
   // Expand the list of definitions.
-  this->AppendDefines(defines, ExpandListWithBacktrace(defines_list));
+  this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list));
 }
 
 void cmLocalGenerator::AppendDefines(
-- 
cgit v0.12


From 78dd7d5292cef930b3d435e6901cc3b10ee02513 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Wed, 24 Nov 2021 16:45:22 +0100
Subject: cmRulePlaceholderExpander: add base class for placeholder expansion
 reuse

---
 Source/CMakeLists.txt                |   2 +
 Source/cmPlaceholderExpander.cxx     |  54 ++++++++
 Source/cmPlaceholderExpander.h       |  19 +++
 Source/cmRulePlaceholderExpander.cxx | 245 +++++++++++++++--------------------
 Source/cmRulePlaceholderExpander.h   |  14 +-
 bootstrap                            |   1 +
 6 files changed, 187 insertions(+), 148 deletions(-)
 create mode 100644 Source/cmPlaceholderExpander.cxx
 create mode 100644 Source/cmPlaceholderExpander.h

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index ddcdd7e..d6e0096 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -358,6 +358,8 @@ set(SRCS
   cmLocalCommonGenerator.h
   cmLocalGenerator.cxx
   cmLocalGenerator.h
+  cmPlaceholderExpander.cxx
+  cmPlaceholderExpander.h
   cmRulePlaceholderExpander.cxx
   cmRulePlaceholderExpander.h
   cmLocalUnixMakefileGenerator3.cxx
diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx
new file mode 100644
index 0000000..118017e
--- /dev/null
+++ b/Source/cmPlaceholderExpander.cxx
@@ -0,0 +1,54 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmPlaceholderExpander.h"
+
+#include <cctype>
+
+std::string& cmPlaceholderExpander::ExpandVariables(std::string& s)
+{
+  std::string::size_type start = s.find('<');
+  // no variables to expand
+  if (start == std::string::npos) {
+    return s;
+  }
+  std::string::size_type pos = 0;
+  std::string expandedInput;
+  while (start != std::string::npos && start < s.size() - 2) {
+    std::string::size_type end = s.find('>', start);
+    // if we find a < with no > we are done
+    if (end == std::string::npos) {
+      s = expandedInput;
+      return s;
+    }
+    char c = s[start + 1];
+    // if the next char after the < is not A-Za-z then
+    // skip it and try to find the next < in the string
+    if (!isalpha(c)) {
+      start = s.find('<', start + 1);
+    } else {
+      // extract the var
+      std::string var = s.substr(start + 1, end - start - 1);
+      std::string replace = this->ExpandVariable(var);
+      expandedInput += s.substr(pos, start - pos);
+
+      // Prevent consecutive whitespace in the output if the rule variable
+      // expands to an empty string.
+      bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
+        end + 1 < s.size() && s[end + 1] == ' ';
+      if (consecutive) {
+        expandedInput.pop_back();
+      }
+
+      expandedInput += replace;
+
+      // move to next one
+      start = s.find('<', start + var.size() + 2);
+      pos = end + 1;
+    }
+  }
+  // add the rest of the input
+  expandedInput += s.substr(pos, s.size() - pos);
+  s = expandedInput;
+
+  return s;
+}
diff --git a/Source/cmPlaceholderExpander.h b/Source/cmPlaceholderExpander.h
new file mode 100644
index 0000000..24225cc
--- /dev/null
+++ b/Source/cmPlaceholderExpander.h
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+class cmPlaceholderExpander
+{
+public:
+  virtual ~cmPlaceholderExpander() = default;
+
+  std::string& ExpandVariables(std::string& string);
+
+protected:
+  virtual std::string ExpandVariable(std::string const& variable) = 0;
+};
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 4cee09d..b63d11c 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmRulePlaceholderExpander.h"
 
-#include <cctype>
 #include <utility>
 
 #include "cmOutputConverter.h"
@@ -20,195 +19,194 @@ cmRulePlaceholderExpander::cmRulePlaceholderExpander(
 {
 }
 
-std::string cmRulePlaceholderExpander::ExpandRuleVariable(
-  cmOutputConverter* outputConverter, std::string const& variable,
-  const RuleVariables& replaceValues)
+std::string cmRulePlaceholderExpander::ExpandVariable(
+  std::string const& variable)
 {
-  if (replaceValues.LinkFlags) {
+  if (this->ReplaceValues->LinkFlags) {
     if (variable == "LINK_FLAGS") {
-      return replaceValues.LinkFlags;
+      return this->ReplaceValues->LinkFlags;
     }
   }
-  if (replaceValues.Manifests) {
+  if (this->ReplaceValues->Manifests) {
     if (variable == "MANIFESTS") {
-      return replaceValues.Manifests;
+      return this->ReplaceValues->Manifests;
     }
   }
-  if (replaceValues.Flags) {
+  if (this->ReplaceValues->Flags) {
     if (variable == "FLAGS") {
-      return replaceValues.Flags;
+      return this->ReplaceValues->Flags;
     }
   }
 
-  if (replaceValues.Source) {
+  if (this->ReplaceValues->Source) {
     if (variable == "SOURCE") {
-      return replaceValues.Source;
+      return this->ReplaceValues->Source;
     }
   }
-  if (replaceValues.DynDepFile) {
+  if (this->ReplaceValues->DynDepFile) {
     if (variable == "DYNDEP_FILE") {
-      return replaceValues.DynDepFile;
+      return this->ReplaceValues->DynDepFile;
     }
   }
-  if (replaceValues.PreprocessedSource) {
+  if (this->ReplaceValues->PreprocessedSource) {
     if (variable == "PREPROCESSED_SOURCE") {
-      return replaceValues.PreprocessedSource;
+      return this->ReplaceValues->PreprocessedSource;
     }
   }
-  if (replaceValues.AssemblySource) {
+  if (this->ReplaceValues->AssemblySource) {
     if (variable == "ASSEMBLY_SOURCE") {
-      return replaceValues.AssemblySource;
+      return this->ReplaceValues->AssemblySource;
     }
   }
-  if (replaceValues.Object) {
+  if (this->ReplaceValues->Object) {
     if (variable == "OBJECT") {
-      return replaceValues.Object;
+      return this->ReplaceValues->Object;
     }
   }
-  if (replaceValues.ObjectDir) {
+  if (this->ReplaceValues->ObjectDir) {
     if (variable == "OBJECT_DIR") {
-      return replaceValues.ObjectDir;
+      return this->ReplaceValues->ObjectDir;
     }
   }
-  if (replaceValues.ObjectFileDir) {
+  if (this->ReplaceValues->ObjectFileDir) {
     if (variable == "OBJECT_FILE_DIR") {
-      return replaceValues.ObjectFileDir;
+      return this->ReplaceValues->ObjectFileDir;
     }
   }
-  if (replaceValues.Objects) {
+  if (this->ReplaceValues->Objects) {
     if (variable == "OBJECTS") {
-      return replaceValues.Objects;
+      return this->ReplaceValues->Objects;
     }
   }
-  if (replaceValues.ObjectsQuoted) {
+  if (this->ReplaceValues->ObjectsQuoted) {
     if (variable == "OBJECTS_QUOTED") {
-      return replaceValues.ObjectsQuoted;
+      return this->ReplaceValues->ObjectsQuoted;
     }
   }
-  if (replaceValues.CudaCompileMode) {
+  if (this->ReplaceValues->CudaCompileMode) {
     if (variable == "CUDA_COMPILE_MODE") {
-      return replaceValues.CudaCompileMode;
+      return this->ReplaceValues->CudaCompileMode;
     }
   }
-  if (replaceValues.AIXExports) {
+  if (this->ReplaceValues->AIXExports) {
     if (variable == "AIX_EXPORTS") {
-      return replaceValues.AIXExports;
+      return this->ReplaceValues->AIXExports;
     }
   }
-  if (replaceValues.ISPCHeader) {
+  if (this->ReplaceValues->ISPCHeader) {
     if (variable == "ISPC_HEADER") {
-      return replaceValues.ISPCHeader;
+      return this->ReplaceValues->ISPCHeader;
     }
   }
-  if (replaceValues.Defines && variable == "DEFINES") {
-    return replaceValues.Defines;
+  if (this->ReplaceValues->Defines && variable == "DEFINES") {
+    return this->ReplaceValues->Defines;
   }
-  if (replaceValues.Includes && variable == "INCLUDES") {
-    return replaceValues.Includes;
+  if (this->ReplaceValues->Includes && variable == "INCLUDES") {
+    return this->ReplaceValues->Includes;
   }
-  if (replaceValues.SwiftLibraryName) {
+  if (this->ReplaceValues->SwiftLibraryName) {
     if (variable == "SWIFT_LIBRARY_NAME") {
-      return replaceValues.SwiftLibraryName;
+      return this->ReplaceValues->SwiftLibraryName;
     }
   }
-  if (replaceValues.SwiftModule) {
+  if (this->ReplaceValues->SwiftModule) {
     if (variable == "SWIFT_MODULE") {
-      return replaceValues.SwiftModule;
+      return this->ReplaceValues->SwiftModule;
     }
   }
-  if (replaceValues.SwiftModuleName) {
+  if (this->ReplaceValues->SwiftModuleName) {
     if (variable == "SWIFT_MODULE_NAME") {
-      return replaceValues.SwiftModuleName;
+      return this->ReplaceValues->SwiftModuleName;
     }
   }
-  if (replaceValues.SwiftOutputFileMap) {
+  if (this->ReplaceValues->SwiftOutputFileMap) {
     if (variable == "SWIFT_OUTPUT_FILE_MAP") {
-      return replaceValues.SwiftOutputFileMap;
+      return this->ReplaceValues->SwiftOutputFileMap;
     }
   }
-  if (replaceValues.SwiftSources) {
+  if (this->ReplaceValues->SwiftSources) {
     if (variable == "SWIFT_SOURCES") {
-      return replaceValues.SwiftSources;
+      return this->ReplaceValues->SwiftSources;
     }
   }
-  if (replaceValues.TargetPDB) {
+  if (this->ReplaceValues->TargetPDB) {
     if (variable == "TARGET_PDB") {
-      return replaceValues.TargetPDB;
+      return this->ReplaceValues->TargetPDB;
     }
   }
-  if (replaceValues.TargetCompilePDB) {
+  if (this->ReplaceValues->TargetCompilePDB) {
     if (variable == "TARGET_COMPILE_PDB") {
-      return replaceValues.TargetCompilePDB;
+      return this->ReplaceValues->TargetCompilePDB;
     }
   }
-  if (replaceValues.DependencyFile) {
+  if (this->ReplaceValues->DependencyFile) {
     if (variable == "DEP_FILE") {
-      return replaceValues.DependencyFile;
+      return this->ReplaceValues->DependencyFile;
     }
   }
-  if (replaceValues.DependencyTarget) {
+  if (this->ReplaceValues->DependencyTarget) {
     if (variable == "DEP_TARGET") {
-      return replaceValues.DependencyTarget;
+      return this->ReplaceValues->DependencyTarget;
     }
   }
-  if (replaceValues.Fatbinary) {
+  if (this->ReplaceValues->Fatbinary) {
     if (variable == "FATBINARY") {
-      return replaceValues.Fatbinary;
+      return this->ReplaceValues->Fatbinary;
     }
   }
-  if (replaceValues.RegisterFile) {
+  if (this->ReplaceValues->RegisterFile) {
     if (variable == "REGISTER_FILE") {
-      return replaceValues.RegisterFile;
+      return this->ReplaceValues->RegisterFile;
     }
   }
 
-  if (replaceValues.Target) {
+  if (this->ReplaceValues->Target) {
     if (variable == "TARGET_QUOTED") {
-      std::string targetQuoted = replaceValues.Target;
+      std::string targetQuoted = this->ReplaceValues->Target;
       if (!targetQuoted.empty() && targetQuoted.front() != '\"') {
         targetQuoted = '\"';
-        targetQuoted += replaceValues.Target;
+        targetQuoted += this->ReplaceValues->Target;
         targetQuoted += '\"';
       }
       return targetQuoted;
     }
     if (variable == "TARGET_UNQUOTED") {
-      std::string unquoted = replaceValues.Target;
+      std::string unquoted = this->ReplaceValues->Target;
       std::string::size_type sz = unquoted.size();
       if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') {
         unquoted = unquoted.substr(1, sz - 2);
       }
       return unquoted;
     }
-    if (replaceValues.LanguageCompileFlags) {
+    if (this->ReplaceValues->LanguageCompileFlags) {
       if (variable == "LANGUAGE_COMPILE_FLAGS") {
-        return replaceValues.LanguageCompileFlags;
+        return this->ReplaceValues->LanguageCompileFlags;
       }
     }
-    if (replaceValues.Target) {
+    if (this->ReplaceValues->Target) {
       if (variable == "TARGET") {
-        return replaceValues.Target;
+        return this->ReplaceValues->Target;
       }
     }
     if (variable == "TARGET_IMPLIB") {
       return this->TargetImpLib;
     }
     if (variable == "TARGET_VERSION_MAJOR") {
-      if (replaceValues.TargetVersionMajor) {
-        return replaceValues.TargetVersionMajor;
+      if (this->ReplaceValues->TargetVersionMajor) {
+        return this->ReplaceValues->TargetVersionMajor;
       }
       return "0";
     }
     if (variable == "TARGET_VERSION_MINOR") {
-      if (replaceValues.TargetVersionMinor) {
-        return replaceValues.TargetVersionMinor;
+      if (this->ReplaceValues->TargetVersionMinor) {
+        return this->ReplaceValues->TargetVersionMinor;
       }
       return "0";
     }
-    if (replaceValues.Target) {
+    if (this->ReplaceValues->Target) {
       if (variable == "TARGET_BASE") {
         // Strip the last extension off the target name.
-        std::string targetBase = replaceValues.Target;
+        std::string targetBase = this->ReplaceValues->Target;
         std::string::size_type pos = targetBase.rfind('.');
         if (pos != std::string::npos) {
           return targetBase.substr(0, pos);
@@ -220,54 +218,54 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
   if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
       variable == "TARGET_INSTALLNAME_DIR") {
     // All these variables depend on TargetSOName
-    if (replaceValues.TargetSOName) {
+    if (this->ReplaceValues->TargetSOName) {
       if (variable == "TARGET_SONAME") {
-        return replaceValues.TargetSOName;
+        return this->ReplaceValues->TargetSOName;
       }
-      if (variable == "SONAME_FLAG" && replaceValues.SONameFlag) {
-        return replaceValues.SONameFlag;
+      if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) {
+        return this->ReplaceValues->SONameFlag;
       }
-      if (replaceValues.TargetInstallNameDir &&
+      if (this->ReplaceValues->TargetInstallNameDir &&
           variable == "TARGET_INSTALLNAME_DIR") {
-        return replaceValues.TargetInstallNameDir;
+        return this->ReplaceValues->TargetInstallNameDir;
       }
     }
     return "";
   }
-  if (replaceValues.LinkLibraries) {
+  if (this->ReplaceValues->LinkLibraries) {
     if (variable == "LINK_LIBRARIES") {
-      return replaceValues.LinkLibraries;
+      return this->ReplaceValues->LinkLibraries;
     }
   }
-  if (replaceValues.Language) {
+  if (this->ReplaceValues->Language) {
     if (variable == "LANGUAGE") {
-      return replaceValues.Language;
+      return this->ReplaceValues->Language;
     }
   }
-  if (replaceValues.CMTargetName) {
+  if (this->ReplaceValues->CMTargetName) {
     if (variable == "TARGET_NAME") {
-      return replaceValues.CMTargetName;
+      return this->ReplaceValues->CMTargetName;
     }
   }
-  if (replaceValues.CMTargetType) {
+  if (this->ReplaceValues->CMTargetType) {
     if (variable == "TARGET_TYPE") {
-      return replaceValues.CMTargetType;
+      return this->ReplaceValues->CMTargetType;
     }
   }
-  if (replaceValues.Output) {
+  if (this->ReplaceValues->Output) {
     if (variable == "OUTPUT") {
-      return replaceValues.Output;
+      return this->ReplaceValues->Output;
     }
   }
   if (variable == "CMAKE_COMMAND") {
-    return outputConverter->ConvertToOutputFormat(
+    return this->OutputConverter->ConvertToOutputFormat(
       cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
   }
 
   auto compIt = this->Compilers.find(variable);
 
   if (compIt != this->Compilers.end()) {
-    std::string ret = outputConverter->ConvertToOutputForExisting(
+    std::string ret = this->OutputConverter->ConvertToOutputForExisting(
       this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]);
     std::string const& compilerArg1 =
       this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"];
@@ -286,11 +284,12 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
       this->VariableMappings["CMAKE_" + compIt->second +
                              "_COMPILE_OPTIONS_SYSROOT"];
 
-    if (compIt->second == replaceValues.Language && replaceValues.Launcher) {
+    if (compIt->second == this->ReplaceValues->Language &&
+        this->ReplaceValues->Launcher) {
       // Add launcher as part of expansion so that it always appears
       // immediately before the command itself, regardless of whether the
       // overall rule template contains other content at the front.
-      ret = cmStrCat(replaceValues.Launcher, " ", ret);
+      ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
     }
 
     // if there are required arguments to the compiler add it
@@ -308,13 +307,14 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
         !compilerOptionExternalToolchain.empty()) {
       ret += " ";
       ret += compilerOptionExternalToolchain;
-      ret += outputConverter->EscapeForShell(compilerExternalToolchain, true);
+      ret +=
+        this->OutputConverter->EscapeForShell(compilerExternalToolchain, true);
     }
     std::string sysroot;
     // Some platforms may use separate sysroots for compiling and linking.
     // If we detect link flags, then we pass the link sysroot instead.
     // FIXME: Use a more robust way to detect link line expansion.
-    if (replaceValues.LinkFlags) {
+    if (this->ReplaceValues->LinkFlags) {
       sysroot = this->LinkerSysroot;
     } else {
       sysroot = this->CompilerSysroot;
@@ -322,7 +322,7 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
     if (!sysroot.empty() && !compilerOptionSysroot.empty()) {
       ret += " ";
       ret += compilerOptionSysroot;
-      ret += outputConverter->EscapeForShell(sysroot, true);
+      ret += this->OutputConverter->EscapeForShell(sysroot, true);
     }
     return ret;
   }
@@ -331,13 +331,13 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
   if (mapIt != this->VariableMappings.end()) {
     if (variable.find("_FLAG") == std::string::npos) {
       std::string ret =
-        outputConverter->ConvertToOutputForExisting(mapIt->second);
+        this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
 
-      if (replaceValues.Launcher && variable == "CMAKE_LINKER") {
+      if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") {
         // Add launcher as part of expansion so that it always appears
         // immediately before the command itself, regardless of whether the
         // overall rule template contains other content at the front.
-        ret = cmStrCat(replaceValues.Launcher, " ", ret);
+        ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
       }
 
       return ret;
@@ -351,47 +351,8 @@ void cmRulePlaceholderExpander::ExpandRuleVariables(
   cmOutputConverter* outputConverter, std::string& s,
   const RuleVariables& replaceValues)
 {
-  std::string::size_type start = s.find('<');
-  // no variables to expand
-  if (start == std::string::npos) {
-    return;
-  }
-  std::string::size_type pos = 0;
-  std::string expandedInput;
-  while (start != std::string::npos && start < s.size() - 2) {
-    std::string::size_type end = s.find('>', start);
-    // if we find a < with no > we are done
-    if (end == std::string::npos) {
-      return;
-    }
-    char c = s[start + 1];
-    // if the next char after the < is not A-Za-z then
-    // skip it and try to find the next < in the string
-    if (!isalpha(c)) {
-      start = s.find('<', start + 1);
-    } else {
-      // extract the var
-      std::string var = s.substr(start + 1, end - start - 1);
-      std::string replace =
-        this->ExpandRuleVariable(outputConverter, var, replaceValues);
-      expandedInput += s.substr(pos, start - pos);
-
-      // Prevent consecutive whitespace in the output if the rule variable
-      // expands to an empty string.
-      bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
-        end + 1 < s.size() && s[end + 1] == ' ';
-      if (consecutive) {
-        expandedInput.pop_back();
-      }
+  this->OutputConverter = outputConverter;
+  this->ReplaceValues = &replaceValues;
 
-      expandedInput += replace;
-
-      // move to next one
-      start = s.find('<', start + var.size() + 2);
-      pos = end + 1;
-    }
-  }
-  // add the rest of the input
-  expandedInput += s.substr(pos, s.size() - pos);
-  s = expandedInput;
+  this->ExpandVariables(s);
 }
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 852954f..23ec405 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -8,9 +8,11 @@
 #include <map>
 #include <string>
 
+#include "cmPlaceholderExpander.h"
+
 class cmOutputConverter;
 
-class cmRulePlaceholderExpander
+class cmRulePlaceholderExpander : public cmPlaceholderExpander
 {
 public:
   cmRulePlaceholderExpander(
@@ -76,16 +78,16 @@ public:
                            std::string& string,
                            const RuleVariables& replaceValues);
 
-  // Expand rule variables in a single string
-  std::string ExpandRuleVariable(cmOutputConverter* outputConverter,
-                                 std::string const& variable,
-                                 const RuleVariables& replaceValues);
-
 private:
+  std::string ExpandVariable(std::string const& variable) override;
+
   std::string TargetImpLib;
 
   std::map<std::string, std::string> Compilers;
   std::map<std::string, std::string> VariableMappings;
   std::string CompilerSysroot;
   std::string LinkerSysroot;
+
+  cmOutputConverter* OutputConverter = nullptr;
+  RuleVariables const* ReplaceValues = nullptr;
 };
diff --git a/bootstrap b/bootstrap
index e0791d5..98b5959 100755
--- a/bootstrap
+++ b/bootstrap
@@ -442,6 +442,7 @@ CMAKE_CXX_SOURCES="\
   cmGccDepfileLexerHelper \
   cmGccDepfileReader \
   cmReturnCommand \
+  cmPlaceholderExpander \
   cmRulePlaceholderExpander \
   cmRuntimeDependencyArchive \
   cmScriptGenerator \
-- 
cgit v0.12


From 42965799b4747ab1e0afa6546be13444f68c1987 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Mon, 1 Nov 2021 15:27:05 +0100
Subject: Genex: Add $<LINK_LIBRARY:...>

This generator expression offers the capability, for the link step, to
decorate libraries with prefix/suffix flags and/or adding any specific flag for each
library.

Fixes: #22812, #18751, #20078, #22703
---
 Help/manual/cmake-generator-expressions.7.rst      |  69 ++++
 Help/manual/cmake-variables.7.rst                  |   4 +
 Help/release/dev/Genex-LINK_LIBRARY.rst            |   8 +
 Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst    |  19 +
 .../CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst    |  13 +
 Help/variable/CMAKE_LINK_USING_FEATURE.rst         |  23 ++
 Help/variable/CMAKE_LINK_USING_FEATURE.txt         | 111 ++++++
 .../CMAKE_LINK_USING_FEATURE_SUPPORTED.rst         |  14 +
 Source/cmComputeLinkDepends.cxx                    | 177 ++++++++--
 Source/cmComputeLinkDepends.h                      |  13 +-
 Source/cmComputeLinkInformation.cxx                | 387 +++++++++++++++++++--
 Source/cmComputeLinkInformation.h                  |  65 +++-
 Source/cmGeneratorExpressionNode.cxx               |  63 ++++
 Source/cmGeneratorTarget.cxx                       |  15 +-
 Source/cmGeneratorTarget.h                         |   3 +-
 Source/cmGlobalXCodeGenerator.cxx                  |   4 +-
 Source/cmLinkLibrariesCommand.cxx                  |   2 +
 Source/cmLinkLineComputer.cxx                      |  10 +-
 Source/cmLinkLineDeviceComputer.cxx                |   8 +-
 Source/cmLocalVisualStudio7Generator.cxx           |   4 +-
 Source/cmMakefile.cxx                              |  25 ++
 Source/cmMakefile.h                                |   1 +
 Source/cmTarget.cxx                                | 140 +++++---
 Source/cmTargetLinkLibrariesCommand.cxx            |   3 +
 Source/cmVisualStudio10TargetGenerator.cxx         |   6 +-
 Tests/RunCMake/CMakeLists.txt                      |  12 +
 Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt   |   3 +
 .../RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake |  25 ++
 .../add_custom_command-result.txt                  |   1 +
 .../add_custom_command-stderr.txt                  |   9 +
 .../GenEx-LINK_LIBRARY/add_custom_command.cmake    |   4 +
 .../add_custom_target-result.txt                   |   1 +
 .../add_custom_target-stderr.txt                   |   9 +
 .../GenEx-LINK_LIBRARY/add_custom_target.cmake     |   3 +
 .../GenEx-LINK_LIBRARY/add_link_options-result.txt |   1 +
 .../GenEx-LINK_LIBRARY/add_link_options-stderr.txt |   9 +
 .../GenEx-LINK_LIBRARY/add_link_options.cmake      |   5 +
 .../GenEx-LINK_LIBRARY/bad-feature1-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature1-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake |   6 +
 .../GenEx-LINK_LIBRARY/bad-feature2-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature2-stderr.txt     |   5 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake |   8 +
 .../GenEx-LINK_LIBRARY/bad-feature3-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature3-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake |   9 +
 .../GenEx-LINK_LIBRARY/bad-feature4-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature4-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake |   9 +
 .../GenEx-LINK_LIBRARY/bad-feature5-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature5-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake |   9 +
 .../GenEx-LINK_LIBRARY/bad-feature6-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature6-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake |   9 +
 .../GenEx-LINK_LIBRARY/bad-feature7-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/bad-feature7-stderr.txt     |   6 +
 .../RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake |   9 +
 .../GenEx-LINK_LIBRARY/compatible-features.cmake   |  15 +
 .../GenEx-LINK_LIBRARY/empty-arguments-result.txt  |   1 +
 .../GenEx-LINK_LIBRARY/empty-arguments-stderr.txt  |   8 +
 .../GenEx-LINK_LIBRARY/empty-arguments.cmake       |   4 +
 Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c          |   0
 .../feature-not-supported-result.txt               |   1 +
 .../feature-not-supported-stderr.txt               |   5 +
 .../GenEx-LINK_LIBRARY/feature-not-supported.cmake |   9 +
 .../forbidden-arguments-result.txt                 |   1 +
 .../forbidden-arguments-stderr.txt                 |  16 +
 .../GenEx-LINK_LIBRARY/forbidden-arguments.cmake   |   6 +
 .../incompatible-features-result.txt               |   1 +
 .../incompatible-features-stderr.txt               |   6 +
 .../GenEx-LINK_LIBRARY/incompatible-features.cmake |  15 +
 .../GenEx-LINK_LIBRARY/library-ignored-stderr.txt  |  14 +
 .../GenEx-LINK_LIBRARY/library-ignored.cmake       |  15 +
 .../GenEx-LINK_LIBRARY/link_directories-result.txt |   1 +
 .../GenEx-LINK_LIBRARY/link_directories-stderr.txt |   9 +
 .../GenEx-LINK_LIBRARY/link_directories.cmake      |   5 +
 .../nested-compatible-features.cmake               |  11 +
 .../nested-incompatible-features-result.txt        |   1 +
 .../nested-incompatible-features-stderr.txt        |   8 +
 .../nested-incompatible-features.cmake             |  14 +
 .../GenEx-LINK_LIBRARY/no-arguments-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/no-arguments-stderr.txt     |   8 +
 .../RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake |   4 +
 .../GenEx-LINK_LIBRARY/only-targets-result.txt     |   1 +
 .../GenEx-LINK_LIBRARY/only-targets-stderr.txt     |  13 +
 .../RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake |  16 +
 .../target_link_directories-result.txt             |   1 +
 .../target_link_directories-stderr.txt             |   9 +
 .../target_link_directories.cmake                  |   4 +
 .../target_link_options-result.txt                 |   1 +
 .../target_link_options-stderr.txt                 |   9 +
 .../GenEx-LINK_LIBRARY/target_link_options.cmake   |   4 +
 .../CMakeLists.txt                                 |   3 +
 .../LINK_LIBRARY-group1-check.cmake                |   4 +
 .../LINK_LIBRARY-group1-result.txt                 |   1 +
 .../LINK_LIBRARY-group2-check.cmake                |   4 +
 .../LINK_LIBRARY-group2-result.txt                 |   1 +
 .../LINK_LIBRARY-link-items1-check.cmake           |   4 +
 .../LINK_LIBRARY-link-items1-result.txt            |   1 +
 .../LINK_LIBRARY-link-items2-check.cmake           |   4 +
 .../LINK_LIBRARY-link-items2-result.txt            |   1 +
 .../LINK_LIBRARY-link-items3-check.cmake           |   4 +
 .../LINK_LIBRARY-link-items3-result.txt            |   1 +
 .../LINK_LIBRARY-link-items4-check.cmake           |   4 +
 .../LINK_LIBRARY-link-items4-result.txt            |   1 +
 .../LINK_LIBRARY-mix-features1-check.cmake         |   4 +
 .../LINK_LIBRARY-mix-features1-result.txt          |   1 +
 .../LINK_LIBRARY-mix-features2-check.cmake         |   4 +
 .../LINK_LIBRARY-mix-features2-result.txt          |   1 +
 .../LINK_LIBRARY-mix-features3-check.cmake         |   4 +
 .../LINK_LIBRARY-mix-features3-result.txt          |   1 +
 .../LINK_LIBRARY-nested-feature1-check.cmake       |   4 +
 .../LINK_LIBRARY-nested-feature1-result.txt        |   1 +
 .../LINK_LIBRARY-nested-feature2-check.cmake       |   4 +
 .../LINK_LIBRARY-nested-feature2-result.txt        |   1 +
 .../LINK_LIBRARY-simple1-check.cmake               |   4 +
 .../LINK_LIBRARY-simple1-result.txt                |   1 +
 .../LINK_LIBRARY-simple2-check.cmake               |   4 +
 .../LINK_LIBRARY-simple2-result.txt                |   1 +
 .../LINK_LIBRARY.cmake                             |  81 +++++
 .../RunCMakeTest.cmake                             |  73 ++++
 .../target_link_libraries-LINK_LIBRARY/base.c      |   9 +
 .../imported-target-result.txt                     |   1 +
 .../imported-target-stdout.txt                     |  18 +
 .../imported-target.cmake                          |  18 +
 .../target_link_libraries-LINK_LIBRARY/lib.c       |  15 +
 .../target_link_libraries-LINK_LIBRARY/main.c      |  18 +
 .../target_link_libraries-LINK_LIBRARY/unref.c     |   8 +
 .../weak_library.cmake                             |  20 ++
 .../whole_archive.cmake                            |  34 ++
 131 files changed, 1825 insertions(+), 151 deletions(-)
 create mode 100644 Help/release/dev/Genex-LINK_LIBRARY.rst
 create mode 100644 Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
 create mode 100644 Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst
 create mode 100644 Help/variable/CMAKE_LINK_USING_FEATURE.rst
 create mode 100644 Help/variable/CMAKE_LINK_USING_FEATURE.txt
 create mode 100644 Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake

diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index df13dd0..b67d479 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1113,6 +1113,75 @@ Output-Related Expressions
   property, perhaps via the :command:`target_link_libraries` command,
   to specify private link dependencies without other usage requirements.
 
+.. genex:: $<LINK_LIBRARY:feature,library-list>
+
+  .. versionadded:: 3.24
+
+  Manage how libraries are specified during the link step.
+  This expression may be used to specify how to link libraries in a target.
+  For example:
+
+  .. code-block:: cmake
+
+    add_library(lib1 STATIC ...)
+    add_library(lib2 ...)
+    target_link_libraries(lib2 PRIVATE $<LINK_LIBRARY:whole_archive,lib1>)
+
+  This specify to use the ``lib1`` target with feature ``whole_archive`` for
+  linking target ``lib2``. The feature must have be defined by
+  :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable or, if
+  :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false,
+  by :variable:`CMAKE_LINK_USING_<FEATURE>` variable.
+
+  .. note::
+
+    The evaluation of this generator expression will use, for the following
+    variables, the values defined at the level of the creation of the target:
+
+    * :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED`
+    * :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`
+    * :variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`
+    * :variable:`CMAKE_LINK_USING_<FEATURE>`
+
+  This expression can only be used to specify link libraries (i.e. part of
+  :command:`link_libraries` or :command:`target_link_libraries` commands and
+  :prop_tgt:`LINK_LIBRARIES` or :prop_tgt:`INTERFACE_LINK_LIBRARIES` target
+  properties).
+
+  .. note::
+
+    If this expression appears in the :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+    property of a target, it will be included in the imported target generated
+    by :command:`install(EXPORT)` command. It is the responsibility of the
+    environment consuming this import to define the link feature used by this
+    expression.
+
+  The ``library-list`` argument can hold CMake targets or external libraries.
+  Any ``CMake`` target of type :ref:`OBJECT <Object Libraries>` or
+  :ref:`INTERFACE <Interface Libraries>` will be ignored by this expression and
+  will be handled in the standard way.
+
+  Each target or external library involved in the link step must have only one
+  kind of feature (the absence of feature is also incompatible with any
+  feature). For example:
+
+  .. code-block:: cmake
+
+    add_library(lib1 ...)
+
+    add_library(lib2 ...)
+    target_link_libraries(lib2 PUBLIC $<LINK_LIBRARY:feature1,lib1>)
+
+    add_library(lib3 ...)
+    target_link_libraries(lib3 PRIVATE lib1 lib2)
+    # an error will be raised here because lib1 has two different features
+
+  .. note::
+
+    This expression does not guarantee that the list of specified libraries
+    will be kept grouped. So, constructs like ``start-group`` and
+    ``end-group``, as supported by ``GNU ld``, cannot be used.
+
 .. genex:: $<INSTALL_INTERFACE:...>
 
   Content of ``...`` when the property is exported using :command:`install(EXPORT)`,
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 59566b5..86e4d4c 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -440,6 +440,8 @@ Variables that Control the Build
    /variable/CMAKE_LANG_LINKER_LAUNCHER
    /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
+   /variable/CMAKE_LANG_LINK_USING_FEATURE
+   /variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
@@ -450,6 +452,8 @@ Variables that Control the Build
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG
+   /variable/CMAKE_LINK_USING_FEATURE
+   /variable/CMAKE_LINK_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_WHAT_YOU_USE
    /variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
    /variable/CMAKE_MACOSX_BUNDLE
diff --git a/Help/release/dev/Genex-LINK_LIBRARY.rst b/Help/release/dev/Genex-LINK_LIBRARY.rst
new file mode 100644
index 0000000..6b87a4f
--- /dev/null
+++ b/Help/release/dev/Genex-LINK_LIBRARY.rst
@@ -0,0 +1,8 @@
+Genex-LINK_LIBRARY
+------------------
+
+* The :genex:`LINK_LIBRARY` generator expression was added to manage how
+  libraries are specified during the link step. The variables
+  :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` and
+  :variable:`CMAKE_LINK_USING_<FEATURE>` are used to define features usable by
+  the :genex:`LINK_LIBRARY` generator expression.
diff --git a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
new file mode 100644
index 0000000..ff8b3d9
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
@@ -0,0 +1,19 @@
+CMAKE_<LANG>_LINK_USING_<FEATURE>
+---------------------------------
+
+.. versionadded:: 3.24
+
+This variable defines, for the specified ``<FEATURE>`` and the linker language
+``<LANG>``, the expression expected by the linker when libraries are specified
+using :genex:`LINK_LIBRARY` generator expression.
+
+.. note::
+
+  Feature names defined in all uppercase are reserved to CMake.
+
+See also the associated variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` and
+:variable:`CMAKE_LINK_USING_<FEATURE>` variable for the definition of features
+independent from the link language.
+
+.. include:: CMAKE_LINK_USING_FEATURE.txt
diff --git a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst
new file mode 100644
index 0000000..5794b15
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst
@@ -0,0 +1,13 @@
+CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED
+-------------------------------------------
+
+.. versionadded:: 3.24
+
+Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`, is supported for the linker
+language ``<LANG>``.
+
+.. note::
+
+  This variable is evaluated before the more generic variable
+  :variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`.
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
new file mode 100644
index 0000000..3d94461
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
@@ -0,0 +1,23 @@
+CMAKE_LINK_USING_<FEATURE>
+--------------------------
+
+.. versionadded:: 3.24
+
+This variable defines, for the specified ``FEATURE``, the expression expected
+by the linker, regardless the linker language, when libraries are specified
+using :genex:`LINK_LIBRARY` generator expression.
+
+.. note::
+
+  Feature names defined in all uppercase are reserved to CMake.
+
+See also the associated variable
+:variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED` and
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable for the definition of
+features dependent from the link language.
+
+This variable will be used by :genex:`LINK_LIBRARY` generator expression if,
+for the linker language, the variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false or not set.
+
+.. include:: CMAKE_LINK_USING_FEATURE.txt
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE.txt b/Help/variable/CMAKE_LINK_USING_FEATURE.txt
new file mode 100644
index 0000000..92fc92d
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE.txt
@@ -0,0 +1,111 @@
+
+It can contain one or three elements.
+
+::
+
+  [<PREFIX>] <LIBRARY_EXPRESSION> [<SUFFIX>]
+
+When ``<PREFIX>`` and/or ``<SUFFIX>`` are specified, they encapsulate the list
+of libraries.
+
+.. note::
+
+  Even if ``<PREFIX>`` and ``<SUFFIX>`` are specified, there is not guarantee
+  that the list of specified libraries, as part of :genex:`LINK_LIBRARY`
+  generator expression, will be kept grouped. So, constructs like
+  ``start-group`` and ``end-group``, as supported by ``GNU ld``, cannot be
+  used.
+
+``<LIBRARY_EXPRESSION>`` is used to specify the decoration for each
+library. For that purpose, the patterns ``<LIBRARY>``, ``<LINK_ITEM>``, and
+``<LIB_ITEM>`` are available:
+
+* ``<LIBRARY>`` is expanded to the library as computed by CMake.
+* ``<LINK_ITEM>`` is expanded to the same expression as if the library was
+  specified in the standard way.
+* ``<LIB_ITEM>`` is equivalent to ``<LIBRARY>`` for CMake targets and is
+  expanded to the item specified by the user for external libraries.
+
+Moreover, it is possible to have different decorations for paths (CMake targets
+and external libraries specified with absolute paths) and other items specified
+by name. For that purpose, ``PATH{}`` and ``NAME{}`` wrappers can be used.
+
+For all three elements of this variable, the ``LINKER:`` prefix can be used:
+
+  .. include:: ../command/LINK_OPTIONS_LINKER.txt
+    :start-line: 3
+
+Examples
+^^^^^^^^
+
+Loading a whole static library
+""""""""""""""""""""""""""""""
+
+A common need is the capability to load a whole static library. This capability
+is offered by various environments but with a specific syntax:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED TRUE)
+  if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+    set(CMAKE_C_LINK_USING_whole_archive "-force_load <LIB_ITEM>")
+  elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU"
+         AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    set(CMAKE_C_LINK_USING_whole_archive "LINKER:--push-state,--whole-archive"
+                                         "<LINK_ITEM>"
+                                         "LINKER:--pop-state")
+  elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+    set(CMAKE_C_LINK_USING_whole_archive "/WHOLEARCHIVE:<LIBRARY>")
+  else()
+    # feature not yet supported for the other environments
+    set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED FALSE)
+  endif()
+
+  add_library(lib1 STATIC ...)
+
+  add_library(lib2 SHARED ...)
+  if(CMAKE_C_LINK_USING_whole_archive_SUPPORTED)
+    target_link_libraries(lib2 PRIVATE
+      $<LINK_LIBRARY:whole_archive,lib1,$<IF:$<LINK_LANG_AND_ID:C,Clang>,libexternal.a,external>>)
+  else()
+    target_link_libraries(lib2 PRIVATE lib1 external)
+  endif()
+
+CMake will generate the following link expressions:
+
+* ``Clang``: ``-force_load /path/to/lib1.a -force_load libexternal.a``
+* ``GNU``: ``-Wl,--whole-archive /path/to/lib1.a -lexternal -Wl,--no-whole-archive``
+* ``MSVC``: ``/WHOLEARCHIVE:/path/to/lib1.lib /WHOLEARCHIVE:external.lib``
+
+CMake will ensure, when possible, that ``<PREFIX>`` and ``<SUFFIX>`` are
+not repeated for each library.
+
+In case of ``Clang``, the pattern ``<LIB_ITEM>`` is used because we need to
+specify the library as defined by the user, not the name computed by CMake
+(in that case ``external``).
+
+Linking a library as weak
+"""""""""""""""""""""""""
+
+On MacOS, it is possible to link a library in weak mode (the library and all
+references are marked as weak imports), but different flags must be used for a
+library specified by path and by name. This constraint by be solved by using
+``PATH{}`` and ``NAME{}`` wrappers:
+
+.. code-block:: cmake
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+    set(CMAKE_LINK_USING_weak_library "PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}")
+    set(CMAKE_LINK_USING_weak_library_SUPPORTED TRUE)
+  endif()
+
+  add_library(lib SHARED ...)
+  add_executable(main ...)
+  if(CMAKE_LINK_USING_weak_library_SUPPORTED)
+    target_link_libraries(main PRIVATE $<LINK_LIBRARY:weak_library,lib,external>)
+  else()
+    target_link_libraries(main PRIVATE lib external)
+  endif()
+
+CMake will generate the following link expression:
+``-weak_library /path/to/lib -Xlinker -weak-lexternal``
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst b/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst
new file mode 100644
index 0000000..31c3108
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst
@@ -0,0 +1,14 @@
+CMAKE_LINK_USING_<FEATURE>_SUPPORTED
+------------------------------------
+
+.. versionadded:: 3.24
+
+Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
+:variable:`CMAKE_LINK_USING_<FEATURE>`, is supported regardless the linker
+language.
+
+.. note::
+
+  This variable is evaluated if, and only if, the variable
+  :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` evaluates to
+  ``FALSE``.
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 370ddff..c3367ac 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include "cmComputeComponentGraph.h"
 #include "cmGeneratorTarget.h"
@@ -18,6 +19,7 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -174,8 +176,33 @@ items that we know the linker will re-use automatically (shared libs).
 
 */
 
+namespace {
+const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
+const auto LL_END = "</LINK_LIBRARY:"_s;
+
+inline std::string ExtractFeature(std::string const& item)
+{
+  return item.substr(LL_BEGIN.length(),
+                     item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
+}
+
+bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
+                        std::string const& feature)
+{
+  auto featureSupported =
+    cmStrCat("CMAKE_", linkLanguage, "_LINK_USING_", feature, "_SUPPORTED");
+  if (makefile->GetDefinition(featureSupported).IsOn()) {
+    return true;
+  }
+
+  featureSupported = cmStrCat("CMAKE_LINK_USING_", feature, "_SUPPORTED");
+  return makefile->GetDefinition(featureSupported).IsOn();
+}
+}
+
 cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
-                                           const std::string& config)
+                                           const std::string& config,
+                                           const std::string& linkLanguage)
 {
   // Store context information.
   this->Target = target;
@@ -183,6 +210,7 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
   this->GlobalGenerator =
     this->Target->GetLocalGenerator()->GetGlobalGenerator();
   this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
+  this->LinkLanguage = linkLanguage;
 
   // The configuration being linked.
   this->HasConfig = !config.empty();
@@ -249,7 +277,7 @@ cmComputeLinkDepends::Compute()
   }
 
   // Compute the final ordering.
-  this->OrderLinkEntires();
+  this->OrderLinkEntries();
 
   // Compute the final set of link entries.
   // Iterate in reverse order so we can keep only the last occurrence
@@ -281,32 +309,33 @@ cmComputeLinkDepends::Compute()
   return this->FinalLinkEntries;
 }
 
-std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
-  cmLinkItem const& item)
+std::pair<std::map<cmLinkItem, int>::iterator, bool>
+cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
 {
   std::map<cmLinkItem, int>::value_type index_entry(
     item, static_cast<int>(this->EntryList.size()));
-  auto lei = this->LinkEntryIndex.insert(index_entry).first;
-  this->EntryList.emplace_back();
-  this->InferredDependSets.emplace_back();
-  this->EntryConstraintGraph.emplace_back();
+  auto lei = this->LinkEntryIndex.insert(index_entry);
+  if (lei.second) {
+    this->EntryList.emplace_back();
+    this->InferredDependSets.emplace_back();
+    this->EntryConstraintGraph.emplace_back();
+  }
   return lei;
 }
 
-int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
+std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
 {
+  // Allocate a spot for the item entry.
+  auto lei = this->AllocateLinkEntry(item);
+
   // Check if the item entry has already been added.
-  auto lei = this->LinkEntryIndex.find(item);
-  if (lei != this->LinkEntryIndex.end()) {
+  if (!lei.second) {
     // Yes.  We do not need to follow the item's dependencies again.
-    return lei->second;
+    return { lei.first->second, false };
   }
 
-  // Allocate a spot for the item entry.
-  lei = this->AllocateLinkEntry(item);
-
   // Initialize the item entry.
-  int index = lei->second;
+  int index = lei.first->second;
   LinkEntry& entry = this->EntryList[index];
   entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
   entry.Target = item.Target;
@@ -332,22 +361,21 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
     }
   }
 
-  return index;
+  return { index, true };
 }
 
 void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
 {
+  // Allocate a spot for the item entry.
+  auto lei = this->AllocateLinkEntry(item);
+
   // Check if the item entry has already been added.
-  auto lei = this->LinkEntryIndex.find(item);
-  if (lei != this->LinkEntryIndex.end()) {
+  if (!lei.second) {
     return;
   }
 
-  // Allocate a spot for the item entry.
-  lei = this->AllocateLinkEntry(item);
-
   // Initialize the item entry.
-  int index = lei->second;
+  int index = lei.first->second;
   LinkEntry& entry = this->EntryList[index];
   entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
   entry.IsObject = true;
@@ -423,14 +451,14 @@ void cmComputeLinkDepends::QueueSharedDependencies(
 
 void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
 {
-  // Check if the target already has an entry.
-  auto lei = this->LinkEntryIndex.find(dep.Item);
-  if (lei == this->LinkEntryIndex.end()) {
-    // Allocate a spot for the item entry.
-    lei = this->AllocateLinkEntry(dep.Item);
+  // Allocate a spot for the item entry.
+  auto lei = this->AllocateLinkEntry(dep.Item);
+  int index = lei.first->second;
 
+  // Check if the target does not already has an entry.
+  if (lei.second) {
     // Initialize the item entry.
-    LinkEntry& entry = this->EntryList[lei->second];
+    LinkEntry& entry = this->EntryList[index];
     entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace);
     entry.Target = dep.Item.Target;
 
@@ -441,7 +469,6 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
   }
 
   // Get the link entry for this target.
-  int index = lei->second;
   LinkEntry& entry = this->EntryList[index];
 
   // This shared library dependency must follow the item that listed
@@ -541,6 +568,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
 {
   // Track inferred dependency sets implied by this list.
   std::map<int, DependSet> dependSets;
+  std::string feature;
 
   // Loop over the libraries linked directly by the depender.
   for (T const& l : libs) {
@@ -550,9 +578,86 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
     if (item.AsStr() == this->Target->GetName() || item.AsStr().empty()) {
       continue;
     }
+    if (cmHasPrefix(item.AsStr(), LL_BEGIN) &&
+        cmHasSuffix(item.AsStr(), '>')) {
+      feature = ExtractFeature(item.AsStr());
+      // emit a warning if an undefined feature is used as part of
+      // an imported target
+      if (depender_index >= 0) {
+        const auto& depender = this->EntryList[depender_index];
+        if (depender.Target != nullptr && depender.Target->IsImported() &&
+            !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) {
+          this->CMakeInstance->IssueMessage(
+            MessageType::AUTHOR_ERROR,
+            cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
+                     "' uses the generator-expression '$<LINK_LIBRARY>' with "
+                     "the feature '",
+                     feature,
+                     "', which is undefined or unsupported.\nDid you miss to "
+                     "define it by setting variables \"CMAKE_",
+                     this->LinkLanguage, "_LINK_USING_", feature,
+                     "\" and \"CMAKE_", this->LinkLanguage, "_LINK_USING_",
+                     feature, "_SUPPORTED\"?"),
+            this->Target->GetBacktrace());
+        }
+      }
+      continue;
+    }
+    if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
+      feature.clear();
+      continue;
+    }
 
     // Add a link entry for this item.
-    int dependee_index = this->AddLinkEntry(l);
+    auto ale = this->AddLinkEntry(item);
+    int dependee_index = ale.first;
+    LinkEntry& entry = this->EntryList[dependee_index];
+    if (!feature.empty()) {
+      if (ale.second) {
+        // current item not yet defined
+        if (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 '", feature,
+                     "', specified as part of a generator-expression "
+                     "'$",
+                     LL_BEGIN, feature, ">', will not be applied to the ",
+                     (entry.Target->GetType() ==
+                          cmStateEnums::TargetType::OBJECT_LIBRARY
+                        ? "OBJECT"
+                        : "INTERFACE"),
+                     " library '", entry.Item.Value, "'."),
+            this->Target->GetBacktrace());
+        } else {
+          entry.Feature = feature;
+        }
+      }
+    }
+
+    bool supportedItem = entry.Target == nullptr ||
+      (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
+       entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+
+    if (supportedItem && entry.Feature != feature) {
+      // 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 ",
+          (feature.empty() ? "without any feature"
+                           : cmStrCat("with the feature '", feature, '\'')),
+          ", has already occurred ",
+          (entry.Feature.empty()
+             ? "without any feature"
+             : cmStrCat("with the feature '", entry.Feature, '\'')),
+          ", which is not allowed."),
+        this->Target->GetBacktrace());
+    }
 
     // The dependee must come after the depender.
     if (depender_index >= 0) {
@@ -667,7 +772,7 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
   fprintf(stderr, "%s\n", e.str().c_str());
 }
 
-void cmComputeLinkDepends::OrderLinkEntires()
+void cmComputeLinkDepends::OrderLinkEntries()
 {
   // Compute the DAG of strongly connected components.  The algorithm
   // used by cmComputeComponentGraph should identify the components in
@@ -869,10 +974,14 @@ void cmComputeLinkDepends::DisplayFinalEntries()
   fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
   for (LinkEntry const& lei : this->FinalLinkEntries) {
     if (lei.Target) {
-      fprintf(stderr, "  target [%s]\n", lei.Target->GetName().c_str());
+      fprintf(stderr, "  target [%s]", lei.Target->GetName().c_str());
     } else {
-      fprintf(stderr, "  item [%s]\n", lei.Item.Value.c_str());
+      fprintf(stderr, "  item [%s]", lei.Item.Value.c_str());
+    }
+    if (!lei.Feature.empty()) {
+      fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
     }
+    fprintf(stderr, "\n");
   }
   fprintf(stderr, "\n");
 }
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 727c666..02bdf50 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -30,7 +30,8 @@ class cmComputeLinkDepends
 {
 public:
   cmComputeLinkDepends(cmGeneratorTarget const* target,
-                       const std::string& config);
+                       const std::string& config,
+                       const std::string& linkLanguage);
   ~cmComputeLinkDepends();
 
   cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -51,6 +52,9 @@ public:
     bool IsSharedDep = false;
     bool IsFlag = false;
     bool IsObject = false;
+    // The following member is for the management of items specified
+    // through genex $<LINK_LIBRARY:...>
+    std::string Feature;
   };
 
   using EntryVector = std::vector<LinkEntry>;
@@ -68,12 +72,13 @@ private:
   cmMakefile* Makefile;
   cmGlobalGenerator const* GlobalGenerator;
   cmake* CMakeInstance;
+  std::string LinkLanguage;
   std::string Config;
   EntryVector FinalLinkEntries;
 
-  std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
+  std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
     cmLinkItem const& item);
-  int AddLinkEntry(cmLinkItem const& item);
+  std::pair<int, bool> AddLinkEntry(cmLinkItem const& item);
   void AddLinkObject(cmLinkItem const& item);
   void AddVarLinkEntries(int depender_index, const char* value);
   void AddDirectLinkEntries();
@@ -131,7 +136,7 @@ private:
   void DisplayConstraintGraph();
 
   // Ordering algorithm.
-  void OrderLinkEntires();
+  void OrderLinkEntries();
   std::vector<char> ComponentVisited;
   std::vector<int> ComponentOrder;
 
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 62a96dd..5c3a96d 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -19,6 +19,7 @@
 #include "cmMessageType.h"
 #include "cmOrderDirectories.h"
 #include "cmOutputConverter.h"
+#include "cmPlaceholderExpander.h"
 #include "cmPolicies.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
@@ -344,6 +345,27 @@ cmComputeLinkInformation::cmComputeLinkInformation(
     this->LinkWithRuntimePath = this->Makefile->IsOn(var);
   }
 
+  // Define some Feature descriptors to handle standard library and object link
+  if (!this->GetLibLinkFileFlag().empty()) {
+    this->LibraryFeatureDescriptors.emplace(
+      "__CMAKE_LINK_LIBRARY",
+      FeatureDescriptor{ "__CMAKE_LINK_LIBRARY",
+                         cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") });
+  }
+  if (!this->GetObjLinkFileFlag().empty()) {
+    this->LibraryFeatureDescriptors.emplace(
+      "__CMAKE_LINK_OBJECT",
+      FeatureDescriptor{ "__CMAKE_LINK_OBJECT",
+                         cmStrCat(this->GetObjLinkFileFlag(), "<LIBRARY>") });
+  }
+  if (!this->LoaderFlag->empty()) {
+    // Define a Feature descriptor for the link of an executable with exports
+    this->LibraryFeatureDescriptors.emplace(
+      "__CMAKE_LINK_EXECUTABLE",
+      FeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE",
+                         cmStrCat(this->LoaderFlag, "<LIBRARY>") });
+  }
+
   // Check the platform policy for missing soname case.
   this->NoSONameUsesPath =
     this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
@@ -510,12 +532,40 @@ bool cmComputeLinkInformation::Compute()
   }
 
   // Compute the ordered link line items.
-  cmComputeLinkDepends cld(this->Target, this->Config);
+  cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
   cld.SetOldLinkDirMode(this->OldLinkDirMode);
   cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
+  FeatureDescriptor const* currentFeature = nullptr;
 
   // Add the link line items.
   for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
+    if (currentFeature != nullptr &&
+        linkEntry.Feature != currentFeature->Name) {
+      // emit feature suffix, if any
+      if (!currentFeature->Suffix.empty()) {
+        this->Items.emplace_back(
+          BT<std::string>{ currentFeature->Suffix,
+                           this->Items.back().Value.Backtrace },
+          ItemIsPath::No);
+      }
+      currentFeature = nullptr;
+    }
+
+    if (!linkEntry.Feature.empty() &&
+        (currentFeature == nullptr ||
+         linkEntry.Feature != currentFeature->Name)) {
+      if (!this->AddLibraryFeature(linkEntry.Feature)) {
+        continue;
+      }
+      currentFeature = this->FindLibraryFeature(linkEntry.Feature);
+      // emit feature prefix, if any
+      if (!currentFeature->Prefix.empty()) {
+        this->Items.emplace_back(
+          BT<std::string>{ currentFeature->Prefix, linkEntry.Item.Backtrace },
+          ItemIsPath::No);
+      }
+    }
+
     if (linkEntry.IsSharedDep) {
       this->AddSharedDepItem(linkEntry);
     } else {
@@ -523,6 +573,16 @@ bool cmComputeLinkInformation::Compute()
     }
   }
 
+  if (currentFeature != nullptr) {
+    // emit feature suffix, if any
+    if (!currentFeature->Suffix.empty()) {
+      this->Items.emplace_back(
+        BT<std::string>{ currentFeature->Suffix,
+                         this->Items.back().Value.Backtrace },
+        ItemIsPath::No);
+    }
+  }
+
   // Restore the target link type so the correct system runtime
   // libraries are found.
   cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
@@ -575,6 +635,270 @@ bool cmComputeLinkInformation::Compute()
   return true;
 }
 
+namespace {
+void FinalizeFeatureFormat(std::string& format, const std::string& activeTag,
+                           const std::string& otherTag)
+{
+  auto pos = format.find(otherTag);
+  if (pos != std::string::npos) {
+    format.erase(pos, format.find('}', pos) - pos + 1);
+  }
+  pos = format.find(activeTag);
+  if (pos != std::string::npos) {
+    format.erase(pos, activeTag.length());
+    pos = format.find('}', pos);
+    if (pos != std::string::npos) {
+      format.erase(pos, 1);
+    }
+  }
+}
+
+bool IsValidFeatureFormat(const std::string& format)
+{
+  return format.find("<LIBRARY>") != std::string::npos ||
+    format.find("<LIB_ITEM>") != std::string::npos ||
+    format.find("<LINK_ITEM>") != std::string::npos;
+}
+}
+
+bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature)
+{
+  auto it = this->LibraryFeatureDescriptors.find(feature);
+  if (it != this->LibraryFeatureDescriptors.end()) {
+    return it->second.Supported;
+  }
+
+  auto featureName =
+    cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_USING_", feature);
+  cmValue featureSupported =
+    this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+  if (!featureSupported.IsOn()) {
+    featureName = cmStrCat("CMAKE_LINK_USING_", feature);
+    featureSupported =
+      this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+  }
+  if (!featureSupported.IsOn()) {
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "Feature '", feature,
+        "', specified through generator-expression '$<LINK_LIBRARY>' to "
+        "link target '",
+        this->Target->GetName(), "', is not supported for the '",
+        this->LinkLanguage, "' link language."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+
+  cmValue langFeature = this->Makefile->GetDefinition(featureName);
+  if (!langFeature) {
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "Feature '", feature,
+        "', specified through generator-expression '$<LINK_LIBRARY>' to "
+        "link target '",
+        this->Target->GetName(), "', is not defined for the '",
+        this->LinkLanguage, "' link language."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+
+  auto items =
+    cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true);
+
+  if ((items.size() == 1 && !IsValidFeatureFormat(items.front().Value)) ||
+      (items.size() == 3 && !IsValidFeatureFormat(items[1].Value))) {
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+               "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+               "\"<LINK_ITEM>\" patterns "
+               "are missing) and cannot be used to link target '",
+               this->Target->GetName(), "'."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+
+  // now, handle possible "PATH{}" and "NAME{}" patterns
+  if (items.size() == 1) {
+    items.push_back(items.front());
+    FinalizeFeatureFormat(items[0].Value, "PATH{", "NAME{");
+    FinalizeFeatureFormat(items[1].Value, "NAME{", "PATH{");
+  } else if (items.size() == 3) {
+    items.insert(items.begin() + 1, items[1]);
+    FinalizeFeatureFormat(items[1].Value, "PATH{", "NAME{");
+    FinalizeFeatureFormat(items[2].Value, "NAME{", "PATH{");
+  } else {
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+               "', is malformed (wrong number of elements) and cannot be used "
+               "to link target '",
+               this->Target->GetName(), "'."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+  if ((items.size() == 2 && !IsValidFeatureFormat(items[0].Value)) ||
+      (items.size() == 4 && !IsValidFeatureFormat(items[1].Value))) {
+    // PATH{} has wrong format
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+               "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+               "\"<LINK_ITEM>\" patterns "
+               "are missing for \"PATH{}\" alternative) and cannot be used to "
+               "link target '",
+               this->Target->GetName(), "'."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+  if ((items.size() == 2 && !IsValidFeatureFormat(items[1].Value)) ||
+      (items.size() == 4 && !IsValidFeatureFormat(items[2].Value))) {
+    // NAME{} has wrong format
+    this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+    this->CMakeInstance->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+               "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+               "\"<LINK_ITEM>\" patterns "
+               "are missing for \"NAME{}\" alternative) and cannot be used to "
+               "link target '",
+               this->Target->GetName(), "'."),
+      this->Target->GetBacktrace());
+
+    return false;
+  }
+
+  // replace LINKER: pattern
+  this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true);
+
+  if (items.size() == 2) {
+    this->LibraryFeatureDescriptors.emplace(
+      feature, FeatureDescriptor{ feature, items[0].Value, items[1].Value });
+  } else {
+    this->LibraryFeatureDescriptors.emplace(
+      feature,
+      FeatureDescriptor{ feature, items[0].Value, items[1].Value,
+                         items[2].Value, items[3].Value });
+  }
+
+  return true;
+}
+
+cmComputeLinkInformation::FeatureDescriptor const&
+cmComputeLinkInformation::GetLibraryFeature(std::string const& feature) const
+{
+  return this->LibraryFeatureDescriptors.find(feature)->second;
+}
+cmComputeLinkInformation::FeatureDescriptor const*
+cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const
+{
+  auto it = this->LibraryFeatureDescriptors.find(feature);
+  if (it == this->LibraryFeatureDescriptors.end()) {
+    return nullptr;
+  }
+
+  return &it->second;
+}
+
+namespace {
+class FeaturePlaceHolderExpander : public cmPlaceholderExpander
+{
+public:
+  FeaturePlaceHolderExpander(const std::string* library,
+                             const std::string* libItem = nullptr,
+                             const std::string* linkItem = nullptr)
+    : Library(library)
+    , LibItem(libItem)
+    , LinkItem(linkItem)
+  {
+  }
+
+private:
+  std::string ExpandVariable(std::string const& variable) override
+  {
+    if (this->Library != nullptr && variable == "LIBRARY") {
+      return *this->Library;
+    }
+    if (this->LibItem != nullptr && variable == "LIB_ITEM") {
+      return *this->LibItem;
+    }
+    if (this->LinkItem != nullptr && variable == "LINK_ITEM") {
+      return *this->LinkItem;
+    }
+
+    return variable;
+  }
+
+  const std::string* Library = nullptr;
+  const std::string* LibItem = nullptr;
+  const std::string* LinkItem = nullptr;
+};
+}
+
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+  std::string name, std::string itemFormat)
+  : Name(std::move(name))
+  , Supported(true)
+  , ItemPathFormat(std::move(itemFormat))
+  , ItemNameFormat(this->ItemPathFormat)
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+  std::string name, std::string itemPathFormat, std::string itemNameFormat)
+  : Name(std::move(name))
+  , Supported(true)
+  , ItemPathFormat(std::move(itemPathFormat))
+  , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+  std::string name, std::string prefix, std::string itemPathFormat,
+  std::string itemNameFormat, std::string suffix)
+  : Name(std::move(name))
+  , Supported(true)
+  , Prefix(std::move(prefix))
+  , Suffix(std::move(suffix))
+  , ItemPathFormat(std::move(itemPathFormat))
+  , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+  std::string const& library, ItemIsPath isPath) const
+{
+  auto format =
+    isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+  // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns with library path
+  FeaturePlaceHolderExpander expander(&library, &library, &library);
+  return expander.ExpandVariables(format);
+}
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+  std::string const& library, std::string const& libItem,
+  std::string const& linkItem, ItemIsPath isPath) const
+{
+  auto format =
+    isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+  // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns
+  FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem);
+  return expander.ExpandVariables(format);
+}
+
 void cmComputeLinkInformation::AddImplicitLinkInfo()
 {
   // The link closure lists all languages whose implicit info is needed.
@@ -657,23 +981,21 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
     if (impexe && this->LoaderFlag) {
       // This link item is an executable that may provide symbols
       // used by this target.  A special flag is needed on this
-      // platform.  Add it now.
-      std::string linkItem = this->LoaderFlag;
+      // platform.  Add it now using a special feature.
       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
         ? cmStateEnums::ImportLibraryArtifact
         : cmStateEnums::RuntimeBinaryArtifact;
-
       std::string exe = tgt->GetFullPath(config, artifact, true);
-      linkItem += exe;
-      this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace),
-                               ItemIsPath::Yes, ItemIsObject::No, tgt);
+      this->Items.emplace_back(
+        BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt,
+        this->FindLibraryFeature(
+          entry.Feature.empty() ? "__CMAKE_LINK_EXECUTABLE" : entry.Feature));
       this->Depends.push_back(std::move(exe));
     } else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       // Add the interface library as an item so it can be considered as part
       // of COMPATIBLE_INTERFACE_ enforcement.  The generators will ignore
       // this for the actual link line.
-      this->Items.emplace_back(std::string(), ItemIsPath::No, ItemIsObject::No,
-                               tgt);
+      this->Items.emplace_back(std::string(), ItemIsPath::No, tgt);
 
       // Also add the item the interface specifies to be used in its place.
       std::string const& libName = tgt->GetImportedLibName(config);
@@ -1098,7 +1420,10 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
   }
 
   // Now add the full path to the library.
-  this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
+  this->Items.emplace_back(item, ItemIsPath::Yes, target,
+                           this->FindLibraryFeature(entry.Feature.empty()
+                                                      ? "__CMAKE_LINK_LIBRARY"
+                                                      : entry.Feature));
 }
 
 void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
@@ -1154,9 +1479,12 @@ void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
   }
 
   // Now add the full path to the library.
-  this->Items.emplace_back(item, ItemIsPath::Yes,
-                           entry.IsObject ? ItemIsObject::Yes
-                                          : ItemIsObject::No);
+  this->Items.emplace_back(
+    item, ItemIsPath::Yes, nullptr,
+    this->FindLibraryFeature(
+      entry.Feature.empty()
+        ? (entry.IsObject ? "__CMAKE_LINK_OBJECT" : "__CMAKE_LINK_LIBRARY")
+        : entry.Feature));
 }
 
 bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
@@ -1221,8 +1549,6 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
   return true;
 }
 
-// void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
-//                                            bool pathNotKnown)
 void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
                                            bool pathNotKnown)
 {
@@ -1236,8 +1562,8 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
 
   BT<std::string> const& item = entry.Item;
 
-  // Pass flags through untouched.
   if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
+    // Pass flags through untouched.
     // if this is a -l option then we might need to warn about
     // CMP0003 so put it in OldUserFlagItems, if it is not a -l
     // or -Wl,-l (-framework -pthread), then allow it without a
@@ -1322,9 +1648,20 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
   }
 
   // Create an option to ask the linker to search for the library.
-  std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
-  this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
-                           ItemIsPath::No);
+  auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
+
+  if (!entry.Feature.empty()) {
+    auto const& feature = this->GetLibraryFeature(entry.Feature);
+    this->Items.emplace_back(
+      BT<std::string>(
+        feature.GetDecoratedItem(cmStrCat(lib, this->LibLinkSuffix),
+                                 item.Value, out, ItemIsPath::No),
+        item.Backtrace),
+      ItemIsPath::No);
+  } else {
+    this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
+                             ItemIsPath::No);
+  }
 
   // Here we could try to find the library the linker will find and
   // add a runtime information entry for it.  It would probably not be
@@ -1374,10 +1711,10 @@ void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
   // user.
   this->CMakeInstance->IssueMessage(
     MessageType::WARNING,
-    cmStrCat(
-      "Target \"", this->Target->GetName(),
-      "\" requests linking to directory \"", item.Value,
-      "\".  Targets may link only to libraries.  CMake is dropping the item."),
+    cmStrCat("Target \"", this->Target->GetName(),
+             "\" requests linking to directory \"", item.Value,
+             "\".  Targets may link only to libraries.  CMake is dropping "
+             "the item."),
     item.Backtrace);
 }
 
@@ -1804,8 +2141,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
     // Add directories explicitly specified by user
     std::string build_rpath;
     if (this->Target->GetBuildRPATH(this->Config, build_rpath)) {
-      // This will not resolve entries to use $ORIGIN, the user is expected to
-      // do that if necessary.
+      // This will not resolve entries to use $ORIGIN, the user is expected
+      // to do that if necessary.
       cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
     }
   }
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 2edcd5f..ce9f393 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -5,6 +5,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -28,6 +29,9 @@ class cmake;
  */
 class cmComputeLinkInformation
 {
+private:
+  class FeatureDescriptor;
+
 public:
   cmComputeLinkInformation(cmGeneratorTarget const* target,
                            const std::string& config);
@@ -43,28 +47,33 @@ public:
     Yes,
   };
 
-  enum class ItemIsObject
-  {
-    No,
-    Yes,
-  };
-
   struct Item
   {
-    Item() = default;
     Item(BT<std::string> v, ItemIsPath isPath,
-         ItemIsObject isObject = ItemIsObject::No,
-         cmGeneratorTarget const* target = nullptr)
+         cmGeneratorTarget const* target = nullptr,
+         FeatureDescriptor const* feature = nullptr)
       : Value(std::move(v))
       , IsPath(isPath)
-      , IsObject(isObject)
       , Target(target)
+      , Feature(feature)
     {
     }
     BT<std::string> Value;
-    ItemIsPath IsPath = ItemIsPath::Yes;
-    ItemIsObject IsObject = ItemIsObject::No;
+    ItemIsPath IsPath = ItemIsPath::No;
     cmGeneratorTarget const* Target = nullptr;
+
+    bool HasFeature() const { return this->Feature != nullptr; }
+
+    BT<std::string> GetFormattedItem(std::string const& path) const
+    {
+      return { (this->Feature != nullptr)
+                 ? this->Feature->GetDecoratedItem(path, this->IsPath)
+                 : path,
+               Value.Backtrace };
+    }
+
+  private:
+    FeatureDescriptor const* Feature = nullptr;
   };
   using ItemVector = std::vector<Item>;
   void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
@@ -237,4 +246,36 @@ private:
   void AddLibraryRuntimeInfo(std::string const& fullPath,
                              const cmGeneratorTarget* target);
   void AddLibraryRuntimeInfo(std::string const& fullPath);
+
+  class FeatureDescriptor
+  {
+  public:
+    FeatureDescriptor() = default;
+    FeatureDescriptor(std::string name, std::string itemFormat);
+    FeatureDescriptor(std::string name, std::string itemPathFormat,
+                      std::string itemNameFormat);
+    FeatureDescriptor(std::string name, std::string prefix,
+                      std::string itemPathFormat, std::string itemNameFormat,
+                      std::string suffix);
+
+    const std::string Name;
+    const bool Supported = false;
+    const std::string Prefix;
+    const std::string Suffix;
+    std::string GetDecoratedItem(std::string const& library,
+                                 ItemIsPath isPath) const;
+    std::string GetDecoratedItem(std::string const& library,
+                                 std::string const& linkItem,
+                                 std::string const& defaultValue,
+                                 ItemIsPath isPath) const;
+
+  private:
+    std::string ItemPathFormat;
+    std::string ItemNameFormat;
+  };
+  std::map<std::string, FeatureDescriptor> LibraryFeatureDescriptors;
+  bool AddLibraryFeature(std::string const& feature);
+  FeatureDescriptor const& GetLibraryFeature(std::string const& feature) const;
+  FeatureDescriptor const* FindLibraryFeature(
+    std::string const& feature) const;
 };
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 396e9c9..b63b90b 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1198,6 +1198,68 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
   }
 } linkLanguageAndIdNode;
 
+static const struct LinkLibraryNode : public cmGeneratorExpressionNode
+{
+  LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
+
+  int NumExpectedParameters() const override { return OneOrMoreParameters; }
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* dagChecker) const override
+  {
+    if (!context->HeadTarget || !dagChecker ||
+        !dagChecker->EvaluatingLinkLibraries()) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<LINK_LIBRARY:...> may only be used with binary targets "
+                  "to specify link libraries.");
+      return std::string();
+    }
+
+    std::vector<std::string> list;
+    cmExpandLists(parameters.begin(), parameters.end(), list);
+    if (list.empty()) {
+      reportError(
+        context, content->GetOriginalExpression(),
+        "$<LINK_LIBRARY:...> expects a feature name as first argument.");
+      return std::string();
+    }
+    if (list.size() == 1) {
+      // no libraries specified, ignore this genex
+      return std::string();
+    }
+
+    auto const& feature = list.front();
+    const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
+    const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
+
+    // filter out $<LINK_LIBRARY:..> tags with same feature
+    // and raise an error for any different feature
+    cm::erase_if(list, [&](const std::string& item) -> bool {
+      return item == LL_BEGIN || item == LL_END;
+    });
+    auto it =
+      std::find_if(list.cbegin() + 1, list.cend(),
+                   [&feature](const std::string& item) -> bool {
+                     return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
+                       item.substr(14, item.find('>', 14) - 14) != feature;
+                   });
+    if (it != list.cend()) {
+      reportError(
+        context, content->GetOriginalExpression(),
+        "$<LINK_LIBRARY:...> with different features cannot be nested.");
+      return std::string();
+    }
+
+    list.front() = LL_BEGIN;
+    list.push_back(LL_END);
+
+    return cmJoin(list, ";"_s);
+  }
+} linkLibraryNode;
+
 static const struct HostLinkNode : public cmGeneratorExpressionNode
 {
   HostLinkNode() {} // NOLINT(modernize-use-equals-default)
@@ -2668,6 +2730,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "COMPILE_LANGUAGE", &languageNode },
     { "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
     { "LINK_LANGUAGE", &linkLanguageNode },
+    { "LINK_LIBRARY", &linkLibraryNode },
     { "HOST_LINK", &hostLinkNode },
     { "DEVICE_LINK", &deviceLinkNode },
     { "SHELL_PATH", &shellPathNode }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9f1029e..58edefb 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -4625,7 +4625,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
 }
 
 std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
-  std::vector<BT<std::string>>& result, const std::string& language) const
+  std::vector<BT<std::string>>& result, const std::string& language,
+  bool joinItems) const
 {
   // replace "LINKER:" prefixed elements by actual linker wrapper
   const std::string wrapper(this->Makefile->GetSafeDefinition(
@@ -4684,7 +4685,14 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
 
     std::vector<BT<std::string>> options = wrapOptions(
       linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
-    result.insert(entry, options.begin(), options.end());
+    if (joinItems) {
+      result.insert(entry,
+                    cmJoin(cmRange<decltype(options.cbegin())>(
+                             options.cbegin(), options.cend()),
+                           " "_s));
+    } else {
+      result.insert(entry, options.begin(), options.end());
+    }
   }
   return result;
 }
@@ -6377,7 +6385,8 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
   std::string const& str = item.AsStr();
   if (!str.empty() &&
       (str[0] == '-' || str[0] == '$' || str[0] == '`' ||
-       str.find_first_of("/\\") != std::string::npos)) {
+       str.find_first_of("/\\") != std::string::npos ||
+       cmHasPrefix(str, "<LINK_LIBRARY:"_s))) {
     return true;
   }
 
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 45639c0..3e30913 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -513,7 +513,8 @@ public:
     std::string const& config, std::string const& language) const;
 
   std::vector<BT<std::string>>& ResolveLinkerWrapper(
-    std::vector<BT<std::string>>& result, const std::string& language) const;
+    std::vector<BT<std::string>>& result, const std::string& language,
+    bool joinItems = false) const;
 
   void GetStaticLibraryLinkOptions(std::vector<std::string>& result,
                                    const std::string& config,
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 203addd..489c7fb 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -3741,7 +3741,9 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
             }
             libPaths.Add("-framework " + this->XCodeEscapePath(fwName));
           } else {
-            libPaths.Add(this->XCodeEscapePath(cleanPath));
+            libPaths.Add(
+              libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
+                .Value);
           }
           if ((!libName.Target || libName.Target->IsImported()) &&
               IsLinkPhaseLibraryExtension(libPath)) {
diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx
index 2b8f836..ed89e91 100644
--- a/Source/cmLinkLibrariesCommand.cxx
+++ b/Source/cmLinkLibrariesCommand.cxx
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
     mf.AppendProperty("LINK_LIBRARIES", *i);
   }
 
+  mf.CheckProperty("LINK_LIBRARIES");
+
   return true;
 }
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 5646368..290642b 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -75,14 +75,8 @@ void cmLinkLineComputer::ComputeLinkLibs(
 
     BT<std::string> linkLib;
     if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
-      if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) {
-        linkLib.Value += cli.GetObjLinkFileFlag();
-      } else {
-        linkLib.Value += cli.GetLibLinkFileFlag();
-      }
-      linkLib.Value += this->ConvertToOutputFormat(
-        this->ConvertToLinkReference(item.Value.Value));
-      linkLib.Backtrace = item.Value.Backtrace;
+      linkLib = item.GetFormattedItem(this->ConvertToOutputFormat(
+        this->ConvertToLinkReference(item.Value.Value)));
     } else {
       linkLib = item.Value;
     }
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 43f161b..71f9f80 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -118,8 +118,10 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
       // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
       if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
           cmHasLiteralSuffix(item.Value.Value, ".lib")) {
-        linkLib.Value += this->ConvertToOutputFormat(
-          this->ConvertToLinkReference(item.Value.Value));
+        linkLib.Value = item
+                          .GetFormattedItem(this->ConvertToOutputFormat(
+                            this->ConvertToLinkReference(item.Value.Value)))
+                          .Value;
       }
     } else if (item.Value == "-framework") {
       // This is the first part of '-framework Name' where the framework
@@ -127,7 +129,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
       skipItemAfterFramework = true;
       continue;
     } else if (cmLinkItemValidForDevice(item.Value.Value)) {
-      linkLib.Value += item.Value.Value;
+      linkLib.Value = item.Value.Value;
     }
 
     if (emitted.insert(linkLib.Value).second) {
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index ed7e888..f65add1 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1294,7 +1294,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries(
   for (auto const& lib : libs) {
     if (lib.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
       std::string rel = lg->MaybeRelativeToCurBinDir(lib.Value.Value);
-      fout << lg->ConvertToXMLOutputPath(rel) << " ";
+      rel = lg->ConvertToXMLOutputPath(rel);
+      fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel)
+           << " ";
     } else if (!lib.Target ||
                lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
       fout << lib.Value.Value << " ";
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 4b1635b..6b17cc9 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3976,6 +3976,31 @@ std::vector<std::string> cmMakefile::GetPropertyKeys() const
   return this->StateSnapshot.GetDirectory().GetPropertyKeys();
 }
 
+void cmMakefile::CheckProperty(const std::string& prop) const
+{
+  // Certain properties need checking.
+  if (prop == "LINK_LIBRARIES") {
+    if (cmValue value = this->GetProperty(prop)) {
+      // Look for <LINK_LIBRARY:> internal pattern
+      static cmsys::RegularExpression linkLibrary(
+        "(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
+      if (!linkLibrary.find(value)) {
+        return;
+      }
+
+      // Report an error.
+      this->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(
+          "Property ", prop, " contains the invalid item \"",
+          linkLibrary.match(2), "\". The ", prop,
+          " property may contain the generator-expression "
+          "\"$<LINK_LIBRARY:...>\" "
+          "which may be used to specify how the libraries are linked."));
+    }
+  }
+}
+
 cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const
 {
   auto i = this->Targets.find(name);
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 85988b8..ecac95e 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -787,6 +787,7 @@ public:
   cmValue GetProperty(const std::string& prop, bool chain) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   std::vector<std::string> GetPropertyKeys() const;
+  void CheckProperty(const std::string& prop) const;
 
   //! Initialize a makefile from its parent
   void InitializeFromParent(cmMakefile* parent);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 87fce92..d40709f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1779,69 +1779,93 @@ void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry)
   this->impl->PrecompileHeadersEntries.push_back(entry);
 }
 
-static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
-                                                  const std::string& value,
-                                                  cmMakefile* context,
-                                                  bool imported)
+namespace {
+void CheckLinkLibraryPattern(const std::string& property,
+                             const std::string& value, cmMakefile* context)
 {
-  // Look for link-type keywords in the value.
-  static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
-  if (!keys.find(value)) {
+  // Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags
+  static cmsys::RegularExpression linkLibrary(
+    "(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
+  if (!linkLibrary.find(value)) {
     return;
   }
 
+  // Report an error.
+  context->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat("Property ", property, " contains the invalid item \"",
+             linkLibrary.match(2), "\". The ", property,
+             " property may contain the generator-expression "
+             "\"$<LINK_LIBRARY:...>\" "
+             "which may be used to specify how the libraries are linked."));
+}
+
+void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
+                                   const std::string& value,
+                                   cmMakefile* context, bool imported)
+{
   // Support imported and non-imported versions of the property.
   const char* base = (imported ? "IMPORTED_LINK_INTERFACE_LIBRARIES"
                                : "LINK_INTERFACE_LIBRARIES");
 
-  // Report an error.
-  std::ostringstream e;
-  e << "Property " << prop << " may not contain link-type keyword \""
-    << keys.match(2) << "\".  "
-    << "The " << base << " property has a per-configuration "
-    << "version called " << base << "_<CONFIG> which may be "
-    << "used to specify per-configuration rules.";
-  if (!imported) {
-    e << "  "
-      << "Alternatively, an IMPORTED library may be created, configured "
-      << "with a per-configuration location, and then named in the "
-      << "property value.  "
-      << "See the add_library command's IMPORTED mode for details."
-      << "\n"
-      << "If you have a list of libraries that already contains the "
-      << "keyword, use the target_link_libraries command with its "
-      << "LINK_INTERFACE_LIBRARIES mode to set the property.  "
-      << "The command automatically recognizes link-type keywords and sets "
-      << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
-      << "properties accordingly.";
-  }
-  context->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
-
-static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value,
-                                                  cmMakefile* context)
-{
   // Look for link-type keywords in the value.
   static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
-  if (!keys.find(value)) {
-    return;
+  if (keys.find(value)) {
+    // Report an error.
+    std::ostringstream e;
+    e << "Property " << prop << " may not contain link-type keyword \""
+      << keys.match(2) << "\".  "
+      << "The " << base << " property has a per-configuration "
+      << "version called " << base << "_<CONFIG> which may be "
+      << "used to specify per-configuration rules.";
+    if (!imported) {
+      e << "  "
+        << "Alternatively, an IMPORTED library may be created, configured "
+        << "with a per-configuration location, and then named in the "
+        << "property value.  "
+        << "See the add_library command's IMPORTED mode for details."
+        << "\n"
+        << "If you have a list of libraries that already contains the "
+        << "keyword, use the target_link_libraries command with its "
+        << "LINK_INTERFACE_LIBRARIES mode to set the property.  "
+        << "The command automatically recognizes link-type keywords and sets "
+        << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
+        << "properties accordingly.";
+    }
+    context->IssueMessage(MessageType::FATAL_ERROR, e.str());
   }
 
-  // Report an error.
-  std::ostringstream e;
+  CheckLinkLibraryPattern(base, value, context);
+}
+
+void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
+{
+  CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
+}
 
-  e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
-       "keyword \""
-    << keys.match(2)
-    << "\".  The INTERFACE_LINK_LIBRARIES "
-       "property may contain configuration-sensitive generator-expressions "
-       "which may be used to specify per-configuration rules.";
+void CheckINTERFACE_LINK_LIBRARIES(const std::string& value,
+                                   cmMakefile* context)
+{
+  // Look for link-type keywords in the value.
+  static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
+  if (keys.find(value)) {
+    // Report an error.
+    std::ostringstream e;
+
+    e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
+         "keyword \""
+      << keys.match(2)
+      << "\".  The INTERFACE_LINK_LIBRARIES "
+         "property may contain configuration-sensitive generator-expressions "
+         "which may be used to specify per-configuration rules.";
+
+    context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  }
 
-  context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  CheckLinkLibraryPattern("INTERFACE_LINK_LIBRARIES", value, context);
 }
 
-static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
-                                         cmMakefile* context)
+void CheckIMPORTED_GLOBAL(const cmTarget* target, cmMakefile* context)
 {
   const auto& targets = context->GetOwnedImportedTargets();
   auto it =
@@ -1857,6 +1881,7 @@ static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
     context->IssueMessage(MessageType::FATAL_ERROR, e.str());
   }
 }
+}
 
 void cmTarget::CheckProperty(const std::string& prop,
                              cmMakefile* context) const
@@ -1864,22 +1889,23 @@ void cmTarget::CheckProperty(const std::string& prop,
   // Certain properties need checking.
   if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) {
     if (cmValue value = this->GetProperty(prop)) {
-      cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
+      CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
     }
-  }
-  if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
+  } else if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
     if (cmValue value = this->GetProperty(prop)) {
-      cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
+      CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
     }
-  }
-  if (prop == "INTERFACE_LINK_LIBRARIES") {
+  } else if (prop == "LINK_LIBRARIES") {
     if (cmValue value = this->GetProperty(prop)) {
-      cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context);
+      CheckLINK_LIBRARIES(*value, context);
     }
-  }
-  if (prop == "IMPORTED_GLOBAL") {
+  } else if (prop == "INTERFACE_LINK_LIBRARIES") {
+    if (cmValue value = this->GetProperty(prop)) {
+      CheckINTERFACE_LINK_LIBRARIES(*value, context);
+    }
+  } else if (prop == "IMPORTED_GLOBAL") {
     if (this->IsImported()) {
-      cmTargetCheckIMPORTED_GLOBAL(this, context);
+      CheckIMPORTED_GLOBAL(this, context);
     }
   }
 }
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index e15c941..0b96b2d 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -318,6 +318,9 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
     target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
   }
 
+  target->CheckProperty("LINK_LIBRARIES", &mf);
+  target->CheckProperty("INTERFACE_LINK_LIBRARIES", &mf);
+
   return true;
 }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 7b197fa..92d64fe 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -3677,7 +3677,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
           this->LocalGenerator->MaybeRelativeToCurBinDir(l.Value.Value);
         ConvertToWindowsSlash(path);
         if (!cmVS10IsTargetsFile(l.Value.Value)) {
-          libVec.push_back(path);
+          libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+                                          : path);
         }
       } else {
         libVec.push_back(l.Value.Value);
@@ -4315,7 +4316,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
       if (cmVS10IsTargetsFile(l.Value.Value)) {
         vsTargetVec.push_back(path);
       } else {
-        libVec.push_back(path);
+        libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+                                        : path);
       }
     } else if (!l.Target ||
                l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 1d7e632..d1a1ddd 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -312,6 +312,7 @@ add_RunCMake_test(GenEx-LINK_LANGUAGE)
 add_RunCMake_test(GenEx-LINK_LANG_AND_ID)
 add_RunCMake_test(GenEx-HOST_LINK)
 add_RunCMake_test(GenEx-DEVICE_LINK)
+add_RunCMake_test(GenEx-LINK_LIBRARY)
 add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
 add_RunCMake_test(GenEx-GENEX_EVAL)
 add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -652,6 +653,17 @@ add_RunCMake_test(target_link_libraries)
 add_RunCMake_test(target_link_libraries-ALIAS)
 add_RunCMake_test(target_link_libraries-LINK_LANGUAGE)
 add_RunCMake_test(target_link_libraries-LINK_LANG_AND_ID)
+add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+                                                     -DMINGW=${MINGW}
+                                                     -DMSYS=${MSYS}
+                                                     -DCYGWIN=${CYGWIN}
+                                                     -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+                                                     -DMSVC_VERSION=${MSVC_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}
+                                                     -DCMAKE_IMPORT_LIBRARY_SUFFIX=${CMAKE_IMPORT_LIBRARY_SUFFIX}
+                                                     -DCMAKE_LINK_LIBRARY_FLAG=${CMAKE_LINK_LIBRARY_FLAG})
 add_RunCMake_test(add_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 add_RunCMake_test(target_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
                                       -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt
new file mode 100644
index 0000000..612169c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.18...3.22)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
new file mode 100644
index 0000000..ab1ac37
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -0,0 +1,25 @@
+include(RunCMake)
+
+run_cmake(add_custom_target)
+run_cmake(add_custom_command)
+run_cmake(add_link_options)
+run_cmake(link_directories)
+run_cmake(target_link_options)
+run_cmake(target_link_directories)
+run_cmake(no-arguments)
+run_cmake(empty-arguments)
+run_cmake(forbidden-arguments)
+run_cmake(bad-feature1)
+run_cmake(bad-feature2)
+run_cmake(bad-feature3)
+run_cmake(bad-feature4)
+run_cmake(bad-feature5)
+run_cmake(bad-feature6)
+run_cmake(bad-feature7)
+run_cmake(feature-not-supported)
+run_cmake(library-ignored)
+run_cmake(compatible-features)
+run_cmake(incompatible-features)
+run_cmake(nested-compatible-features)
+run_cmake(nested-incompatible-features)
+run_cmake(only-targets)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt
new file mode 100644
index 0000000..d8ff0eb
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_custom_command.cmake:[0-9]+ \(add_custom_command\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake
new file mode 100644
index 0000000..3583a67
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake
@@ -0,0 +1,4 @@
+add_custom_target(drive)
+add_custom_command(TARGET drive PRE_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
+)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt
new file mode 100644
index 0000000..8ca384d
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_custom_target.cmake:[0-9]+ \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake
new file mode 100644
index 0000000..ef00965
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake
@@ -0,0 +1,3 @@
+add_custom_target(drive
+  COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
+)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt
new file mode 100644
index 0000000..399a413
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_link_options.cmake:[0-9]+ \(add_link_options\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake
new file mode 100644
index 0000000..fdccf95
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+add_link_options("$<LINK_LIBRARY:feat>")
+
+add_library(empty SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt
new file mode 100644
index 0000000..0ff8aca
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature1.cmake:[0-9]+ \(add_library\):
+  Feature 'bad_feat', specified through generator-expression
+  '\$<LINK_LIBRARY>' to link target 'lib', is not supported for the 'C' link
+  language.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake
new file mode 100644
index 0000000..5e540cf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake
@@ -0,0 +1,6 @@
+enable_language(C)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:bad_feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt
new file mode 100644
index 0000000..9e878cc
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at bad-feature2.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
+  link target 'lib', is not defined for the 'C' link language.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake
new file mode 100644
index 0000000..15b5ca0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake
@@ -0,0 +1,8 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt
new file mode 100644
index 0000000..69963fe
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature3.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+  malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
+  missing\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake
new file mode 100644
index 0000000..7960465
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt
new file mode 100644
index 0000000..7385115
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature4.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+  malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
+  missing\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake
new file mode 100644
index 0000000..b40cfaf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "-opt")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt
new file mode 100644
index 0000000..9894577
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature5.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+  malformed \(wrong number of elements\) and cannot be used to link target
+  'lib'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake
new file mode 100644
index 0000000..8ce1ecf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "-prefix" "<LIBRARY>")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt
new file mode 100644
index 0000000..d8d0e19
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature6.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+  malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
+  for "PATH{}" alternative\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake
new file mode 100644
index 0000000..7b72ad5
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "PATH{}NAME{<LIBRARY>}")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt
new file mode 100644
index 0000000..2a498cd
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature7.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+  malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
+  for "NAME{}" alternative\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake
new file mode 100644
index 0000000..173f86c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "NAME{}PATH{<LIBRARY>}")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
new file mode 100644
index 0000000..e79764c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt
new file mode 100644
index 0000000..1530f61
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at empty-arguments.cmake:[0-9]+ \(target_link_libraries\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:,>
+
+  \$<LINK_LIBRARY:...> expects a feature name as first argument.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake
new file mode 100644
index 0000000..c6e2260
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:,>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt
new file mode 100644
index 0000000..6067bce
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at feature-not-supported.cmake:[0-9]+ \(add_library\):
+  Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
+  link target 'lib', is not supported for the 'C' link language.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake
new file mode 100644
index 0000000..0666227
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED FALSE)
+set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt
new file mode 100644
index 0000000..5245dd8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt
@@ -0,0 +1,16 @@
+CMake Error at forbidden-arguments.cmake:[0-9]+ \(link_libraries\):
+  Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
+  The LINK_LIBRARIES property may contain the generator-expression
+  "\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
+  linked.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+
+
+CMake Error at forbidden-arguments.cmake:[0-9]+ \(target_link_libraries\):
+  Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
+  The LINK_LIBRARIES property may contain the generator-expression
+  "\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
+  linked.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake
new file mode 100644
index 0000000..1c51c44
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake
@@ -0,0 +1,6 @@
+enable_language(C)
+
+link_libraries(<LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE <LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
new file mode 100644
index 0000000..22219a2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at incompatible-features.cmake:[0-9]+ \(add_library\):
+  Impossible to link target 'lib' because the link item 'dep1', specified
+  with the feature 'feat1', has already occurred with the feature 'feat2',
+  which is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
new file mode 100644
index 0000000..d96b214
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
new file mode 100644
index 0000000..f9a99af
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -0,0 +1,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 INTERFACE library
+  'front'.
+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 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.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
new file mode 100644
index 0000000..e000b97
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
+
+add_library(dep OBJECT empty.c)
+
+add_library(lib SHARED empty.c)
+
+add_library(front INTERFACE)
+target_link_libraries(front INTERFACE lib)
+
+
+add_library(lib2 SHARED empty.c)
+target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat,front,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt
new file mode 100644
index 0000000..aeb32f2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at link_directories.cmake:[0-9]+ \(link_directories\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake
new file mode 100644
index 0000000..b6d9a36
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+link_directories("$<LINK_LIBRARY:feat>")
+
+add_library(empty SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake
new file mode 100644
index 0000000..d3b04e8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake
@@ -0,0 +1,11 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat1,dep1,$<LINK_LIBRARY:feat1,dep2>>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt
new file mode 100644
index 0000000..3f6c504
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at nested-incompatible-features.cmake:[0-9]+ \(target_link_libraries\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat2,dep1,\$<LINK_LIBRARY:feat1,dep2>>
+
+  \$<LINK_LIBRARY:...> with different features cannot be nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake
new file mode 100644
index 0000000..8565fa9
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake
@@ -0,0 +1,14 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,$<LINK_LIBRARY:feat1,dep2>>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt
new file mode 100644
index 0000000..af58fa0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at no-arguments.cmake:[0-9]+ \(target_link_libraries\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY>
+
+  \$<LINK_LIBRARY> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake
new file mode 100644
index 0000000..0645dc7
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt
new file mode 100644
index 0000000..6b770f0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at only-targets.cmake:[0-9]+ \(target_link_libraries\):
+  Target "lib2" has LINK_LIBRARIES_ONLY_TARGETS enabled, but it links to:
+
+    external
+
+  which is not a target.  Possible reasons include:
+
+    \* There is a typo in the target name.
+    \* A find_package call is missing for an IMPORTED target.
+    \* An ALIAS target is missing.
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake
new file mode 100644
index 0000000..e29ad6c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake
@@ -0,0 +1,16 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_LINK_LIBRARIES_ONLY_TARGETS 1)
+
+add_library(dep1 SHARED empty.c)
+
+add_library(lib1 SHARED empty.c)
+# accepted
+target_link_libraries(lib1 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib2 SHARED empty.c)
+# invalid
+target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat1,external>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt
new file mode 100644
index 0000000..e0c60c4
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at target_link_directories.cmake:[0-9]+ \(target_link_directories\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:feat>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake
new file mode 100644
index 0000000..e8cc670
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(empty SHARED empty.c)
+target_link_directories(empty PRIVATE "$<LINK_LIBRARY:feat>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt
new file mode 100644
index 0000000..6c9aab1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at target_link_options.cmake:[0-9]+ \(target_link_options\):
+  Error evaluating generator expression:
+
+    \$<LINK_LIBRARY:FEAT>
+
+  \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+  libraries.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake
new file mode 100644
index 0000000..800124c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(empty SHARED empty.c)
+target_link_options(empty PRIVATE $<LINK_LIBRARY:FEAT>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt
new file mode 100644
index 0000000..915fc41
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.1...3.22)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
new file mode 100644
index 0000000..255c9a6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
new file mode 100644
index 0000000..a8e0da7
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
new file mode 100644
index 0000000..54cef2c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP <base1> <other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
new file mode 100644
index 0000000..7c38134
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
new file mode 100644
index 0000000..88b5cf6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAGother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<base1> --LIBFLAG<other> --ITEMFLAG<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
new file mode 100644
index 0000000..c473637
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
new file mode 100644
index 0000000..858dcfe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
new file mode 100644
index 0000000..ab06726
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
new file mode 100644
index 0000000..62aa17c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}")
+  set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base3> --SUFFIXGROUP <other2> --PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP <other1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
new file mode 100644
index 0000000..255c9a6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
new file mode 100644
index 0000000..a8e0da7
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
new file mode 100644
index 0000000..32b58fe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
new file mode 100644
index 0000000..32b58fe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
new file mode 100644
index 0000000..3223a95
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
@@ -0,0 +1,81 @@
+
+enable_language(C)
+
+# ensure command line is always displayed and do not use any response file
+set(CMAKE_VERBOSE_MAKEFILE TRUE)
+set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+
+if (CMAKE_GENERATOR MATCHES "Borland|NMake")
+  string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
+  string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
+endif()
+
+add_library(base1 SHARED base.c)
+add_library(base2 SHARED base.c)
+
+set(CMAKE_C_LINK_USING_feat1 "--LIBFLAG<LIBRARY>")
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat1_1 "--LIBFLAG_C<LIBRARY>")
+set(CMAKE_C_LINK_USING_feat1_1_SUPPORTED FALSE)
+set(CMAKE_LINK_USING_feat1_1 "--LIBFLAG<LIBRARY>")
+set(CMAKE_LINK_USING_feat1_1_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat2 "--PREFIXGROUP" "--LIBGROUP<LIBRARY>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat3 "--PREFIXGROUP" "<LINK_ITEM>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat3_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat4 "--PREFIXGROUP" "--LIBFLAG<LIBRARY> --ITEMFLAG<LIB_ITEM>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat4_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat5 "--PREFIXGROUP" "PATH{--LIBFLAG<LIBRARY>}NAME{--ITEMFLAG<LIB_ITEM>}" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat5_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat6 "<LINK_ITEM>")
+set(CMAKE_C_LINK_USING_feat6_SUPPORTED TRUE)
+
+
+add_library(LinkLibrary_simple1 SHARED lib.c)
+target_link_libraries(LinkLibrary_simple1 PRIVATE "$<LINK_LIBRARY:feat1,base1>")
+
+add_library(LinkLibrary_simple2 SHARED lib.c)
+target_link_libraries(LinkLibrary_simple2 PRIVATE "$<LINK_LIBRARY:feat1_1,base1>")
+
+add_library(LinkLibrary_group1 SHARED lib.c)
+target_link_libraries(LinkLibrary_group1 PRIVATE "$<LINK_LIBRARY:feat1,base1,base2>")
+
+add_library(LinkLibrary_group2 SHARED lib.c)
+target_link_libraries(LinkLibrary_group2 PRIVATE "$<LINK_LIBRARY:feat2,base1,base2>")
+
+add_library(LinkLibrary_nested_feature1 SHARED lib.c)
+target_link_libraries(LinkLibrary_nested_feature1 PRIVATE "$<LINK_LIBRARY:feat1,base1,$<LINK_LIBRARY:feat1,base2>>")
+
+add_library(LinkLibrary_nested_feature2 SHARED lib.c)
+target_link_libraries(LinkLibrary_nested_feature2 PRIVATE "$<LINK_LIBRARY:feat2,base1,$<LINK_LIBRARY:feat2,base2>>")
+
+add_library(LinkLibrary_link_items1 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items1 PRIVATE "$<LINK_LIBRARY:feat3,base1,other>")
+
+add_library(LinkLibrary_link_items2 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items2 PRIVATE "$<LINK_LIBRARY:feat2,base1,other>")
+
+add_library(LinkLibrary_link_items3 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items3 PRIVATE "$<LINK_LIBRARY:feat4,base1,other>")
+
+add_library(LinkLibrary_link_items4 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items4 PRIVATE "$<LINK_LIBRARY:feat5,base1,other>")
+
+add_library(base3 SHARED base.c)
+target_link_libraries(base3 PRIVATE "$<LINK_LIBRARY:feat6,base1>")
+add_library(LinkLibrary_mix_features1 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features1 PRIVATE "$<LINK_LIBRARY:feat2,base1,base3>")
+
+target_link_libraries(base3 INTERFACE "$<LINK_LIBRARY:feat2,base1>")
+add_library(LinkLibrary_mix_features2 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features2 PRIVATE "$<LINK_LIBRARY:feat2,base1,base3>")
+
+target_link_libraries(base3 INTERFACE other1)
+add_library(LinkLibrary_mix_features3 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features3 PRIVATE base2 "$<LINK_LIBRARY:feat2,base1,base3>" other2)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
new file mode 100644
index 0000000..febada0
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -0,0 +1,73 @@
+
+include(RunCMake)
+
+cmake_policy(SET CMP0054 NEW)
+
+macro(run_cmake_target test subtest target)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --target ${target} --config Release --verbose ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+# Some environments are excluded because they are not able to honor verbose mode
+if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode"
+    OR (RunCMake_GENERATOR MATCHES "Visual Studio" AND MSVC_VERSION GREATER_EQUAL "1600"))
+    AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
+
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+
+  if (CMAKE_SYSTEM_NAME STREQUAL "Windows"
+      OR CMAKE_SYSTEM_NAME STREQUAL "CYGWIN"
+      OR CMAKE_SYSTEM_NAME STREQUAL "MSYS")
+    set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_IMPORT_LIBRARY_PREFIX})
+    set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_IMPORT_LIBRARY_SUFFIX})
+  else()
+    set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
+    set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
+  endif()
+  if (MINGW OR MSYS OR CYGWIN)
+    set(LINK_EXTERN_LIBRARY_SUFFIX "")
+  else()
+    set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
+  endif()
+
+  run_cmake(LINK_LIBRARY)
+
+  run_cmake_target(LINK_LIBRARY simple1 LinkLibrary_simple1)
+  run_cmake_target(LINK_LIBRARY simple2 LinkLibrary_simple2)
+  run_cmake_target(LINK_LIBRARY group1 LinkLibrary_group1)
+  run_cmake_target(LINK_LIBRARY group2 LinkLibrary_group2)
+  run_cmake_target(LINK_LIBRARY nested-feature1 LinkLibrary_nested_feature1)
+  run_cmake_target(LINK_LIBRARY nested-feature2 LinkLibrary_nested_feature2)
+  run_cmake_target(LINK_LIBRARY link-items1 LinkLibrary_link_items1)
+  run_cmake_target(LINK_LIBRARY link-items2 LinkLibrary_link_items2)
+  run_cmake_target(LINK_LIBRARY link-items3 LinkLibrary_link_items3)
+  run_cmake_target(LINK_LIBRARY link-items4 LinkLibrary_link_items4)
+  run_cmake_target(LINK_LIBRARY mix-features1 LinkLibrary_mix_features1)
+  run_cmake_target(LINK_LIBRARY mix-features2 LinkLibrary_mix_features2)
+  run_cmake_target(LINK_LIBRARY mix-features3 LinkLibrary_mix_features3)
+
+  run_cmake(imported-target)
+
+  # tests using features as described in the documentation
+  if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
+      OR (CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER "1900")
+      OR (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
+    run_cmake(whole_archive)
+    run_cmake_target(whole_archive link-exe main)
+  endif()
+  if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+    run_cmake(weak_library)
+    run_cmake_target(weak_library link-exe main)
+  endif()
+
+  unset(RunCMake_TEST_OPTIONS)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c
new file mode 100644
index 0000000..a5075d4
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c
@@ -0,0 +1,9 @@
+
+#if !defined(STATIC_BASE)
+#  if defined(_WIN32)
+__declspec(dllexport)
+#  endif
+#endif
+  void base()
+{
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
new file mode 100644
index 0000000..b3a86cc
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
@@ -0,0 +1,18 @@
+CMake Warning \(dev\) at imported-target.cmake:[0-9]+ \(add_library\):
+  The 'IMPORTED' target 'NS::lib2' uses the generator-expression
+  '\$<LINK_LIBRARY>' with the feature 'whole_archive', which is undefined or
+  unsupported.
+
+  Did you miss to define it by setting variables
+  "CMAKE_C_LINK_USING_whole_archive" and
+  "CMAKE_C_LINK_USING_whole_archive_SUPPORTED"\?
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at imported-target.cmake:[0-9]+ \(add_library\):
+  Feature 'whole_archive', specified through generator-expression
+  '\$<LINK_LIBRARY>' to link target 'lib', is not supported for the 'C' link
+  language.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake
new file mode 100644
index 0000000..9283054
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake
@@ -0,0 +1,18 @@
+
+enable_language(C)
+
+# Create imported target NS::lib
+add_library(NS::lib STATIC IMPORTED)
+
+# Create imported target NS::lib2
+add_library(NS::lib2 SHARED IMPORTED)
+
+set_target_properties(NS::lib2 PROPERTIES
+  IMPORTED_LOCATION "/path/to/lib"
+  IMPORTED_IMPLIB "/path/to/import.lib"
+  INTERFACE_LINK_LIBRARIES "$<LINK_LIBRARY:whole_archive,NS::lib>"
+)
+
+
+add_library(lib SHARED lib.c)
+target_link_libraries(lib PRIVATE NS::lib2)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c
new file mode 100644
index 0000000..35ab367
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c
@@ -0,0 +1,15 @@
+
+#if !defined(STATIC_BASE)
+#  if defined(_WIN32)
+__declspec(dllimport)
+#  endif
+#endif
+  void base();
+
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  void lib()
+{
+  base();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c
new file mode 100644
index 0000000..601bd96
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c
@@ -0,0 +1,18 @@
+
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+  void lib();
+
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+  void unref();
+
+int main()
+{
+  lib();
+  unref();
+
+  return 0;
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c
new file mode 100644
index 0000000..37c3206
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c
@@ -0,0 +1,8 @@
+
+
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  void unref()
+{
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake
new file mode 100644
index 0000000..135326e
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake
@@ -0,0 +1,20 @@
+
+enable_language(C)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+  set(CMAKE_LINK_USING_weak_library "PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}")
+  set(CMAKE_LINK_USING_weak_library_SUPPORTED TRUE)
+else()
+  # feature not yet supported for the other environments
+  set(CMAKE_LINK_USING_whole_library_SUPPORTED FALSE)
+endif()
+
+add_library(lib SHARED base.c lib.c unref.c)
+set_property(TARGET lib PROPERTY OUTPUT_NAME base)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE "$<LINK_LIBRARY:weak_library,lib>")
+
+add_executable(main2 main.c)
+target_link_directories(main2 PRIVATE "$<TARGET_FILE_DIR:lib>")
+target_link_libraries(main2 PRIVATE "$<LINK_LIBRARY:weak_library,base>")
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake
new file mode 100644
index 0000000..5214565
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake
@@ -0,0 +1,34 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED TRUE)
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+  set(CMAKE_C_LINK_USING_whole_archive "-force_load <LIB_ITEM>")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  execute_process(COMMAND "${CMAKE_LINKER}" --help
+                          OUTPUT_VARIABLE linker_help
+                          ERROR_VARIABLE linker_help)
+  if(linker_help MATCHES "--push-state" AND linker_help MATCHES "--pop-state")
+    set(CMAKE_C_LINK_USING_whole_archive "LINKER:--push-state,--whole-archive"
+                                         "<LINK_ITEM>"
+                                         "LINKER:--pop-state")
+  else()
+    set(CMAKE_C_LINK_USING_whole_archive "LINKER:--whole-archive"
+                                         "<LINK_ITEM>"
+                                         "LINKER:--no-whole-archive")
+  endif()
+elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+  set(CMAKE_C_LINK_USING_whole_archive "/WHOLEARCHIVE:<LIBRARY>")
+else()
+  # feature not yet supported for the other environments
+  set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED FALSE)
+endif()
+
+add_library(base STATIC base.c unref.c)
+target_compile_definitions(base PUBLIC STATIC_BASE)
+
+add_library(lib SHARED lib.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:whole_archive,base>")
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
-- 
cgit v0.12


From 2a6b0415d71db893b6d8edd1c5058d42eb40fca6 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Fri, 19 Nov 2021 19:08:30 +0100
Subject: $<LINK_LIBRARY>: Add LINK_LIBRARY_OVERRIDE target property

To enable the management of incompatible $<LINK_LIBRARY> declarations,
add LINK_LIBRARY_OVERRIDE and LINK_LIBRARY_OVERRIDE_<LIBRARY> target
properties.
---
 Help/manual/cmake-generator-expressions.7.rst      |  8 ++
 Help/manual/cmake-properties.7.rst                 |  2 +
 Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst            | 54 +++++++++++++
 Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst    | 45 +++++++++++
 Help/release/dev/Genex-LINK_LIBRARY.rst            |  5 +-
 Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst    |  7 ++
 Help/variable/CMAKE_LINK_USING_FEATURE.rst         |  7 ++
 Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt |  5 ++
 Modules/CMakeGenericSystem.cmake                   |  6 ++
 Source/cmComputeLinkDepends.cxx                    | 90 ++++++++++++++++++----
 Source/cmComputeLinkDepends.h                      |  8 +-
 Source/cmComputeLinkInformation.cxx                | 17 ++--
 Source/cmGeneratorExpressionDAGChecker.cxx         |  2 +-
 .../RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake | 11 ++-
 .../GenEx-LINK_LIBRARY/compatible-features.cmake   | 10 ++-
 .../incompatible-features-result.txt               |  1 -
 .../incompatible-features-stderr.txt               |  6 --
 .../GenEx-LINK_LIBRARY/incompatible-features.cmake | 15 ----
 .../incompatible-features1-result.txt              |  1 +
 .../incompatible-features1-stderr.txt              |  6 ++
 .../incompatible-features1.cmake                   | 15 ++++
 .../incompatible-features2-result.txt              |  1 +
 .../incompatible-features2-stderr.txt              |  6 ++
 .../incompatible-features2.cmake                   | 15 ++++
 .../incompatible-features3-result.txt              |  1 +
 .../incompatible-features3-stderr.txt              |  6 ++
 .../incompatible-features3.cmake                   | 15 ++++
 .../GenEx-LINK_LIBRARY/override-features1.cmake    |  4 +
 .../GenEx-LINK_LIBRARY/override-features2.cmake    |  4 +
 .../GenEx-LINK_LIBRARY/override-features3.cmake    |  7 ++
 .../GenEx-LINK_LIBRARY/override-features4.cmake    |  9 +++
 .../GenEx-LINK_LIBRARY/override-features5.cmake    |  7 ++
 .../LINK_LIBRARY-override-features1-check.cmake    |  4 +
 .../LINK_LIBRARY-override-features1-result.txt     |  1 +
 .../LINK_LIBRARY-override-features2-check.cmake    |  4 +
 .../LINK_LIBRARY-override-features2-result.txt     |  1 +
 .../LINK_LIBRARY-override-features3-check.cmake    |  4 +
 .../LINK_LIBRARY-override-features3-result.txt     |  1 +
 .../LINK_LIBRARY-override-features4-check.cmake    |  4 +
 .../LINK_LIBRARY-override-features4-result.txt     |  1 +
 .../LINK_LIBRARY-override-with-DEFAULT-check.cmake |  4 +
 .../LINK_LIBRARY-override-with-DEFAULT-result.txt  |  1 +
 .../LINK_LIBRARY.cmake                             | 27 ++++++-
 .../RunCMakeTest.cmake                             |  8 ++
 44 files changed, 404 insertions(+), 52 deletions(-)
 create mode 100644 Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst
 create mode 100644 Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst
 create mode 100644 Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
 delete mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
 delete mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
 delete mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-stderr.txt
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/override-features1.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/override-features2.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/override-features3.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/override-features4.cmake
 create mode 100644 Tests/RunCMake/GenEx-LINK_LIBRARY/override-features5.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake
 create mode 100644 Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt

diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index b67d479..7c34671 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1176,12 +1176,20 @@ Output-Related Expressions
     target_link_libraries(lib3 PRIVATE lib1 lib2)
     # an error will be raised here because lib1 has two different features
 
+  To resolve such incompatibilities, the :prop_tgt:`LINK_LIBRARY_OVERRIDE`
+  and  :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties can be
+  used.
+
   .. note::
 
     This expression does not guarantee that the list of specified libraries
     will be kept grouped. So, constructs like ``start-group`` and
     ``end-group``, as supported by ``GNU ld``, cannot be used.
 
+  ``CMake`` pre-defines some features of general interest:
+
+  .. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
+
 .. genex:: $<INSTALL_INTERFACE:...>
 
   Content of ``...`` when the property is exported using :command:`install(EXPORT)`,
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index f4efd3c..e8048b3 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -308,6 +308,8 @@ Properties on Targets
    /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG
    /prop_tgt/LINK_LIBRARIES
    /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS
+   /prop_tgt/LINK_LIBRARY_OVERRIDE
+   /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY
    /prop_tgt/LINK_OPTIONS
    /prop_tgt/LINK_SEARCH_END_STATIC
    /prop_tgt/LINK_SEARCH_START_STATIC
diff --git a/Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst b/Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst
new file mode 100644
index 0000000..e9c76b0
--- /dev/null
+++ b/Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst
@@ -0,0 +1,54 @@
+LINK_LIBRARY_OVERRIDE
+---------------------
+
+.. versionadded:: 3.24
+
+To resolve incompatible features introduced by :genex:`LINK_LIBRARY` generator
+expression, this property offers the possibility to override, per ``link-item``
+(``CMake`` target or external library) involved in the link step, any defined
+features with a new one.
+
+This property takes a :ref:`;-list <CMake Language Lists>` of override
+declarations which have the following format:
+
+::
+
+  feature[,link-item]*
+
+For the list of ``link-item`` (``CMake`` target or external library) specified,
+the feature ``feature`` will be used in place of any declared feature. For
+example:
+
+.. code-block:: cmake
+
+  add_library(lib1 ...)
+  target_link_libraries(lib1 PUBLIC $<LINK_LIBRARY:feature1,external>)
+
+  add_library(lib2 ...)
+  target_link_libraries(lib2 PUBLIC $<LINK_LIBRARY:feature2,lib1>)
+
+  add_library(lib3 ...)
+  target_link_libraries(lib3 PRIVATE lib1 lib2)
+  # Here, lib1 has two different features which prevents to link lib3
+  # So, define LINK_LIBRARY_OVERRIDE property to ensure correct link
+  set_property(TARGET lib3 PROPERTY LINK_LIBRARY_OVERRIDE "feature2,lib1,external")
+  # The lib1 and external will be used with FEATURE2 to link lib3
+
+It is also possible to override any feature with the pre-defined feature
+``DEFAULT`` to get the standard behavior (i.e. no feature):
+
+.. code-block:: cmake
+
+  set_property(TARGET lib3 PROPERTY LINK_LIBRARY_OVERRIDE "DEFAULT,lib1"
+                                                          "feature2,external")
+  # The lib1 will be used without any feature and external will use feature2 to link lib3
+
+Contents of ``LINK_LIBRARY_OVERRIDE`` may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+See also :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target property for
+a per linked target oriented approach to override features.
+
+For more information about features, see
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`
+and :variable:`CMAKE_LINK_USING_<FEATURE>` variables.
diff --git a/Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst b/Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst
new file mode 100644
index 0000000..58141c9
--- /dev/null
+++ b/Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst
@@ -0,0 +1,45 @@
+LINK_LIBRARY_OVERRIDE_<LIBRARY>
+-------------------------------
+
+.. versionadded:: 3.24
+
+To resolve incompatible features introduced by :genex:`LINK_LIBRARY` generator
+expression, this property offers the possibility to override, for a
+``link-item`` (``CMake`` target or external library) involved in the link step,
+any defined features with a new one.
+
+This property takes a ``feature`` name which will be applied to the
+``link-item`` specified by ``<LIBRARY>`` suffix property. For example:
+
+.. code-block:: cmake
+
+  add_library(lib1 ...)
+  target_link_libraries(lib1 PUBLIC $<LINK_LIBRARY:feature1,external>)
+
+  add_library(lib2 ...)
+  target_link_libraries(lib2 PUBLIC $<LINK_LIBRARY:feature2,lib1>)
+
+  add_library(lib3 ...)
+  target_link_libraries(lib3 PRIVATE lib1 lib2)
+  # Here, lib1 has two different features which prevents to link lib3
+  # So, define LINK_LIBRARY_OVERRIDE_lib1 property to ensure correct link
+  set_property(TARGET lib3 PROPERTY LINK_LIBRARY_OVERRIDE_lib1 feature2)
+  # The lib1 will be used with feature2 to link lib3
+
+It is also possible to override any feature with the pre-defined feature
+``DEFAULT`` to get the standard behavior (i.e. no feature):
+
+.. code-block:: cmake
+
+  set_property(TARGET lib3 PROPERTY LINK_LIBRARY_OVERRIDE_lib1 DEFAULT)
+  # The lib1 will be used without any feature to link lib3
+
+Contents of ``LINK_LIBRARY_OVERRIDE_<LIBRARY>`` may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property takes precedence over :prop_tgt:`LINK_LIBRARY_OVERRIDE`
+target property.
+
+For more information about features, see
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`
+and :variable:`CMAKE_LINK_USING_<FEATURE>` variables.
diff --git a/Help/release/dev/Genex-LINK_LIBRARY.rst b/Help/release/dev/Genex-LINK_LIBRARY.rst
index 6b87a4f..3234acb 100644
--- a/Help/release/dev/Genex-LINK_LIBRARY.rst
+++ b/Help/release/dev/Genex-LINK_LIBRARY.rst
@@ -5,4 +5,7 @@ Genex-LINK_LIBRARY
   libraries are specified during the link step. The variables
   :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` and
   :variable:`CMAKE_LINK_USING_<FEATURE>` are used to define features usable by
-  the :genex:`LINK_LIBRARY` generator expression.
+  the :genex:`LINK_LIBRARY` generator expression. Moreover, the
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE` and
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties are available
+  to resolve incompatible features.
diff --git a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
index ff8b3d9..9d7f87a 100644
--- a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
+++ b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
@@ -17,3 +17,10 @@ See also the associated variable
 independent from the link language.
 
 .. include:: CMAKE_LINK_USING_FEATURE.txt
+
+Predefined Features
+^^^^^^^^^^^^^^^^^^^
+
+``CMake`` pre-defines some features of general interest:
+
+.. include:: LINK_LIBRARY_PREDEFINED_FEATURES.txt
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
index 3d94461..0c9cadc 100644
--- a/Help/variable/CMAKE_LINK_USING_FEATURE.rst
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
@@ -21,3 +21,10 @@ for the linker language, the variable
 :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false or not set.
 
 .. include:: CMAKE_LINK_USING_FEATURE.txt
+
+Predefined Features
+^^^^^^^^^^^^^^^^^^^
+
+``CMake`` pre-defines some features of general interest:
+
+.. include:: LINK_LIBRARY_PREDEFINED_FEATURES.txt
diff --git a/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
new file mode 100644
index 0000000..dd22e14
--- /dev/null
+++ b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
@@ -0,0 +1,5 @@
+**Features available in all environments**
+
+* ``DEFAULT``: This feature enables default link expression. This is mainly
+  useful with :prop_tgt:`LINK_LIBRARY_OVERRIDE` and
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties.
diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake
index 649b6f7..9ae68c4 100644
--- a/Modules/CMakeGenericSystem.cmake
+++ b/Modules/CMakeGenericSystem.cmake
@@ -24,6 +24,12 @@ set(CMAKE_DL_LIBS "dl")
 set(CMAKE_FIND_LIBRARY_PREFIXES "lib")
 set(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a")
 
+# Define feature "DEFAULT" as supported. This special feature generates the
+# default option to link a library
+# This feature is intended to be used in LINK_LIBRARY_OVERRIDE and
+# LINK_LIBRARY_OVERRIDE_<LIBRARY> target properties
+set(CMAKE_LINK_USING_DEFAULT_SUPPORTED TRUE)
+
 set(CMAKE_AUTOGEN_ORIGIN_DEPENDS ON)
 set(CMAKE_AUTOMOC_COMPILER_PREDEFINES ON)
 if(NOT DEFINED CMAKE_AUTOMOC_PATH_PREFIX)
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index c3367ac..e6073cb 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,9 +11,12 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmComputeComponentGraph.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
@@ -200,6 +203,8 @@ bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
 }
 }
 
+const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
+
 cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
                                            const std::string& config,
                                            const std::string& linkLanguage)
@@ -212,6 +217,49 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
   this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
   this->LinkLanguage = linkLanguage;
 
+  // target oriented feature override property takes precedence over
+  // global override property
+  cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
+  auto const& keys = this->Target->GetPropertyKeys();
+  std::for_each(
+    keys.cbegin(), keys.cend(),
+    [this, &lloPrefix, &config, &linkLanguage](std::string const& key) {
+      if (cmHasPrefix(key, lloPrefix)) {
+        if (cmValue feature = this->Target->GetProperty(key)) {
+          if (!feature->empty() && key.length() > lloPrefix.length()) {
+            auto item = key.substr(lloPrefix.length());
+            cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(),
+                                                 this->Target,
+                                                 "LINK_LIBRARY_OVERRIDE",
+                                                 nullptr, nullptr };
+            auto overrideFeature = cmGeneratorExpression::Evaluate(
+              feature, this->Target->GetLocalGenerator(), config, this->Target,
+              &dag, this->Target, linkLanguage);
+            this->LinkLibraryOverride.emplace(item, overrideFeature);
+          }
+        }
+      }
+    });
+  // global override property
+  if (cmValue linkLibraryOverride =
+        this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
+    cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
+                                         "LINK_LIBRARY_OVERRIDE", nullptr,
+                                         nullptr };
+    auto overrideValue = cmGeneratorExpression::Evaluate(
+      linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
+      target, linkLanguage);
+
+    auto overrideList = cmTokenize(overrideValue, ","_s);
+    if (overrideList.size() >= 2) {
+      auto const& feature = overrideList.front();
+      for_each(overrideList.cbegin() + 1, overrideList.cend(),
+               [this, &feature](std::string const& item) {
+                 this->LinkLibraryOverride.emplace(item, feature);
+               });
+    }
+  }
+
   // The configuration being linked.
   this->HasConfig = !config.empty();
   this->Config = (this->HasConfig) ? config : std::string();
@@ -309,6 +357,13 @@ cmComputeLinkDepends::Compute()
   return this->FinalLinkEntries;
 }
 
+std::string const& cmComputeLinkDepends::GetCurrentFeature(
+  std::string const& item, std::string const& defaultFeature) const
+{
+  auto it = this->LinkLibraryOverride.find(item);
+  return it == this->LinkLibraryOverride.end() ? defaultFeature : it->second;
+}
+
 std::pair<std::map<cmLinkItem, int>::iterator, bool>
 cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
 {
@@ -568,7 +623,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
 {
   // Track inferred dependency sets implied by this list.
   std::map<int, DependSet> dependSets;
-  std::string feature;
+  std::string feature = LinkEntry::DEFAULT;
 
   // Loop over the libraries linked directly by the depender.
   for (T const& l : libs) {
@@ -604,7 +659,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
       continue;
     }
     if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
-      feature.clear();
+      feature = LinkEntry::DEFAULT;
       continue;
     }
 
@@ -612,7 +667,9 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
     auto ale = this->AddLinkEntry(item);
     int dependee_index = ale.first;
     LinkEntry& entry = this->EntryList[dependee_index];
-    if (!feature.empty()) {
+    auto const& itemFeature =
+      this->GetCurrentFeature(entry.Item.Value, feature);
+    if (itemFeature != LinkEntry::DEFAULT) {
       if (ale.second) {
         // current item not yet defined
         if (entry.Target != nullptr &&
@@ -633,7 +690,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
                      " library '", entry.Item.Value, "'."),
             this->Target->GetBacktrace());
         } else {
-          entry.Feature = feature;
+          entry.Feature = itemFeature;
         }
       }
     }
@@ -642,20 +699,21 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
       (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
        entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
 
-    if (supportedItem && entry.Feature != feature) {
+    if (supportedItem && 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 ",
-          (feature.empty() ? "without any feature"
-                           : cmStrCat("with the feature '", feature, '\'')),
-          ", has already occurred ",
-          (entry.Feature.empty()
-             ? "without any feature"
-             : cmStrCat("with the feature '", entry.Feature, '\'')),
-          ", which is not allowed."),
+        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());
     }
 
@@ -978,7 +1036,7 @@ void cmComputeLinkDepends::DisplayFinalEntries()
     } else {
       fprintf(stderr, "  item [%s]", lei.Item.Value.c_str());
     }
-    if (!lei.Feature.empty()) {
+    if (lei.Feature != LinkEntry::DEFAULT) {
       fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
     }
     fprintf(stderr, "\n");
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 02bdf50..64603e0 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -47,6 +47,8 @@ public:
     {
     }
 
+    static const std::string DEFAULT;
+
     BT<std::string> Item;
     cmGeneratorTarget const* Target = nullptr;
     bool IsSharedDep = false;
@@ -54,7 +56,7 @@ public:
     bool IsObject = false;
     // The following member is for the management of items specified
     // through genex $<LINK_LIBRARY:...>
-    std::string Feature;
+    std::string Feature = std::string(DEFAULT);
   };
 
   using EntryVector = std::vector<LinkEntry>;
@@ -75,6 +77,10 @@ private:
   std::string LinkLanguage;
   std::string Config;
   EntryVector FinalLinkEntries;
+  std::map<std::string, std::string> LinkLibraryOverride;
+
+  std::string const& GetCurrentFeature(
+    std::string const& item, std::string const& defaultFeature) const;
 
   std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
     cmLinkItem const& item);
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 5c3a96d..15e9d60 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -431,6 +431,10 @@ cmComputeLinkInformation::cmComputeLinkInformation(
 
 cmComputeLinkInformation::~cmComputeLinkInformation() = default;
 
+namespace {
+const std::string& DEFAULT = cmComputeLinkDepends::LinkEntry::DEFAULT;
+}
+
 void cmComputeLinkInformation::AppendValues(
   std::string& result, std::vector<BT<std::string>>& values)
 {
@@ -551,7 +555,7 @@ bool cmComputeLinkInformation::Compute()
       currentFeature = nullptr;
     }
 
-    if (!linkEntry.Feature.empty() &&
+    if (linkEntry.Feature != DEFAULT &&
         (currentFeature == nullptr ||
          linkEntry.Feature != currentFeature->Name)) {
       if (!this->AddLibraryFeature(linkEntry.Feature)) {
@@ -988,8 +992,9 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
       std::string exe = tgt->GetFullPath(config, artifact, true);
       this->Items.emplace_back(
         BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt,
-        this->FindLibraryFeature(
-          entry.Feature.empty() ? "__CMAKE_LINK_EXECUTABLE" : entry.Feature));
+        this->FindLibraryFeature(entry.Feature == DEFAULT
+                                   ? "__CMAKE_LINK_EXECUTABLE"
+                                   : entry.Feature));
       this->Depends.push_back(std::move(exe));
     } else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       // Add the interface library as an item so it can be considered as part
@@ -1421,7 +1426,7 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
 
   // Now add the full path to the library.
   this->Items.emplace_back(item, ItemIsPath::Yes, target,
-                           this->FindLibraryFeature(entry.Feature.empty()
+                           this->FindLibraryFeature(entry.Feature == DEFAULT
                                                       ? "__CMAKE_LINK_LIBRARY"
                                                       : entry.Feature));
 }
@@ -1482,7 +1487,7 @@ void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
   this->Items.emplace_back(
     item, ItemIsPath::Yes, nullptr,
     this->FindLibraryFeature(
-      entry.Feature.empty()
+      entry.Feature == DEFAULT
         ? (entry.IsObject ? "__CMAKE_LINK_OBJECT" : "__CMAKE_LINK_LIBRARY")
         : entry.Feature));
 }
@@ -1650,7 +1655,7 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
   // Create an option to ask the linker to search for the library.
   auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
 
-  if (!entry.Feature.empty()) {
+  if (entry.Feature != DEFAULT) {
     auto const& feature = this->GetLibraryFeature(entry.Feature);
     this->Items.emplace_back(
       BT<std::string>(
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index d4b02a5..d35d428 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -167,7 +167,7 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
   cm::string_view property(this->Top()->Property);
 
   return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
-    property == "LINK_DEPENDS"_s;
+    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
 }
 
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
index ab1ac37..d5438b8 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -19,7 +19,16 @@ run_cmake(bad-feature7)
 run_cmake(feature-not-supported)
 run_cmake(library-ignored)
 run_cmake(compatible-features)
-run_cmake(incompatible-features)
+run_cmake(incompatible-features1)
+run_cmake(incompatible-features2)
+run_cmake(incompatible-features3)
 run_cmake(nested-compatible-features)
 run_cmake(nested-incompatible-features)
 run_cmake(only-targets)
+
+# testing target propertes LINK_LIBRARY_OVERRIDE and LINK_LIBRARY_OVERRIDE_<LIBRARY>
+run_cmake(override-features1)
+run_cmake(override-features2)
+run_cmake(override-features3)
+run_cmake(override-features4)
+run_cmake(override-features5)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
index e79764c..2579a5f 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
@@ -11,5 +11,11 @@ add_library(dep1 SHARED empty.c)
 add_library(dep2 SHARED empty.c)
 target_link_libraries(dep2 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
 
-add_library(lib SHARED empty.c)
-target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
+add_library(dep3 SHARED empty.c)
+target_link_libraries(dep3 PUBLIC dep2)
+
+add_library(lib1 SHARED empty.c)
+target_link_libraries(lib1 PRIVATE $<LINK_LIBRARY:feat2,dep1,dep2>)
+
+add_library(lib2 SHARED empty.c)
+target_link_libraries(lib2 PRIVATE $<LINK_LIBRARY:DEFAULT,dep2,dep3>)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
deleted file mode 100644
index 22219a2..0000000
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at incompatible-features.cmake:[0-9]+ \(add_library\):
-  Impossible to link target 'lib' because the link item 'dep1', specified
-  with the feature 'feat1', has already occurred with the feature 'feat2',
-  which is not allowed.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
deleted file mode 100644
index d96b214..0000000
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-enable_language(C)
-
-set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
-set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
-
-set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
-set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
-
-add_library(dep1 SHARED empty.c)
-
-add_library(dep2 SHARED empty.c)
-target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
-
-add_library(lib SHARED empty.c)
-target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt
new file mode 100644
index 0000000..1b31faa
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at incompatible-features1.cmake:[0-9]+ \(add_library\):
+  Impossible to link target 'lib' because the link item 'dep1', specified
+  with the feature 'feat1', has already occurred with the feature 'feat2',
+  which is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake
new file mode 100644
index 0000000..d96b214
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-stderr.txt
new file mode 100644
index 0000000..0855481
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at incompatible-features2.cmake:[0-9]+ \(add_library\):
+  Impossible to link target 'lib' because the link item 'dep1', specified
+  without any feature or 'DEFAULT' feature, has already occurred with the
+  feature 'feat2', which is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2.cmake
new file mode 100644
index 0000000..1845fdb
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PUBLIC dep1)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE $<LINK_LIBRARY:feat2,dep1,dep2>)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-stderr.txt
new file mode 100644
index 0000000..2f40a1d
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at incompatible-features3.cmake:[0-9]+ \(add_library\):
+  Impossible to link target 'lib' because the link item 'dep1', specified
+  with the feature 'feat1', has already occurred without any feature or
+  'DEFAULT' feature, which is not allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3.cmake
new file mode 100644
index 0000000..1198d91
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PUBLIC $<LINK_LIBRARY:feat1,dep1>)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE dep1 dep2)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features1.cmake
new file mode 100644
index 0000000..6306c5d
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features1.cmake
@@ -0,0 +1,4 @@
+
+include(incompatible-features1.cmake)
+
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE "feat1,dep1")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features2.cmake
new file mode 100644
index 0000000..aa6ee76
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features2.cmake
@@ -0,0 +1,4 @@
+
+include(incompatible-features1.cmake)
+
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE "feat2,dep1")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features3.cmake
new file mode 100644
index 0000000..7f010fd
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features3.cmake
@@ -0,0 +1,7 @@
+
+include(incompatible-features1.cmake)
+
+set(CMAKE_C_LINK_USING_feat3_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat3 "<LIBRARY>")
+
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE "feat3,dep1")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features4.cmake
new file mode 100644
index 0000000..405cc8a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features4.cmake
@@ -0,0 +1,9 @@
+
+include(incompatible-features1.cmake)
+
+
+set(CMAKE_C_LINK_USING_feat3_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat3 "<LIBRARY>")
+
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE "feat3,dep1")
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE_dep1 feat1)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features5.cmake
new file mode 100644
index 0000000..1406d2a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/override-features5.cmake
@@ -0,0 +1,7 @@
+
+include(incompatible-features1.cmake)
+
+
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE "feat1,dep1")
+# next property will be ignored because no feature is specified
+set_property(TARGET lib PROPERTY LINK_LIBRARY_OVERRIDE_dep1)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake
new file mode 100644
index 0000000..a9fba20
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake
new file mode 100644
index 0000000..58c117e
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake
new file mode 100644
index 0000000..a9fba20
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake
new file mode 100644
index 0000000..58c117e
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake
new file mode 100644
index 0000000..d022f7e
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}\"?")
+  set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> <base1> <other1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
index 3223a95..ced689e 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
@@ -1,4 +1,3 @@
-
 enable_language(C)
 
 # ensure command line is always displayed and do not use any response file
@@ -78,4 +77,28 @@ target_link_libraries(LinkLibrary_mix_features2 PRIVATE "$<LINK_LIBRARY:feat2,ba
 
 target_link_libraries(base3 INTERFACE other1)
 add_library(LinkLibrary_mix_features3 SHARED lib.c)
-target_link_libraries(LinkLibrary_mix_features3 PRIVATE base2 "$<LINK_LIBRARY:feat2,base1,base3>" other2)
+target_link_libraries(LinkLibrary_mix_features3 PRIVATE base2 $<LINK_LIBRARY:feat2,base1,base3> other2)
+
+# testing LINK_LIBRARY_OVERRIDE property
+add_library(LinkLibrary_override_features1 SHARED lib.c)
+target_link_libraries(LinkLibrary_override_features1 PRIVATE $<LINK_LIBRARY:feat1,base1,base3>)
+set_property(TARGET LinkLibrary_override_features1 PROPERTY LINK_LIBRARY_OVERRIDE "feat1,base1")
+
+add_library(LinkLibrary_override_features2 SHARED lib.c)
+target_link_libraries(LinkLibrary_override_features2 PRIVATE $<LINK_LIBRARY:feat1,base1,base3>)
+set_property(TARGET LinkLibrary_override_features2 PROPERTY LINK_LIBRARY_OVERRIDE "feat2,base1,other1")
+
+add_library(LinkLibrary_override_with_default SHARED lib.c)
+target_link_libraries(LinkLibrary_override_with_default PRIVATE $<LINK_LIBRARY:feat1,base1,base3>)
+set_property(TARGET LinkLibrary_override_with_default PROPERTY LINK_LIBRARY_OVERRIDE "$<$<LINK_LANGUAGE:C>:DEFAULT,base1,other1>")
+
+# testing LINK_LIBRARY_OVERRIDE_<LIBRARY> property
+add_library(LinkLibrary_override_features3 SHARED lib.c)
+target_link_libraries(LinkLibrary_override_features3 PRIVATE $<LINK_LIBRARY:feat1,base1,base3>)
+set_property(TARGET LinkLibrary_override_features3 PROPERTY LINK_LIBRARY_OVERRIDE_base1 feat1)
+
+add_library(LinkLibrary_override_features4 SHARED lib.c)
+target_link_libraries(LinkLibrary_override_features4 PRIVATE $<LINK_LIBRARY:feat1,base1,base3>)
+set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE "feat3,base1,other1")
+set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_base1 feat2)
+set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_other1 feat2)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
index febada0..9ebbdb7 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -53,6 +53,14 @@ if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode"
   run_cmake_target(LINK_LIBRARY mix-features2 LinkLibrary_mix_features2)
   run_cmake_target(LINK_LIBRARY mix-features3 LinkLibrary_mix_features3)
 
+  # testing target property LINK_LIBRARY_OVERRIDE
+  run_cmake_target(LINK_LIBRARY override-features1 LinkLibrary_override_features1)
+  run_cmake_target(LINK_LIBRARY override-features2 LinkLibrary_override_features2)
+  run_cmake_target(LINK_LIBRARY override-with-DEFAULT LinkLibrary_override_with_default)
+  # testing target property LINK_LIBRARY_OVERRIDE_<LIBRARY>
+  run_cmake_target(LINK_LIBRARY override-features3 LinkLibrary_override_features3)
+  run_cmake_target(LINK_LIBRARY override-features4 LinkLibrary_override_features4)
+
   run_cmake(imported-target)
 
   # tests using features as described in the documentation
-- 
cgit v0.12