Browse Source

Add support of "LINKER:" prefix for CMAKE_<TYPE>_LINKER_FLAGS variable

The following variables now support the LINKER: prefix:
* CMAKE_<TYPE>_LINKER_FLAGS
* CMAKE_<TYPE>_LINKER_FLAGS_<CONFIG>

Fixes: #26171
Marc Chevrier 1 year ago
parent
commit
35350c419d
38 changed files with 328 additions and 66 deletions
  1. 4 0
      Help/command/try_compile.rst
  2. 8 0
      Help/manual/cmake-policies.7.rst
  3. 41 0
      Help/policy/CMP0181.rst
  4. 13 0
      Help/release/dev/CMAKE_TYPE_LINKER_FLAGS-LINKER-prefix-support.rst
  5. 2 0
      Help/variable/CMAKE_EXE_LINKER_FLAGS.rst
  6. 2 0
      Help/variable/CMAKE_EXE_LINKER_FLAGS_CONFIG.rst
  7. 2 0
      Help/variable/CMAKE_MODULE_LINKER_FLAGS.rst
  8. 2 0
      Help/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG.rst
  9. 2 0
      Help/variable/CMAKE_SHARED_LINKER_FLAGS.rst
  10. 2 0
      Help/variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG.rst
  11. 5 0
      Help/variable/LINKER_FLAGS.txt
  12. 8 0
      Source/cmCoreTryCompile.cxx
  13. 2 1
      Source/cmGlobalXCodeGenerator.cxx
  14. 79 14
      Source/cmLocalGenerator.cxx
  15. 13 0
      Source/cmLocalGenerator.h
  16. 21 35
      Source/cmLocalVisualStudio7Generator.cxx
  17. 3 4
      Source/cmLocalVisualStudio7Generator.h
  18. 2 1
      Source/cmMakefileExecutableTargetGenerator.cxx
  19. 6 3
      Source/cmMakefileLibraryTargetGenerator.cxx
  20. 6 2
      Source/cmPolicies.h
  21. 3 6
      Source/cmVisualStudio10TargetGenerator.cxx
  22. 1 0
      Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
  23. 3 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-EXE_LINKER_FLAGS-check.cmake
  24. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-EXE_LINKER_FLAGS-result.txt
  25. 3 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-MODULE_LINKER_FLAGS-check.cmake
  26. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-MODULE_LINKER_FLAGS-result.txt
  27. 3 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-SHARED_LINKER_FLAGS-check.cmake
  28. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-SHARED_LINKER_FLAGS-result.txt
  29. 2 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-EXE_LINKER_FLAGS-check.cmake
  30. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-EXE_LINKER_FLAGS-result.txt
  31. 2 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-MODULE_LINKER_FLAGS-check.cmake
  32. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-MODULE_LINKER_FLAGS-result.txt
  33. 2 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-SHARED_LINKER_FLAGS-check.cmake
  34. 1 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-SHARED_LINKER_FLAGS-result.txt
  35. 5 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-validation.cmake
  36. 56 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2.cmake
  37. 15 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/RunCMakeTest.cmake
  38. 4 0
      Tests/RunCMake/target_link_libraries-LINKER-prefix/main.c

+ 4 - 0
Help/command/try_compile.rst

@@ -336,6 +336,10 @@ Other Behavior Settings
 The current settings of :policy:`CMP0065` and :policy:`CMP0083` are propagated
 through to the generated test project.
 
+.. versionadded:: 3.32
+  The current setting of :policy:`CMP0181` policy is propagated through to the
+  generated test project.
+
 Set variable :variable:`CMAKE_TRY_COMPILE_CONFIGURATION` to choose a build
 configuration:
 

+ 8 - 0
Help/manual/cmake-policies.7.rst

@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.32
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0181: Link command-line fragment variables are parsed and re-quoted. </policy/CMP0181>
+
 Policies Introduced by CMake 3.31
 =================================
 

+ 41 - 0
Help/policy/CMP0181.rst

@@ -0,0 +1,41 @@
+CMP0181
+-------
+
+.. versionadded:: 3.32
+
+The :variable:`CMAKE_EXE_LINKER_FLAGS`,
+:variable:`CMAKE_EXE_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_MODULE_LINKER_FLAGS`,
+and :variable:`CMAKE_MODULE_LINKER_FLAGS_<CONFIG>` variables are parsed and
+re-quoted and support the ``LINKER:`` prefix.
+
+CMake 3.31 and below use the content of these variables as is.
+
+CMake 3.32 and above parse the content of these variables and manage the
+escaping of special characters. Moreover, the ``LINKER:`` prefix is now
+recognized and expanded.
+
+The ``OLD`` behavior of this policy is to consume the content of the
+:variable:`CMAKE_EXE_LINKER_FLAGS`,
+:variable:`CMAKE_EXE_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_MODULE_LINKER_FLAGS`,
+and :variable:`CMAKE_MODULE_LINKER_FLAGS_<CONFIG>` variables as is.
+
+The ``NEW`` behavior of this policy is to parse and re-quote the content of the
+:variable:`CMAKE_EXE_LINKER_FLAGS`,
+:variable:`CMAKE_EXE_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS`,
+:variable:`CMAKE_SHARED_LINKER_FLAGS_<CONFIG>`,
+:variable:`CMAKE_MODULE_LINKER_FLAGS`,
+and :variable:`CMAKE_MODULE_LINKER_FLAGS_<CONFIG>` variables as well as to
+expand the ``LINKER:`` prefix.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.32
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt

+ 13 - 0
Help/release/dev/CMAKE_TYPE_LINKER_FLAGS-LINKER-prefix-support.rst

@@ -0,0 +1,13 @@
+CMAKE_TYPE_LINKER_FLAGS-LINKER-prefix-support
+---------------------------------------------
+
+* The :variable:`CMAKE_EXE_LINKER_FLAGS`,
+  :variable:`CMAKE_EXE_LINKER_FLAGS_<CONFIG>`,
+  :variable:`CMAKE_SHARED_LINKER_FLAGS`,
+  :variable:`CMAKE_SHARED_LINKER_FLAGS_<CONFIG>`,
+  :variable:`CMAKE_MODULE_LINKER_FLAGS`,
+  and :variable:`CMAKE_MODULE_LINKER_FLAGS_<CONFIG>` variables learned to
+  support the ``LINKER:`` prefix.
+
+  This support implies to parse and re-quote the content of these variables.
+  This parsing is controlled by :policy:`CMP0181` policy.

+ 2 - 0
Help/variable/CMAKE_EXE_LINKER_FLAGS.rst

@@ -4,3 +4,5 @@ CMAKE_EXE_LINKER_FLAGS
 Linker flags to be used to create executables.
 
 These flags will be used by the linker when creating an executable.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 2 - 0
Help/variable/CMAKE_EXE_LINKER_FLAGS_CONFIG.rst

@@ -5,3 +5,5 @@ Flags to be used when linking an executable.
 
 Same as ``CMAKE_C_FLAGS_*`` but used by the linker when creating
 executables.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 2 - 0
Help/variable/CMAKE_MODULE_LINKER_FLAGS.rst

@@ -4,3 +4,5 @@ CMAKE_MODULE_LINKER_FLAGS
 Linker flags to be used to create modules.
 
 These flags will be used by the linker when creating a module.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 2 - 0
Help/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG.rst

@@ -4,3 +4,5 @@ CMAKE_MODULE_LINKER_FLAGS_<CONFIG>
 Flags to be used when linking a module.
 
 Same as ``CMAKE_C_FLAGS_*`` but used by the linker when creating modules.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 2 - 0
Help/variable/CMAKE_SHARED_LINKER_FLAGS.rst

@@ -4,3 +4,5 @@ CMAKE_SHARED_LINKER_FLAGS
 Linker flags to be used to create shared libraries.
 
 These flags will be used by the linker when creating a shared library.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 2 - 0
Help/variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG.rst

@@ -5,3 +5,5 @@ Flags to be used when linking a shared library.
 
 Same as ``CMAKE_C_FLAGS_*`` but used by the linker when creating shared
 libraries.
+
+.. include:: ../variable/LINKER_FLAGS.txt

+ 5 - 0
Help/variable/LINKER_FLAGS.txt

@@ -0,0 +1,5 @@
+
+.. include:: ../command/LINK_LIBRARIES_LINKER.txt
+
+This support implies to parse and re-quote the content of the variable. See
+policy :policy:`CMP0181`.

+ 8 - 0
Source/cmCoreTryCompile.cxx

@@ -910,6 +910,14 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
         ? "OLD"
         : "NEW");
 
+    /* Set the appropriate policy information for the LINKER: prefix expansion
+     */
+    fprintf(fout, "cmake_policy(SET CMP0181 %s)\n",
+            this->Makefile->GetPolicyStatus(cmPolicies::CMP0181) ==
+                cmPolicies::NEW
+              ? "NEW"
+              : "OLD");
+
     // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
     // adding that flag in the platform and compiler language files
     fprintf(fout,

+ 2 - 1
Source/cmGlobalXCodeGenerator.cxx

@@ -2538,7 +2538,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
   }
   if (!extraLinkOptionsVar.empty()) {
     this->CurrentLocalGenerator->AddConfigVariableFlags(
-      extraLinkOptions, extraLinkOptionsVar, configName);
+      extraLinkOptions, extraLinkOptionsVar, gtgt, cmBuildStep::Link, llang,
+      configName);
   }
 
   if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY ||

+ 79 - 14
Source/cmLocalGenerator.cxx

@@ -1495,17 +1495,16 @@ void cmLocalGenerator::GetTargetFlags(
       libraryLinkVariable = "CMAKE_MODULE_LINKER_FLAGS";
       CM_FALLTHROUGH;
     case cmStateEnums::SHARED_LIBRARY: {
-      std::string sharedLibFlags;
       if (this->IsSplitSwiftBuild() || linkLanguage != "Swift") {
-        sharedLibFlags = cmStrCat(
-          this->Makefile->GetSafeDefinition(libraryLinkVariable), ' ');
-        if (!configUpper.empty()) {
-          std::string build = cmStrCat(libraryLinkVariable, '_', configUpper);
-          sharedLibFlags += this->Makefile->GetSafeDefinition(build);
-          sharedLibFlags += " ";
+        std::string libFlags;
+        this->AddConfigVariableFlags(libFlags, libraryLinkVariable, target,
+                                     cmBuildStep::Link, linkLanguage, config);
+        if (!libFlags.empty()) {
+          linkFlags.emplace_back(std::move(libFlags));
         }
       }
 
+      std::string sharedLibFlags;
       cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
       if (targetLinkFlags) {
         sharedLibFlags += *targetLinkFlags;
@@ -1538,7 +1537,6 @@ void cmLocalGenerator::GetTargetFlags(
       }
     } break;
     case cmStateEnums::EXECUTABLE: {
-      std::string exeFlags;
       if (linkLanguage.empty()) {
         cmSystemTools::Error(
           "CMake can not determine linker language for target: " +
@@ -1547,15 +1545,16 @@ void cmLocalGenerator::GetTargetFlags(
       }
 
       if (linkLanguage != "Swift") {
-        exeFlags = this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS");
-        exeFlags += " ";
-        if (!configUpper.empty()) {
-          exeFlags += this->Makefile->GetSafeDefinition(
-            cmStrCat("CMAKE_EXE_LINKER_FLAGS_", configUpper));
-          exeFlags += " ";
+        std::string exeFlags;
+        this->AddConfigVariableFlags(exeFlags, "CMAKE_EXE_LINKER_FLAGS",
+                                     target, cmBuildStep::Link, linkLanguage,
+                                     config);
+        if (!exeFlags.empty()) {
+          linkFlags.emplace_back(std::move(exeFlags));
         }
       }
 
+      std::string exeFlags;
       if (target->IsWin32Executable(config)) {
         exeFlags += this->Makefile->GetSafeDefinition(
           cmStrCat("CMAKE_", linkLanguage, "_CREATE_WIN32_EXE"));
@@ -2621,6 +2620,19 @@ void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
     this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar));
   }
 }
+void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
+                                              const std::string& var,
+                                              cmGeneratorTarget const* target,
+                                              cmBuildStep compileOrLink,
+                                              const std::string& lang,
+                                              const std::string& config)
+{
+  std::string newFlags;
+  this->AddConfigVariableFlags(newFlags, var, config);
+  if (!newFlags.empty()) {
+    this->AppendFlags(flags, newFlags, var, target, compileOrLink, lang);
+  }
+}
 
 void cmLocalGenerator::AppendFlags(std::string& flags,
                                    const std::string& newFlags) const
@@ -2651,6 +2663,59 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags,
     this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti()));
 }
 
+void cmLocalGenerator::AppendFlags(std::string& flags,
+                                   std::string const& newFlags,
+                                   const std::string& name,
+                                   const cmGeneratorTarget* target,
+                                   cmBuildStep compileOrLink,
+                                   const std::string& language)
+{
+  switch (target->GetPolicyStatusCMP0181()) {
+    case cmPolicies::WARN:
+      if (!this->Makefile->GetCMakeInstance()->GetIsInTryCompile() &&
+          this->Makefile->PolicyOptionalWarningEnabled(
+            "CMAKE_POLICY_WARNING_CMP0181")) {
+        this->Makefile->GetCMakeInstance()->IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0181),
+                   "\nSince the policy is not set, the contents of variable '",
+                   name,
+                   "' will "
+                   "be used as is."),
+          target->GetBacktrace());
+      }
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD:
+      this->AppendFlags(flags, newFlags);
+      break;
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::REQUIRED_ALWAYS:
+      this->Makefile->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0181),
+        target->GetBacktrace());
+      CM_FALLTHROUGH;
+    case cmPolicies::NEW:
+      if (compileOrLink == cmBuildStep::Link) {
+        std::vector<std::string> options;
+        cmSystemTools::ParseUnixCommandLine(newFlags.c_str(), options);
+        this->SetLinkScriptShell(this->GlobalGenerator->GetUseLinkScript());
+        std::vector<BT<std::string>> optionsWithBT{ options.size() };
+        std::transform(options.cbegin(), options.cend(), optionsWithBT.begin(),
+                       [](const std::string& item) -> BT<std::string> {
+                         return BT<std::string>{ item };
+                       });
+        target->ResolveLinkerWrapper(optionsWithBT, language);
+        for (const auto& item : optionsWithBT) {
+          this->AppendFlagEscape(flags, item.Value);
+        }
+        this->SetLinkScriptShell(false);
+      } else {
+        this->AppendFlags(flags, newFlags);
+      }
+  }
+}
+
 void cmLocalGenerator::AddISPCDependencies(cmGeneratorTarget* target)
 {
   std::vector<std::string> enabledLanguages =

+ 13 - 0
Source/cmLocalGenerator.h

@@ -174,6 +174,12 @@ public:
                                 const std::string& lang);
   void AddConfigVariableFlags(std::string& flags, const std::string& var,
                               const std::string& config);
+  // Handle prefixes processing (like LINKER:)
+  void AddConfigVariableFlags(std::string& flags, const std::string& var,
+                              cmGeneratorTarget const* target,
+                              cmBuildStep compileOrLink,
+                              const std::string& lang,
+                              const std::string& config);
   void AddColorDiagnosticsFlags(std::string& flags, const std::string& lang);
   //! Append flags to a string.
   virtual void AppendFlags(std::string& flags,
@@ -182,6 +188,13 @@ public:
                            const std::vector<BT<std::string>>& newFlags) const;
   virtual void AppendFlagEscape(std::string& flags,
                                 const std::string& rawFlag) const;
+  /**
+   * Append flags after parsing, prefixes processing (like LINKER:) and
+   * escaping
+   */
+  void AppendFlags(std::string& flags, std::string const& newFlags,
+                   const std::string& name, const cmGeneratorTarget* target,
+                   cmBuildStep compileOrLink, const std::string& lang);
   void AddISPCDependencies(cmGeneratorTarget* target);
   void AddPchDependencies(cmGeneratorTarget* target);
   void AddUnityBuild(cmGeneratorTarget* target);

+ 21 - 35
Source/cmLocalVisualStudio7Generator.cxx

@@ -664,16 +664,16 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(
   }
   std::string flags;
   std::string langForClCompile;
+  const std::string& linkLanguage =
+    (this->FortranProject ? std::string("Fortran")
+                          : target->GetLinkerLanguage(configName));
+  if (linkLanguage.empty()) {
+    cmSystemTools::Error(
+      cmStrCat("CMake can not determine linker language for target: ",
+               target->GetName()));
+    return;
+  }
   if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) {
-    const std::string& linkLanguage =
-      (this->FortranProject ? std::string("Fortran")
-                            : target->GetLinkerLanguage(configName));
-    if (linkLanguage.empty()) {
-      cmSystemTools::Error(
-        cmStrCat("CMake can not determine linker language for target: ",
-                 target->GetName()));
-      return;
-    }
     langForClCompile = linkLanguage;
     if (langForClCompile == "C" || langForClCompile == "CXX" ||
         langForClCompile == "Fortran") {
@@ -957,26 +957,14 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(
   }
 
   this->OutputTargetRules(fout, configName, target, libName);
-  this->OutputBuildTool(fout, configName, target, targetOptions);
+  this->OutputBuildTool(fout, linkLanguage, configName, target, targetOptions);
   this->OutputDeploymentDebuggerTool(fout, configName, target);
   fout << "\t\t</Configuration>\n";
 }
 
-std::string cmLocalVisualStudio7Generator::GetBuildTypeLinkerFlags(
-  std::string const& rootLinkerFlags, const std::string& configName)
-{
-  std::string configTypeUpper = cmSystemTools::UpperCase(configName);
-  std::string extraLinkOptionsBuildTypeDef =
-    cmStrCat(rootLinkerFlags, '_', configTypeUpper);
-
-  const std::string& extraLinkOptionsBuildType =
-    this->Makefile->GetRequiredDefinition(extraLinkOptionsBuildTypeDef);
-
-  return extraLinkOptionsBuildType;
-}
-
 void cmLocalVisualStudio7Generator::OutputBuildTool(
-  std::ostream& fout, const std::string& configName, cmGeneratorTarget* target,
+  std::ostream& fout, const std::string& linkLanguage,
+  const std::string& configName, cmGeneratorTarget* target,
   const Options& targetOptions)
 {
   cmGlobalVisualStudio7Generator* gg =
@@ -984,19 +972,19 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
   std::string temp;
   std::string extraLinkOptions;
   if (target->GetType() == cmStateEnums::EXECUTABLE) {
-    extraLinkOptions = cmStrCat(
-      this->Makefile->GetRequiredDefinition("CMAKE_EXE_LINKER_FLAGS"), ' ',
-      GetBuildTypeLinkerFlags("CMAKE_EXE_LINKER_FLAGS", configName));
+    this->AddConfigVariableFlags(extraLinkOptions, "CMAKE_EXE_LINKER_FLAGS",
+                                 target, cmBuildStep::Link, linkLanguage,
+                                 configName);
   }
   if (target->GetType() == cmStateEnums::SHARED_LIBRARY) {
-    extraLinkOptions = cmStrCat(
-      this->Makefile->GetRequiredDefinition("CMAKE_SHARED_LINKER_FLAGS"), ' ',
-      GetBuildTypeLinkerFlags("CMAKE_SHARED_LINKER_FLAGS", configName));
+    this->AddConfigVariableFlags(extraLinkOptions, "CMAKE_SHARED_LINKER_FLAGS",
+                                 target, cmBuildStep::Link, linkLanguage,
+                                 configName);
   }
   if (target->GetType() == cmStateEnums::MODULE_LIBRARY) {
-    extraLinkOptions = cmStrCat(
-      this->Makefile->GetRequiredDefinition("CMAKE_MODULE_LINKER_FLAGS"), ' ',
-      GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName));
+    this->AddConfigVariableFlags(extraLinkOptions, "CMAKE_MODULE_LINKER_FLAGS",
+                                 target, cmBuildStep::Link, linkLanguage,
+                                 configName);
   }
 
   cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
@@ -1089,7 +1077,6 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
         return;
       }
       cmComputeLinkInformation& cli = *pcli;
-      std::string linkLanguage = cli.GetLinkLanguage();
 
       if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
         // Visual Studio 10 or upper is required for this feature
@@ -1173,7 +1160,6 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
         return;
       }
       cmComputeLinkInformation& cli = *pcli;
-      std::string linkLanguage = cli.GetLinkLanguage();
 
       if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
         // Visual Studio 10 or upper is required for this feature

+ 3 - 4
Source/cmLocalVisualStudio7Generator.h

@@ -99,8 +99,6 @@ protected:
 private:
   using Options = cmVS7GeneratorOptions;
   using FCInfo = cmLocalVisualStudio7GeneratorFCInfo;
-  std::string GetBuildTypeLinkerFlags(std::string const& rootLinkerFlags,
-                                      const std::string& configName);
   void FixGlobalTargets();
   void WriteVCProjHeader(std::ostream& fout, const std::string& libName,
                          cmGeneratorTarget* tgt,
@@ -119,8 +117,9 @@ private:
   void OutputTargetRules(std::ostream& fout, const std::string& configName,
                          cmGeneratorTarget* target,
                          const std::string& libName);
-  void OutputBuildTool(std::ostream& fout, const std::string& configName,
-                       cmGeneratorTarget* t, const Options& targetOptions);
+  void OutputBuildTool(std::ostream& fout, const std::string& linkLanguage,
+                       const std::string& configName, cmGeneratorTarget* t,
+                       const Options& targetOptions);
   void OutputDeploymentDebuggerTool(std::ostream& fout,
                                     std::string const& config,
                                     cmGeneratorTarget* target);

+ 2 - 1
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -370,7 +370,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
 
   // Add flags to create an executable.
   this->LocalGenerator->AddConfigVariableFlags(
-    linkFlags, "CMAKE_EXE_LINKER_FLAGS", this->GetConfigName());
+    linkFlags, "CMAKE_EXE_LINKER_FLAGS", this->GeneratorTarget,
+    cmBuildStep::Link, linkLanguage, this->GetConfigName());
 
   if (this->GeneratorTarget->IsWin32Executable(
         this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) {

+ 6 - 3
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -177,7 +177,8 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
-    extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->GetConfigName());
+    extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->GeneratorTarget,
+    cmBuildStep::Link, linkLanguage, this->GetConfigName());
 
   std::unique_ptr<cmLinkLineComputer> linkLineComputer =
     this->CreateLinkLineComputer(
@@ -212,7 +213,8 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
-    extraFlags, "CMAKE_MODULE_LINKER_FLAGS", this->GetConfigName());
+    extraFlags, "CMAKE_MODULE_LINKER_FLAGS", this->GeneratorTarget,
+    cmBuildStep::Link, linkLanguage, this->GetConfigName());
 
   std::unique_ptr<cmLinkLineComputer> linkLineComputer =
     this->CreateLinkLineComputer(
@@ -239,7 +241,8 @@ void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
   std::string extraFlags;
   this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
-    extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS", this->GetConfigName());
+    extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS", this->GeneratorTarget,
+    cmBuildStep::Link, linkLanguage, this->GetConfigName());
 
   this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
 }

+ 6 - 2
Source/cmPolicies.h

@@ -552,7 +552,10 @@ class cmMakefile;
          3, 31, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0180,                                                     \
          "project() always sets <PROJECT-NAME>_* as normal variables.", 3,    \
-         31, 0, cmPolicies::WARN)
+         31, 0, cmPolicies::WARN)                                             \
+  SELECT(POLICY, CMP0181,                                                     \
+         "Link command-line fragment variables are parsed and re-quoted.", 3, \
+         32, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -597,7 +600,8 @@ class cmMakefile;
   F(CMP0157)                                                                  \
   F(CMP0160)                                                                  \
   F(CMP0162)                                                                  \
-  F(CMP0179)
+  F(CMP0179)                                                                  \
+  F(CMP0181)
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \

+ 3 - 6
Source/cmVisualStudio10TargetGenerator.cxx

@@ -4426,12 +4426,9 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions(
     linkType = "EXE";
   }
   std::string flags;
-  std::string linkFlagVarBase = cmStrCat("CMAKE_", linkType, "_LINKER_FLAGS");
-  flags += ' ';
-  flags += this->Makefile->GetRequiredDefinition(linkFlagVarBase);
-  std::string linkFlagVar = cmStrCat(linkFlagVarBase, '_', CONFIG);
-  flags += ' ';
-  flags += this->Makefile->GetRequiredDefinition(linkFlagVar);
+  this->LocalGenerator->AddConfigVariableFlags(
+    flags, cmStrCat("CMAKE_", linkType, "_LINKER_FLAGS"),
+    this->GeneratorTarget, cmBuildStep::Link, linkLanguage, config);
   cmValue targetLinkFlags = this->GeneratorTarget->GetProperty("LINK_FLAGS");
   if (targetLinkFlags) {
     flags += ' ';

+ 1 - 0
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

@@ -44,6 +44,7 @@
    \* CMP0160
    \* CMP0162
    \* CMP0179
+   \* CMP0181
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-EXE_LINKER_FLAGS-check.cmake

@@ -0,0 +1,3 @@
+
+set(reference_file "LINKER.txt")
+include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-EXE_LINKER_FLAGS-result.txt

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

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-MODULE_LINKER_FLAGS-check.cmake

@@ -0,0 +1,3 @@
+
+set(reference_file "LINKER.txt")
+include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-MODULE_LINKER_FLAGS-result.txt

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

+ 3 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-SHARED_LINKER_FLAGS-check.cmake

@@ -0,0 +1,3 @@
+
+set(reference_file "LINKER.txt")
+include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake")

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-NEW-SHARED_LINKER_FLAGS-result.txt

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

+ 2 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-EXE_LINKER_FLAGS-check.cmake

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

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-EXE_LINKER_FLAGS-result.txt

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

+ 2 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-MODULE_LINKER_FLAGS-check.cmake

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

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-MODULE_LINKER_FLAGS-result.txt

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

+ 2 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-SHARED_LINKER_FLAGS-check.cmake

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

+ 1 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-SHARED_LINKER_FLAGS-result.txt

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

+ 5 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2-CMP0181-OLD-validation.cmake

@@ -0,0 +1,5 @@
+
+if (NOT actual_stdout MATCHES "LINKER:-foo,bar")
+  set (RunCMake_TEST_FAILED "LINKER: prefix was expanded.")
+  return()
+endif()

+ 56 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/LINKER_expansion2.cmake

@@ -0,0 +1,56 @@
+
+enable_language(C)
+
+cmake_policy(SET CMP0181 ${CMP0181})
+
+# ensure command line is always displayed and do not use any response file
+set(CMAKE_VERBOSE_MAKEFILE TRUE)
+
+if (CMAKE_GENERATOR MATCHES "Borland|NMake")
+  string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}")
+  string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}")
+
+  string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
+  string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
+
+  string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE}")
+  string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE}")
+endif()
+
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} LINKER:-foo,bar")
+add_executable(exe_linker_flags main.c)
+
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} LINKER:-foo,bar")
+add_library(shared_linker_flags SHARED LinkOptionsLib.c)
+
+set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} LINKER:-foo,bar")
+add_library(module_linker_flags MODULE LinkOptionsLib.c)
+
+
+# generate reference for LINKER flag
+if (CMP0181 STREQUAL "NEW")
+  if (CMAKE_C_LINKER_WRAPPER_FLAG)
+    set(linker_flag ${CMAKE_C_LINKER_WRAPPER_FLAG})
+    list(GET linker_flag -1 linker_space)
+    if (linker_space STREQUAL " ")
+      list(REMOVE_AT linker_flag -1)
+    else()
+      set(linker_space)
+    endif()
+    list (JOIN linker_flag " " linker_flag)
+    if (CMAKE_C_LINKER_WRAPPER_FLAG_SEP)
+      set(linker_sep "${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}")
+
+      string (APPEND  linker_flag "${linker_space}" "-foo${linker_sep}bar")
+    else()
+      set(linker_prefix "${linker_flag}${linker_space}")
+
+      set (linker_flag "${linker_prefix}-foo ${linker_prefix}bar")
+    endif()
+  else()
+    set(linker_flag "-foo bar")
+  endif()
+
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER.txt" "${linker_flag}")
+endif()

+ 15 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/RunCMakeTest.cmake

@@ -20,3 +20,18 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
   run_cmake_target(LINKER_expansion LINKER_SHELL linker_shell)
   run_cmake_target(LINKER_expansion LINKER_CONSUMER linker_consumer)
 endif()
+
+# Some environments are excluded because they are not able to honor verbose mode
+if (RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode|Visual Studio"
+    AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+
+  foreach(policy IN ITEMS OLD NEW)
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LINKER_expansion2-CMP0181-${policy}-build)
+    run_cmake_with_options(LINKER_expansion2 -DCMP0181=${policy})
+
+    run_cmake_target(LINKER_expansion2-CMP0181-${policy} EXE_LINKER_FLAGS exe_linker_flags --verbose)
+    run_cmake_target(LINKER_expansion2-CMP0181-${policy} SHARED_LINKER_FLAGS shared_linker_flags --verbose)
+    run_cmake_target(LINKER_expansion2-CMP0181-${policy} MODULE_LINKER_FLAGS module_linker_flags --verbose)
+  endforeach()
+endif()

+ 4 - 0
Tests/RunCMake/target_link_libraries-LINKER-prefix/main.c

@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}