浏览代码

Genex: Add $<LINK_LANGUAGE:...> and $<LINK_LANG_AND_ID:...>

This MR may help to solve issues #19757 and #18008

Fixes: #19965
Marc Chevrier 5 年之前
父节点
当前提交
461efa7b51
共有 100 个文件被更改,包括 908 次插入53 次删除
  1. 114 0
      Help/manual/cmake-generator-expressions.7.rst
  2. 5 0
      Help/release/dev/genex-LINK_LANGUAGE.rst
  3. 3 0
      Source/cmGeneratorExpression.cxx
  4. 5 0
      Source/cmGeneratorExpression.h
  5. 1 0
      Source/cmGeneratorExpressionContext.cxx
  6. 1 0
      Source/cmGeneratorExpressionContext.h
  7. 26 8
      Source/cmGeneratorExpressionDAGChecker.cxx
  8. 1 0
      Source/cmGeneratorExpressionDAGChecker.h
  9. 149 0
      Source/cmGeneratorExpressionNode.cxx
  10. 130 34
      Source/cmGeneratorTarget.cxx
  11. 22 5
      Source/cmGeneratorTarget.h
  12. 6 0
      Source/cmLinkItem.h
  13. 22 0
      Tests/BuildDepends/CMakeLists.txt
  14. 8 0
      Tests/BuildDepends/Project/CMakeLists.txt
  15. 0 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_command-result.txt
  16. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_command-stderr.txt
  17. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_command.cmake
  18. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target-result.txt
  19. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target-stderr.txt
  20. 3 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target.cmake
  21. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable-result.txt
  22. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable-stderr.txt
  23. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable.cmake
  24. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library-result.txt
  25. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library-stderr.txt
  26. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library.cmake
  27. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test-result.txt
  28. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test-stderr.txt
  29. 5 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test.cmake
  30. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate-result.txt
  31. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate-stderr.txt
  32. 6 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate.cmake
  33. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install-result.txt
  34. 10 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install-stderr.txt
  35. 5 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install.cmake
  36. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources-result.txt
  37. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources-stderr.txt
  38. 2 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources.cmake
  39. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-unknown-lang.cmake
  40. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1-result.txt
  41. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1-stderr.txt
  42. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1.cmake
  43. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2-result.txt
  44. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2-stderr.txt
  45. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2.cmake
  46. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3-result.txt
  47. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3-stderr.txt
  48. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3.cmake
  49. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4-result.txt
  50. 8 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4-stderr.txt
  51. 7 0
      Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4.cmake
  52. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_command-result.txt
  53. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_command-stderr.txt
  54. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_command.cmake
  55. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target-result.txt
  56. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target-stderr.txt
  57. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target.cmake
  58. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable-result.txt
  59. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable-stderr.txt
  60. 5 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable.cmake
  61. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library-result.txt
  62. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library-stderr.txt
  63. 2 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library.cmake
  64. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test-result.txt
  65. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test-stderr.txt
  66. 5 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test.cmake
  67. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate-result.txt
  68. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate-stderr.txt
  69. 6 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate.cmake
  70. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install-result.txt
  71. 10 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install-stderr.txt
  72. 5 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install.cmake
  73. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources-result.txt
  74. 8 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources-stderr.txt
  75. 2 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources.cmake
  76. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-unknown-lang.cmake
  77. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1-result.txt
  78. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1-stderr.txt
  79. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1.cmake
  80. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2-result.txt
  81. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2-stderr.txt
  82. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2.cmake
  83. 1 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3-result.txt
  84. 9 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3-stderr.txt
  85. 4 0
      Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3.cmake
  86. 25 0
      Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
  87. 1 0
      Tests/RunCMake/Languages/LINKER_LANGUAGE-genex-result.txt
  88. 1 1
      Tests/RunCMake/Languages/LINKER_LANGUAGE-genex-stderr.txt
  89. 0 0
      Tests/RunCMake/Languages/LINKER_LANGUAGE-genex.cmake
  90. 1 1
      Tests/RunCMake/Languages/RunCMakeTest.cmake
  91. 16 0
      Tests/RunCMake/add_link_options/GENEX_LINK_LANG.cmake
  92. 4 1
      Tests/RunCMake/add_link_options/LINKER_expansion-validation.cmake
  93. 4 1
      Tests/RunCMake/add_link_options/LINK_OPTIONS-exe-check.cmake
  94. 4 1
      Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-check.cmake
  95. 4 1
      Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-check.cmake
  96. 7 0
      Tests/RunCMake/add_link_options/LinkOptionsLib.cxx
  97. 15 0
      Tests/RunCMake/add_link_options/RunCMakeTest.cmake
  98. 2 0
      Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-exe-check.cmake
  99. 1 0
      Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-exe-result.txt
  100. 2 0
      Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-mod-check.cmake

+ 114 - 0
Help/manual/cmake-generator-expressions.7.rst

@@ -259,6 +259,109 @@ Variable Queries
     add_executable(myapp main.cpp)
     target_link_libraries(myapp myapp_c myapp_cxx)
 
+.. _`Boolean LINK_LANGUAGE Generator Expression`:
+
+``$<LINK_LANG_AND_ID:language,compiler_ids>``
+  ``1`` when the language used for link step matches ``language`` and the
+  CMake's compiler id of the language linker matches any one of the entries
+  in ``compiler_ids``, otherwise ``0``. This expression is a short form for the
+  combination of ``$<LINK_LANGUAGE:language>`` and
+  ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
+  link libraries, link options, link directories and link dependencies of a
+  particular language and linker combination in a target. For example:
+
+  .. code-block:: cmake
+
+    add_library(libC_Clang ...)
+    add_library(libCXX_Clang ...)
+    add_library(libC_Intel ...)
+    add_library(libCXX_Intel ...)
+
+    add_executable(myapp main.c)
+    if (CXX_CONFIG)
+      target_sources(myapp PRIVATE file.cxx)
+    endif()
+    target_link_libraries(myapp
+      PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang>
+              $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang>
+              $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel>
+              $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>)
+
+  This specifies the use of different link libraries based on both the
+  compiler id and link language. This example will have target ``libCXX_Clang``
+  as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX``
+  linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker.
+  Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target
+  ``libC_Clang`` will be added as link dependency and ``libC_Intel`` when
+  ``Intel`` is the ``C`` linker.
+
+  See :ref:`the note related to
+  <Constraints LINK_LANGUAGE Generator Expression>`
+  ``$<LINK_LANGUAGE:language>`` for constraints about the usage of this
+  generator expression.
+
+``$<LINK_LANGUAGE:languages>``
+  ``1`` when the language used for link step matches any of the entries
+  in ``languages``, otherwise ``0``.  This expression may be used to specify
+  link libraries, link options, link directories and link dependencies of a
+  particular language in a target. For example:
+
+  .. code-block:: cmake
+
+    add_library(api_C ...)
+    add_library(api_CXX ...)
+    add_library(api INTERFACE)
+    target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c>
+                                        $<$<LINK_LANGUAGE:CXX>:-opt_cxx>)
+    target_link_libraries(api INTERFACE $<$<LINK_LANGUAGE:C>:api_C>
+                                        $<$<LINK_LANGUAGE:CXX>:api_CXX>)
+
+    add_executable(myapp1 main.c)
+    target_link_options(myapp1 PRIVATE api)
+
+    add_executable(myapp2 main.cpp)
+    target_link_options(myapp2 PRIVATE api)
+
+  This specifies to use the ``api`` target for linking targets ``myapp1`` and
+  ``myapp2``. In practice, ``myapp1`` will link with target ``api_C`` and
+  option ``-opt_c`` because it will use ``C`` as link language. And ``myapp2``
+  will link with ``api_CXX`` and option ``-opt_cxx`` because ``CXX`` will be
+  the link language.
+
+  .. _`Constraints LINK_LANGUAGE Generator Expression`:
+
+  .. note::
+
+    To determine the link language of a target, it is required to collect,
+    transitively, all the targets which will be linked to it. So, for link
+    libraries properties, a double evaluation will be done. During the first
+    evaluation, ``$<LINK_LANGUAGE:..>`` expressions will always return ``0``.
+    The link language computed after this first pass will be used to do the
+    second pass. To avoid inconsistency, it is required that the second pass
+    do not change the link language. Moreover, to avoid unexpected
+    side-effects, it is required to specify complete entities as part of the
+    ``$<LINK_LANGUAGE:..>`` expression. For example:
+
+    .. code-block:: cmake
+
+      add_library(lib STATIC file.cxx)
+      add_library(libother STATIC file.c)
+
+      # bad usage
+      add_executable(myapp1 main.c)
+      target_link_libraries(myapp1 PRIVATE lib$<$<LINK_LANGUAGE:C>:other>)
+
+      # correct usage
+      add_executable(myapp2 main.c)
+      target_link_libraries(myapp2 PRIVATE $<$<LINK_LANGUAGE:C>:libother>)
+
+    In this example, for ``myapp1``, the first pass will, unexpectedly,
+    determine that the link language is ``CXX`` because the evaluation of the
+    generator expression will be an empty string so ``myapp1`` will depends on
+    target ``lib`` which is ``C++``. On the contrary, for ``myapp2``, the first
+    evaluation will give ``C`` as link language, so the second pass will
+    correctly add target ``libother`` as link dependency.
+
 String-Valued Generator Expressions
 ===================================
 
@@ -450,6 +553,17 @@ Variable Queries
   <Boolean COMPILE_LANGUAGE Generator Expression>`
   ``$<COMPILE_LANGUAGE:language>``
   for notes about the portability of this generator expression.
+``$<LINK_LANGUAGE>``
+  The link language of target when evaluating link options.
+  See :ref:`the related boolean expression
+  <Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:language>``
+  for notes about the portability of this generator expression.
+
+  .. note::
+
+    This generator expression is not supported by the link libraries
+    properties to avoid side-effects due to the double evaluation of
+    these properties.
 
 Target-Dependent Queries
 ------------------------

+ 5 - 0
Help/release/dev/genex-LINK_LANGUAGE.rst

@@ -0,0 +1,5 @@
+genex-LINK_LANGUAGE
+===================
+
+* The ``$<LINK_LANGUAGE:...>`` and ``$<LINK_LANG_AND_ID:...>``
+  :manual:`generator expressions <cmake-generator-expressions(7)>` were added.

+ 3 - 0
Source/cmGeneratorExpression.cxx

@@ -103,6 +103,8 @@ const std::string& cmCompiledGeneratorExpression::EvaluateWithContext(
   if (!context.HadError) {
     this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
     this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
+    this->HadLinkLanguageSensitiveCondition =
+      context.HadLinkLanguageSensitiveCondition;
     this->SourceSensitiveTargets = context.SourceSensitiveTargets;
   }
 
@@ -119,6 +121,7 @@ cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
   , Quiet(false)
   , HadContextSensitiveCondition(false)
   , HadHeadSensitiveCondition(false)
+  , HadLinkLanguageSensitiveCondition(false)
 {
   cmGeneratorExpressionLexer l;
   std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);

+ 5 - 0
Source/cmGeneratorExpression.h

@@ -137,6 +137,10 @@ public:
   {
     return this->HadHeadSensitiveCondition;
   }
+  bool GetHadLinkLanguageSensitiveCondition() const
+  {
+    return this->HadLinkLanguageSensitiveCondition;
+  }
   std::set<cmGeneratorTarget const*> GetSourceSensitiveTargets() const
   {
     return this->SourceSensitiveTargets;
@@ -178,6 +182,7 @@ private:
   mutable std::string Output;
   mutable bool HadContextSensitiveCondition;
   mutable bool HadHeadSensitiveCondition;
+  mutable bool HadLinkLanguageSensitiveCondition;
   mutable std::set<cmGeneratorTarget const*> SourceSensitiveTargets;
 };
 

+ 1 - 0
Source/cmGeneratorExpressionContext.cxx

@@ -19,6 +19,7 @@ cmGeneratorExpressionContext::cmGeneratorExpressionContext(
   , HadError(false)
   , HadContextSensitiveCondition(false)
   , HadHeadSensitiveCondition(false)
+  , HadLinkLanguageSensitiveCondition(false)
   , EvaluateForBuildsystem(evaluateForBuildsystem)
 {
 }

+ 1 - 0
Source/cmGeneratorExpressionContext.h

@@ -40,6 +40,7 @@ struct cmGeneratorExpressionContext
   bool HadError;
   bool HadContextSensitiveCondition;
   bool HadHeadSensitiveCondition;
+  bool HadLinkLanguageSensitiveCondition;
   bool EvaluateForBuildsystem;
 };
 

+ 26 - 8
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -6,6 +6,10 @@
 #include <sstream>
 #include <utility>
 
+#include <cm/string_view>
+
+#include "cm_static_string_view.hxx"
+
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionEvaluator.h"
 #include "cmGeneratorTarget.h"
@@ -170,6 +174,21 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression()
   return top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
 }
 
+bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression()
+{
+  const cmGeneratorExpressionDAGChecker* top = this;
+  const cmGeneratorExpressionDAGChecker* parent = this->Parent;
+  while (parent) {
+    top = parent;
+    parent = parent->Parent;
+  }
+
+  cm::string_view property(top->Property);
+
+  return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
+    property == "LINK_DEPENDS"_s;
+}
+
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
   cmGeneratorTarget const* tgt)
 {
@@ -180,18 +199,17 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
     parent = parent->Parent;
   }
 
-  const char* prop = top->Property.c_str();
+  cm::string_view prop(top->Property);
 
   if (tgt) {
-    return top->Target == tgt && strcmp(prop, "LINK_LIBRARIES") == 0;
+    return top->Target == tgt && prop == "LINK_LIBRARIES"_s;
   }
 
-  return (strcmp(prop, "LINK_LIBRARIES") == 0 ||
-          strcmp(prop, "LINK_INTERFACE_LIBRARIES") == 0 ||
-          strcmp(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES") == 0 ||
-          cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
-          cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_")) ||
-    strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0;
+  return prop == "LINK_LIBRARIES"_s || prop == "LINK_INTERFACE_LIBRARIES"_s ||
+    prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
+    cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
+    cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_") ||
+    prop == "INTERFACE_LINK_LIBRARIES"_s;
 }
 
 cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const

+ 1 - 0
Source/cmGeneratorExpressionDAGChecker.h

@@ -68,6 +68,7 @@ struct cmGeneratorExpressionDAGChecker
 
   bool EvaluatingGenexExpression();
   bool EvaluatingPICExpression();
+  bool EvaluatingLinkExpression();
   bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr);
 
 #define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;

+ 149 - 0
Source/cmGeneratorExpressionNode.cxx

@@ -62,6 +62,9 @@ std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
   if (cge->GetHadHeadSensitiveCondition()) {
     context->HadHeadSensitiveCondition = true;
   }
+  if (cge->GetHadLinkLanguageSensitiveCondition()) {
+    context->HadLinkLanguageSensitiveCondition = true;
+  }
   return result;
 }
 
@@ -1039,6 +1042,150 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
   }
 } languageAndIdNode;
 
+static const struct LinkLanguageNode : public cmGeneratorExpressionNode
+{
+  LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
+
+  int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* dagChecker) const override
+  {
+    if (!context->HeadTarget || !dagChecker ||
+        !(dagChecker->EvaluatingLinkExpression() ||
+          dagChecker->EvaluatingLinkLibraries())) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<LINK_LANGUAGE:...> may only be used with binary targets "
+                  "to specify link libraries, link directories, link options "
+                  "and link depends.");
+      return std::string();
+    }
+    if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) {
+      reportError(
+        context, content->GetOriginalExpression(),
+        "$<LINK_LANGUAGE> is not supported in link libraries expression.");
+      return std::string();
+    }
+
+    cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
+    std::string genName = gg->GetName();
+    if (genName.find("Makefiles") == std::string::npos &&
+        genName.find("Ninja") == std::string::npos &&
+        genName.find("Visual Studio") == std::string::npos &&
+        genName.find("Xcode") == std::string::npos &&
+        genName.find("Watcom WMake") == std::string::npos) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<LINK_LANGUAGE:...> not supported for this generator.");
+      return std::string();
+    }
+
+    if (dagChecker->EvaluatingLinkLibraries()) {
+      context->HadHeadSensitiveCondition = true;
+      context->HadLinkLanguageSensitiveCondition = true;
+    }
+
+    if (parameters.empty()) {
+      return context->Language;
+    }
+
+    for (auto& param : parameters) {
+      if (context->Language == param) {
+        return "1";
+      }
+    }
+    return "0";
+  }
+} linkLanguageNode;
+
+namespace {
+struct LinkerId
+{
+  static std::string Evaluate(const std::vector<std::string>& parameters,
+                              cmGeneratorExpressionContext* context,
+                              const GeneratorExpressionContent* content,
+                              const std::string& lang)
+  {
+    std::string const& linkerId =
+      context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
+                                                    "_COMPILER_ID");
+    if (parameters.empty()) {
+      return linkerId;
+    }
+    if (linkerId.empty()) {
+      return parameters.front().empty() ? "1" : "0";
+    }
+    static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
+
+    for (auto& param : parameters) {
+      if (!linkerIdValidator.find(param)) {
+        reportError(context, content->GetOriginalExpression(),
+                    "Expression syntax not recognized.");
+        return std::string();
+      }
+
+      if (param == linkerId) {
+        return "1";
+      }
+    }
+    return "0";
+  }
+};
+}
+
+static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
+{
+  LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
+
+  int NumExpectedParameters() const override { return TwoOrMoreParameters; }
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* dagChecker) const override
+  {
+    if (!context->HeadTarget || !dagChecker ||
+        !(dagChecker->EvaluatingLinkExpression() ||
+          dagChecker->EvaluatingLinkLibraries())) {
+      reportError(
+        context, content->GetOriginalExpression(),
+        "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
+        "to specify link libraries, link directories, link options, and link "
+        "depends.");
+      return std::string();
+    }
+
+    cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
+    std::string genName = gg->GetName();
+    if (genName.find("Makefiles") == std::string::npos &&
+        genName.find("Ninja") == std::string::npos &&
+        genName.find("Visual Studio") == std::string::npos &&
+        genName.find("Xcode") == std::string::npos &&
+        genName.find("Watcom WMake") == std::string::npos) {
+      reportError(
+        context, content->GetOriginalExpression(),
+        "$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
+      return std::string();
+    }
+
+    if (dagChecker->EvaluatingLinkLibraries()) {
+      context->HadHeadSensitiveCondition = true;
+      context->HadLinkLanguageSensitiveCondition = true;
+    }
+
+    const std::string& lang = context->Language;
+    if (lang == parameters.front()) {
+      std::vector<std::string> idParameter((parameters.cbegin() + 1),
+                                           parameters.cend());
+      return LinkerId::Evaluate(idParameter, context, content, lang);
+    }
+    return "0";
+  }
+} linkLanguageAndIdNode;
+
 std::string getLinkedTargetsContent(
   cmGeneratorTarget const* target, std::string const& prop,
   cmGeneratorExpressionContext* context,
@@ -2314,6 +2461,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "LINK_ONLY", &linkOnlyNode },
     { "COMPILE_LANG_AND_ID", &languageAndIdNode },
     { "COMPILE_LANGUAGE", &languageNode },
+    { "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
+    { "LINK_LANGUAGE", &linkLanguageNode },
     { "SHELL_PATH", &shellPathNode }
   };
 

+ 130 - 34
Source/cmGeneratorTarget.cxx

@@ -17,6 +17,7 @@
 
 #include <cm/memory>
 #include <cm/string_view>
+#include <cmext/algorithm>
 
 #include "cmsys/RegularExpression.hxx"
 
@@ -307,6 +308,13 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
                                      this->SourceEntries, true);
 
   this->PolicyMap = t->GetPolicyMap();
+
+  // Get hard-coded linker language
+  if (this->Target->GetProperty("HAS_CXX")) {
+    this->LinkerLanguage = "CXX";
+  } else {
+    this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE");
+  }
 }
 
 cmGeneratorTarget::~cmGeneratorTarget() = default;
@@ -2222,11 +2230,12 @@ public:
   cmTargetCollectLinkLanguages(cmGeneratorTarget const* target,
                                std::string config,
                                std::unordered_set<std::string>& languages,
-                               cmGeneratorTarget const* head)
+                               cmGeneratorTarget const* head, bool secondPass)
     : Config(std::move(config))
     , Languages(languages)
     , HeadTarget(head)
     , Target(target)
+    , SecondPass(secondPass)
   {
     this->Visited.insert(target);
   }
@@ -2268,11 +2277,14 @@ public:
     if (!this->Visited.insert(item.Target).second) {
       return;
     }
-    cmLinkInterface const* iface =
-      item.Target->GetLinkInterface(this->Config, this->HeadTarget);
+    cmLinkInterface const* iface = item.Target->GetLinkInterface(
+      this->Config, this->HeadTarget, this->SecondPass);
     if (!iface) {
       return;
     }
+    if (iface->HadLinkLanguageSensitiveCondition) {
+      this->HadLinkLanguageSensitiveCondition = true;
+    }
 
     for (std::string const& language : iface->Languages) {
       this->Languages.insert(language);
@@ -2283,12 +2295,19 @@ public:
     }
   }
 
+  bool GetHadLinkLanguageSensitiveCondition()
+  {
+    return HadLinkLanguageSensitiveCondition;
+  }
+
 private:
   std::string Config;
   std::unordered_set<std::string>& Languages;
   cmGeneratorTarget const* HeadTarget;
   const cmGeneratorTarget* Target;
   std::set<cmGeneratorTarget const*> Visited;
+  bool SecondPass;
+  bool HadLinkLanguageSensitiveCondition = false;
 };
 
 cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure(
@@ -2319,7 +2338,7 @@ public:
   {
     this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator();
   }
-  void Consider(const char* lang)
+  void Consider(const std::string& lang)
   {
     int preference = this->GG->GetLinkerPreference(lang);
     if (preference > this->Preference) {
@@ -2352,40 +2371,36 @@ public:
   }
 };
 
-void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
-                                           LinkClosure& lc) const
+bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
+                                           LinkClosure& lc,
+                                           bool secondPass) const
 {
   // Get languages built in this target.
   std::unordered_set<std::string> languages;
-  cmLinkImplementation const* impl = this->GetLinkImplementation(config);
+  cmLinkImplementation const* impl =
+    this->GetLinkImplementation(config, secondPass);
   assert(impl);
-  for (std::string const& li : impl->Languages) {
-    languages.insert(li);
-  }
+  languages.insert(impl->Languages.cbegin(), impl->Languages.cend());
 
   // Add interface languages from linked targets.
-  cmTargetCollectLinkLanguages cll(this, config, languages, this);
+  // cmTargetCollectLinkLanguages cll(this, config, languages, this,
+  // secondPass);
+  cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass);
   for (cmLinkImplItem const& lib : impl->Libraries) {
     cll.Visit(lib);
   }
 
   // Store the transitive closure of languages.
-  for (std::string const& lang : languages) {
-    lc.Languages.push_back(lang);
-  }
+  cm::append(lc.Languages, languages);
 
   // Choose the language whose linker should be used.
-  if (this->GetProperty("HAS_CXX")) {
-    lc.LinkerLanguage = "CXX";
-  } else if (const char* linkerLang = this->GetProperty("LINKER_LANGUAGE")) {
-    lc.LinkerLanguage = linkerLang;
-  } else {
+  if (secondPass || lc.LinkerLanguage.empty()) {
     // Find the language with the highest preference value.
     cmTargetSelectLinker tsl(this);
 
     // First select from the languages compiled directly in this target.
     for (std::string const& l : impl->Languages) {
-      tsl.Consider(l.c_str());
+      tsl.Consider(l);
     }
 
     // Now consider languages that propagate from linked targets.
@@ -2393,12 +2408,50 @@ void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
       std::string propagates =
         "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES";
       if (this->Makefile->IsOn(propagates)) {
-        tsl.Consider(lang.c_str());
+        tsl.Consider(lang);
       }
     }
 
     lc.LinkerLanguage = tsl.Choose();
   }
+
+  return impl->HadLinkLanguageSensitiveCondition ||
+    cll.GetHadLinkLanguageSensitiveCondition();
+}
+
+void cmGeneratorTarget::ComputeLinkClosure(const std::string& config,
+                                           LinkClosure& lc) const
+{
+  bool secondPass = false;
+
+  {
+    LinkClosure linkClosure;
+    linkClosure.LinkerLanguage = this->LinkerLanguage;
+
+    // Get languages built in this target.
+    secondPass = this->ComputeLinkClosure(config, linkClosure, false);
+    this->LinkerLanguage = linkClosure.LinkerLanguage;
+    if (!secondPass) {
+      lc = std::move(linkClosure);
+    }
+  }
+
+  if (secondPass) {
+    LinkClosure linkClosure;
+
+    this->ComputeLinkClosure(config, linkClosure, secondPass);
+    lc = std::move(linkClosure);
+
+    // linker language must not be changed between the two passes
+    if (this->LinkerLanguage != lc.LinkerLanguage) {
+      std::ostringstream e;
+      e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> "
+           "changes\nthe linker language for target \""
+        << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '"
+        << lc.LinkerLanguage << "') which is invalid.";
+      cmSystemTools::Error(e.str());
+    }
+  }
 }
 
 void cmGeneratorTarget::GetFullNameComponents(
@@ -5479,7 +5532,8 @@ void cmGeneratorTarget::LookupLinkItems(std::vector<std::string> const& names,
 void cmGeneratorTarget::ExpandLinkItems(
   std::string const& prop, std::string const& value, std::string const& config,
   cmGeneratorTarget const* headTarget, bool usage_requirements_only,
-  std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition) const
+  std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition,
+  bool& hadLinkLanguageSensitiveCondition) const
 {
   // Keep this logic in sync with ComputeLinkImplementationLibraries.
   cmGeneratorExpression ge;
@@ -5491,19 +5545,28 @@ void cmGeneratorTarget::ExpandLinkItems(
   }
   std::vector<std::string> libs;
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
-  cmExpandList(
-    cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker, this),
-    libs);
+  cmExpandList(cge->Evaluate(this->LocalGenerator, config, headTarget,
+                             &dagChecker, this, headTarget->LinkerLanguage),
+               libs);
   this->LookupLinkItems(libs, cge->GetBacktrace(), items);
   hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition();
+  hadLinkLanguageSensitiveCondition =
+    cge->GetHadLinkLanguageSensitiveCondition();
 }
 
 cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
   const std::string& config, cmGeneratorTarget const* head) const
+{
+  return this->GetLinkInterface(config, head, false);
+}
+
+cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
+  const std::string& config, cmGeneratorTarget const* head,
+  bool secondPass) const
 {
   // Imported targets have their own link interface.
   if (this->IsImported()) {
-    return this->GetImportLinkInterface(config, head, false);
+    return this->GetImportLinkInterface(config, head, false, secondPass);
   }
 
   // Link interfaces are not supported for executables that do not
@@ -5516,6 +5579,10 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
   // Lookup any existing link interface for this configuration.
   cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config);
 
+  if (secondPass) {
+    hm.erase(head);
+  }
+
   // If the link interface does not depend on the head target
   // then return the one we computed first.
   if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
@@ -5530,7 +5597,7 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
   if (!iface.AllDone) {
     iface.AllDone = true;
     if (iface.Exists) {
-      this->ComputeLinkInterface(config, iface, head);
+      this->ComputeLinkInterface(config, iface, head, secondPass);
     }
   }
 
@@ -5540,6 +5607,13 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
 void cmGeneratorTarget::ComputeLinkInterface(
   const std::string& config, cmOptionalLinkInterface& iface,
   cmGeneratorTarget const* headTarget) const
+{
+  this->ComputeLinkInterface(config, iface, headTarget, false);
+}
+
+void cmGeneratorTarget::ComputeLinkInterface(
+  const std::string& config, cmOptionalLinkInterface& iface,
+  cmGeneratorTarget const* headTarget, bool secondPass) const
 {
   if (iface.Explicit) {
     if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
@@ -5552,7 +5626,8 @@ void cmGeneratorTarget::ComputeLinkInterface(
         emitted.insert(lib);
       }
       if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
-        cmLinkImplementation const* impl = this->GetLinkImplementation(config);
+        cmLinkImplementation const* impl =
+          this->GetLinkImplementation(config, secondPass);
         for (cmLinkImplItem const& lib : impl->Libraries) {
           if (emitted.insert(lib).second) {
             if (lib.Target) {
@@ -5582,7 +5657,7 @@ void cmGeneratorTarget::ComputeLinkInterface(
   if (this->LinkLanguagePropagatesToDependents()) {
     // Targets using this archive need its language runtime libraries.
     if (cmLinkImplementation const* impl =
-          this->GetLinkImplementation(config)) {
+          this->GetLinkImplementation(config, secondPass)) {
       iface.Languages = impl->Languages;
     }
   }
@@ -5978,7 +6053,8 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
     // The interface libraries have been explicitly set.
     this->ExpandLinkItems(linkIfaceProp, explicitLibraries, config, headTarget,
                           usage_requirements_only, iface.Libraries,
-                          iface.HadHeadSensitiveCondition);
+                          iface.HadHeadSensitiveCondition,
+                          iface.HadLinkLanguageSensitiveCondition);
   } else if (!cmp0022NEW)
   // If CMP0022 is NEW then the plain tll signature sets the
   // INTERFACE_LINK_LIBRARIES, so if we get here then the project
@@ -5998,9 +6074,11 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
       static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
       if (const char* newExplicitLibraries = this->GetProperty(newProp)) {
         bool hadHeadSensitiveConditionDummy = false;
+        bool hadLinkLanguageSensitiveConditionDummy = false;
         this->ExpandLinkItems(newProp, newExplicitLibraries, config,
                               headTarget, usage_requirements_only, ifaceLibs,
-                              hadHeadSensitiveConditionDummy);
+                              hadHeadSensitiveConditionDummy,
+                              hadLinkLanguageSensitiveConditionDummy);
       }
       if (ifaceLibs != iface.Libraries) {
         std::string oldLibraries = cmJoin(impl->Libraries, ";");
@@ -6037,7 +6115,7 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
 
 const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
   const std::string& config, cmGeneratorTarget const* headTarget,
-  bool usage_requirements_only) const
+  bool usage_requirements_only, bool secondPass) const
 {
   cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config);
   if (!info) {
@@ -6050,6 +6128,10 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
        ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
        : this->GetHeadToLinkInterfaceMap(config));
 
+  if (secondPass) {
+    hm.erase(headTarget);
+  }
+
   // If the link interface does not depend on the head target
   // then return the one we computed first.
   if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
@@ -6063,7 +6145,8 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
     cmExpandList(info->Languages, iface.Languages);
     this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
                           headTarget, usage_requirements_only, iface.Libraries,
-                          iface.HadHeadSensitiveCondition);
+                          iface.HadHeadSensitiveCondition,
+                          iface.HadLinkLanguageSensitiveCondition);
     std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
     this->LookupLinkItems(deps, cmListFileBacktrace(), iface.SharedDeps);
   }
@@ -6267,6 +6350,12 @@ cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap(
 
 const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
   const std::string& config) const
+{
+  return this->GetLinkImplementation(config, false);
+}
+
+const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
+  const std::string& config, bool secondPass) const
 {
   // There is no link implementation for imported targets.
   if (this->IsImported()) {
@@ -6275,6 +6364,9 @@ const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
 
   std::string CONFIG = cmSystemTools::UpperCase(config);
   cmOptionalLinkImplementation& impl = this->LinkImplMap[CONFIG][this];
+  if (secondPass) {
+    impl = cmOptionalLinkImplementation();
+  }
   if (!impl.LibrariesDone) {
     impl.LibrariesDone = true;
     this->ComputeLinkImplementationLibraries(config, impl, this);
@@ -6572,11 +6664,15 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
     cmGeneratorExpression ge(*btIt);
     std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le);
     std::string const& evaluated =
-      cge->Evaluate(this->LocalGenerator, config, head, &dagChecker);
+      cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
+                    this->LinkerLanguage);
     cmExpandList(evaluated, llibs);
     if (cge->GetHadHeadSensitiveCondition()) {
       impl.HadHeadSensitiveCondition = true;
     }
+    if (cge->GetHadLinkLanguageSensitiveCondition()) {
+      impl.HadLinkLanguageSensitiveCondition = true;
+    }
 
     for (std::string const& lib : llibs) {
       if (this->IsLinkLookupScope(lib, lg)) {

+ 22 - 5
Source/cmGeneratorTarget.h

@@ -357,7 +357,6 @@ public:
   };
 
   LinkClosure const* GetLinkClosure(const std::string& config) const;
-  void ComputeLinkClosure(const std::string& config, LinkClosure& lc) const;
 
   cmLinkImplementation const* GetLinkImplementation(
     const std::string& config) const;
@@ -816,6 +815,7 @@ private:
                            std::string& outPrefix, std::string& outBase,
                            std::string& outSuffix) const;
 
+  mutable std::string LinkerLanguage;
   using LinkClosureMapType = std::map<std::string, LinkClosure>;
   mutable LinkClosureMapType LinkClosureMap;
 
@@ -850,6 +850,10 @@ private:
   void CheckPropertyCompatibility(cmComputeLinkInformation& info,
                                   const std::string& config) const;
 
+  void ComputeLinkClosure(const std::string& config, LinkClosure& lc) const;
+  bool ComputeLinkClosure(const std::string& config, LinkClosure& lc,
+                          bool secondPass) const;
+
   struct LinkImplClosure : public std::vector<cmGeneratorTarget const*>
   {
     bool Done = false;
@@ -868,6 +872,17 @@ private:
   std::string GetLinkInterfaceDependentStringAsBoolProperty(
     const std::string& p, const std::string& config) const;
 
+  friend class cmTargetCollectLinkLanguages;
+  cmLinkInterface const* GetLinkInterface(const std::string& config,
+                                          const cmGeneratorTarget* headTarget,
+                                          bool secondPass) const;
+  void ComputeLinkInterface(const std::string& config,
+                            cmOptionalLinkInterface& iface,
+                            const cmGeneratorTarget* head,
+                            bool secondPass) const;
+  cmLinkImplementation const* GetLinkImplementation(const std::string& config,
+                                                    bool secondPass) const;
+
   // Cache import information from properties for each configuration.
   struct ImportInfo
   {
@@ -894,9 +909,10 @@ private:
       the link dependencies of this target.  */
   std::string CheckCMP0004(std::string const& item) const;
 
-  cmLinkInterface const* GetImportLinkInterface(
-    const std::string& config, const cmGeneratorTarget* head,
-    bool usage_requirements_only) const;
+  cmLinkInterface const* GetImportLinkInterface(const std::string& config,
+                                                const cmGeneratorTarget* head,
+                                                bool usage_requirements_only,
+                                                bool secondPass = false) const;
 
   using KindedSourcesMapType = std::map<std::string, KindedSources>;
   mutable KindedSourcesMapType KindedSourcesMap;
@@ -940,7 +956,8 @@ private:
                        const cmGeneratorTarget* headTarget,
                        bool usage_requirements_only,
                        std::vector<cmLinkItem>& items,
-                       bool& hadHeadSensitiveCondition) const;
+                       bool& hadHeadSensitiveCondition,
+                       bool& hadLinkLanguageSensitiveCondition) const;
   void LookupLinkItems(std::vector<std::string> const& names,
                        cmListFileBacktrace const& bt,
                        std::vector<cmLinkItem>& items) const;

+ 6 - 0
Source/cmLinkItem.h

@@ -81,6 +81,9 @@ struct cmLinkInterface : public cmLinkInterfaceLibraries
   std::vector<cmLinkItem> WrongConfigLibraries;
 
   bool ImplementationIsInterface = false;
+
+  // Whether the list depends on a link language genex.
+  bool HadLinkLanguageSensitiveCondition = false;
 };
 
 struct cmOptionalLinkInterface : public cmLinkInterface
@@ -100,6 +103,9 @@ struct cmLinkImplementation : public cmLinkImplementationLibraries
 {
   // Languages whose runtime libraries must be linked.
   std::vector<std::string> Languages;
+
+  // Whether the list depends on a link language genex.
+  bool HadLinkLanguageSensitiveCondition = false;
 };
 
 // Cache link implementation computation from each configuration.

+ 22 - 0
Tests/BuildDepends/CMakeLists.txt

@@ -366,6 +366,28 @@ is not newer than dependency
   ${linkdep2}
 is not newer than dependency
   ${TEST_LINK_DEPENDS}
+")
+  endif()
+
+  set(linkdep3 ${BuildDepends_BINARY_DIR}/Project/linkdep3${CMAKE_EXECUTABLE_SUFFIX})
+  if(${linkdep3} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
+    message("$<LINK_LANGUAGE> in LINK_DEPENDS worked")
+  else()
+    message(SEND_ERROR "$<LINK_LANGUAGE> in LINK_DEPENDS failed.  Executable
+  ${linkdep3}
+is not newer than dependency
+  ${TEST_LINK_DEPENDS}
+")
+  endif()
+
+  set(linkdep4 ${BuildDepends_BINARY_DIR}/Project/linkdep4${CMAKE_EXECUTABLE_SUFFIX})
+  if(${linkdep4} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
+    message("$<LINK_LANGUAGE> in INTERFACE_LINK_DEPENDS worked")
+  else()
+    message(SEND_ERROR "$<LINK_LANGUAGE> in INTERFACE_LINK_DEPENDS failed.  Executable
+  ${linkdep4}
+is not newer than dependency
+  ${TEST_LINK_DEPENDS}
 ")
   endif()
 endif()

+ 8 - 0
Tests/BuildDepends/Project/CMakeLists.txt

@@ -118,6 +118,14 @@ if(TEST_LINK_DEPENDS)
   set_property(TARGET foo_interface PROPERTY INTERFACE_LINK_DEPENDS $<1:${TEST_LINK_DEPENDS}>)
   add_executable(linkdep2 linkdep.cxx)
   target_link_libraries(linkdep2 PRIVATE foo_interface)
+
+  add_executable(linkdep3 linkdep.cxx)
+  set_property(TARGET linkdep3 PROPERTY LINK_DEPENDS $<$<LINK_LANGUAGE:CXX>:${TEST_LINK_DEPENDS}>)
+
+  add_library(foo_interface2 INTERFACE)
+  set_property(TARGET foo_interface2 PROPERTY INTERFACE_LINK_DEPENDS $<$<LINK_LANGUAGE:CXX>:${TEST_LINK_DEPENDS}>)
+  add_executable(linkdep4 linkdep.cxx)
+  target_link_libraries(linkdep4 PRIVATE foo_interface2)
 endif()
 
 add_library(link_depends_no_shared_lib SHARED link_depends_no_shared_lib.c

+ 0 - 0
Tests/RunCMake/Languages/LINK_LANGUAGE-genex-result.txt → Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_command-result.txt


+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_command-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-add_custom_command.cmake:2 \(add_custom_command\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-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_LANGUAGE>
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-add_custom_target.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 3 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_custom_target.cmake

@@ -0,0 +1,3 @@
+add_custom_target(drive
+  COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANGUAGE>
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-add_executable.cmake:1 \(add_executable\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_executable.cmake

@@ -0,0 +1 @@
+add_executable(empty empty.$<LINK_LANGUAGE>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-add_library.cmake:1 \(add_library\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_library.cmake

@@ -0,0 +1 @@
+add_library(empty empty.$<LINK_LANGUAGE>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-add_test.cmake:5 \(add_test\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-add_test.cmake

@@ -0,0 +1,5 @@
+
+include(CTest)
+enable_testing()
+
+add_test(NAME dummy COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANGUAGE>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-file_generate.cmake:3 \(file\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 6 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-file_generate.cmake

@@ -0,0 +1,6 @@
+enable_language(CXX C)
+
+file(GENERATE
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
+  CONTENT "LANG_IS_$<LINK_LANGUAGE>\n"
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install-result.txt

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

+ 10 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error:
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 5 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-install.cmake

@@ -0,0 +1,5 @@
+
+install(FILES
+  empty.$<LINK_LANGUAGE>
+  DESTINATION src
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-target_sources.cmake:2 \(target_sources\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 2 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-target_sources.cmake

@@ -0,0 +1,2 @@
+add_library(empty)
+target_sources(empty PRIVATE empty.$<LINK_LANGUAGE>)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-unknown-lang.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_link_options(empty PRIVATE $<$<LINK_LANGUAGE:CXX>:$<TARGET_EXISTS:too,many,parameters>>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-wrong-usage1.cmake:4 \(target_compile_definitions\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE:C>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage1.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_compile_definitions(empty PRIVATE $<$<LINK_LANGUAGE:C>:DEF>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-wrong-usage2.cmake:4 \(target_compile_options\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE:C>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage2.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_compile_options(empty PRIVATE $<$<LINK_LANGUAGE:C>:-OPT>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANGUAGE-wrong-usage3.cmake:4 \(target_include_directories\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE:C>
+
+  \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link
+  libraries, link directories, link options and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage3.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_include_directories(empty PRIVATE $<$<LINK_LANGUAGE:C>:/DIR>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4-result.txt

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

+ 8 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at LINK_LANGUAGE-wrong-usage4.cmake:7 \(target_link_libraries\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANGUAGE>
+
+  \$<LINK_LANGUAGE> is not supported in link libraries expression.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 7 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANGUAGE-wrong-usage4.cmake

@@ -0,0 +1,7 @@
+
+enable_language(C)
+
+add_library(libC empty.c)
+
+add_executable(empty empty.c)
+target_link_libraries(empty PRIVATE lib$<LINK_LANGUAGE>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_command-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_command-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-add_custom_command.cmake:2 \(add_custom_command\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:LANG,ID>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-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_LANG_AND_ID:LANG,ID>
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-add_custom_target.cmake:2 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:LANG,ID>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_custom_target.cmake

@@ -0,0 +1,4 @@
+
+add_custom_target(drive
+  COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANG_AND_ID:LANG,ID>
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-add_executable.cmake:1 \(add_executable\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,MSVC>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_executable.cmake

@@ -0,0 +1,5 @@
+add_executable(empty main.c
+        $<$<LINK_LANG_AND_ID:C,MSVC>:empty.c>
+        $<$<LINK_LANG_AND_ID:C,GNU>:empty2.c>
+        $<$<LINK_LANG_AND_ID:C,Clang>:empty3.c>
+        )

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-add_library.cmake:2 \(add_library\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,MSVC>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 2 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_library.cmake

@@ -0,0 +1,2 @@
+
+add_library(empty empty.$<LINK_LANG_AND_ID:C,MSVC>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-add_test.cmake:5 \(add_test\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:CXX,GNU>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-add_test.cmake

@@ -0,0 +1,5 @@
+
+include(CTest)
+enable_testing()
+
+add_test(NAME dummy COMMAND ${CMAKE_COMMAND} -E echo $<LINK_LANG_AND_ID:CXX,GNU>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-file_generate.cmake:3 \(file\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,GNU>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 6 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-file_generate.cmake

@@ -0,0 +1,6 @@
+enable_language(CXX C)
+
+file(GENERATE
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
+  CONTENT "LANG_IS_$<$<LINK_LANG_AND_ID:C,GNU>:C>\n"
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install-result.txt

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

+ 10 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error:
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,MSVC>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 5 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-install.cmake

@@ -0,0 +1,5 @@
+
+install(FILES
+  empty.$<LINK_LANG_AND_ID:C,MSVC>
+  DESTINATION src
+)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources-result.txt

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

+ 8 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at LINK_LANG_AND_ID-target_sources.cmake:2 \(target_sources\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID>
+
+  \$<LINK_LANG_AND_ID> expression requires at least two parameters.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 2 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-target_sources.cmake

@@ -0,0 +1,2 @@
+add_library(empty)
+target_sources(empty PRIVATE empty.$<LINK_LANG_AND_ID>)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-unknown-lang.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_link_options(empty PRIVATE $<$<LINK_LANG_AND_ID:CXX,GNU>:$<TARGET_EXISTS:too,many,parameters>>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-wrong-usage1.cmake:4 \(target_compile_definitions\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,GNU>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage1.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_compile_definitions(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:DEF>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-wrong-usage2.cmake:4 \(target_compile_options\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,GNU>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage2.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_compile_options(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:-OPT>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3-result.txt

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

+ 9 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3-stderr.txt

@@ -0,0 +1,9 @@
+CMake Error at LINK_LANG_AND_ID-wrong-usage3.cmake:4 \(target_include_directories\):
+  Error evaluating generator expression:
+
+    \$<LINK_LANG_AND_ID:C,GNU>
+
+  \$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets to specify
+  link libraries, link directories, link options, and link depends.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/GeneratorExpression/LINK_LANG_AND_ID-wrong-usage3.cmake

@@ -0,0 +1,4 @@
+
+enable_language(C)
+add_executable(empty empty.c)
+target_include_directories(empty PRIVATE $<$<LINK_LANG_AND_ID:C,GNU>:/DIR>)

+ 25 - 0
Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake

@@ -39,6 +39,31 @@ run_cmake(COMPILE_LANG_AND_ID-add_executable)
 run_cmake(COMPILE_LANG_AND_ID-add_library)
 run_cmake(COMPILE_LANG_AND_ID-add_test)
 run_cmake(COMPILE_LANG_AND_ID-unknown-lang)
+run_cmake(LINK_LANGUAGE-add_custom_target)
+run_cmake(LINK_LANGUAGE-add_custom_command)
+run_cmake(LINK_LANGUAGE-install)
+run_cmake(LINK_LANGUAGE-target_sources)
+run_cmake(LINK_LANGUAGE-add_executable)
+run_cmake(LINK_LANGUAGE-add_library)
+run_cmake(LINK_LANGUAGE-add_test)
+run_cmake(LINK_LANGUAGE-unknown-lang)
+run_cmake(LINK_LANGUAGE-wrong-usage1)
+run_cmake(LINK_LANGUAGE-wrong-usage2)
+run_cmake(LINK_LANGUAGE-wrong-usage3)
+run_cmake(LINK_LANGUAGE-wrong-usage4)
+run_cmake(LINK_LANGUAGE-file_generate)
+run_cmake(LINK_LANG_AND_ID-add_custom_target)
+run_cmake(LINK_LANG_AND_ID-add_custom_command)
+run_cmake(LINK_LANG_AND_ID-install)
+run_cmake(LINK_LANG_AND_ID-target_sources)
+run_cmake(LINK_LANG_AND_ID-add_executable)
+run_cmake(LINK_LANG_AND_ID-add_library)
+run_cmake(LINK_LANG_AND_ID-add_test)
+run_cmake(LINK_LANG_AND_ID-unknown-lang)
+run_cmake(LINK_LANG_AND_ID-wrong-usage1)
+run_cmake(LINK_LANG_AND_ID-wrong-usage2)
+run_cmake(LINK_LANG_AND_ID-wrong-usage3)
+run_cmake(LINK_LANG_AND_ID-file_generate)
 run_cmake(TARGET_FILE-recursion)
 run_cmake(OUTPUT_NAME-recursion)
 run_cmake(TARGET_FILE_PREFIX)

+ 1 - 0
Tests/RunCMake/Languages/LINKER_LANGUAGE-genex-result.txt

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

+ 1 - 1
Tests/RunCMake/Languages/LINK_LANGUAGE-genex-stderr.txt → Tests/RunCMake/Languages/LINKER_LANGUAGE-genex-stderr.txt

@@ -1,4 +1,4 @@
-CMake Error at LINK_LANGUAGE-genex.cmake:[0-9]+ \(target_link_libraries\):
+CMake Error at LINKER_LANGUAGE-genex.cmake:[0-9]+ \(target_link_libraries\):
   Error evaluating generator expression:
 
     \$<TARGET_PROPERTY:LINKER_LANGUAGE>

+ 0 - 0
Tests/RunCMake/Languages/LINK_LANGUAGE-genex.cmake → Tests/RunCMake/Languages/LINKER_LANGUAGE-genex.cmake


+ 1 - 1
Tests/RunCMake/Languages/RunCMakeTest.cmake

@@ -1,7 +1,7 @@
 include(RunCMake)
 
 run_cmake(NoLangSHARED)
-run_cmake(LINK_LANGUAGE-genex)
+run_cmake(LINKER_LANGUAGE-genex)
 run_cmake(link-libraries-TARGET_FILE-genex)
 run_cmake(link-libraries-TARGET_FILE-genex-ok)
 

+ 16 - 0
Tests/RunCMake/add_link_options/GENEX_LINK_LANG.cmake

@@ -0,0 +1,16 @@
+
+enable_language(C)
+
+set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
+if(BORLAND)
+  set(pre -)
+endif()
+
+add_link_options ($<$<LINK_LANGUAGE:C>:${pre}BADFLAG_LANG_C${obj}>
+                  $<$<LINK_LANGUAGE:CXX>:${pre}BADFLAG_LANG_CXX${obj}>)
+
+add_library(LinkOptions_shared SHARED LinkOptionsLib.c)
+
+add_library(LinkOptions_mod MODULE LinkOptionsLib.c)
+
+add_executable(LinkOptions_exe LinkOptionsExe.c)

+ 4 - 1
Tests/RunCMake/add_link_options/LINKER_expansion-validation.cmake

@@ -11,5 +11,8 @@ endif()
 file(READ "${RunCMake_TEST_BINARY_DIR}/LINKER.txt" linker_flag)
 
 if (NOT actual_stdout MATCHES "${linker_flag}")
-  set (RunCMake_TEST_FAILED "LINKER: was not expanded correctly.")
+  if (RunCMake_TEST_FAILED)
+    string (APPEND RunCMake_TEST_FAILED "\n")
+  endif()
+  string (APPEND RunCMake_TEST_FAILED "LINKER: was not expanded correctly.")
 endif()

+ 4 - 1
Tests/RunCMake/add_link_options/LINK_OPTIONS-exe-check.cmake

@@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_EXECUTABLE_RELEASE")
   set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_EXECUTABLE_RELEASE'.")
 endif()
 if (actual_stdout MATCHES "BADFLAG_(SHARED|MODULE)_RELEASE")
-  set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|MODULE)_RELEASE'.")
+  if (RunCMake_TEST_FAILED)
+    string (APPEND RunCMake_TEST_FAILED "\n")
+  endif()
+  string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|MODULE)_RELEASE'.")
 endif()

+ 4 - 1
Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-check.cmake

@@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_MODULE_RELEASE")
   set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_MODULE_RELEASE'.")
 endif()
 if (actual_stdout MATCHES "BADFLAG_(SHARED|EXECUTABLE)_RELEASE")
-  set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|EXECUTABLE)_RELEASE'.")
+  if (RunCMake_TEST_FAILED)
+    string (APPEND RunCMake_TEST_FAILED "\n")
+  endif()
+  string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(SHARED|EXECUTABLE)_RELEASE'.")
 endif()

+ 4 - 1
Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-check.cmake

@@ -3,5 +3,8 @@ if (NOT actual_stdout MATCHES "BADFLAG_SHARED_RELEASE")
   set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_SHARED_RELEASE'.")
 endif()
 if (actual_stdout MATCHES "BADFLAG_(MODULE|EXECUTABLE)_RELEASE")
-  set (RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(MODULE|EXECUTABLE)_RELEASE'.")
+  if (RunCMake_TEST_FAILED)
+    string (APPEND RunCMake_TEST_FAILED "\n")
+  endif()
+  string (APPEND RunCMake_TEST_FAILED "Found unexpected 'BADFLAG_(MODULE|EXECUTABLE)_RELEASE'.")
 endif()

+ 7 - 0
Tests/RunCMake/add_link_options/LinkOptionsLib.cxx

@@ -0,0 +1,7 @@
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  int flags_lib(void)
+{
+  return 0;
+}

+ 15 - 0
Tests/RunCMake/add_link_options/RunCMakeTest.cmake

@@ -23,6 +23,21 @@ if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
   run_cmake_target(LINK_OPTIONS mod LinkOptions_mod --config Release)
   run_cmake_target(LINK_OPTIONS exe LinkOptions_exe --config Release)
 
+
+  run_cmake(genex_LINK_LANGUAGE)
+
+  run_cmake_target(genex_LINK_LANGUAGE shared_c LinkOptions_shared_c --config Release)
+  run_cmake_target(genex_LINK_LANGUAGE shared_cxx LinkOptions_shared_cxx --config Release)
+  run_cmake_target(genex_LINK_LANGUAGE mod LinkOptions_mod --config Release)
+  run_cmake_target(genex_LINK_LANGUAGE exe LinkOptions_exe --config Release)
+
+  run_cmake(genex_LINK_LANG_AND_ID)
+
+  run_cmake_target(genex_LINK_LANG_AND_ID shared_c LinkOptions_shared_c --config Release)
+  run_cmake_target(genex_LINK_LANG_AND_ID shared_cxx LinkOptions_shared_cxx --config Release)
+  run_cmake_target(genex_LINK_LANG_AND_ID mod LinkOptions_mod --config Release)
+  run_cmake_target(genex_LINK_LANG_AND_ID exe LinkOptions_exe --config Release)
+
   unset(RunCMake_TEST_OPTIONS)
   unset(RunCMake_TEST_OUTPUT_MERGE)
 endif()

+ 2 - 0
Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-exe-check.cmake

@@ -0,0 +1,2 @@
+
+include ("${CMAKE_CURRENT_LIST_DIR}/genex_LINK_LANGUAGE-validation.cmake")

+ 1 - 0
Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-exe-result.txt

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

+ 2 - 0
Tests/RunCMake/add_link_options/genex_LINK_LANGUAGE-mod-check.cmake

@@ -0,0 +1,2 @@
+
+include ("${CMAKE_CURRENT_LIST_DIR}/genex_LINK_LANGUAGE-validation.cmake")

部分文件因为文件数量过多而无法显示