Просмотр исходного кода

Merge topic 'LINK_OPTIONS'

66ea1a3795 LINK_OPTIONS: Add support of "LINKER:" prefix
c1f5a44b28 LINK_OPTIONS: Add new family of properties
8e28d2630a Makefile generator: link flags management refactoring

Acked-by: Kitware Robot <[email protected]>
Acked-by: Alex Turbov <[email protected]>
Merge-request: !2033
Brad King 7 лет назад
Родитель
Сommit
812eaa465c
100 измененных файлов с 1069 добавлено и 77 удалено
  1. 10 0
      Help/command/LINK_OPTIONS_LINKER.txt
  2. 1 1
      Help/command/OPTIONS_SHELL.txt
  3. 1 1
      Help/command/add_compile_options.rst
  4. 26 0
      Help/command/add_link_options.rst
  5. 1 1
      Help/command/target_compile_options.rst
  6. 2 1
      Help/command/target_link_libraries.rst
  7. 42 0
      Help/command/target_link_options.rst
  8. 2 0
      Help/manual/cmake-commands.7.rst
  9. 3 0
      Help/manual/cmake-properties.7.rst
  10. 2 0
      Help/manual/cmake-variables.7.rst
  11. 16 0
      Help/prop_dir/LINK_OPTIONS.rst
  12. 9 0
      Help/prop_tgt/INTERFACE_LINK_OPTIONS.rst
  13. 6 2
      Help/prop_tgt/LINK_FLAGS.rst
  14. 4 0
      Help/prop_tgt/LINK_FLAGS_CONFIG.rst
  15. 21 0
      Help/prop_tgt/LINK_OPTIONS.rst
  16. 11 0
      Help/release/dev/LINK_OPTIONS.rst
  17. 39 0
      Help/variable/CMAKE_LANG_LINKER_WRAPPER_FLAG.rst
  18. 9 0
      Help/variable/CMAKE_LANG_LINKER_WRAPPER_FLAG_SEP.rst
  19. 2 0
      Modules/Compiler/ARMCC.cmake
  20. 1 0
      Modules/Compiler/Absoft-Fortran.cmake
  21. 2 0
      Modules/Compiler/Bruce-C.cmake
  22. 2 0
      Modules/Compiler/Clang.cmake
  23. 2 0
      Modules/Compiler/G95-Fortran.cmake
  24. 3 0
      Modules/Compiler/GNU.cmake
  25. 3 0
      Modules/Compiler/HP-C.cmake
  26. 3 0
      Modules/Compiler/HP-CXX.cmake
  27. 3 0
      Modules/Compiler/HP-Fortran.cmake
  28. 3 0
      Modules/Compiler/PGI.cmake
  29. 3 0
      Modules/Compiler/QCC.cmake
  30. 3 0
      Modules/Compiler/SCO.cmake
  31. 3 0
      Modules/Compiler/SunPro-C.cmake
  32. 3 0
      Modules/Compiler/SunPro-CXX.cmake
  33. 3 0
      Modules/Compiler/SunPro-Fortran.cmake
  34. 3 0
      Modules/Compiler/TinyCC-C.cmake
  35. 3 0
      Modules/Compiler/XL.cmake
  36. 3 0
      Modules/Platform/Apple-Intel.cmake
  37. 3 1
      Modules/Platform/Generic-ADSP-C.cmake
  38. 3 1
      Modules/Platform/Generic-ADSP-CXX.cmake
  39. 2 1
      Modules/Platform/Generic-SDCC-C.cmake
  40. 3 0
      Modules/Platform/Linux-Intel.cmake
  41. 2 0
      Modules/Platform/Windows-Embarcadero.cmake
  42. 4 0
      Source/CMakeLists.txt
  43. 20 0
      Source/cmAddLinkOptionsCommand.cxx
  44. 31 0
      Source/cmAddLinkOptionsCommand.h
  45. 5 0
      Source/cmCommands.cxx
  46. 6 0
      Source/cmExportBuildAndroidMKGenerator.cxx
  47. 3 0
      Source/cmExportBuildFileGenerator.cxx
  48. 3 0
      Source/cmExportInstallFileGenerator.cxx
  49. 2 1
      Source/cmGeneratorExpressionDAGChecker.h
  50. 164 11
      Source/cmGeneratorTarget.cxx
  51. 6 0
      Source/cmGeneratorTarget.h
  52. 4 0
      Source/cmGlobalXCodeGenerator.cxx
  53. 8 0
      Source/cmLocalGenerator.cxx
  54. 7 0
      Source/cmLocalVisualStudio7Generator.cxx
  55. 15 0
      Source/cmMakefile.cxx
  56. 3 0
      Source/cmMakefile.h
  57. 2 12
      Source/cmMakefileExecutableTargetGenerator.cxx
  58. 8 34
      Source/cmMakefileLibraryTargetGenerator.cxx
  59. 1 2
      Source/cmMakefileLibraryTargetGenerator.h
  60. 17 0
      Source/cmMakefileTargetGenerator.cxx
  61. 2 0
      Source/cmMakefileTargetGenerator.h
  62. 2 0
      Source/cmState.cxx
  63. 52 0
      Source/cmStateDirectory.cxx
  64. 7 0
      Source/cmStateDirectory.h
  65. 4 0
      Source/cmStatePrivate.h
  66. 7 0
      Source/cmStateSnapshot.cxx
  67. 71 6
      Source/cmTarget.cxx
  68. 6 0
      Source/cmTarget.h
  69. 41 0
      Source/cmTargetLinkOptionsCommand.cxx
  70. 41 0
      Source/cmTargetLinkOptionsCommand.h
  71. 5 0
      Source/cmVisualStudio10TargetGenerator.cxx
  72. 20 0
      Tests/CMakeCommands/add_link_options/CMakeLists.txt
  73. 4 0
      Tests/CMakeCommands/add_link_options/LinkOptionsExe.c
  74. 19 0
      Tests/CMakeCommands/target_link_options/CMakeLists.txt
  75. 7 0
      Tests/CMakeCommands/target_link_options/LinkOptionsLib.c
  76. 3 0
      Tests/CMakeLists.txt
  77. 10 0
      Tests/ExportImport/Export/CMakeLists.txt
  78. 5 0
      Tests/ExportImport/Import/A/CMakeLists.txt
  79. 8 0
      Tests/ExportImport/Import/A/imp_testLinkOptions.cpp
  80. 4 2
      Tests/RunCMake/AndroidMK/AndroidMK.cmake
  81. 8 0
      Tests/RunCMake/AndroidMK/expectedBuildAndroidMK.txt
  82. 8 0
      Tests/RunCMake/AndroidMK/expectedInstallAndroidMK.txt
  83. 2 0
      Tests/RunCMake/CMakeLists.txt
  84. 5 0
      Tests/RunCMake/add_link_options/CMakeLists.txt
  85. 2 0
      Tests/RunCMake/add_link_options/LINKER_SHELL_expansion-build-check.cmake
  86. 4 0
      Tests/RunCMake/add_link_options/LINKER_SHELL_expansion.cmake
  87. 2 0
      Tests/RunCMake/add_link_options/LINKER_expansion-build-check.cmake
  88. 36 0
      Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake
  89. 15 0
      Tests/RunCMake/add_link_options/LINKER_expansion-validation.cmake
  90. 4 0
      Tests/RunCMake/add_link_options/LINKER_expansion.cmake
  91. 7 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-exe-check.cmake
  92. 1 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-exe-result.txt
  93. 7 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-check.cmake
  94. 1 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-result.txt
  95. 7 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-check.cmake
  96. 1 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-result.txt
  97. 17 0
      Tests/RunCMake/add_link_options/LINK_OPTIONS.cmake
  98. 4 0
      Tests/RunCMake/add_link_options/LinkOptionsExe.c
  99. 7 0
      Tests/RunCMake/add_link_options/LinkOptionsLib.c
  100. 38 0
      Tests/RunCMake/add_link_options/RunCMakeTest.cmake

+ 10 - 0
Help/command/LINK_OPTIONS_LINKER.txt

@@ -0,0 +1,10 @@
+To pass options to the linker tool, each compiler driver has is own syntax.
+The ``LINKER:`` prefix can be used to specify, in a portable way, options
+to pass to the linker tool. The ``LINKER:`` prefix is replaced by the required
+driver option and the rest of the option string defines linker arguments using
+``,`` as separator. These arguments will be formatted according to the
+:variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG` and
+:variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP` variables.
+
+For example, ``"LINKER:-z,defs"`` becomes ``-Xlinker -z -Xlinker defs`` for
+``Clang`` and ``-Wl,-z,defs`` for ``GNU GCC``.

+ 1 - 1
Help/command/COMPILE_OPTIONS_SHELL.txt → Help/command/OPTIONS_SHELL.txt

@@ -1,4 +1,4 @@
-The final set of compile options used for a target is constructed by
+The final set of compile or link options used for a target is constructed by
 accumulating options from the current target and the usage requirements of
 it dependencies.  The set of options is de-duplicated to avoid repetition.
 While beneficial for individual options, the de-duplication step can break

+ 1 - 1
Help/command/add_compile_options.rst

@@ -22,4 +22,4 @@ the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
 manual for more on defining buildsystem properties.
 
-.. include:: COMPILE_OPTIONS_SHELL.txt
+.. include:: OPTIONS_SHELL.txt

+ 26 - 0
Help/command/add_link_options.rst

@@ -0,0 +1,26 @@
+add_link_options
+----------------
+
+Adds options to the link of targets.
+
+::
+
+  add_link_options(<option> ...)
+
+Adds options to the link step for targets in the current directory and below
+that are added after this command is invoked. See documentation of the
+:prop_dir:`directory <LINK_OPTIONS>` and
+:prop_tgt:`target <LINK_OPTIONS>` ``LINK_OPTIONS`` properties.
+
+This command can be used to add any options, but alternative commands
+exist to add libraries (:command:`target_link_libraries` or
+:command:`link_libraries`).
+
+Arguments to ``add_link_options`` may use "generator expressions" with
+the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.
+
+.. include:: LINK_OPTIONS_LINKER.txt
+
+.. include:: OPTIONS_SHELL.txt

+ 1 - 1
Help/command/target_compile_options.rst

@@ -39,4 +39,4 @@ with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
 manual for more on defining buildsystem properties.
 
-.. include:: COMPILE_OPTIONS_SHELL.txt
+.. include:: OPTIONS_SHELL.txt

+ 2 - 1
Help/command/target_link_libraries.rst

@@ -70,7 +70,8 @@ Each ``<item>`` may be:
 
   Link flags specified here are inserted into the link command in the same
   place as the link libraries. This might not be correct, depending on
-  the linker. Use the :prop_tgt:`LINK_FLAGS` target property to add link
+  the linker. Use the :prop_tgt:`LINK_OPTIONS` target property or
+  :command:`target_link_options` command to add link
   flags explicitly. The flags will then be placed at the toolchain-defined
   flag position in the link command.
 

+ 42 - 0
Help/command/target_link_options.rst

@@ -0,0 +1,42 @@
+target_link_options
+-------------------
+
+Add link options to a target.
+
+::
+
+  target_link_options(<target> [BEFORE]
+    <INTERFACE|PUBLIC|PRIVATE> [items1...]
+    [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
+
+Specify link options to use when linking a given target.  The
+named ``<target>`` must have been created by a command such as
+:command:`add_executable` or :command:`add_library` and must not be an
+:ref:`ALIAS target <Alias Targets>`.
+
+If ``BEFORE`` is specified, the content will be prepended to the property
+instead of being appended.
+
+This command can be used to add any options, but
+alternative commands exist to add libraries
+(:command:`target_link_libraries` and :command:`link_libraries`).
+See documentation of the :prop_dir:`directory <LINK_OPTIONS>` and
+:prop_tgt:`target <LINK_OPTIONS>` ``LINK_OPTIONS`` properties.
+
+The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
+specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
+items will populate the :prop_tgt:`LINK_OPTIONS` property of
+``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+:prop_tgt:`INTERFACE_LINK_OPTIONS` property of ``<target>``.
+(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items.)
+The following arguments specify link options.  Repeated calls for the same
+``<target>`` append items in the order called.
+
+Arguments to ``target_link_options`` may use "generator expressions"
+with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.
+
+.. include:: LINK_OPTIONS_LINKER.txt
+
+.. include:: OPTIONS_SHELL.txt

+ 2 - 0
Help/manual/cmake-commands.7.rst

@@ -78,6 +78,7 @@ These commands are available only in CMake projects.
    /command/add_dependencies
    /command/add_executable
    /command/add_library
+   /command/add_link_options
    /command/add_subdirectory
    /command/add_test
    /command/aux_source_directory
@@ -111,6 +112,7 @@ These commands are available only in CMake projects.
    /command/target_compile_options
    /command/target_include_directories
    /command/target_link_libraries
+   /command/target_link_options
    /command/target_sources
    /command/try_compile
    /command/try_run

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

@@ -77,6 +77,7 @@ Properties on Directories
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION
    /prop_dir/LABELS
    /prop_dir/LINK_DIRECTORIES
+   /prop_dir/LINK_OPTIONS
    /prop_dir/LISTFILE_STACK
    /prop_dir/MACROS
    /prop_dir/PARENT_DIRECTORY
@@ -225,6 +226,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
+   /prop_tgt/INTERFACE_LINK_OPTIONS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
    /prop_tgt/INTERFACE_SOURCES
    /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
@@ -254,6 +256,7 @@ Properties on Targets
    /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG
    /prop_tgt/LINK_INTERFACE_MULTIPLICITY
    /prop_tgt/LINK_LIBRARIES
+   /prop_tgt/LINK_OPTIONS
    /prop_tgt/LINK_SEARCH_END_STATIC
    /prop_tgt/LINK_SEARCH_START_STATIC
    /prop_tgt/LINK_WHAT_YOU_USE

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

@@ -460,6 +460,8 @@ Variables for Languages
    /variable/CMAKE_LANG_LIBRARY_ARCHITECTURE
    /variable/CMAKE_LANG_LINKER_PREFERENCE
    /variable/CMAKE_LANG_LINKER_PREFERENCE_PROPAGATES
+   /variable/CMAKE_LANG_LINKER_WRAPPER_FLAG
+   /variable/CMAKE_LANG_LINKER_WRAPPER_FLAG_SEP
    /variable/CMAKE_LANG_LINK_EXECUTABLE
    /variable/CMAKE_LANG_OUTPUT_EXTENSION
    /variable/CMAKE_LANG_PLATFORM_ID

+ 16 - 0
Help/prop_dir/LINK_OPTIONS.rst

@@ -0,0 +1,16 @@
+LINK_OPTIONS
+------------
+
+List of options to use for the link step.
+
+This property holds a :ref:`;-list <CMake Language Lists>` of options
+given so far to the :command:`add_link_options` command.
+
+This property is used to initialize the :prop_tgt:`LINK_OPTIONS` target
+property when a target is created, which is used by the generators to set
+the options for the compiler.
+
+Contents of ``LINK_OPTIONS`` may use "generator expressions" with the
+syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)` manual
+for available expressions.  See the :manual:`cmake-buildsystem(7)` manual
+for more on defining buildsystem properties.

+ 9 - 0
Help/prop_tgt/INTERFACE_LINK_OPTIONS.rst

@@ -0,0 +1,9 @@
+INTERFACE_LINK_OPTIONS
+----------------------
+
+.. |property_name| replace:: link options
+.. |command_name| replace:: :command:`target_link_options`
+.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_LINK_OPTIONS``
+.. |PROPERTY_LINK| replace:: :prop_tgt:`LINK_OPTIONS`
+.. |PROPERTY_GENEX| replace:: ``$<TARGET_PROPERTY:foo,INTERFACE_LINK_OPTIONS>``
+.. include:: INTERFACE_BUILD_PROPERTY.txt

+ 6 - 2
Help/prop_tgt/LINK_FLAGS.rst

@@ -3,7 +3,11 @@ LINK_FLAGS
 
 Additional flags to use when linking this target.
 
-The LINK_FLAGS property can be used to add extra flags to the link
-step of a target.  :prop_tgt:`LINK_FLAGS_<CONFIG>` will add to the
+The LINK_FLAGS property, managed as a string, can be used to add extra flags
+to the link step of a target.  :prop_tgt:`LINK_FLAGS_<CONFIG>` will add to the
 configuration ``<CONFIG>``, for example, ``DEBUG``, ``RELEASE``,
 ``MINSIZEREL``, ``RELWITHDEBINFO``, ...
+
+.. note::
+
+  This property has been superseded by :prop_tgt:`LINK_OPTIONS` property.

+ 4 - 0
Help/prop_tgt/LINK_FLAGS_CONFIG.rst

@@ -4,3 +4,7 @@ LINK_FLAGS_<CONFIG>
 Per-configuration linker flags for a target.
 
 This is the configuration-specific version of LINK_FLAGS.
+
+.. note::
+
+  This property has been superseded by :prop_tgt:`LINK_OPTIONS` property.

+ 21 - 0
Help/prop_tgt/LINK_OPTIONS.rst

@@ -0,0 +1,21 @@
+LINK_OPTIONS
+------------
+
+List of options to use when linking this target.
+
+This property holds a :ref:`;-list <CMake Language Lists>` of options
+specified so far for its target.  Use the :command:`target_link_options`
+command to append more options.
+
+This property is initialized by the :prop_dir:`LINK_OPTIONS` directory
+property when a target is created, and is used by the generators to set
+the options for the compiler.
+
+Contents of ``LINK_OPTIONS`` may use "generator expressions" with the
+syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)` manual
+for available expressions.  See the :manual:`cmake-buildsystem(7)` manual
+for more on defining buildsystem properties.
+
+.. note::
+
+  This property must be used in preference to :prop_tgt:`LINK_FLAGS` property.

+ 11 - 0
Help/release/dev/LINK_OPTIONS.rst

@@ -0,0 +1,11 @@
+LINK_OPTIONS
+------------
+
+* CMake gained new capabilities to manage link step:
+
+  * :prop_dir:`LINK_OPTIONS` directory property.
+  * :prop_tgt:`LINK_OPTIONS` and :prop_tgt:`INTERFACE_LINK_OPTIONS` target
+    properties.
+  * :command:`add_link_options` command to add link options in the current
+    directory.
+  * :command:`target_link_options` command to add link options to targets.

+ 39 - 0
Help/variable/CMAKE_LANG_LINKER_WRAPPER_FLAG.rst

@@ -0,0 +1,39 @@
+CMAKE_<LANG>_LINKER_WRAPPER_FLAG
+--------------------------------
+
+Defines the syntax of compiler driver option to pass options to the linker
+tool. It will be used to translate the ``LINKER:`` prefix in the link options
+(see :command:`add_link_options` and :command:`target_link_options`).
+
+This variable holds a :ref:`;-list <CMake Language Lists>` of tokens.
+If a space (i.e. " ") is specified as last token, flag and ``LINKER:``
+arguments will be specified as separate arguments to the compiler driver.
+The :variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP` variable can be specified
+to manage concatenation of arguments.
+
+For example, for ``Clang`` we have:
+
+.. code-block:: cmake
+
+  set (CMAKE_C_LINKER_WRAPPER_FLAG "-Xlinker" " ")
+
+Specifying ``"LINKER:-z defs"`` will be transformed in
+``-Xlinker -z -Xlinker defs``.
+
+For ``GNU GCC``:
+
+.. code-block:: cmake
+
+  set (CMAKE_C_LINKER_WRAPPER_FLAG "-Wl,")
+  set (CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")
+
+Specifying ``"LINKER:-z defs"`` will be transformed in ``-Wl,-z,defs``.
+
+And for ``SunPro``:
+
+.. code-block:: cmake
+
+  set (CMAKE_C_LINKER_WRAPPER_FLAG "-Qoption" "ld" " ")
+  set (CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")
+
+Specifying ``"LINKER:-z defs"`` will be transformed in ``-Qoption ld -z,defs``.

+ 9 - 0
Help/variable/CMAKE_LANG_LINKER_WRAPPER_FLAG_SEP.rst

@@ -0,0 +1,9 @@
+CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP
+------------------------------------
+
+This variable is used with :variable:`CMAKE_<LANG>_LINKER_WRAPPER_FLAG`
+variable to format ``LINKER:`` prefix in the link options
+(see :command:`add_link_options` and :command:`target_link_options`).
+
+When specified, arguments of the ``LINKER:`` prefix will be concatenated using
+this value as separator.

+ 2 - 0
Modules/Compiler/ARMCC.cmake

@@ -34,4 +34,6 @@ macro(__compiler_armcc lang)
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY  "<CMAKE_AR> --create -cr <TARGET> <LINK_FLAGS> <OBJECTS>")
 
   set(CMAKE_DEPFILE_FLAGS_${lang} "--depend=<DEPFILE> --depend_single_line --no_depend_system_headers")
+
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
 endmacro()

+ 1 - 0
Modules/Compiler/Absoft-Fortran.cmake

@@ -8,3 +8,4 @@ set(CMAKE_Fortran_MODPATH_FLAG "-p")
 set(CMAKE_Fortran_VERBOSE_FLAG "-v")
 set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed")
 set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-X")

+ 2 - 0
Modules/Compiler/Bruce-C.cmake

@@ -5,3 +5,5 @@ string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " -g")
 string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
 string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG")
 string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " -g -DNDEBUG")
+
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-X")

+ 2 - 0
Modules/Compiler/Clang.cmake

@@ -30,6 +30,8 @@ else()
       set(CMAKE_${lang}_COMPILE_OPTIONS_TARGET "--target=")
       set(CMAKE_${lang}_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN "--gcc-toolchain=")
     endif()
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP)
 
     set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
     set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)

+ 2 - 0
Modules/Compiler/G95-Fortran.cmake

@@ -7,3 +7,5 @@ set(CMAKE_Fortran_MODDIR_FLAG "-fmod=")
 set(CMAKE_Fortran_VERBOSE_FLAG "-v")
 set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form")
 set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",")

+ 3 - 0
Modules/Compiler/GNU.cmake

@@ -24,6 +24,9 @@ macro(__compiler_gnu lang)
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared")
   set(CMAKE_${lang}_COMPILE_OPTIONS_SYSROOT "--sysroot=")
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+
   # Older versions of gcc (< 4.5) contain a bug causing them to report a missing
   # header file as a warning if depfiles are enabled, causing check_header_file
   # tests to always succeed.  Work around this by disabling dependency tracking

+ 3 - 0
Modules/Compiler/HP-C.cmake

@@ -2,3 +2,6 @@ set(CMAKE_C_VERBOSE_FLAG "-v")
 
 set(CMAKE_C_CREATE_ASSEMBLY_SOURCE "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 set(CMAKE_C_CREATE_PREPROCESSED_SOURCE "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
+
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")

+ 3 - 0
Modules/Compiler/HP-CXX.cmake

@@ -3,6 +3,9 @@ set(CMAKE_CXX_VERBOSE_FLAG "-v")
 set(CMAKE_CXX_CREATE_ASSEMBLY_SOURCE "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 set(CMAKE_CXX_CREATE_PREPROCESSED_SOURCE "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
 
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG_SEP ",")
+
 # HP aCC since version 3.80 supports the flag +hpxstd98 to get ANSI C++98
 # template support. It is known that version 6.25 doesn't need that flag.
 # Current assumption: the flag is needed for every version from 3.80 to 4

+ 3 - 0
Modules/Compiler/HP-Fortran.cmake

@@ -4,3 +4,6 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "+source=free")
 
 set(CMAKE_Fortran_CREATE_ASSEMBLY_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 set(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
+
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG ",")

+ 3 - 0
Modules/Compiler/PGI.cmake

@@ -25,6 +25,9 @@ macro(__compiler_pgi lang)
     string(APPEND CMAKE_${lang}_FLAGS_INIT " -Bdynamic")
   endif()
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG ",")
+
   set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
   if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le AND (NOT CMAKE_HOST_WIN32 OR CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 16.3))
     set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)

+ 3 - 0
Modules/Compiler/QCC.cmake

@@ -13,6 +13,9 @@ macro(__compiler_qcc lang)
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-Wp,-isystem,")
   set(CMAKE_DEPFILE_FLAGS_${lang} "-Wc,-MD,<DEPFILE>,-MT,<OBJECT>,-MF,<DEPFILE>")
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+
   set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE NO)
   set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO)
 

+ 3 - 0
Modules/Compiler/SCO.cmake

@@ -15,4 +15,7 @@ macro(__compiler_sco lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_DLL -belf)
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-Kpic -belf")
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-belf -Wl,-Bexport")
+
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
 endmacro()

+ 3 - 0
Modules/Compiler/SunPro-C.cmake

@@ -29,6 +29,9 @@ foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
   set(CMAKE_${type}_LINK_DYNAMIC_C_FLAGS "-Bdynamic")
 endforeach()
 
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-Qoption" "ld" " ")
+set(CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")
+
 if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.13)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c89")
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=c89")

+ 3 - 0
Modules/Compiler/SunPro-CXX.cmake

@@ -29,6 +29,9 @@ foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
   set(CMAKE_${type}_LINK_DYNAMIC_CXX_FLAGS "-Bdynamic")
 endforeach()
 
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG "-Qoption" "ld" " ")
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG_SEP ",")
+
 set(CMAKE_CXX_CREATE_PREPROCESSED_SOURCE "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
 set(CMAKE_CXX_CREATE_ASSEMBLY_SOURCE "<CMAKE_CXX_COMPILER> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 

+ 3 - 0
Modules/Compiler/SunPro-Fortran.cmake

@@ -18,6 +18,9 @@ string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -g -xO2 -DNDEBUG")
 set(CMAKE_Fortran_MODDIR_FLAG "-moddir=")
 set(CMAKE_Fortran_MODPATH_FLAG "-M")
 
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Qoption" "ld" " ")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",")
+
 set(CMAKE_Fortran_PREPROCESS_SOURCE
   "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -F -fpp <SOURCE> -o <PREPROCESSED_SOURCE>")
 

+ 3 - 0
Modules/Compiler/TinyCC-C.cmake

@@ -6,3 +6,6 @@ string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " -g")
 string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
 string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG")
 string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " -g -DNDEBUG")
+
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")

+ 3 - 0
Modules/Compiler/XL.cmake

@@ -23,6 +23,9 @@ macro(__compiler_xl lang)
   set(CMAKE_${lang}_RESPONSE_FILE_FLAG "-qoptfile=")
   set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-qoptfile=")
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+
   string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
   string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O")
   string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O")

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

@@ -10,6 +10,9 @@ macro(__apple_compiler_intel lang)
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names")
   set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS "-bundle -Wl,-headerpad_max_install_names")
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+
   if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0)
     set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=")
   endif()

+ 3 - 1
Modules/Platform/Generic-ADSP-C.cmake

@@ -9,6 +9,9 @@ string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " ")
 string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " ")
 string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " ")
 
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-flags-link" " ")
+set(CMAKE_C_LINKER_WRAPPER_FLAG_SEP ",")
+
 set(CMAKE_C_CREATE_STATIC_LIBRARY
     "<CMAKE_C_COMPILER> -build-lib -proc ${ADSP_PROCESSOR} -si-revision ${ADSP_PROCESSOR_SILICIUM_REVISION} -o <TARGET> <CMAKE_C_LINK_FLAGS> <OBJECTS>")
 
@@ -17,4 +20,3 @@ set(CMAKE_C_LINK_EXECUTABLE
 
 set(CMAKE_C_CREATE_SHARED_LIBRARY)
 set(CMAKE_C_CREATE_MODULE_LIBRARY)
-

+ 3 - 1
Modules/Platform/Generic-ADSP-CXX.cmake

@@ -7,6 +7,9 @@ string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL_INIT " ")
 string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " ")
 string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT " ")
 
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG "-flags-link" " ")
+set(CMAKE_CXX_LINKER_WRAPPER_FLAG_SEP ",")
+
 set(CMAKE_CXX_CREATE_STATIC_LIBRARY
      "<CMAKE_CXX_COMPILER> -build-lib -proc ${ADSP_PROCESSOR} -si-revision ${ADSP_PROCESSOR_SILICIUM_REVISION} -o <TARGET> <CMAKE_CXX_LINK_FLAGS> <OBJECTS>")
 
@@ -15,4 +18,3 @@ set(CMAKE_CXX_LINK_EXECUTABLE
 
 set(CMAKE_CXX_CREATE_SHARED_LIBRARY)
 set(CMAKE_CXX_CREATE_MODULE_LIBRARY)
-

+ 2 - 1
Modules/Platform/Generic-SDCC-C.cmake

@@ -37,6 +37,8 @@ if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_INIT)
   set (CMAKE_EXE_LINKER_FLAGS_INIT --model-small)
 endif()
 
+set(CMAKE_C_LINKER_WRAPPER_FLAG "-Wl" ",")
+
 # compile a C file into an object file
 set(CMAKE_C_COMPILE_OBJECT  "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")
 
@@ -51,4 +53,3 @@ set(CMAKE_C_CREATE_STATIC_LIBRARY
 # not supported by sdcc
 set(CMAKE_C_CREATE_SHARED_LIBRARY "")
 set(CMAKE_C_CREATE_MODULE_LIBRARY "")
-

+ 3 - 0
Modules/Platform/Linux-Intel.cmake

@@ -30,6 +30,9 @@ macro(__linux_compiler_intel lang)
   # executables that use dlopen but do not set ENABLE_EXPORTS.
   set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic")
 
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+
   set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
 
   if(XIAR)

+ 2 - 0
Modules/Platform/Windows-Embarcadero.cmake

@@ -76,6 +76,8 @@ macro(__embarcadero_language lang)
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "${_tD}") # ... while this is a space separated string.
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1)
 
+  set (CMAKE_${lang}_LINKER_WRAPPER_FLAG "-l")
+
   # compile a source file into an object file
   # place <DEFINES> outside the response file because Borland refuses
   # to parse quotes from the response file.

+ 4 - 0
Source/CMakeLists.txt

@@ -379,6 +379,8 @@ set(SRCS
   cmAddCompileDefinitionsCommand.h
   cmAddCompileOptionsCommand.cxx
   cmAddCompileOptionsCommand.h
+  cmAddLinkOptionsCommand.cxx
+  cmAddLinkOptionsCommand.h
   cmAddCustomCommandCommand.cxx
   cmAddCustomCommandCommand.h
   cmAddCustomTargetCommand.cxx
@@ -574,6 +576,8 @@ set(SRCS
   cmTargetCompileOptionsCommand.h
   cmTargetIncludeDirectoriesCommand.cxx
   cmTargetIncludeDirectoriesCommand.h
+  cmTargetLinkOptionsCommand.cxx
+  cmTargetLinkOptionsCommand.h
   cmTargetLinkLibrariesCommand.cxx
   cmTargetLinkLibrariesCommand.h
   cmTargetPropCommandBase.cxx

+ 20 - 0
Source/cmAddLinkOptionsCommand.cxx

@@ -0,0 +1,20 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmAddLinkOptionsCommand.h"
+
+#include "cmMakefile.h"
+
+class cmExecutionStatus;
+
+bool cmAddLinkOptionsCommand::InitialPass(std::vector<std::string> const& args,
+                                          cmExecutionStatus&)
+{
+  if (args.empty()) {
+    return true;
+  }
+
+  for (std::string const& i : args) {
+    this->Makefile->AddLinkOption(i);
+  }
+  return true;
+}

+ 31 - 0
Source/cmAddLinkOptionsCommand.h

@@ -0,0 +1,31 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmAddLinkOptionsCommand_h
+#define cmAddLinkOptionsCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include "cmCommand.h"
+
+class cmExecutionStatus;
+
+class cmAddLinkOptionsCommand : public cmCommand
+{
+public:
+  /**
+   * This is a virtual constructor for the command.
+   */
+  cmCommand* Clone() override { return new cmAddLinkOptionsCommand; }
+
+  /**
+   * This is called when the command is first encountered in
+   * the CMakeLists.txt file.
+   */
+  bool InitialPass(std::vector<std::string> const& args,
+                   cmExecutionStatus& status) override;
+};
+
+#endif

+ 5 - 0
Source/cmCommands.cxx

@@ -82,6 +82,7 @@
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #  include "cmAddCompileOptionsCommand.h"
+#  include "cmAddLinkOptionsCommand.h"
 #  include "cmAuxSourceDirectoryCommand.h"
 #  include "cmBuildNameCommand.h"
 #  include "cmCMakeHostSystemInformationCommand.h"
@@ -100,6 +101,7 @@
 #  include "cmRemoveDefinitionsCommand.h"
 #  include "cmSourceGroupCommand.h"
 #  include "cmSubdirDependsCommand.h"
+#  include "cmTargetLinkOptionsCommand.h"
 #  include "cmUseMangledMesaCommand.h"
 #  include "cmUtilitySourceCommand.h"
 #  include "cmVariableRequiresCommand.h"
@@ -272,7 +274,10 @@ void GetProjectCommands(cmState* state)
   state->AddBuiltinCommand("include_external_msproject",
                            new cmIncludeExternalMSProjectCommand);
   state->AddBuiltinCommand("install_programs", new cmInstallProgramsCommand);
+  state->AddBuiltinCommand("add_link_options", new cmAddLinkOptionsCommand);
   state->AddBuiltinCommand("link_libraries", new cmLinkLibrariesCommand);
+  state->AddBuiltinCommand("target_link_options",
+                           new cmTargetLinkOptionsCommand);
   state->AddBuiltinCommand("load_cache", new cmLoadCacheCommand);
   state->AddBuiltinCommand("qt_wrap_cpp", new cmQTWrapCPPCommand);
   state->AddBuiltinCommand("qt_wrap_ui", new cmQTWrapUICommand);

+ 6 - 0
Source/cmExportBuildAndroidMKGenerator.cxx

@@ -7,6 +7,7 @@
 #include <sstream>
 #include <utility>
 
+#include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorTarget.h"
@@ -169,6 +170,11 @@ void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
           end = "\\\n";
         }
         os << "\n";
+      } else if (property.first == "INTERFACE_LINK_OPTIONS") {
+        os << "LOCAL_EXPORT_LDFLAGS := ";
+        std::vector<std::string> linkFlagsList;
+        cmSystemTools::ExpandListArgument(property.second, linkFlagsList);
+        os << cmJoin(linkFlagsList, " ") << "\n";
       } else {
         os << "# " << property.first << " " << (property.second) << "\n";
       }

+ 3 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -95,6 +95,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
                                     properties);
 

+ 3 - 0
Source/cmExportInstallFileGenerator.cxx

@@ -103,6 +103,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
+                                    cmGeneratorExpression::InstallInterface,
+                                    properties, missingTargets);
 
     std::string errorMessage;
     if (!this->PopulateExportProperties(gt, properties, errorMessage)) {

+ 2 - 1
Source/cmGeneratorExpressionDAGChecker.h

@@ -25,7 +25,8 @@ struct cmGeneratorExpressionContext;
   SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS)                        \
   SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS)                        \
   SELECT(F, EvaluatingSources, SOURCES)                                       \
-  SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)
+  SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
+  SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)

+ 164 - 11
Source/cmGeneratorTarget.cxx

@@ -102,6 +102,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   , DebugCompileOptionsDone(false)
   , DebugCompileFeaturesDone(false)
   , DebugCompileDefinitionsDone(false)
+  , DebugLinkOptionsDone(false)
   , DebugSourcesDone(false)
   , LinkImplementationLanguageIsContextDependent(true)
   , UtilityItemsDone(false)
@@ -128,6 +129,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
                                      t->GetCompileDefinitionsBacktraces(),
                                      this->CompileDefinitionsEntries);
 
+  CreatePropertyGeneratorExpressions(t->GetLinkOptionsEntries(),
+                                     t->GetLinkOptionsBacktraces(),
+                                     this->LinkOptionsEntries);
+
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
                                      t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
@@ -145,6 +150,7 @@ cmGeneratorTarget::~cmGeneratorTarget()
   cmDeleteAll(this->CompileOptionsEntries);
   cmDeleteAll(this->CompileFeaturesEntries);
   cmDeleteAll(this->CompileDefinitionsEntries);
+  cmDeleteAll(this->LinkOptionsEntries);
   cmDeleteAll(this->SourceEntries);
   cmDeleteAll(this->LinkInformation);
 }
@@ -2633,7 +2639,7 @@ enum class OptionsParse
   Shell
 };
 
-static void processCompileOptionsInternal(
+static void processOptionsInternal(
   cmGeneratorTarget const* tgt,
   const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
   std::vector<std::string>& options,
@@ -2665,7 +2671,7 @@ static void processCompileOptionsInternal(
     if (!usedOptions.empty()) {
       tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
         cmake::LOG,
-        std::string("Used compile ") + logName + std::string(" for target ") +
+        std::string("Used ") + logName + std::string(" for target ") +
           tgt->GetName() + ":\n" + usedOptions,
         entry->ge->GetBacktrace());
     }
@@ -2680,9 +2686,9 @@ static void processCompileOptions(
   cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
   bool debugOptions, std::string const& language)
 {
-  processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
-                                dagChecker, config, debugOptions, "options",
-                                language, OptionsParse::Shell);
+  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
+                         config, debugOptions, "compile options", language,
+                         OptionsParse::Shell);
 }
 
 void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result,
@@ -2734,9 +2740,9 @@ static void processCompileFeatures(
   cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
   bool debugOptions)
 {
-  processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
-                                dagChecker, config, debugOptions, "features",
-                                std::string(), OptionsParse::None);
+  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
+                         config, debugOptions, "compile features",
+                         std::string(), OptionsParse::None);
 }
 
 void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result,
@@ -2784,9 +2790,9 @@ static void processCompileDefinitions(
   cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
   bool debugOptions, std::string const& language)
 {
-  processCompileOptionsInternal(tgt, entries, options, uniqueOptions,
-                                dagChecker, config, debugOptions,
-                                "definitions", language, OptionsParse::None);
+  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
+                         config, debugOptions, "compile definitions", language,
+                         OptionsParse::None);
 }
 
 void cmGeneratorTarget::GetCompileDefinitions(
@@ -2855,6 +2861,153 @@ void cmGeneratorTarget::GetCompileDefinitions(
   cmDeleteAll(linkInterfaceCompileDefinitionsEntries);
 }
 
+namespace {
+void processLinkOptions(
+  cmGeneratorTarget const* tgt,
+  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
+  std::vector<std::string>& options,
+  std::unordered_set<std::string>& uniqueOptions,
+  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
+  bool debugOptions, std::string const& language)
+{
+  processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
+                         config, debugOptions, "link options", language,
+                         OptionsParse::Shell);
+}
+}
+
+void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
+                                       const std::string& config,
+                                       const std::string& language) const
+{
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this->GetName(), "LINK_OPTIONS",
+                                             nullptr, nullptr);
+
+  std::vector<std::string> debugProperties;
+  const char* debugProp =
+    this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
+  if (debugProp) {
+    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+  }
+
+  bool debugOptions = !this->DebugLinkOptionsDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "LINK_OPTIONS") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugLinkOptionsDone = true;
+  }
+
+  processLinkOptions(this, this->LinkOptionsEntries, result, uniqueOptions,
+                     &dagChecker, config, debugOptions, language);
+
+  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
+    linkInterfaceLinkOptionsEntries;
+
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS",
+                      linkInterfaceLinkOptionsEntries);
+
+  processLinkOptions(this, linkInterfaceLinkOptionsEntries, result,
+                     uniqueOptions, &dagChecker, config, debugOptions,
+                     language);
+
+  cmDeleteAll(linkInterfaceLinkOptionsEntries);
+
+  // Last step: replace "LINKER:" prefixed elements by
+  // actual linker wrapper
+  const std::string wrapper(this->Makefile->GetSafeDefinition(
+    "CMAKE_" + language + "_LINKER_WRAPPER_FLAG"));
+  std::vector<std::string> wrapperFlag;
+  cmSystemTools::ExpandListArgument(wrapper, wrapperFlag);
+  const std::string wrapperSep(this->Makefile->GetSafeDefinition(
+    "CMAKE_" + language + "_LINKER_WRAPPER_FLAG_SEP"));
+  bool concatFlagAndArgs = true;
+  if (!wrapperFlag.empty() && wrapperFlag.back() == " ") {
+    concatFlagAndArgs = false;
+    wrapperFlag.pop_back();
+  }
+
+  const std::string LINKER{ "LINKER:" };
+  const std::string SHELL{ "SHELL:" };
+  const std::string LINKER_SHELL = LINKER + SHELL;
+
+  std::vector<std::string>::iterator entry;
+  while ((entry = std::find_if(result.begin(), result.end(),
+                               [&LINKER](const std::string& item) -> bool {
+                                 return item.compare(0, LINKER.length(),
+                                                     LINKER) == 0;
+                               })) != result.end()) {
+    std::vector<std::string> linkerOptions;
+    if (entry->compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) {
+      cmSystemTools::ParseUnixCommandLine(
+        entry->c_str() + LINKER_SHELL.length(), linkerOptions);
+    } else {
+      linkerOptions =
+        cmSystemTools::tokenize(entry->substr(LINKER.length()), ",");
+    }
+    entry = result.erase(entry);
+
+    if (linkerOptions.empty() ||
+        (linkerOptions.size() == 1 && linkerOptions.front().empty())) {
+      continue;
+    }
+
+    // for now, raise an error if prefix SHELL: is part of arguments
+    if (std::find_if(linkerOptions.begin(), linkerOptions.end(),
+                     [&SHELL](const std::string& item) -> bool {
+                       return item.find(SHELL) != std::string::npos;
+                     }) != linkerOptions.end()) {
+      this->LocalGenerator->GetCMakeInstance()->IssueMessage(
+        cmake::FATAL_ERROR,
+        "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.",
+        this->GetBacktrace());
+      return;
+    }
+
+    if (wrapperFlag.empty()) {
+      // nothing specified, insert elements as is
+      result.insert(entry, linkerOptions.begin(), linkerOptions.end());
+    } else {
+      std::vector<std::string> options;
+
+      if (!wrapperSep.empty()) {
+        if (concatFlagAndArgs) {
+          // insert flag elements except last one
+          options.insert(options.end(), wrapperFlag.begin(),
+                         wrapperFlag.end() - 1);
+          // concatenate last flag element and all LINKER list values
+          // in one option
+          options.push_back(wrapperFlag.back() +
+                            cmJoin(linkerOptions, wrapperSep));
+        } else {
+          options.insert(options.end(), wrapperFlag.begin(),
+                         wrapperFlag.end());
+          // concatenate all LINKER list values in one option
+          options.push_back(cmJoin(linkerOptions, wrapperSep));
+        }
+      } else {
+        // prefix each element of LINKER list with wrapper
+        if (concatFlagAndArgs) {
+          std::transform(
+            linkerOptions.begin(), linkerOptions.end(), linkerOptions.begin(),
+            [&wrapperFlag](const std::string& value) -> std::string {
+              return wrapperFlag.back() + value;
+            });
+        }
+        for (const auto& value : linkerOptions) {
+          options.insert(options.end(), wrapperFlag.begin(),
+                         concatFlagAndArgs ? wrapperFlag.end() - 1
+                                           : wrapperFlag.end());
+          options.push_back(value);
+        }
+      }
+      result.insert(entry, options.begin(), options.end());
+    }
+  }
+}
+
 void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
 {
   if (this->IsImported()) {

+ 6 - 0
Source/cmGeneratorTarget.h

@@ -418,6 +418,10 @@ public:
                              const std::string& config,
                              const std::string& language) const;
 
+  void GetLinkOptions(std::vector<std::string>& result,
+                      const std::string& config,
+                      const std::string& language) const;
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;
@@ -803,6 +807,7 @@ private:
   std::vector<TargetPropertyEntry*> CompileOptionsEntries;
   std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
+  std::vector<TargetPropertyEntry*> LinkOptionsEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   mutable std::set<std::string> LinkImplicitNullProperties;
 
@@ -851,6 +856,7 @@ private:
   mutable bool DebugCompileOptionsDone;
   mutable bool DebugCompileFeaturesDone;
   mutable bool DebugCompileDefinitionsDone;
+  mutable bool DebugLinkOptionsDone;
   mutable bool DebugSourcesDone;
   mutable bool LinkImplementationLanguageIsContextDependent;
   mutable bool UtilityItemsDone;

+ 4 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -1817,6 +1817,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
         this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, linkFlags);
       }
     }
+    std::vector<std::string> opts;
+    gtgt->GetLinkOptions(opts, configName, llang);
+    // LINK_OPTIONS are escaped.
+    this->CurrentLocalGenerator->AppendCompileOptions(extraLinkOptions, opts);
   }
 
   // Set target-specific architectures.

+ 8 - 0
Source/cmLocalGenerator.cxx

@@ -1042,6 +1042,10 @@ void cmLocalGenerator::GetTargetFlags(
           linkFlags += " ";
         }
       }
+      std::vector<std::string> opts;
+      target->GetLinkOptions(opts, config, linkLanguage);
+      // LINK_OPTIONS are escaped.
+      this->AppendCompileOptions(linkFlags, opts);
       if (pcli) {
         this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
                                   frameworkPath, linkPath);
@@ -1113,6 +1117,10 @@ void cmLocalGenerator::GetTargetFlags(
           linkFlags += " ";
         }
       }
+      std::vector<std::string> opts;
+      target->GetLinkOptions(opts, config, linkLanguage);
+      // LINK_OPTIONS are escaped.
+      this->AppendCompileOptions(linkFlags, opts);
     } break;
     default:
       break;

+ 7 - 0
Source/cmLocalVisualStudio7Generator.cxx

@@ -977,6 +977,13 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(
     extraLinkOptions += " ";
     extraLinkOptions += targetLinkFlags;
   }
+
+  std::vector<std::string> opts;
+  target->GetLinkOptions(opts, configName,
+                         target->GetLinkerLanguage(configName));
+  // LINK_OPTIONS are escaped.
+  this->AppendCompileOptions(extraLinkOptions, opts);
+
   Options linkOptions(this, Options::Linker);
   if (this->FortranProject) {
     linkOptions.AddTable(cmLocalVisualStudio7GeneratorFortranLinkFlagTable);

+ 15 - 0
Source/cmMakefile.cxx

@@ -216,6 +216,16 @@ cmBacktraceRange cmMakefile::GetCompileDefinitionsBacktraces() const
     .GetCompileDefinitionsEntryBacktraces();
 }
 
+cmStringRange cmMakefile::GetLinkOptionsEntries() const
+{
+  return this->StateSnapshot.GetDirectory().GetLinkOptionsEntries();
+}
+
+cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const
+{
+  return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces();
+}
+
 cmListFileBacktrace cmMakefile::GetBacktrace() const
 {
   return this->Backtrace;
@@ -1205,6 +1215,11 @@ void cmMakefile::AddCompileOption(std::string const& option)
   this->AppendProperty("COMPILE_OPTIONS", option.c_str());
 }
 
+void cmMakefile::AddLinkOption(std::string const& option)
+{
+  this->AppendProperty("LINK_OPTIONS", option.c_str());
+}
+
 bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove)
 {
   // Create a regular expression to match valid definitions.

+ 3 - 0
Source/cmMakefile.h

@@ -171,6 +171,7 @@ public:
   void RemoveDefineFlag(std::string const& definition);
   void AddCompileDefinition(std::string const& definition);
   void AddCompileOption(std::string const& option);
+  void AddLinkOption(std::string const& option);
 
   /** Create a new imported target with the name and type given.  */
   cmTarget* AddImportedTarget(const std::string& name,
@@ -788,6 +789,8 @@ public:
   cmBacktraceRange GetCompileOptionsBacktraces() const;
   cmStringRange GetCompileDefinitionsEntries() const;
   cmBacktraceRange GetCompileDefinitionsBacktraces() const;
+  cmStringRange GetLinkOptionsEntries() const;
+  cmBacktraceRange GetLinkOptionsBacktraces() const;
 
   std::set<std::string> const& GetSystemIncludeDirectories() const
   {

+ 2 - 12
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -154,12 +154,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
                                              linkLanguage, this->ConfigName);
 
   // Add target-specific linker flags.
-  this->LocalGenerator->AppendFlags(
-    linkFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
-  this->LocalGenerator->AppendFlags(
-    linkFlags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+  this->GetTargetLinkFlags(linkFlags, linkLanguage);
 
   // Construct a list of files associated with this executable that
   // may need to be cleaned.
@@ -437,12 +432,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
                                              linkLanguage, this->ConfigName);
 
   // Add target-specific linker flags.
-  this->LocalGenerator->AppendFlags(
-    linkFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
-  this->LocalGenerator->AppendFlags(
-    linkFlags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+  this->GetTargetLinkFlags(linkFlags, linkLanguage);
 
   {
     std::unique_ptr<cmLinkLineComputer> linkLineComputer(

+ 8 - 34
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -137,10 +137,7 @@ void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules()
     this->GeneratorTarget->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
   if (hasCUDA && resolveDeviceSymbols) {
     std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
-    std::string extraFlags;
-    this->LocalGenerator->AppendFlags(
-      extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-    this->WriteDeviceLibraryRules(linkRuleVar, extraFlags, false);
+    this->WriteDeviceLibraryRules(linkRuleVar, false);
   }
 
   std::string linkLanguage =
@@ -173,10 +170,7 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
                  cuda_lang) != closure->Languages.end());
     if (hasCUDA) {
       std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
-      std::string extraFlags;
-      this->LocalGenerator->AppendFlags(
-        extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-      this->WriteDeviceLibraryRules(linkRuleVar, extraFlags, relink);
+      this->WriteDeviceLibraryRules(linkRuleVar, relink);
     }
   }
 
@@ -187,13 +181,7 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
   linkRuleVar += "_CREATE_SHARED_LIBRARY";
 
   std::string extraFlags;
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
-
+  this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
     extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->ConfigName);
 
@@ -223,10 +211,7 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
                  cuda_lang) != closure->Languages.end());
     if (hasCUDA) {
       std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
-      std::string extraFlags;
-      this->LocalGenerator->AppendFlags(
-        extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-      this->WriteDeviceLibraryRules(linkRuleVar, extraFlags, relink);
+      this->WriteDeviceLibraryRules(linkRuleVar, relink);
     }
   }
 
@@ -237,12 +222,7 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
   linkRuleVar += "_CREATE_SHARED_MODULE";
 
   std::string extraFlags;
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+  this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
     extraFlags, "CMAKE_MODULE_LINKER_FLAGS", this->ConfigName);
 
@@ -265,12 +245,7 @@ void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
   linkRuleVar += "_CREATE_MACOSX_FRAMEWORK";
 
   std::string extraFlags;
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
-  std::string linkFlagsConfig = "LINK_FLAGS_";
-  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
-  this->LocalGenerator->AppendFlags(
-    extraFlags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+  this->GetTargetLinkFlags(extraFlags, linkLanguage);
   this->LocalGenerator->AddConfigVariableFlags(
     extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS", this->ConfigName);
 
@@ -278,7 +253,7 @@ void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
 }
 
 void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
-  const std::string& linkRuleVar, const std::string& extraFlags, bool relink)
+  const std::string& linkRuleVar, bool relink)
 {
 #ifdef CMAKE_BUILD_WITH_CMAKE
   // TODO: Merge the methods that call this method to avoid
@@ -296,7 +271,7 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
 
   // Create set of linking flags.
   std::string linkFlags;
-  this->LocalGenerator->AppendFlags(linkFlags, extraFlags);
+  this->GetTargetLinkFlags(linkFlags, linkLanguage);
 
   // Get the name of the device object to generate.
   std::string const targetOutputReal =
@@ -458,7 +433,6 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
   this->WriteTargetDriverRule(targetOutputReal, relink);
 #else
   static_cast<void>(linkRuleVar);
-  static_cast<void>(extraFlags);
   static_cast<void>(relink);
 #endif
 }

+ 1 - 2
Source/cmMakefileLibraryTargetGenerator.h

@@ -27,8 +27,7 @@ protected:
   void WriteSharedLibraryRules(bool relink);
   void WriteModuleLibraryRules(bool relink);
 
-  void WriteDeviceLibraryRules(const std::string& linkRule,
-                               const std::string& extraFlags, bool relink);
+  void WriteDeviceLibraryRules(const std::string& linkRule, bool relink);
   void WriteLibraryRules(const std::string& linkRule,
                          const std::string& extraFlags, bool relink);
   // MacOSX Framework support methods

+ 17 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -82,6 +82,23 @@ cmMakefileTargetGenerator* cmMakefileTargetGenerator::New(
   return result;
 }
 
+void cmMakefileTargetGenerator::GetTargetLinkFlags(
+  std::string& flags, const std::string& linkLanguage)
+{
+  this->LocalGenerator->AppendFlags(
+    flags, this->GeneratorTarget->GetProperty("LINK_FLAGS"));
+
+  std::string linkFlagsConfig = "LINK_FLAGS_";
+  linkFlagsConfig += cmSystemTools::UpperCase(this->ConfigName);
+  this->LocalGenerator->AppendFlags(
+    flags, this->GeneratorTarget->GetProperty(linkFlagsConfig));
+
+  std::vector<std::string> opts;
+  this->GeneratorTarget->GetLinkOptions(opts, this->ConfigName, linkLanguage);
+  // LINK_OPTIONS are escaped.
+  this->LocalGenerator->AppendCompileOptions(flags, opts);
+}
+
 void cmMakefileTargetGenerator::CreateRuleFile()
 {
   // Create a directory for this target.

+ 2 - 0
Source/cmMakefileTargetGenerator.h

@@ -52,6 +52,8 @@ public:
   cmGeneratorTarget* GetGeneratorTarget() { return this->GeneratorTarget; }
 
 protected:
+  void GetTargetLinkFlags(std::string& flags, const std::string& linkLanguage);
+
   // create the file and directory etc
   void CreateRuleFile();
 

+ 2 - 0
Source/cmState.cxx

@@ -280,6 +280,8 @@ cmStateSnapshot cmState::Reset()
     it->CompileDefinitionsBacktraces.clear();
     it->CompileOptions.clear();
     it->CompileOptionsBacktraces.clear();
+    it->LinkOptions.clear();
+    it->LinkOptionsBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
     it->Properties.clear();

+ 52 - 0
Source/cmStateDirectory.cxx

@@ -360,6 +360,42 @@ void cmStateDirectory::ClearCompileOptions()
                this->Snapshot_.Position->CompileOptionsPosition);
 }
 
+cmStringRange cmStateDirectory::GetLinkOptionsEntries() const
+{
+  return GetPropertyContent(this->DirectoryState->LinkOptions,
+                            this->Snapshot_.Position->LinkOptionsPosition);
+}
+
+cmBacktraceRange cmStateDirectory::GetLinkOptionsEntryBacktraces() const
+{
+  return GetPropertyBacktraces(this->DirectoryState->LinkOptions,
+                               this->DirectoryState->LinkOptionsBacktraces,
+                               this->Snapshot_.Position->LinkOptionsPosition);
+}
+
+void cmStateDirectory::AppendLinkOptionsEntry(const std::string& vec,
+                                              const cmListFileBacktrace& lfbt)
+{
+  AppendEntry(this->DirectoryState->LinkOptions,
+              this->DirectoryState->LinkOptionsBacktraces,
+              this->Snapshot_.Position->LinkOptionsPosition, vec, lfbt);
+}
+
+void cmStateDirectory::SetLinkOptions(const std::string& vec,
+                                      const cmListFileBacktrace& lfbt)
+{
+  SetContent(this->DirectoryState->LinkOptions,
+             this->DirectoryState->LinkOptionsBacktraces,
+             this->Snapshot_.Position->LinkOptionsPosition, vec, lfbt);
+}
+
+void cmStateDirectory::ClearLinkOptions()
+{
+  ClearContent(this->DirectoryState->LinkOptions,
+               this->DirectoryState->LinkOptionsBacktraces,
+               this->Snapshot_.Position->LinkOptionsPosition);
+}
+
 void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
                                    cmListFileBacktrace const& lfbt)
 {
@@ -387,6 +423,14 @@ void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
     this->SetCompileDefinitions(value, lfbt);
     return;
   }
+  if (prop == "LINK_OPTIONS") {
+    if (!value) {
+      this->ClearLinkOptions();
+      return;
+    }
+    this->SetLinkOptions(value, lfbt);
+    return;
+  }
 
   this->DirectoryState->Properties.SetProperty(prop, value);
 }
@@ -407,6 +451,10 @@ void cmStateDirectory::AppendProperty(const std::string& prop,
     this->AppendCompileDefinitionsEntry(value, lfbt);
     return;
   }
+  if (prop == "LINK_OPTIONS") {
+    this->AppendLinkOptionsEntry(value, lfbt);
+    return;
+  }
 
   this->DirectoryState->Properties.AppendProperty(prop, value, asString);
 }
@@ -490,6 +538,10 @@ const char* cmStateDirectory::GetProperty(const std::string& prop,
     output = cmJoin(this->GetCompileDefinitionsEntries(), ";");
     return output.c_str();
   }
+  if (prop == "LINK_OPTIONS") {
+    output = cmJoin(this->GetLinkOptionsEntries(), ";");
+    return output.c_str();
+  }
 
   const char* retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
   if (!retVal && chain) {

+ 7 - 0
Source/cmStateDirectory.h

@@ -58,6 +58,13 @@ public:
                          cmListFileBacktrace const& lfbt);
   void ClearCompileOptions();
 
+  cmStringRange GetLinkOptionsEntries() const;
+  cmBacktraceRange GetLinkOptionsEntryBacktraces() const;
+  void AppendLinkOptionsEntry(std::string const& vec,
+                              cmListFileBacktrace const& lfbt);
+  void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt);
+  void ClearLinkOptions();
+
   void SetProperty(const std::string& prop, const char* value,
                    cmListFileBacktrace const& lfbt);
   void AppendProperty(const std::string& prop, const char* value,

+ 4 - 0
Source/cmStatePrivate.h

@@ -42,6 +42,7 @@ struct cmStateDetail::SnapshotDataType
   std::vector<std::string>::size_type IncludeDirectoryPosition;
   std::vector<std::string>::size_type CompileDefinitionsPosition;
   std::vector<std::string>::size_type CompileOptionsPosition;
+  std::vector<std::string>::size_type LinkOptionsPosition;
 };
 
 struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap
@@ -84,6 +85,9 @@ struct cmStateDetail::BuildsystemDirectoryStateType
   std::vector<std::string> CompileOptions;
   std::vector<cmListFileBacktrace> CompileOptionsBacktraces;
 
+  std::vector<std::string> LinkOptions;
+  std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
+
   std::vector<std::string> NormalTargetNames;
 
   std::string ProjectName;

+ 7 - 0
Source/cmStateSnapshot.cxx

@@ -390,6 +390,13 @@ void cmStateSnapshot::InitializeFromParent()
     this->Position->BuildSystemDirectory->CompileOptionsBacktraces,
     this->Position->CompileOptionsPosition);
 
+  InitializeContentFromParent(
+    parent->BuildSystemDirectory->LinkOptions,
+    this->Position->BuildSystemDirectory->LinkOptions,
+    parent->BuildSystemDirectory->LinkOptionsBacktraces,
+    this->Position->BuildSystemDirectory->LinkOptionsBacktraces,
+    this->Position->LinkOptionsPosition);
+
   const char* include_regex =
     parent->BuildSystemDirectory->Properties.GetPropertyValue(
       "INCLUDE_REGULAR_EXPRESSION");

+ 71 - 6
Source/cmTarget.cxx

@@ -166,6 +166,8 @@ public:
   std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
   std::vector<std::string> SourceEntries;
   std::vector<cmListFileBacktrace> SourceBacktraces;
+  std::vector<std::string> LinkOptionsEntries;
+  std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
   std::vector<std::string> LinkImplementationPropertyEntries;
   std::vector<cmListFileBacktrace> LinkImplementationPropertyBacktraces;
 };
@@ -343,17 +345,29 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     this->SystemIncludeDirectories.insert(parentSystemIncludes.begin(),
                                           parentSystemIncludes.end());
 
-    const cmStringRange parentOptions =
+    const cmStringRange parentCompileOptions =
       this->Makefile->GetCompileOptionsEntries();
-    const cmBacktraceRange parentOptionsBts =
+    const cmBacktraceRange parentCompileOptionsBts =
       this->Makefile->GetCompileOptionsBacktraces();
 
     this->Internal->CompileOptionsEntries.insert(
-      this->Internal->CompileOptionsEntries.end(), parentOptions.begin(),
-      parentOptions.end());
+      this->Internal->CompileOptionsEntries.end(),
+      parentCompileOptions.begin(), parentCompileOptions.end());
     this->Internal->CompileOptionsBacktraces.insert(
-      this->Internal->CompileOptionsBacktraces.end(), parentOptionsBts.begin(),
-      parentOptionsBts.end());
+      this->Internal->CompileOptionsBacktraces.end(),
+      parentCompileOptionsBts.begin(), parentCompileOptionsBts.end());
+
+    const cmStringRange parentLinkOptions =
+      this->Makefile->GetLinkOptionsEntries();
+    const cmBacktraceRange parentLinkOptionsBts =
+      this->Makefile->GetLinkOptionsBacktraces();
+
+    this->Internal->LinkOptionsEntries.insert(
+      this->Internal->LinkOptionsEntries.end(), parentLinkOptions.begin(),
+      parentLinkOptions.end());
+    this->Internal->LinkOptionsBacktraces.insert(
+      this->Internal->LinkOptionsBacktraces.end(),
+      parentLinkOptionsBts.begin(), parentLinkOptionsBts.end());
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
@@ -822,6 +836,16 @@ cmBacktraceRange cmTarget::GetSourceBacktraces() const
   return cmMakeRange(this->Internal->SourceBacktraces);
 }
 
+cmStringRange cmTarget::GetLinkOptionsEntries() const
+{
+  return cmMakeRange(this->Internal->LinkOptionsEntries);
+}
+
+cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const
+{
+  return cmMakeRange(this->Internal->LinkOptionsBacktraces);
+}
+
 cmStringRange cmTarget::GetLinkImplementationEntries() const
 {
   return cmMakeRange(this->Internal->LinkImplementationPropertyEntries);
@@ -847,6 +871,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
   MAKE_STATIC_PROP(EXPORT_NAME);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(INCLUDE_DIRECTORIES);
+  MAKE_STATIC_PROP(LINK_OPTIONS);
   MAKE_STATIC_PROP(LINK_LIBRARIES);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
   MAKE_STATIC_PROP(NAME);
@@ -925,6 +950,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->CompileDefinitionsBacktraces.push_back(lfbt);
     }
+  } else if (prop == propLINK_OPTIONS) {
+    this->Internal->LinkOptionsEntries.clear();
+    this->Internal->LinkOptionsBacktraces.clear();
+    if (value) {
+      this->Internal->LinkOptionsEntries.push_back(value);
+      cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+      this->Internal->LinkOptionsBacktraces.push_back(lfbt);
+    }
   } else if (prop == propLINK_LIBRARIES) {
     this->Internal->LinkImplementationPropertyEntries.clear();
     this->Internal->LinkImplementationPropertyBacktraces.clear();
@@ -1030,6 +1063,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->CompileDefinitionsBacktraces.push_back(lfbt);
     }
+  } else if (prop == "LINK_OPTIONS") {
+    if (value && *value) {
+      this->Internal->LinkOptionsEntries.push_back(value);
+      cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+      this->Internal->LinkOptionsBacktraces.push_back(lfbt);
+    }
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
@@ -1111,6 +1150,21 @@ void cmTarget::InsertCompileDefinition(std::string const& entry,
   this->Internal->CompileDefinitionsBacktraces.push_back(bt);
 }
 
+void cmTarget::InsertLinkOption(std::string const& entry,
+                                cmListFileBacktrace const& bt, bool before)
+{
+  std::vector<std::string>::iterator position = before
+    ? this->Internal->LinkOptionsEntries.begin()
+    : this->Internal->LinkOptionsEntries.end();
+
+  std::vector<cmListFileBacktrace>::iterator btPosition = before
+    ? this->Internal->LinkOptionsBacktraces.begin()
+    : this->Internal->LinkOptionsBacktraces.end();
+
+  this->Internal->LinkOptionsEntries.insert(position, entry);
+  this->Internal->LinkOptionsBacktraces.insert(btPosition, bt);
+}
+
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
                                                   const char* value,
                                                   cmMakefile* context,
@@ -1230,6 +1284,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
   MAKE_STATIC_PROP(COMPILE_FEATURES);
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
+  MAKE_STATIC_PROP(LINK_OPTIONS);
   MAKE_STATIC_PROP(IMPORTED);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1245,6 +1300,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
     specialProps.insert(propCOMPILE_FEATURES);
     specialProps.insert(propCOMPILE_OPTIONS);
     specialProps.insert(propCOMPILE_DEFINITIONS);
+    specialProps.insert(propLINK_OPTIONS);
     specialProps.insert(propIMPORTED);
     specialProps.insert(propIMPORTED_GLOBAL);
     specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES);
@@ -1303,6 +1359,15 @@ const char* cmTarget::GetProperty(const std::string& prop) const
       output = cmJoin(this->Internal->CompileDefinitionsEntries, ";");
       return output.c_str();
     }
+    if (prop == propLINK_OPTIONS) {
+      if (this->Internal->LinkOptionsEntries.empty()) {
+        return nullptr;
+      }
+
+      static std::string output;
+      output = cmJoin(this->Internal->LinkOptionsEntries, ";");
+      return output.c_str();
+    }
     if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
       if (this->Utilities.empty()) {
         return nullptr;

+ 6 - 0
Source/cmTarget.h

@@ -239,6 +239,8 @@ public:
                            cmListFileBacktrace const& bt, bool before = false);
   void InsertCompileDefinition(std::string const& entry,
                                cmListFileBacktrace const& bt);
+  void InsertLinkOption(std::string const& entry,
+                        cmListFileBacktrace const& bt, bool before = false);
 
   void AppendBuildInterfaceIncludes();
 
@@ -265,6 +267,10 @@ public:
 
   cmStringRange GetSourceEntries() const;
   cmBacktraceRange GetSourceBacktraces() const;
+
+  cmStringRange GetLinkOptionsEntries() const;
+  cmBacktraceRange GetLinkOptionsBacktraces() const;
+
   cmStringRange GetLinkImplementationEntries() const;
   cmBacktraceRange GetLinkImplementationBacktraces() const;
 

+ 41 - 0
Source/cmTargetLinkOptionsCommand.cxx

@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTargetLinkOptionsCommand.h"
+
+#include <sstream>
+
+#include "cmAlgorithms.h"
+#include "cmListFileCache.h"
+#include "cmMakefile.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+class cmExecutionStatus;
+
+bool cmTargetLinkOptionsCommand::InitialPass(
+  std::vector<std::string> const& args, cmExecutionStatus&)
+{
+  return this->HandleArguments(args, "LINK_OPTIONS", PROCESS_BEFORE);
+}
+
+void cmTargetLinkOptionsCommand::HandleMissingTarget(const std::string& name)
+{
+  std::ostringstream e;
+  e << "Cannot specify link options for target \"" << name
+    << "\" which is not built by this project.";
+  this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+}
+
+std::string cmTargetLinkOptionsCommand::Join(
+  const std::vector<std::string>& content)
+{
+  return cmJoin(content, ";");
+}
+
+bool cmTargetLinkOptionsCommand::HandleDirectContent(
+  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
+{
+  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+  tgt->InsertLinkOption(this->Join(content), lfbt);
+  return true; // Successfully handled.
+}

+ 41 - 0
Source/cmTargetLinkOptionsCommand.h

@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetLinkOptionsCommand_h
+#define cmTargetLinkOptionsCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include "cmTargetPropCommandBase.h"
+
+class cmCommand;
+class cmExecutionStatus;
+class cmTarget;
+
+class cmTargetLinkOptionsCommand : public cmTargetPropCommandBase
+{
+public:
+  /**
+   * This is a virtual constructor for the command.
+   */
+  cmCommand* Clone() override { return new cmTargetLinkOptionsCommand; }
+
+  /**
+   * This is called when the command is first encountered in
+   * the CMakeLists.txt file.
+   */
+  bool InitialPass(std::vector<std::string> const& args,
+                   cmExecutionStatus& status) override;
+
+private:
+  void HandleMissingTarget(const std::string& name) override;
+
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool system) override;
+  std::string Join(const std::vector<std::string>& content) override;
+};
+
+#endif

+ 5 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -3234,6 +3234,11 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions(
     flags += flagsConfig;
   }
 
+  std::vector<std::string> opts;
+  this->GeneratorTarget->GetLinkOptions(opts, config, linkLanguage);
+  // LINK_OPTIONS are escaped.
+  this->LocalGenerator->AppendCompileOptions(flags, opts);
+
   cmComputeLinkInformation* pcli =
     this->GeneratorTarget->GetLinkInformation(config);
   if (!pcli) {

+ 20 - 0
Tests/CMakeCommands/add_link_options/CMakeLists.txt

@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.11)
+
+project(add_link_options LANGUAGES C)
+
+
+add_link_options(-LINK_FLAG)
+
+add_executable(add_link_options EXCLUDE_FROM_ALL LinkOptionsExe.c)
+
+get_target_property(result add_link_options LINK_OPTIONS)
+if (NOT result MATCHES "-LINK_FLAG")
+  message(SEND_ERROR "add_link_options not populated the LINK_OPTIONS target property")
+endif()
+
+
+add_library(imp UNKNOWN IMPORTED)
+get_target_property(result imp LINK_OPTIONS)
+if (result)
+  message(FATAL_ERROR "add_link_options populated the LINK_OPTIONS target property")
+endif()

+ 4 - 0
Tests/CMakeCommands/add_link_options/LinkOptionsExe.c

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

+ 19 - 0
Tests/CMakeCommands/target_link_options/CMakeLists.txt

@@ -0,0 +1,19 @@
+
+cmake_minimum_required(VERSION 3.11)
+
+project(target_link_options LANGUAGES C)
+
+add_library(target_link_options SHARED LinkOptionsLib.c)
+# Test no items
+target_link_options(target_link_options PRIVATE)
+
+add_library(target_link_options_2 SHARED EXCLUDE_FROM_ALL LinkOptionsLib.c)
+target_link_options(target_link_options_2 PRIVATE -PRIVATE_FLAG INTERFACE -INTERFACE_FLAG)
+get_target_property(result target_link_options_2 LINK_OPTIONS)
+if (NOT result MATCHES "-PRIVATE_FLAG")
+  message(SEND_ERROR "target_link_options not populated the LINK_OPTIONS target property")
+endif()
+get_target_property(result target_link_options_2 INTERFACE_LINK_OPTIONS)
+if (NOT result MATCHES "-INTERFACE_FLAG")
+  message(SEND_ERROR "target_link_options not populated the INTERFACE_LINK_OPTIONS target property")
+endif()

+ 7 - 0
Tests/CMakeCommands/target_link_options/LinkOptionsLib.c

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

+ 3 - 0
Tests/CMakeLists.txt

@@ -2840,6 +2840,9 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
   ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
   ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
 
+  ADD_TEST_MACRO(CMakeCommands.add_link_options)
+  ADD_TEST_MACRO(CMakeCommands.target_link_options)
+
   # The cmake server-mode test requires python for a simple client.
   find_package(PythonInterp QUIET)
   if(PYTHON_EXECUTABLE)

+ 10 - 0
Tests/ExportImport/Export/CMakeLists.txt

@@ -597,3 +597,13 @@ install(
   )
 install(DIRECTORY $<1:include/abs>$<0:/wrong> DESTINATION $<1:include>$<0:/wrong>)
 install(EXPORT expAbs NAMESPACE expAbs_ DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/expAbs)
+
+
+#------------------------------------------------------------------------------
+# test export of INTERFACE_LINK_OPTIONS
+add_library(testLinkOptions INTERFACE)
+target_link_options(testLinkOptions INTERFACE INTERFACE_FLAG)
+
+install(TARGETS testLinkOptions
+        EXPORT RequiredExp DESTINATION lib)
+export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)

+ 5 - 0
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -472,3 +472,8 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA
     endif()
   endif()
 endif()
+
+#---------------------------------------------------------------------------------
+# check that imported libraries have the expected INTERFACE_LINK_OPTIONS property
+checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
+checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")

+ 8 - 0
Tests/ExportImport/Import/A/imp_testLinkOptions.cpp

@@ -0,0 +1,8 @@
+
+#include "testSharedLibRequired.h"
+
+int foo()
+{
+  TestSharedLibRequired req;
+  return req.foo();
+}

+ 4 - 2
Tests/RunCMake/AndroidMK/AndroidMK.cmake

@@ -5,7 +5,9 @@ add_library(car foo.cxx)
 add_library(bar bar.c)
 add_library(dog  foo.cxx)
 target_link_libraries(foo PRIVATE car bar dog debug -lm)
-export(TARGETS bar dog car foo  ANDROID_MK
+add_library(foo2  foo.cxx)
+target_link_options(foo2 INTERFACE -lm)
+export(TARGETS bar dog car foo foo2 ANDROID_MK
   ${build_BINARY_DIR}/Android.mk)
-install(TARGETS bar dog car foo DESTINATION lib EXPORT myexp)
+install(TARGETS bar dog car foo foo2 DESTINATION lib EXPORT myexp)
 install(EXPORT_ANDROID_MK myexp DESTINATION share/ndk-modules)

+ 8 - 0
Tests/RunCMake/AndroidMK/expectedBuildAndroidMK.txt

@@ -24,3 +24,11 @@ LOCAL_STATIC_LIBRARIES.*car bar dog
 LOCAL_EXPORT_LDLIBS := -lm
 LOCAL_HAS_CPP := true
 include.*PREBUILT_STATIC_LIBRARY.*
+.*
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*foo2
+LOCAL_SRC_FILES.*.*foo2.*
+LOCAL_CPP_FEATURES.*rtti exceptions
+LOCAL_EXPORT_LDFLAGS := -lm
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*

+ 8 - 0
Tests/RunCMake/AndroidMK/expectedInstallAndroidMK.txt

@@ -26,3 +26,11 @@ LOCAL_STATIC_LIBRARIES.*car bar dog
 LOCAL_EXPORT_LDLIBS := -lm
 LOCAL_HAS_CPP := true
 include.*PREBUILT_STATIC_LIBRARY.*
+
+include.*CLEAR_VARS.*
+LOCAL_MODULE.*foo2
+LOCAL_SRC_FILES.*_IMPORT_PREFIX\)/lib.*foo2.*
+LOCAL_CPP_FEATURES.*rtti exceptions
+LOCAL_EXPORT_LDFLAGS := -lm
+LOCAL_HAS_CPP := true
+include.*PREBUILT_STATIC_LIBRARY.*

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -332,6 +332,8 @@ endif()
 add_RunCMake_test(File_Generate)
 add_RunCMake_test(ExportWithoutLanguage)
 add_RunCMake_test(target_link_libraries)
+add_RunCMake_test(add_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
+add_RunCMake_test(target_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 
 add_RunCMake_test(target_compile_features)
 add_RunCMake_test(CheckModules)

+ 5 - 0
Tests/RunCMake/add_link_options/CMakeLists.txt

@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.11)
+
+project(${RunCMake_TEST} LANGUAGES NONE)
+
+include(${RunCMake_TEST}.cmake)

+ 2 - 0
Tests/RunCMake/add_link_options/LINKER_SHELL_expansion-build-check.cmake

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

+ 4 - 0
Tests/RunCMake/add_link_options/LINKER_SHELL_expansion.cmake

@@ -0,0 +1,4 @@
+
+set (LINKER_OPTION "LINKER:SHELL:-foo bar")
+
+include ("LINKER_expansion-list.cmake")

+ 2 - 0
Tests/RunCMake/add_link_options/LINKER_expansion-build-check.cmake

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

+ 36 - 0
Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake

@@ -0,0 +1,36 @@
+
+enable_language(C)
+
+add_executable(dump dump.c)
+
+add_link_options("${LINKER_OPTION}")
+
+# ensure no temp file will be used
+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}")
+
+add_library(example SHARED LinkOptionsLib.c)
+# use LAUNCH facility to dump linker command
+set_property(TARGET example PROPERTY RULE_LAUNCH_LINK "\"${CMAKE_CURRENT_BINARY_DIR}/dump${CMAKE_EXECUTABLE_SUFFIX}\"")
+
+add_dependencies (example dump)
+
+# generate reference for LINKER flag
+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)
+    string (APPEND  linker_flag "${linker_space}" "-foo${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}bar")
+  else()
+    set (linker_flag "${linker_flag}${linker_space}-foo ${linker_flag}${linker_space}bar")
+  endif()
+else()
+  set(linker_flag "-foo bar")
+endif()
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER.txt" "${linker_flag}")

+ 15 - 0
Tests/RunCMake/add_link_options/LINKER_expansion-validation.cmake

@@ -0,0 +1,15 @@
+
+if (actual_stdout MATCHES "(LINKER|SHELL):")
+  set (RunCMake_TEST_FAILED "LINKER: prefix was not expanded.")
+  return()
+endif()
+
+if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/LINKER.txt")
+  set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/LINKER.txt: Reference file not found.")
+  return()
+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.")
+endif()

+ 4 - 0
Tests/RunCMake/add_link_options/LINKER_expansion.cmake

@@ -0,0 +1,4 @@
+
+set (LINKER_OPTION "LINKER:-foo,bar")
+
+include ("LINKER_expansion-list.cmake")

+ 7 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS-exe-check.cmake

@@ -0,0 +1,7 @@
+
+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'.")
+endif()

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

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

+ 7 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-check.cmake

@@ -0,0 +1,7 @@
+
+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'.")
+endif()

+ 1 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS-mod-result.txt

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

+ 7 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-check.cmake

@@ -0,0 +1,7 @@
+
+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'.")
+endif()

+ 1 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS-shared-result.txt

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

+ 17 - 0
Tests/RunCMake/add_link_options/LINK_OPTIONS.cmake

@@ -0,0 +1,17 @@
+
+enable_language(C)
+
+set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
+if(BORLAND)
+  set(pre -)
+endif()
+
+add_link_options($<$<AND:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>,$<CONFIG:Release>>:${pre}BADFLAG_SHARED_RELEASE${obj}>)
+add_link_options($<$<AND:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,MODULE_LIBRARY>,$<CONFIG:Release>>:${pre}BADFLAG_MODULE_RELEASE${obj}>)
+add_link_options($<$<AND:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>,$<CONFIG:Release>>:${pre}BADFLAG_EXECUTABLE_RELEASE${obj}>)
+
+add_library(LinkOptions_shared SHARED LinkOptionsLib.c)
+
+add_library(LinkOptions_mod MODULE LinkOptionsLib.c)
+
+add_executable(LinkOptions_exe LinkOptionsExe.c)

+ 4 - 0
Tests/RunCMake/add_link_options/LinkOptionsExe.c

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

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

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

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

@@ -0,0 +1,38 @@
+
+include(RunCMake)
+
+macro(run_cmake_target test subtest target)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --target ${target} ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
+  # Intel compiler does not reject bad flags or objects!
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+
+  run_cmake(LINK_OPTIONS)
+
+  run_cmake_target(LINK_OPTIONS shared LinkOptions_shared --config Release)
+  run_cmake_target(LINK_OPTIONS mod LinkOptions_mod --config Release)
+  run_cmake_target(LINK_OPTIONS exe LinkOptions_exe --config Release)
+
+  unset(RunCMake_TEST_OPTIONS)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+endif()
+
+run_cmake(bad_SHELL_usage)
+
+if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
+  run_cmake(LINKER_expansion)
+  run_cmake_target(LINKER_expansion build all)
+
+  run_cmake(LINKER_SHELL_expansion)
+  run_cmake_target(LINKER_SHELL_expansion build all)
+endif()

Некоторые файлы не были показаны из-за большого количества измененных файлов