Browse Source

$<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.
Marc Chevrier 4 years ago
parent
commit
2a6b0415d7
41 changed files with 383 additions and 31 deletions
  1. 8 0
      Help/manual/cmake-generator-expressions.7.rst
  2. 2 0
      Help/manual/cmake-properties.7.rst
  3. 54 0
      Help/prop_tgt/LINK_LIBRARY_OVERRIDE.rst
  4. 45 0
      Help/prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY.rst
  5. 4 1
      Help/release/dev/Genex-LINK_LIBRARY.rst
  6. 7 0
      Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
  7. 7 0
      Help/variable/CMAKE_LINK_USING_FEATURE.rst
  8. 5 0
      Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
  9. 6 0
      Modules/CMakeGenericSystem.cmake
  10. 74 16
      Source/cmComputeLinkDepends.cxx
  11. 7 1
      Source/cmComputeLinkDepends.h
  12. 11 6
      Source/cmComputeLinkInformation.cxx
  13. 1 1
      Source/cmGeneratorExpressionDAGChecker.cxx
  14. 10 1
      Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
  15. 8 2
      Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
  16. 0 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt
  17. 1 1
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt
  18. 0 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake
  19. 1 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt
  20. 6 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-stderr.txt
  21. 15 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2.cmake
  22. 1 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt
  23. 6 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-stderr.txt
  24. 15 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3.cmake
  25. 4 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/override-features1.cmake
  26. 4 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/override-features2.cmake
  27. 7 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/override-features3.cmake
  28. 9 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/override-features4.cmake
  29. 7 0
      Tests/RunCMake/GenEx-LINK_LIBRARY/override-features5.cmake
  30. 4 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake
  31. 1 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
  32. 4 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake
  33. 1 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
  34. 4 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake
  35. 1 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
  36. 4 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake
  37. 1 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
  38. 4 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake
  39. 1 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt
  40. 25 2
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
  41. 8 0
      Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake

+ 8 - 0
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)`,

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

+ 54 - 0
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.

+ 45 - 0
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.

+ 4 - 1
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.

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

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

+ 5 - 0
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.

+ 6 - 0
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)

+ 74 - 16
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");

+ 7 - 1
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);

+ 11 - 6
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>(

+ 1 - 1
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

+ 10 - 1
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)

+ 8 - 2
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>)

+ 0 - 0
Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt → Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-result.txt


+ 1 - 1
Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt → Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1-stderr.txt

@@ -1,4 +1,4 @@
-CMake Error at incompatible-features.cmake:[0-9]+ \(add_library\):
+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.

+ 0 - 0
Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake → Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features1.cmake


+ 1 - 0
Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features2-result.txt

@@ -0,0 +1 @@
+1

+ 6 - 0
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\)

+ 15 - 0
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>)

+ 1 - 0
Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features3-result.txt

@@ -0,0 +1 @@
+1

+ 6 - 0
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\)

+ 15 - 0
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)

+ 4 - 0
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")

+ 4 - 0
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")

+ 7 - 0
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")

+ 9 - 0
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)

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

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt

@@ -0,0 +1 @@
+.*

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt

@@ -0,0 +1 @@
+.*

+ 25 - 2
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)

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