Bläddra i källkod

Add options to specify linker tool

Offer the capability, through variable `CMAKE_LINKER_TYPE`, as well as
the target property `LINKER_TYPE` to specify which linker must be used.

The implementation of this capability is specified by variables specific
to the language and linker type: `CMAKE_<LANG>_USING_LINKER_<TYPE>`.
Some definitions are provided as part of `CMake`.

For example, to select the `LLVM` linker rather than the standard one,
the type `LLD` should be specified through the variable `CMAKE_LINKER_TYPE`.
And, on `Apple`, `Linux` and some environments on `Windows`, the variable
`CMAKE_<LANG>_USING_LINKER_LLD` has value `-fuse-ld=lld`. And for `Windows`
environments  based on `MSVC`, where the linker is used directly, the tool
`lld-link.exe` will be used rather than `link.exe`.

Fixes: #19174, #24254, #24990
Marc Chevrier 2 år sedan
förälder
incheckning
96a953b1ed
59 ändrade filer med 623 tillägg och 16 borttagningar
  1. 1 0
      Help/manual/cmake-properties.7.rst
  2. 3 0
      Help/manual/cmake-variables.7.rst
  3. 26 0
      Help/prop_tgt/LINKER_TYPE.rst
  4. 6 0
      Help/release/dev/Linker-user-selection.rst
  5. 15 0
      Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst
  6. 30 0
      Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst
  7. 18 0
      Help/variable/CMAKE_LINKER_TYPE.rst
  8. 57 0
      Help/variable/LINKER_PREDEFINED_TYPES.txt
  9. 2 0
      Modules/CMakeASMCompiler.cmake.in
  10. 2 0
      Modules/CMakeCCompiler.cmake.in
  11. 2 0
      Modules/CMakeCUDACompiler.cmake.in
  12. 2 0
      Modules/CMakeCXXCompiler.cmake.in
  13. 11 0
      Modules/CMakeFindBinUtils.cmake
  14. 2 0
      Modules/CMakeHIPCompiler.cmake.in
  15. 2 0
      Modules/CMakeOBJCCompiler.cmake.in
  16. 2 0
      Modules/CMakeOBJCXXCompiler.cmake.in
  17. 7 0
      Modules/Platform/Apple-Clang.cmake
  18. 3 0
      Modules/Platform/Apple-GNU.cmake
  19. 2 0
      Modules/Platform/Linux-Clang-CUDA.cmake
  20. 7 0
      Modules/Platform/Linux-GNU.cmake
  21. 7 0
      Modules/Platform/Linux-NVIDIA-CUDA.cmake
  22. 8 2
      Modules/Platform/Windows-Clang.cmake
  23. 5 0
      Modules/Platform/Windows-GNU.cmake
  24. 6 0
      Modules/Platform/Windows-MSVC.cmake
  25. 6 0
      Modules/Platform/Windows-NVIDIA-CUDA.cmake
  26. 16 0
      Source/cmCoreTryCompile.cxx
  27. 3 2
      Source/cmGeneratorExpressionDAGChecker.cxx
  28. 69 0
      Source/cmGeneratorTarget.cxx
  29. 7 0
      Source/cmGeneratorTarget.h
  30. 13 0
      Source/cmGhsMultiTargetGenerator.cxx
  31. 3 0
      Source/cmGlobalXCodeGenerator.cxx
  32. 44 1
      Source/cmLocalGenerator.cxx
  33. 3 0
      Source/cmLocalGenerator.h
  34. 21 0
      Source/cmLocalVisualStudio7Generator.cxx
  35. 3 0
      Source/cmMakefileExecutableTargetGenerator.cxx
  36. 3 0
      Source/cmMakefileLibraryTargetGenerator.cxx
  37. 2 0
      Source/cmMakefileTargetGenerator.cxx
  38. 8 0
      Source/cmNinjaNormalTargetGenerator.cxx
  39. 14 11
      Source/cmRulePlaceholderExpander.cxx
  40. 1 0
      Source/cmRulePlaceholderExpander.h
  41. 1 0
      Source/cmTarget.cxx
  42. 10 0
      Source/cmVisualStudio10TargetGenerator.cxx
  43. 8 0
      Tests/RunCMake/CMakeLists.txt
  44. 7 0
      Tests/RunCMake/LinkerSelection/AppleClassic.cmake
  45. 3 0
      Tests/RunCMake/LinkerSelection/CMakeLists.txt
  46. 2 0
      Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake
  47. 36 0
      Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake
  48. 1 0
      Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt
  49. 3 0
      Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt
  50. 5 0
      Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake
  51. 9 0
      Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake
  52. 44 0
      Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
  53. 1 0
      Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt
  54. 3 0
      Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt
  55. 5 0
      Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake
  56. 2 0
      Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake
  57. 32 0
      Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake
  58. 4 0
      Tests/RunCMake/LinkerSelection/main.c
  59. 5 0
      Tests/RunCMake/LinkerSelection/main.cu

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -336,6 +336,7 @@ Properties on Targets
    /prop_tgt/LINK_SEARCH_START_STATIC
    /prop_tgt/LINK_WHAT_YOU_USE
    /prop_tgt/LINKER_LANGUAGE
+   /prop_tgt/LINKER_TYPE
    /prop_tgt/LOCATION
    /prop_tgt/LOCATION_CONFIG
    /prop_tgt/MACHO_COMPATIBILITY_VERSION

+ 3 - 0
Help/manual/cmake-variables.7.rst

@@ -474,6 +474,8 @@ Variables that Control the Build
    /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
    /variable/CMAKE_LANG_LINKER_LAUNCHER
+   /variable/CMAKE_LANG_USING_LINKER_MODE
+   /variable/CMAKE_LANG_USING_LINKER_TYPE
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -490,6 +492,7 @@ Variables that Control the Build
    /variable/CMAKE_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_WHAT_YOU_USE
    /variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
+   /variable/CMAKE_LINKER_TYPE
    /variable/CMAKE_MACOSX_BUNDLE
    /variable/CMAKE_MACOSX_RPATH
    /variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG

+ 26 - 0
Help/prop_tgt/LINKER_TYPE.rst

@@ -0,0 +1,26 @@
+LINKER_TYPE
+-----------
+
+.. versionadded:: 3.29
+
+Specify which linker will be used for the link step. The property value may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+.. code-block:: cmake
+
+  add_library(lib1 SHARED ...)
+  set_property(TARGET lib1 PROPERTY LINKER_TYPE LLD)
+
+This specifies that ``lib1`` should use linker type ``LLD`` for the link step.
+The implementation details will be provided by the variable
+:variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` with ``<TYPE>`` having the value
+``LLD``.
+
+This property is not supported on :generator:`Green Hills MULTI` and
+:generator:`Visual Studio 9 2008` generators.
+
+.. note::
+  It is assumed that the linker specified is fully compatible with the standard
+  one. CMake will not do any options translation.
+
+.. include:: ../variable/LINKER_PREDEFINED_TYPES.txt

+ 6 - 0
Help/release/dev/Linker-user-selection.rst

@@ -0,0 +1,6 @@
+Linker-user-selection
+---------------------
+
+* The linker tool can now be specified for a selection of compilers/platforms
+  by setting :variable:`CMAKE_LINKER_TYPE` variable or :prop_tgt:`LINKER_TYPE`
+  target property.

+ 15 - 0
Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst

@@ -0,0 +1,15 @@
+CMAKE_<LANG>_USING_LINKER_MODE
+------------------------------
+
+.. versionadded:: 3.29
+
+This variable specify what is the type of data stored in variable
+ :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>`. There are two possible values:
+
+``FLAG``
+  :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds compiler flags. This is
+  the default.
+
+``TOOL``
+  :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds the path to the linker
+  tool.

+ 30 - 0
Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst

@@ -0,0 +1,30 @@
+CMAKE_<LANG>_USING_LINKER_<TYPE>
+--------------------------------
+
+.. versionadded:: 3.29
+
+This variable defines how to specify the linker for the link step for the type
+as specified by the variable :variable:`CMAKE_LINKER_TYPE` or the target
+property :prop_tgt:`LINKER_TYPE`. It can hold compiler flags for the link step
+or directly the linker tool. The type of data is given by the variable
+:variable:`CMAKE_<LANG>_USING_LINKER_MODE`.
+
+For example, to specify the ``LLVM`` linker for ``GNU`` compilers, we have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld")
+
+Or on ``Windows`` platform, for ``Clang`` compilers simulating ``MSVC``, we
+have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld-link")
+
+And for the ``MSVC`` compiler, linker is directly used, so we have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "/path/to/lld-link.exe")
+  set(CMAKE_C_USING_LINKER_MODE TOOL)

+ 18 - 0
Help/variable/CMAKE_LINKER_TYPE.rst

@@ -0,0 +1,18 @@
+CMAKE_LINKER_TYPE
+-----------------
+
+.. versionadded:: 3.29
+
+Specify which linker will be used for the link step.
+
+.. note::
+  It is assumed that the linker specified is fully compatible with the standard
+  one. CMake will not do any options translation.
+
+This variable is used to initialize the :prop_tgt:`LINKER_TYPE` target
+property when they are created by calls to :command:`add_library` or
+:command:`add_executable` commands. It is meaningful only for targets having a
+link step. If set, its value is also used by the :command:`try_compile`
+command.
+
+.. include:: LINKER_PREDEFINED_TYPES.txt

+ 57 - 0
Help/variable/LINKER_PREDEFINED_TYPES.txt

@@ -0,0 +1,57 @@
+Linker types are case-sensitive and may only contain letters, numbers and
+underscores. Linker types defined in all uppercase are reserved for CMake's own
+built-in types. The pre-defined linker types are:
+
+``DEFAULT``
+  This type corresponds to standard linking, essentially equivalent to
+  not specifying :prop_tgt:`LINKER_TYPE` target property.
+
+``SYSTEM``
+  Use the standard linker delivered by the platform or the standard toolkit
+  (for example, ``SYSTEM`` imply Microsoft linker for all ``MSVC`` compatible
+  compilers). This type is supported for the following platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+  * All Apple variants for ``AppleClang``, ``Clang`` and ``GNU`` compilers.
+  * Windows, for ``MSVC``, ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+
+``LLD``
+  Use the ``LLVM`` linker. This type is supported for the following
+  platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+  * All Apple variants for ``Clang`` and ``AppleClang`` compilers.
+  * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end and
+    ``CLang``, ``MSVC`` and ``NVIDIA`` compilers with ``MSVC`` front-end.
+
+``BFD``
+  Use the ``GNU`` linker.  This type is supported for the following
+  platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+  * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end.
+
+``GOLD``
+  Supported on Linux platform for ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+
+``MOLD``
+  Use the `mold linker <https://github.com/rui314/mold>`_. This type is
+  supported on the following platforms:
+
+  * Linux platform for ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+  * All Apple variants for ``Clang`` and ``AppleClang`` compilers as an
+    alias to ``SOLD``.
+
+``SOLD``
+  Use the `sold linker <https://github.com/bluewhalesystems/sold>`_. This type
+  is only supported on Apple platforms for ``Clang`` and ``AppleClang``
+  compilers.
+
+``APPLE_CLASSIC``
+  Use the Apple linker in the classic behavior (i.e. before ``Xcode 15.0``).
+  This type is only supported on Apple platforms for ``GNU``, ``Clang`` and
+  ``AppleClang`` compilers.
+
+``MSVC``
+  Use the Microsoft linker. This type is only supported on Windows
+  platform for ``MSVC`` and ``Clang`` compiler with ``MSVC`` front-end.

+ 2 - 0
Modules/CMakeASMCompiler.cmake.in

@@ -5,6 +5,8 @@ set(CMAKE_ASM@ASM_DIALECT@_COMPILER_AR "@_CMAKE_ASM_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)

+ 2 - 0
Modules/CMakeCCompiler.cmake.in

@@ -26,6 +26,8 @@ set(CMAKE_C_COMPILER_AR "@CMAKE_C_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)

+ 2 - 0
Modules/CMakeCUDACompiler.cmake.in

@@ -72,5 +72,7 @@ set(CMAKE_CUDA_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CUDA_IMPLICIT_LINK_FR
 @_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT@
 
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_AR "@CMAKE_AR@")
 set(CMAKE_MT "@CMAKE_MT@")

+ 2 - 0
Modules/CMakeCXXCompiler.cmake.in

@@ -27,6 +27,8 @@ set(CMAKE_CXX_COMPILER_AR "@CMAKE_CXX_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)

+ 11 - 0
Modules/CMakeFindBinUtils.cmake

@@ -60,6 +60,13 @@ endfunction()
 __resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker")
 __resolve_tool_path(CMAKE_MT     "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool")
 
+macro(__resolve_linker_path __linker_type __name __search_path __doc)
+  if(NOT CMAKE_LINKER_${__linker_type})
+    set( CMAKE_LINKER_${__linker_type} "${__name}")
+  endif()
+  __resolve_tool_path(CMAKE_LINKER_${__linker_type} "${__search_path}" "${__doc}")
+endmacro()
+
 set(_CMAKE_TOOL_VARS "")
 
 # if it's the MS C/CXX compiler, search for link
@@ -93,6 +100,10 @@ if(("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" AND
 
   list(APPEND _CMAKE_TOOL_VARS LINKER MT AR)
 
+  # look-up for possible usable linker
+  __resolve_linker_path(LINK "link" "${_CMAKE_TOOLCHAIN_LOCATION}" "link Linker")
+  __resolve_linker_path(LLD "lld-link" "${_CMAKE_TOOLCHAIN_LOCATION}" "lld-link Linker")
+
 elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Open)?Watcom$")
   set(_CMAKE_LINKER_NAMES "wlink")
   set(_CMAKE_AR_NAMES "wlib")

+ 2 - 0
Modules/CMakeHIPCompiler.cmake.in

@@ -74,5 +74,7 @@ set(CMAKE_HIP_COMPILER_AR "@CMAKE_HIP_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")

+ 2 - 0
Modules/CMakeOBJCCompiler.cmake.in

@@ -24,6 +24,8 @@ set(CMAKE_OBJC_COMPILER_AR "@CMAKE_OBJC_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)

+ 2 - 0
Modules/CMakeOBJCXXCompiler.cmake.in

@@ -25,6 +25,8 @@ set(CMAKE_OBJCXX_COMPILER_AR "@CMAKE_OBJCXX_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)

+ 7 - 0
Modules/Platform/Apple-Clang.cmake

@@ -18,6 +18,13 @@ macro(__apple_compiler_clang lang)
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
 
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld")
+  set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "-fuse-ld=ld" "LINKER:-ld_classic")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+  set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold")
+  set(CMAKE_${lang}_USING_LINKER_SOLD "-fuse-ld=sold")
+
   if(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneOS")
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-miphoneos-version-min=")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneSimulator")

+ 3 - 0
Modules/Platform/Apple-GNU.cmake

@@ -17,6 +17,9 @@ macro(__apple_compiler_gnu lang)
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic")
 endmacro()
 
 macro(cmake_gnu_set_sysroot_flag lang)

+ 2 - 0
Modules/Platform/Linux-Clang-CUDA.cmake

@@ -0,0 +1,2 @@
+include(Platform/Linux-GNU)
+__linux_compiler_gnu(CUDA)

+ 7 - 0
Modules/Platform/Linux-GNU.cmake

@@ -12,4 +12,11 @@ macro(__linux_compiler_gnu lang)
   # We pass this for historical reasons.  Projects may have
   # executables that use dlopen but do not set ENABLE_EXPORTS.
   set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic")
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+  set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd")
+  set(CMAKE_${lang}_USING_LINKER_GOLD "-fuse-ld=gold")
+  set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold")
 endmacro()

+ 7 - 0
Modules/Platform/Linux-NVIDIA-CUDA.cmake

@@ -0,0 +1,7 @@
+
+# linker selection
+set(CMAKE_CUDA_USING_LINKER_SYSTEM "")
+set(CMAKE_CUDA_USING_LINKER_LLD "-fuse-ld=lld")
+set(CMAKE_CUDA_USING_LINKER_BFD "-fuse-ld=bfd")
+set(CMAKE_CUDA_USING_LINKER_GOLD "-fuse-ld=gold")
+set(CMAKE_CUDA_USING_LINKER_MOLD "-fuse-ld=mold")

+ 8 - 2
Modules/Platform/Windows-Clang.cmake

@@ -53,6 +53,12 @@ macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "")
 
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link")
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=link")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld-link")
+  set(CMAKE_${lang}_USING_LINKER_MSVC "-fuse-ld=link")
+
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1)
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1)
@@ -74,10 +80,10 @@ macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>")
+    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>")
   set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>")
+    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>")
 
   set(CMAKE_${lang}_CREATE_WIN32_EXE "-Xlinker /subsystem:windows")
   set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Xlinker /subsystem:console")

+ 5 - 0
Modules/Platform/Windows-GNU.cmake

@@ -112,6 +112,11 @@ macro(__windows_compiler_gnu lang)
     set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
   endforeach()
 
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+
   # No -fPIC on Windows
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")

+ 6 - 0
Modules/Platform/Windows-MSVC.cmake

@@ -512,6 +512,12 @@ macro(__windows_compiler_msvc lang)
     set(CMAKE_DEPFILE_FLAGS_${lang} "/showIncludes")
     set(CMAKE_${lang}_DEPFILE_FORMAT msvc)
   endif()
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}")
+  set(CMAKE_${lang}_USING_LINKER_LLD "${CMAKE_LINKER_LLD}")
+  set(CMAKE_${lang}_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}")
+  set(CMAKE_${lang}_USING_LINKER_MODE TOOL)
 endmacro()
 
 macro(__windows_compiler_msvc_enable_rc flags)

+ 6 - 0
Modules/Platform/Windows-NVIDIA-CUDA.cmake

@@ -47,6 +47,12 @@ set(CMAKE_CUDA_DEVICE_LINK_EXECUTABLE
   "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS} -shared -dlink <OBJECTS> -o <TARGET> <LINK_LIBRARIES> -Xcompiler=-Fd<TARGET_COMPILE_PDB>,-FS${__IMPLICIT_DLINK_FLAGS}")
 unset(__IMPLICIT_DLINK_FLAGS)
 
+# linker selection
+set(CMAKE_CUDA_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}")
+set(CMAKE_CUDA_USING_LINKER_LLD "${CMAKE_LINKER_LLD}")
+set(CMAKE_CUDA_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}")
+set(CMAKE_CUDA_USING_LINKER_MODE TOOL)
+
 string(REPLACE "/D" "-D" _PLATFORM_DEFINES_CUDA "${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_CXX}")
 
 if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)

+ 16 - 0
Source/cmCoreTryCompile.cxx

@@ -14,6 +14,7 @@
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
 
 #include "cmArgumentParser.h"
 #include "cmConfigureLog.h"
@@ -83,6 +84,7 @@ std::string const kCMAKE_HIP_PLATFORM = "CMAKE_HIP_PLATFORM";
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
 std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
+std::string const kCMAKE_LINKER_TYPE = "CMAKE_LINKER_TYPE";
 std::string const kCMAKE_LINK_SEARCH_END_STATIC =
   "CMAKE_LINK_SEARCH_END_STATIC";
 std::string const kCMAKE_LINK_SEARCH_START_STATIC =
@@ -1114,6 +1116,20 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
       vars.insert(varList.begin(), varList.end());
     }
 
+    if (this->Makefile->GetDefinition(kCMAKE_LINKER_TYPE)) {
+      // propagate various variables to support linker selection
+      vars.insert(kCMAKE_LINKER_TYPE);
+      auto defs = this->Makefile->GetDefinitions();
+      cmsys::RegularExpression linkerTypeDef{
+        "^CMAKE_[A-Za-z]+_USING_LINKER_"
+      };
+      for (auto const& def : defs) {
+        if (linkerTypeDef.find(def)) {
+          vars.insert(def);
+        }
+      }
+    }
+
     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
         cmPolicies::NEW) {
       // To ensure full support of PIE, propagate cache variables

+ 3 - 2
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -175,14 +175,15 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
   cm::string_view property(this->Top()->Property);
 
   return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
-    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
+    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s ||
+    property == "LINKER_TYPE"_s;
 }
 
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
 {
   cm::string_view property(this->Top()->Property);
 
-  return property == "LINK_OPTIONS"_s;
+  return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s;
 }
 
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const

+ 69 - 0
Source/cmGeneratorTarget.cxx

@@ -868,6 +868,31 @@ cmValue cmGeneratorTarget::GetFeature(const std::string& feature,
   return this->LocalGenerator->GetFeature(feature, config);
 }
 
+std::string cmGeneratorTarget::GetLinkerTypeProperty(
+  std::string const& lang, std::string const& config) const
+{
+  std::string propName{ "LINKER_TYPE" };
+  auto linkerType = this->GetProperty(propName);
+  if (!linkerType.IsEmpty()) {
+    cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
+                                               nullptr);
+    auto ltype =
+      cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
+                                      config, this, &dagChecker, this, lang);
+    if (this->IsDeviceLink()) {
+      cmList list{ ltype };
+      const auto DL_BEGIN = "<DEVICE_LINK>"_s;
+      const auto DL_END = "</DEVICE_LINK>"_s;
+      cm::erase_if(list, [&](const std::string& item) {
+        return item == DL_BEGIN || item == DL_END;
+      });
+      return list.to_string();
+    }
+    return ltype;
+  }
+  return std::string{};
+}
+
 const char* cmGeneratorTarget::GetLinkPIEProperty(
   const std::string& config) const
 {
@@ -5515,6 +5540,50 @@ std::string cmGeneratorTarget::GetLinkerLanguage(
   return this->GetLinkClosure(config)->LinkerLanguage;
 }
 
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& config) const
+{
+  return this->GetLinkerTool(this->GetLinkerLanguage(config), config);
+}
+
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& lang,
+                                             const std::string& config) const
+{
+  auto usingLinker =
+    cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "",
+             "LINKER_");
+  auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+  if (!format || format != "TOOL"_s) {
+    return this->Makefile->GetDefinition("CMAKE_LINKER");
+  }
+
+  auto linkerType = this->GetLinkerTypeProperty(lang, config);
+  if (linkerType.empty()) {
+    linkerType = "DEFAULT";
+  }
+  usingLinker = cmStrCat(usingLinker, linkerType);
+  auto linkerTool = this->Makefile->GetDefinition(usingLinker);
+
+  if (!linkerTool) {
+    if (this->GetGlobalGenerator()->IsVisualStudio() &&
+        linkerType == "DEFAULT"_s) {
+      return std::string{};
+    }
+
+    // fall-back to generic definition
+    linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER");
+
+    if (linkerType != "DEFAULT"_s) {
+      this->LocalGenerator->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("LINKER_TYPE '", linkerType,
+                 "' is unknown. Did you forgot to define '", usingLinker,
+                 "' variable?"));
+    }
+  }
+
+  return linkerTool;
+}
+
 std::string cmGeneratorTarget::GetPDBOutputName(
   const std::string& config) const
 {

+ 7 - 0
Source/cmGeneratorTarget.h

@@ -205,6 +205,9 @@ public:
   cmValue GetFeature(const std::string& feature,
                      const std::string& config) const;
 
+  std::string GetLinkerTypeProperty(std::string const& lang,
+                                    std::string const& config) const;
+
   const char* GetLinkPIEProperty(const std::string& config) const;
 
   bool IsIPOEnabled(std::string const& lang, std::string const& config) const;
@@ -788,6 +791,10 @@ public:
 
   //! Return the preferred linker language for this target
   std::string GetLinkerLanguage(const std::string& config) const;
+  //! Return the preferred linker tool for this target
+  std::string GetLinkerTool(const std::string& config) const;
+  std::string GetLinkerTool(const std::string& lang,
+                            const std::string& config) const;
 
   /** Does this target have a GNU implib to convert to MS format?  */
   bool HasImplibGNUtoMS(std::string const& config) const;

+ 13 - 0
Source/cmGhsMultiTargetGenerator.cxx

@@ -116,6 +116,19 @@ void cmGhsMultiTargetGenerator::Generate()
 
 void cmGhsMultiTargetGenerator::GenerateTarget()
 {
+  if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE &&
+      !this->GeneratorTarget
+         ->GetLinkerTypeProperty(
+           this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+           this->ConfigName)
+         .empty()) {
+    // Green Hill MULTI does not support this feature.
+    cmSystemTools::Message(
+      cmStrCat("'LINKER_TYPE' property, specified on target '",
+               this->GeneratorTarget->GetName(),
+               "', is not supported by this generator."));
+  }
+
   // Open the target file in copy-if-different mode.
   std::string fproj =
     cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',

+ 3 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -2500,6 +2500,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     this->CurrentLocalGenerator->GetStaticLibraryFlags(
       extraLinkOptions, configName, llang, gtgt);
   } else {
+    this->CurrentLocalGenerator->AppendLinkerTypeFlags(extraLinkOptions, gtgt,
+                                                       configName, llang);
+
     cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
     if (targetLinkFlags) {
       this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,

+ 44 - 1
Source/cmLocalGenerator.cxx

@@ -82,7 +82,6 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
                                 "CMAKE_CURRENT_SOURCE_DIR",
                                 "CMAKE_CURRENT_BINARY_DIR",
                                 "CMAKE_RANLIB",
-                                "CMAKE_LINKER",
                                 "CMAKE_MT",
                                 "CMAKE_TAPI",
                                 "CMAKE_CUDA_HOST_COMPILER",
@@ -1604,6 +1603,7 @@ void cmLocalGenerator::GetTargetFlags(
   }
 
   std::string extraLinkFlags;
+  this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage);
   this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
                                              linkLanguage);
   this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
@@ -3200,6 +3200,49 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
   }
 }
 
+void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags,
+                                             cmGeneratorTarget* target,
+                                             const std::string& config,
+                                             const std::string& linkLanguage)
+{
+  switch (target->GetType()) {
+    case cmStateEnums::EXECUTABLE:
+    case cmStateEnums::SHARED_LIBRARY:
+    case cmStateEnums::MODULE_LIBRARY:
+      break;
+    default:
+      return;
+  }
+
+  auto usingLinker =
+    cmStrCat("CMAKE_", linkLanguage, "_USING_",
+             target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_");
+
+  auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+  if (format && format != "FLAG"_s) {
+    return;
+  }
+
+  auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config);
+  if (linkerType.empty()) {
+    linkerType = "DEFAULT";
+  }
+  usingLinker = cmStrCat(usingLinker, linkerType);
+  auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker);
+  if (linkerTypeFlags) {
+    if (!linkerTypeFlags.IsEmpty()) {
+      auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags);
+      target->ResolveLinkerWrapper(linkerFlags, linkLanguage);
+      this->AppendFlags(flags, linkerFlags);
+    }
+  } else if (linkerType != "DEFAULT"_s) {
+    this->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("LINKER_TYPE '", linkerType,
+                                "' is unknown. Did you forgot to define '",
+                                usingLinker, "' variable?"));
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,

+ 3 - 0
Source/cmLocalGenerator.h

@@ -177,6 +177,9 @@ public:
   void AddPchDependencies(cmGeneratorTarget* target);
   void AddUnityBuild(cmGeneratorTarget* target);
   virtual void AddXCConfigSources(cmGeneratorTarget* /* target */) {}
+  void AppendLinkerTypeFlags(std::string& flags, cmGeneratorTarget* target,
+                             const std::string& config,
+                             const std::string& linkLanguage);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);

+ 21 - 0
Source/cmLocalVisualStudio7Generator.cxx

@@ -32,6 +32,7 @@
 #include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmSourceFile.h"
@@ -1085,6 +1086,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
       cmComputeLinkInformation& cli = *pcli;
       std::string linkLanguage = cli.GetLinkLanguage();
 
+      if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+        // Visual Studio 10 or upper is required for this feature
+        this->GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("'LINKER_TYPE' property, specified on target '",
+                   target->GetName(),
+                   "', is not supported by this generator."),
+          target->GetBacktrace());
+      }
+
       // Compute the variable name to lookup standard libraries for this
       // language.
       std::string standardLibsVar =
@@ -1161,6 +1172,16 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
       cmComputeLinkInformation& cli = *pcli;
       std::string linkLanguage = cli.GetLinkLanguage();
 
+      if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+        // Visual Studio 10 or upper is required for this feature
+        this->GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("'LINKER_TYPE' property, specified on target '",
+                   target->GetName(),
+                   "', is not supported by this generator."),
+          target->GetBacktrace());
+      }
+
       bool isWin32Executable = target->IsWin32Executable(configName);
 
       // Compute the variable name to lookup standard libraries for this

+ 3 - 0
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -344,6 +344,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
     return;
   }
 
+  auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
   // Build list of dependencies.
   std::vector<std::string> depends;
   this->AppendLinkDepends(depends, linkLanguage);
@@ -533,6 +535,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
     vars.CMTargetType =
       cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
     vars.Language = linkLanguage.c_str();
+    vars.Linker = linker.c_str();
     vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();

+ 3 - 0
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -441,6 +441,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
     return;
   }
 
+  auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
   // Build list of dependencies.
   std::vector<std::string> depends;
   this->AppendLinkDepends(depends, linkLanguage);
@@ -766,6 +768,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
     vars.CMTargetType =
       cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
     vars.Language = linkLanguage.c_str();
+    vars.Linker = linker.c_str();
     vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();

+ 2 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -153,6 +153,8 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags(
   this->LocalGenerator->AppendCompileOptions(flags, opts);
   this->LocalGenerator->SetLinkScriptShell(false);
 
+  this->LocalGenerator->AppendLinkerTypeFlags(
+    flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
   this->LocalGenerator->AppendPositionIndependentLinkerFlags(
     flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
   this->LocalGenerator->AppendDependencyInfoLinkerFlags(

+ 8 - 0
Source/cmNinjaNormalTargetGenerator.cxx

@@ -294,6 +294,9 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
         .c_str();
 
     vars.Language = "CUDA";
+    std::string linker =
+      this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+    vars.Linker = linker.c_str();
 
     // build response file name
     std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
@@ -400,6 +403,9 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
   vars.Fatbinary = "$FATBIN";
   vars.RegisterFile = "$REGISTER";
   vars.LinkFlags = "$LINK_FLAGS";
+  std::string linker =
+    this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+  vars.Linker = linker.c_str();
 
   std::string flags = this->GetFlags("CUDA", config);
   vars.Flags = flags.c_str();
@@ -441,6 +447,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
     vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
 
+    std::string linker = this->GetGeneratorTarget()->GetLinkerTool(config);
+    vars.Linker = linker.c_str();
     std::string lang = this->TargetLinkLanguage(config);
     vars.Language = lang.c_str();
     vars.AIXExports = "$AIX_EXPORTS";

+ 14 - 11
Source/cmRulePlaceholderExpander.cxx

@@ -27,6 +27,19 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
       return this->ReplaceValues->LinkFlags;
     }
   }
+  if (this->ReplaceValues->Linker) {
+    if (variable == "CMAKE_LINKER") {
+      auto result = this->OutputConverter->ConvertToOutputForExisting(
+        this->ReplaceValues->Linker);
+      if (this->ReplaceValues->Launcher) {
+        // Add launcher as part of expansion so that it always appears
+        // immediately before the command itself, regardless of whether the
+        // overall rule template contains other content at the front.
+        result = cmStrCat(this->ReplaceValues->Launcher, " ", result);
+      }
+      return result;
+    }
+  }
   if (this->ReplaceValues->Manifests) {
     if (variable == "MANIFESTS") {
       return this->ReplaceValues->Manifests;
@@ -325,17 +338,7 @@ std::string cmRulePlaceholderExpander::ExpandVariable(
   auto mapIt = this->VariableMappings.find(variable);
   if (mapIt != this->VariableMappings.end()) {
     if (variable.find("_FLAG") == std::string::npos) {
-      std::string ret =
-        this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
-
-      if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") {
-        // Add launcher as part of expansion so that it always appears
-        // immediately before the command itself, regardless of whether the
-        // overall rule template contains other content at the front.
-        ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
-      }
-
-      return ret;
+      return this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
     }
     return mapIt->second;
   }

+ 1 - 0
Source/cmRulePlaceholderExpander.h

@@ -53,6 +53,7 @@ public:
     const char* SONameFlag = nullptr;
     const char* TargetSOName = nullptr;
     const char* TargetInstallNameDir = nullptr;
+    const char* Linker = nullptr;
     const char* LinkFlags = nullptr;
     const char* Manifests = nullptr;
     const char* LanguageCompileFlags = nullptr;

+ 1 - 0
Source/cmTarget.cxx

@@ -456,6 +456,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "AUTORCC_EXECUTABLE"_s, IC::CanCompileSources },
 
   // Linking properties
+  { "LINKER_TYPE"_s, IC::CanCompileSources },
   { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },

+ 10 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -2987,6 +2987,16 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
       e1.WritePlatformConfigTag(
         "IntDir", cond, R"($(Platform)\$(Configuration)\$(ProjectName)\)");
     } else {
+      if (ttype == cmStateEnums::SHARED_LIBRARY ||
+          ttype == cmStateEnums::MODULE_LIBRARY ||
+          ttype == cmStateEnums::EXECUTABLE) {
+        auto linker = this->GeneratorTarget->GetLinkerTool(config);
+        if (!linker.empty()) {
+          ConvertToWindowsSlash(linker);
+          e1.WritePlatformConfigTag("LinkToolExe", cond, linker);
+        }
+      }
+
       std::string intermediateDir = cmStrCat(
         this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
         config, '/');

+ 8 - 0
Tests/RunCMake/CMakeLists.txt

@@ -737,6 +737,14 @@ if((CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
   endif()
 endif()
 
+if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)"
+    AND CMAKE_C_COMPILER_ID MATCHES "^(AppleClang|Clang|GNU|MSVC|NVIDIA)$"
+    AND NOT CMAKE_GENERATOR STREQUAL "Green Hills MULTI")
+  add_RunCMake_test(LinkerSelection -DCMake_TEST_CUDA=${CMake_TEST_CUDA}
+                                    -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+                                    -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
+endif()
+
 add_RunCMake_test(File_Archive)
 add_RunCMake_test(File_Configure)
 add_RunCMake_test(File_Generate)

+ 7 - 0
Tests/RunCMake/LinkerSelection/AppleClassic.cmake

@@ -0,0 +1,7 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE APPLE_CLASSIC)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE m m)

+ 3 - 0
Tests/RunCMake/LinkerSelection/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 2 - 0
Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake

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

+ 36 - 0
Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake

@@ -0,0 +1,36 @@
+
+enable_language(C)
+
+set(CMAKE_C_USING_LINKER_FOO_C "${CMAKE_C_USING_LINKER_LLD}")
+
+add_executable(main main.c)
+set_property(TARGET main PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>")
+
+if(CMake_TEST_CUDA)
+  enable_language(CUDA)
+
+  set(CMAKE_CUDA_USING_LINKER_FOO_CUDA "${CMAKE_CUDA_USING_LINKER_LLD}")
+
+  add_executable(mainCU main.cu)
+  set_property(TARGET mainCU PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>")
+endif()
+
+#
+# Generate file for validation
+#
+if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL")
+  cmake_path(GET CMAKE_C_USING_LINKER_FOO_C FILENAME LINKER_TYPE_OPTION)
+else()
+  set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_FOO_C}")
+endif()
+if(CMake_TEST_CUDA)
+  if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL")
+    cmake_path(GET CMAKE_CUDA_USING_LINKER_FOO_CUDA FILENAME CUDA_LINKER)
+  else()
+    set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_FOO_CUDA}")
+  endif()
+  string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}")
+endif()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake"
+  "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n")

+ 1 - 0
Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt

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

+ 3 - 0
Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error in CMakeLists.txt:
+  LINKER_TYPE 'FOO' is unknown.  Did you forgot to define
+  'CMAKE_C_USING_LINKER_FOO' variable\?

+ 5 - 0
Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake

@@ -0,0 +1,5 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE FOO)
+add_executable(main main.c)

+ 9 - 0
Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake

@@ -0,0 +1,9 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/LINKER_TYPE_OPTION.cmake")
+
+# In some environment, `=` character is escaped
+string(REPLACE "=" "\\\\?=" LINKER_TYPE_OPTION "${LINKER_TYPE_OPTION}")
+
+if (NOT actual_stdout MATCHES "${LINKER_TYPE_OPTION}")
+    set (RunCMake_TEST_FAILED "Not found expected '${LINKER_TYPE_OPTION}'.")
+endif()

+ 44 - 0
Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake

@@ -0,0 +1,44 @@
+include(RunCMake)
+
+if (RunCMake_GENERATOR MATCHES "Visual Studio 9 2008")
+  run_cmake(UnsupportedLinkerType)
+  return()
+endif()
+
+run_cmake(InvalidLinkerType)
+
+# look-up for LLVM linker
+if (WIN32)
+  set (LINKER_NAMES lld-link)
+else()
+  set(LINKER_NAMES ld.lld ld64.lld)
+endif()
+find_program(LLD_LINKER NAMES ${LINKER_NAMES})
+
+macro(run_cmake_and_build test)
+  run_cmake_with_options(${test} -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(CMake_TEST_CUDA)
+    string(APPEND "|${CMAKE_CUDA_USING_LINKER_LLD}")
+  endif()
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release --verbose ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+if(LLD_LINKER)
+  block(SCOPE_FOR VARIABLES)
+    set(CMAKE_VERBOSE_MAKEFILE TRUE)
+    set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+    set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+
+    run_cmake_and_build(ValidLinkerType)
+    run_cmake_and_build(CustomLinkerType)
+  endblock()
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0")
+  run_cmake_and_build(AppleClassic)
+endif()

+ 1 - 0
Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt

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

+ 3 - 0
Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error at UnsupportedLinkerType.cmake:[0-9]+ \(add_executable\):
+  'LINKER_TYPE' property, specified on target 'main', is not supported by
+  this generator.

+ 5 - 0
Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake

@@ -0,0 +1,5 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE LDD)
+add_executable(main main.c)

+ 2 - 0
Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake

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

+ 32 - 0
Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake

@@ -0,0 +1,32 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE LLD)
+
+add_executable(main main.c)
+
+if(CMake_TEST_CUDA)
+  enable_language(CUDA)
+
+  add_executable(mainCU main.cu)
+endif()
+
+#
+# Generate file for validation
+#
+if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL")
+  cmake_path(GET CMAKE_C_USING_LINKER_LLD FILENAME LINKER_TYPE_OPTION)
+else()
+  set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_LLD}")
+endif()
+if(CMake_TEST_CUDA)
+  if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL")
+    cmake_path(GET CMAKE_CUDA_USING_LINKER_LLD FILENAME CUDA_LINKER)
+  else()
+    set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_LLD}")
+  endif()
+  string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}")
+endif()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake"
+  "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n")

+ 4 - 0
Tests/RunCMake/LinkerSelection/main.c

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

+ 5 - 0
Tests/RunCMake/LinkerSelection/main.cu

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