summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst69
-rw-r--r--Help/manual/cmake-variables.7.rst4
-rw-r--r--Help/release/dev/Genex-LINK_LIBRARY.rst8
-rw-r--r--Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst19
-rw-r--r--Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst13
-rw-r--r--Help/variable/CMAKE_LINK_USING_FEATURE.rst23
-rw-r--r--Help/variable/CMAKE_LINK_USING_FEATURE.txt111
-rw-r--r--Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst14
-rw-r--r--Source/cmComputeLinkDepends.cxx177
-rw-r--r--Source/cmComputeLinkDepends.h13
-rw-r--r--Source/cmComputeLinkInformation.cxx387
-rw-r--r--Source/cmComputeLinkInformation.h65
-rw-r--r--Source/cmGeneratorExpressionNode.cxx63
-rw-r--r--Source/cmGeneratorTarget.cxx15
-rw-r--r--Source/cmGeneratorTarget.h3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx4
-rw-r--r--Source/cmLinkLibrariesCommand.cxx2
-rw-r--r--Source/cmLinkLineComputer.cxx10
-rw-r--r--Source/cmLinkLineDeviceComputer.cxx8
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx4
-rw-r--r--Source/cmMakefile.cxx25
-rw-r--r--Source/cmMakefile.h1
-rw-r--r--Source/cmTarget.cxx140
-rw-r--r--Source/cmTargetLinkLibrariesCommand.cxx3
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx6
-rw-r--r--Tests/RunCMake/CMakeLists.txt12
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake25
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake4
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake3
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake5
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt5
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake8
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake15
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake4
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c0
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt5
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt16
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt6
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake15
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt14
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake15
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake5
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake11
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake14
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake4
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt13
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake16
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake4
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake4
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake81
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake73
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c9
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt1
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt18
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake18
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c15
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c18
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c8
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake20
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake34
131 files changed, 1825 insertions, 151 deletions
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index df13dd0..b67d479 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1113,6 +1113,75 @@ Output-Related Expressions
property, perhaps via the :command:`target_link_libraries` command,
to specify private link dependencies without other usage requirements.
+.. genex:: $<LINK_LIBRARY:feature,library-list>
+
+ .. versionadded:: 3.24
+
+ Manage how libraries are specified during the link step.
+ This expression may be used to specify how to link libraries in a target.
+ For example:
+
+ .. code-block:: cmake
+
+ add_library(lib1 STATIC ...)
+ add_library(lib2 ...)
+ target_link_libraries(lib2 PRIVATE $<LINK_LIBRARY:whole_archive,lib1>)
+
+ This specify to use the ``lib1`` target with feature ``whole_archive`` for
+ linking target ``lib2``. The feature must have be defined by
+ :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable or, if
+ :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false,
+ by :variable:`CMAKE_LINK_USING_<FEATURE>` variable.
+
+ .. note::
+
+ The evaluation of this generator expression will use, for the following
+ variables, the values defined at the level of the creation of the target:
+
+ * :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`
+ * :variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_LINK_USING_<FEATURE>`
+
+ This expression can only be used to specify link libraries (i.e. part of
+ :command:`link_libraries` or :command:`target_link_libraries` commands and
+ :prop_tgt:`LINK_LIBRARIES` or :prop_tgt:`INTERFACE_LINK_LIBRARIES` target
+ properties).
+
+ .. note::
+
+ If this expression appears in the :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+ property of a target, it will be included in the imported target generated
+ by :command:`install(EXPORT)` command. It is the responsibility of the
+ environment consuming this import to define the link feature used by this
+ expression.
+
+ The ``library-list`` argument can hold CMake targets or external libraries.
+ Any ``CMake`` target of type :ref:`OBJECT <Object Libraries>` or
+ :ref:`INTERFACE <Interface Libraries>` will be ignored by this expression and
+ will be handled in the standard way.
+
+ Each target or external library involved in the link step must have only one
+ kind of feature (the absence of feature is also incompatible with any
+ feature). For example:
+
+ .. code-block:: cmake
+
+ add_library(lib1 ...)
+
+ add_library(lib2 ...)
+ target_link_libraries(lib2 PUBLIC $<LINK_LIBRARY:feature1,lib1>)
+
+ add_library(lib3 ...)
+ target_link_libraries(lib3 PRIVATE lib1 lib2)
+ # an error will be raised here because lib1 has two different features
+
+ .. note::
+
+ This expression does not guarantee that the list of specified libraries
+ will be kept grouped. So, constructs like ``start-group`` and
+ ``end-group``, as supported by ``GNU ld``, cannot be used.
+
.. genex:: $<INSTALL_INTERFACE:...>
Content of ``...`` when the property is exported using :command:`install(EXPORT)`,
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 59566b5..86e4d4c 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -440,6 +440,8 @@ Variables that Control the Build
/variable/CMAKE_LANG_LINKER_LAUNCHER
/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
/variable/CMAKE_LANG_LINK_LIBRARY_FLAG
+ /variable/CMAKE_LANG_LINK_USING_FEATURE
+ /variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED
/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
/variable/CMAKE_LANG_VISIBILITY_PRESET
/variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
@@ -450,6 +452,8 @@ Variables that Control the Build
/variable/CMAKE_LINK_INTERFACE_LIBRARIES
/variable/CMAKE_LINK_LIBRARY_FILE_FLAG
/variable/CMAKE_LINK_LIBRARY_FLAG
+ /variable/CMAKE_LINK_USING_FEATURE
+ /variable/CMAKE_LINK_USING_FEATURE_SUPPORTED
/variable/CMAKE_LINK_WHAT_YOU_USE
/variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
/variable/CMAKE_MACOSX_BUNDLE
diff --git a/Help/release/dev/Genex-LINK_LIBRARY.rst b/Help/release/dev/Genex-LINK_LIBRARY.rst
new file mode 100644
index 0000000..6b87a4f
--- /dev/null
+++ b/Help/release/dev/Genex-LINK_LIBRARY.rst
@@ -0,0 +1,8 @@
+Genex-LINK_LIBRARY
+------------------
+
+* The :genex:`LINK_LIBRARY` generator expression was added to manage how
+ libraries are specified during the link step. The variables
+ :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` and
+ :variable:`CMAKE_LINK_USING_<FEATURE>` are used to define features usable by
+ the :genex:`LINK_LIBRARY` generator expression.
diff --git a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
new file mode 100644
index 0000000..ff8b3d9
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE.rst
@@ -0,0 +1,19 @@
+CMAKE_<LANG>_LINK_USING_<FEATURE>
+---------------------------------
+
+.. versionadded:: 3.24
+
+This variable defines, for the specified ``<FEATURE>`` and the linker language
+``<LANG>``, the expression expected by the linker when libraries are specified
+using :genex:`LINK_LIBRARY` generator expression.
+
+.. note::
+
+ Feature names defined in all uppercase are reserved to CMake.
+
+See also the associated variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` and
+:variable:`CMAKE_LINK_USING_<FEATURE>` variable for the definition of features
+independent from the link language.
+
+.. include:: CMAKE_LINK_USING_FEATURE.txt
diff --git a/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst
new file mode 100644
index 0000000..5794b15
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED.rst
@@ -0,0 +1,13 @@
+CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED
+-------------------------------------------
+
+.. versionadded:: 3.24
+
+Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`, is supported for the linker
+language ``<LANG>``.
+
+.. note::
+
+ This variable is evaluated before the more generic variable
+ :variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`.
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE.rst b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
new file mode 100644
index 0000000..3d94461
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE.rst
@@ -0,0 +1,23 @@
+CMAKE_LINK_USING_<FEATURE>
+--------------------------
+
+.. versionadded:: 3.24
+
+This variable defines, for the specified ``FEATURE``, the expression expected
+by the linker, regardless the linker language, when libraries are specified
+using :genex:`LINK_LIBRARY` generator expression.
+
+.. note::
+
+ Feature names defined in all uppercase are reserved to CMake.
+
+See also the associated variable
+:variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED` and
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable for the definition of
+features dependent from the link language.
+
+This variable will be used by :genex:`LINK_LIBRARY` generator expression if,
+for the linker language, the variable
+:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false or not set.
+
+.. include:: CMAKE_LINK_USING_FEATURE.txt
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE.txt b/Help/variable/CMAKE_LINK_USING_FEATURE.txt
new file mode 100644
index 0000000..92fc92d
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE.txt
@@ -0,0 +1,111 @@
+
+It can contain one or three elements.
+
+::
+
+ [<PREFIX>] <LIBRARY_EXPRESSION> [<SUFFIX>]
+
+When ``<PREFIX>`` and/or ``<SUFFIX>`` are specified, they encapsulate the list
+of libraries.
+
+.. note::
+
+ Even if ``<PREFIX>`` and ``<SUFFIX>`` are specified, there is not guarantee
+ that the list of specified libraries, as part of :genex:`LINK_LIBRARY`
+ generator expression, will be kept grouped. So, constructs like
+ ``start-group`` and ``end-group``, as supported by ``GNU ld``, cannot be
+ used.
+
+``<LIBRARY_EXPRESSION>`` is used to specify the decoration for each
+library. For that purpose, the patterns ``<LIBRARY>``, ``<LINK_ITEM>``, and
+``<LIB_ITEM>`` are available:
+
+* ``<LIBRARY>`` is expanded to the library as computed by CMake.
+* ``<LINK_ITEM>`` is expanded to the same expression as if the library was
+ specified in the standard way.
+* ``<LIB_ITEM>`` is equivalent to ``<LIBRARY>`` for CMake targets and is
+ expanded to the item specified by the user for external libraries.
+
+Moreover, it is possible to have different decorations for paths (CMake targets
+and external libraries specified with absolute paths) and other items specified
+by name. For that purpose, ``PATH{}`` and ``NAME{}`` wrappers can be used.
+
+For all three elements of this variable, the ``LINKER:`` prefix can be used:
+
+ .. include:: ../command/LINK_OPTIONS_LINKER.txt
+ :start-line: 3
+
+Examples
+^^^^^^^^
+
+Loading a whole static library
+""""""""""""""""""""""""""""""
+
+A common need is the capability to load a whole static library. This capability
+is offered by various environments but with a specific syntax:
+
+.. code-block:: cmake
+
+ set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED TRUE)
+ if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_C_LINK_USING_whole_archive "-force_load <LIB_ITEM>")
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU"
+ AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(CMAKE_C_LINK_USING_whole_archive "LINKER:--push-state,--whole-archive"
+ "<LINK_ITEM>"
+ "LINKER:--pop-state")
+ elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_C_LINK_USING_whole_archive "/WHOLEARCHIVE:<LIBRARY>")
+ else()
+ # feature not yet supported for the other environments
+ set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED FALSE)
+ endif()
+
+ add_library(lib1 STATIC ...)
+
+ add_library(lib2 SHARED ...)
+ if(CMAKE_C_LINK_USING_whole_archive_SUPPORTED)
+ target_link_libraries(lib2 PRIVATE
+ $<LINK_LIBRARY:whole_archive,lib1,$<IF:$<LINK_LANG_AND_ID:C,Clang>,libexternal.a,external>>)
+ else()
+ target_link_libraries(lib2 PRIVATE lib1 external)
+ endif()
+
+CMake will generate the following link expressions:
+
+* ``Clang``: ``-force_load /path/to/lib1.a -force_load libexternal.a``
+* ``GNU``: ``-Wl,--whole-archive /path/to/lib1.a -lexternal -Wl,--no-whole-archive``
+* ``MSVC``: ``/WHOLEARCHIVE:/path/to/lib1.lib /WHOLEARCHIVE:external.lib``
+
+CMake will ensure, when possible, that ``<PREFIX>`` and ``<SUFFIX>`` are
+not repeated for each library.
+
+In case of ``Clang``, the pattern ``<LIB_ITEM>`` is used because we need to
+specify the library as defined by the user, not the name computed by CMake
+(in that case ``external``).
+
+Linking a library as weak
+"""""""""""""""""""""""""
+
+On MacOS, it is possible to link a library in weak mode (the library and all
+references are marked as weak imports), but different flags must be used for a
+library specified by path and by name. This constraint by be solved by using
+``PATH{}`` and ``NAME{}`` wrappers:
+
+.. code-block:: cmake
+
+ if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_LINK_USING_weak_library "PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}")
+ set(CMAKE_LINK_USING_weak_library_SUPPORTED TRUE)
+ endif()
+
+ add_library(lib SHARED ...)
+ add_executable(main ...)
+ if(CMAKE_LINK_USING_weak_library_SUPPORTED)
+ target_link_libraries(main PRIVATE $<LINK_LIBRARY:weak_library,lib,external>)
+ else()
+ target_link_libraries(main PRIVATE lib external)
+ endif()
+
+CMake will generate the following link expression:
+``-weak_library /path/to/lib -Xlinker -weak-lexternal``
diff --git a/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst b/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst
new file mode 100644
index 0000000..31c3108
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED.rst
@@ -0,0 +1,14 @@
+CMAKE_LINK_USING_<FEATURE>_SUPPORTED
+------------------------------------
+
+.. versionadded:: 3.24
+
+Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
+:variable:`CMAKE_LINK_USING_<FEATURE>`, is supported regardless the linker
+language.
+
+.. note::
+
+ This variable is evaluated if, and only if, the variable
+ :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` evaluates to
+ ``FALSE``.
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 370ddff..c3367ac 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,6 +11,7 @@
#include <utility>
#include <cm/memory>
+#include <cmext/string_view>
#include "cmComputeComponentGraph.h"
#include "cmGeneratorTarget.h"
@@ -18,6 +19,7 @@
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -174,8 +176,33 @@ items that we know the linker will re-use automatically (shared libs).
*/
+namespace {
+const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
+const auto LL_END = "</LINK_LIBRARY:"_s;
+
+inline std::string ExtractFeature(std::string const& item)
+{
+ return item.substr(LL_BEGIN.length(),
+ item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
+}
+
+bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
+ std::string const& feature)
+{
+ auto featureSupported =
+ cmStrCat("CMAKE_", linkLanguage, "_LINK_USING_", feature, "_SUPPORTED");
+ if (makefile->GetDefinition(featureSupported).IsOn()) {
+ return true;
+ }
+
+ featureSupported = cmStrCat("CMAKE_LINK_USING_", feature, "_SUPPORTED");
+ return makefile->GetDefinition(featureSupported).IsOn();
+}
+}
+
cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
- const std::string& config)
+ const std::string& config,
+ const std::string& linkLanguage)
{
// Store context information.
this->Target = target;
@@ -183,6 +210,7 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
this->GlobalGenerator =
this->Target->GetLocalGenerator()->GetGlobalGenerator();
this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
+ this->LinkLanguage = linkLanguage;
// The configuration being linked.
this->HasConfig = !config.empty();
@@ -249,7 +277,7 @@ cmComputeLinkDepends::Compute()
}
// Compute the final ordering.
- this->OrderLinkEntires();
+ this->OrderLinkEntries();
// Compute the final set of link entries.
// Iterate in reverse order so we can keep only the last occurrence
@@ -281,32 +309,33 @@ cmComputeLinkDepends::Compute()
return this->FinalLinkEntries;
}
-std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
- cmLinkItem const& item)
+std::pair<std::map<cmLinkItem, int>::iterator, bool>
+cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
{
std::map<cmLinkItem, int>::value_type index_entry(
item, static_cast<int>(this->EntryList.size()));
- auto lei = this->LinkEntryIndex.insert(index_entry).first;
- this->EntryList.emplace_back();
- this->InferredDependSets.emplace_back();
- this->EntryConstraintGraph.emplace_back();
+ auto lei = this->LinkEntryIndex.insert(index_entry);
+ if (lei.second) {
+ this->EntryList.emplace_back();
+ this->InferredDependSets.emplace_back();
+ this->EntryConstraintGraph.emplace_back();
+ }
return lei;
}
-int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
+std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
{
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(item);
+
// Check if the item entry has already been added.
- auto lei = this->LinkEntryIndex.find(item);
- if (lei != this->LinkEntryIndex.end()) {
+ if (!lei.second) {
// Yes. We do not need to follow the item's dependencies again.
- return lei->second;
+ return { lei.first->second, false };
}
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(item);
-
// Initialize the item entry.
- int index = lei->second;
+ int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.Target = item.Target;
@@ -332,22 +361,21 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
}
}
- return index;
+ return { index, true };
}
void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
{
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(item);
+
// Check if the item entry has already been added.
- auto lei = this->LinkEntryIndex.find(item);
- if (lei != this->LinkEntryIndex.end()) {
+ if (!lei.second) {
return;
}
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(item);
-
// Initialize the item entry.
- int index = lei->second;
+ int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.IsObject = true;
@@ -423,14 +451,14 @@ void cmComputeLinkDepends::QueueSharedDependencies(
void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
{
- // Check if the target already has an entry.
- auto lei = this->LinkEntryIndex.find(dep.Item);
- if (lei == this->LinkEntryIndex.end()) {
- // Allocate a spot for the item entry.
- lei = this->AllocateLinkEntry(dep.Item);
+ // Allocate a spot for the item entry.
+ auto lei = this->AllocateLinkEntry(dep.Item);
+ int index = lei.first->second;
+ // Check if the target does not already has an entry.
+ if (lei.second) {
// Initialize the item entry.
- LinkEntry& entry = this->EntryList[lei->second];
+ LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace);
entry.Target = dep.Item.Target;
@@ -441,7 +469,6 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
}
// Get the link entry for this target.
- int index = lei->second;
LinkEntry& entry = this->EntryList[index];
// This shared library dependency must follow the item that listed
@@ -541,6 +568,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
{
// Track inferred dependency sets implied by this list.
std::map<int, DependSet> dependSets;
+ std::string feature;
// Loop over the libraries linked directly by the depender.
for (T const& l : libs) {
@@ -550,9 +578,86 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
if (item.AsStr() == this->Target->GetName() || item.AsStr().empty()) {
continue;
}
+ if (cmHasPrefix(item.AsStr(), LL_BEGIN) &&
+ cmHasSuffix(item.AsStr(), '>')) {
+ feature = ExtractFeature(item.AsStr());
+ // emit a warning if an undefined feature is used as part of
+ // an imported target
+ if (depender_index >= 0) {
+ const auto& depender = this->EntryList[depender_index];
+ if (depender.Target != nullptr && depender.Target->IsImported() &&
+ !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_ERROR,
+ cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
+ "' uses the generator-expression '$<LINK_LIBRARY>' with "
+ "the feature '",
+ feature,
+ "', which is undefined or unsupported.\nDid you miss to "
+ "define it by setting variables \"CMAKE_",
+ this->LinkLanguage, "_LINK_USING_", feature,
+ "\" and \"CMAKE_", this->LinkLanguage, "_LINK_USING_",
+ feature, "_SUPPORTED\"?"),
+ this->Target->GetBacktrace());
+ }
+ }
+ continue;
+ }
+ if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
+ feature.clear();
+ continue;
+ }
// Add a link entry for this item.
- int dependee_index = this->AddLinkEntry(l);
+ auto ale = this->AddLinkEntry(item);
+ int dependee_index = ale.first;
+ LinkEntry& entry = this->EntryList[dependee_index];
+ if (!feature.empty()) {
+ if (ale.second) {
+ // current item not yet defined
+ if (entry.Target != nullptr &&
+ (entry.Target->GetType() ==
+ cmStateEnums::TargetType::OBJECT_LIBRARY ||
+ entry.Target->GetType() ==
+ cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("The feature '", feature,
+ "', specified as part of a generator-expression "
+ "'$",
+ LL_BEGIN, feature, ">', will not be applied to the ",
+ (entry.Target->GetType() ==
+ cmStateEnums::TargetType::OBJECT_LIBRARY
+ ? "OBJECT"
+ : "INTERFACE"),
+ " library '", entry.Item.Value, "'."),
+ this->Target->GetBacktrace());
+ } else {
+ entry.Feature = feature;
+ }
+ }
+ }
+
+ bool supportedItem = entry.Target == nullptr ||
+ (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
+ entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+
+ if (supportedItem && entry.Feature != feature) {
+ // incompatibles features occurred
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Impossible to link target '", this->Target->GetName(),
+ "' because the link item '", entry.Item.Value, "', specified ",
+ (feature.empty() ? "without any feature"
+ : cmStrCat("with the feature '", feature, '\'')),
+ ", has already occurred ",
+ (entry.Feature.empty()
+ ? "without any feature"
+ : cmStrCat("with the feature '", entry.Feature, '\'')),
+ ", which is not allowed."),
+ this->Target->GetBacktrace());
+ }
// The dependee must come after the depender.
if (depender_index >= 0) {
@@ -667,7 +772,7 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
fprintf(stderr, "%s\n", e.str().c_str());
}
-void cmComputeLinkDepends::OrderLinkEntires()
+void cmComputeLinkDepends::OrderLinkEntries()
{
// Compute the DAG of strongly connected components. The algorithm
// used by cmComputeComponentGraph should identify the components in
@@ -869,10 +974,14 @@ void cmComputeLinkDepends::DisplayFinalEntries()
fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
for (LinkEntry const& lei : this->FinalLinkEntries) {
if (lei.Target) {
- fprintf(stderr, " target [%s]\n", lei.Target->GetName().c_str());
+ fprintf(stderr, " target [%s]", lei.Target->GetName().c_str());
} else {
- fprintf(stderr, " item [%s]\n", lei.Item.Value.c_str());
+ fprintf(stderr, " item [%s]", lei.Item.Value.c_str());
+ }
+ if (!lei.Feature.empty()) {
+ fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
}
+ fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 727c666..02bdf50 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -30,7 +30,8 @@ class cmComputeLinkDepends
{
public:
cmComputeLinkDepends(cmGeneratorTarget const* target,
- const std::string& config);
+ const std::string& config,
+ const std::string& linkLanguage);
~cmComputeLinkDepends();
cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -51,6 +52,9 @@ public:
bool IsSharedDep = false;
bool IsFlag = false;
bool IsObject = false;
+ // The following member is for the management of items specified
+ // through genex $<LINK_LIBRARY:...>
+ std::string Feature;
};
using EntryVector = std::vector<LinkEntry>;
@@ -68,12 +72,13 @@ private:
cmMakefile* Makefile;
cmGlobalGenerator const* GlobalGenerator;
cmake* CMakeInstance;
+ std::string LinkLanguage;
std::string Config;
EntryVector FinalLinkEntries;
- std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
+ std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
cmLinkItem const& item);
- int AddLinkEntry(cmLinkItem const& item);
+ std::pair<int, bool> AddLinkEntry(cmLinkItem const& item);
void AddLinkObject(cmLinkItem const& item);
void AddVarLinkEntries(int depender_index, const char* value);
void AddDirectLinkEntries();
@@ -131,7 +136,7 @@ private:
void DisplayConstraintGraph();
// Ordering algorithm.
- void OrderLinkEntires();
+ void OrderLinkEntries();
std::vector<char> ComponentVisited;
std::vector<int> ComponentOrder;
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 62a96dd..5c3a96d 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -19,6 +19,7 @@
#include "cmMessageType.h"
#include "cmOrderDirectories.h"
#include "cmOutputConverter.h"
+#include "cmPlaceholderExpander.h"
#include "cmPolicies.h"
#include "cmState.h"
#include "cmStateTypes.h"
@@ -344,6 +345,27 @@ cmComputeLinkInformation::cmComputeLinkInformation(
this->LinkWithRuntimePath = this->Makefile->IsOn(var);
}
+ // Define some Feature descriptors to handle standard library and object link
+ if (!this->GetLibLinkFileFlag().empty()) {
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_LIBRARY",
+ FeatureDescriptor{ "__CMAKE_LINK_LIBRARY",
+ cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") });
+ }
+ if (!this->GetObjLinkFileFlag().empty()) {
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_OBJECT",
+ FeatureDescriptor{ "__CMAKE_LINK_OBJECT",
+ cmStrCat(this->GetObjLinkFileFlag(), "<LIBRARY>") });
+ }
+ if (!this->LoaderFlag->empty()) {
+ // Define a Feature descriptor for the link of an executable with exports
+ this->LibraryFeatureDescriptors.emplace(
+ "__CMAKE_LINK_EXECUTABLE",
+ FeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE",
+ cmStrCat(this->LoaderFlag, "<LIBRARY>") });
+ }
+
// Check the platform policy for missing soname case.
this->NoSONameUsesPath =
this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
@@ -510,12 +532,40 @@ bool cmComputeLinkInformation::Compute()
}
// Compute the ordered link line items.
- cmComputeLinkDepends cld(this->Target, this->Config);
+ cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
cld.SetOldLinkDirMode(this->OldLinkDirMode);
cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
+ FeatureDescriptor const* currentFeature = nullptr;
// Add the link line items.
for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
+ if (currentFeature != nullptr &&
+ linkEntry.Feature != currentFeature->Name) {
+ // emit feature suffix, if any
+ if (!currentFeature->Suffix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Suffix,
+ this->Items.back().Value.Backtrace },
+ ItemIsPath::No);
+ }
+ currentFeature = nullptr;
+ }
+
+ if (!linkEntry.Feature.empty() &&
+ (currentFeature == nullptr ||
+ linkEntry.Feature != currentFeature->Name)) {
+ if (!this->AddLibraryFeature(linkEntry.Feature)) {
+ continue;
+ }
+ currentFeature = this->FindLibraryFeature(linkEntry.Feature);
+ // emit feature prefix, if any
+ if (!currentFeature->Prefix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Prefix, linkEntry.Item.Backtrace },
+ ItemIsPath::No);
+ }
+ }
+
if (linkEntry.IsSharedDep) {
this->AddSharedDepItem(linkEntry);
} else {
@@ -523,6 +573,16 @@ bool cmComputeLinkInformation::Compute()
}
}
+ if (currentFeature != nullptr) {
+ // emit feature suffix, if any
+ if (!currentFeature->Suffix.empty()) {
+ this->Items.emplace_back(
+ BT<std::string>{ currentFeature->Suffix,
+ this->Items.back().Value.Backtrace },
+ ItemIsPath::No);
+ }
+ }
+
// Restore the target link type so the correct system runtime
// libraries are found.
cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
@@ -575,6 +635,270 @@ bool cmComputeLinkInformation::Compute()
return true;
}
+namespace {
+void FinalizeFeatureFormat(std::string& format, const std::string& activeTag,
+ const std::string& otherTag)
+{
+ auto pos = format.find(otherTag);
+ if (pos != std::string::npos) {
+ format.erase(pos, format.find('}', pos) - pos + 1);
+ }
+ pos = format.find(activeTag);
+ if (pos != std::string::npos) {
+ format.erase(pos, activeTag.length());
+ pos = format.find('}', pos);
+ if (pos != std::string::npos) {
+ format.erase(pos, 1);
+ }
+ }
+}
+
+bool IsValidFeatureFormat(const std::string& format)
+{
+ return format.find("<LIBRARY>") != std::string::npos ||
+ format.find("<LIB_ITEM>") != std::string::npos ||
+ format.find("<LINK_ITEM>") != std::string::npos;
+}
+}
+
+bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature)
+{
+ auto it = this->LibraryFeatureDescriptors.find(feature);
+ if (it != this->LibraryFeatureDescriptors.end()) {
+ return it->second.Supported;
+ }
+
+ auto featureName =
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_USING_", feature);
+ cmValue featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ if (!featureSupported.IsOn()) {
+ featureName = cmStrCat("CMAKE_LINK_USING_", feature);
+ featureSupported =
+ this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
+ }
+ if (!featureSupported.IsOn()) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Feature '", feature,
+ "', specified through generator-expression '$<LINK_LIBRARY>' to "
+ "link target '",
+ this->Target->GetName(), "', is not supported for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ cmValue langFeature = this->Makefile->GetDefinition(featureName);
+ if (!langFeature) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Feature '", feature,
+ "', specified through generator-expression '$<LINK_LIBRARY>' to "
+ "link target '",
+ this->Target->GetName(), "', is not defined for the '",
+ this->LinkLanguage, "' link language."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ auto items =
+ cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true);
+
+ if ((items.size() == 1 && !IsValidFeatureFormat(items.front().Value)) ||
+ (items.size() == 3 && !IsValidFeatureFormat(items[1].Value))) {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing) and cannot be used to link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ // now, handle possible "PATH{}" and "NAME{}" patterns
+ if (items.size() == 1) {
+ items.push_back(items.front());
+ FinalizeFeatureFormat(items[0].Value, "PATH{", "NAME{");
+ FinalizeFeatureFormat(items[1].Value, "NAME{", "PATH{");
+ } else if (items.size() == 3) {
+ items.insert(items.begin() + 1, items[1]);
+ FinalizeFeatureFormat(items[1].Value, "PATH{", "NAME{");
+ FinalizeFeatureFormat(items[2].Value, "NAME{", "PATH{");
+ } else {
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (wrong number of elements) and cannot be used "
+ "to link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+ if ((items.size() == 2 && !IsValidFeatureFormat(items[0].Value)) ||
+ (items.size() == 4 && !IsValidFeatureFormat(items[1].Value))) {
+ // PATH{} has wrong format
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing for \"PATH{}\" alternative) and cannot be used to "
+ "link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+ if ((items.size() == 2 && !IsValidFeatureFormat(items[1].Value)) ||
+ (items.size() == 4 && !IsValidFeatureFormat(items[2].Value))) {
+ // NAME{} has wrong format
+ this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{});
+ this->CMakeInstance->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Feature '", feature, "', specified by variable '", featureName,
+ "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or "
+ "\"<LINK_ITEM>\" patterns "
+ "are missing for \"NAME{}\" alternative) and cannot be used to "
+ "link target '",
+ this->Target->GetName(), "'."),
+ this->Target->GetBacktrace());
+
+ return false;
+ }
+
+ // replace LINKER: pattern
+ this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true);
+
+ if (items.size() == 2) {
+ this->LibraryFeatureDescriptors.emplace(
+ feature, FeatureDescriptor{ feature, items[0].Value, items[1].Value });
+ } else {
+ this->LibraryFeatureDescriptors.emplace(
+ feature,
+ FeatureDescriptor{ feature, items[0].Value, items[1].Value,
+ items[2].Value, items[3].Value });
+ }
+
+ return true;
+}
+
+cmComputeLinkInformation::FeatureDescriptor const&
+cmComputeLinkInformation::GetLibraryFeature(std::string const& feature) const
+{
+ return this->LibraryFeatureDescriptors.find(feature)->second;
+}
+cmComputeLinkInformation::FeatureDescriptor const*
+cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const
+{
+ auto it = this->LibraryFeatureDescriptors.find(feature);
+ if (it == this->LibraryFeatureDescriptors.end()) {
+ return nullptr;
+ }
+
+ return &it->second;
+}
+
+namespace {
+class FeaturePlaceHolderExpander : public cmPlaceholderExpander
+{
+public:
+ FeaturePlaceHolderExpander(const std::string* library,
+ const std::string* libItem = nullptr,
+ const std::string* linkItem = nullptr)
+ : Library(library)
+ , LibItem(libItem)
+ , LinkItem(linkItem)
+ {
+ }
+
+private:
+ std::string ExpandVariable(std::string const& variable) override
+ {
+ if (this->Library != nullptr && variable == "LIBRARY") {
+ return *this->Library;
+ }
+ if (this->LibItem != nullptr && variable == "LIB_ITEM") {
+ return *this->LibItem;
+ }
+ if (this->LinkItem != nullptr && variable == "LINK_ITEM") {
+ return *this->LinkItem;
+ }
+
+ return variable;
+ }
+
+ const std::string* Library = nullptr;
+ const std::string* LibItem = nullptr;
+ const std::string* LinkItem = nullptr;
+};
+}
+
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string itemFormat)
+ : Name(std::move(name))
+ , Supported(true)
+ , ItemPathFormat(std::move(itemFormat))
+ , ItemNameFormat(this->ItemPathFormat)
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string itemPathFormat, std::string itemNameFormat)
+ : Name(std::move(name))
+ , Supported(true)
+ , ItemPathFormat(std::move(itemPathFormat))
+ , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
+ std::string name, std::string prefix, std::string itemPathFormat,
+ std::string itemNameFormat, std::string suffix)
+ : Name(std::move(name))
+ , Supported(true)
+ , Prefix(std::move(prefix))
+ , Suffix(std::move(suffix))
+ , ItemPathFormat(std::move(itemPathFormat))
+ , ItemNameFormat(std::move(itemNameFormat))
+{
+}
+
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+ std::string const& library, ItemIsPath isPath) const
+{
+ auto format =
+ isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+ // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns with library path
+ FeaturePlaceHolderExpander expander(&library, &library, &library);
+ return expander.ExpandVariables(format);
+}
+std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
+ std::string const& library, std::string const& libItem,
+ std::string const& linkItem, ItemIsPath isPath) const
+{
+ auto format =
+ isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
+
+ // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns
+ FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem);
+ return expander.ExpandVariables(format);
+}
+
void cmComputeLinkInformation::AddImplicitLinkInfo()
{
// The link closure lists all languages whose implicit info is needed.
@@ -657,23 +981,21 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
if (impexe && this->LoaderFlag) {
// This link item is an executable that may provide symbols
// used by this target. A special flag is needed on this
- // platform. Add it now.
- std::string linkItem = this->LoaderFlag;
+ // platform. Add it now using a special feature.
cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
? cmStateEnums::ImportLibraryArtifact
: cmStateEnums::RuntimeBinaryArtifact;
-
std::string exe = tgt->GetFullPath(config, artifact, true);
- linkItem += exe;
- this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace),
- ItemIsPath::Yes, ItemIsObject::No, tgt);
+ this->Items.emplace_back(
+ BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt,
+ this->FindLibraryFeature(
+ entry.Feature.empty() ? "__CMAKE_LINK_EXECUTABLE" : entry.Feature));
this->Depends.push_back(std::move(exe));
} else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
// Add the interface library as an item so it can be considered as part
// of COMPATIBLE_INTERFACE_ enforcement. The generators will ignore
// this for the actual link line.
- this->Items.emplace_back(std::string(), ItemIsPath::No, ItemIsObject::No,
- tgt);
+ this->Items.emplace_back(std::string(), ItemIsPath::No, tgt);
// Also add the item the interface specifies to be used in its place.
std::string const& libName = tgt->GetImportedLibName(config);
@@ -1098,7 +1420,10 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
}
// Now add the full path to the library.
- this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
+ this->Items.emplace_back(item, ItemIsPath::Yes, target,
+ this->FindLibraryFeature(entry.Feature.empty()
+ ? "__CMAKE_LINK_LIBRARY"
+ : entry.Feature));
}
void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
@@ -1154,9 +1479,12 @@ void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
}
// Now add the full path to the library.
- this->Items.emplace_back(item, ItemIsPath::Yes,
- entry.IsObject ? ItemIsObject::Yes
- : ItemIsObject::No);
+ this->Items.emplace_back(
+ item, ItemIsPath::Yes, nullptr,
+ this->FindLibraryFeature(
+ entry.Feature.empty()
+ ? (entry.IsObject ? "__CMAKE_LINK_OBJECT" : "__CMAKE_LINK_LIBRARY")
+ : entry.Feature));
}
bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
@@ -1221,8 +1549,6 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
return true;
}
-// void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
-// bool pathNotKnown)
void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
bool pathNotKnown)
{
@@ -1236,8 +1562,8 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
BT<std::string> const& item = entry.Item;
- // Pass flags through untouched.
if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
+ // Pass flags through untouched.
// if this is a -l option then we might need to warn about
// CMP0003 so put it in OldUserFlagItems, if it is not a -l
// or -Wl,-l (-framework -pthread), then allow it without a
@@ -1322,9 +1648,20 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
}
// Create an option to ask the linker to search for the library.
- std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
- this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
- ItemIsPath::No);
+ auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
+
+ if (!entry.Feature.empty()) {
+ auto const& feature = this->GetLibraryFeature(entry.Feature);
+ this->Items.emplace_back(
+ BT<std::string>(
+ feature.GetDecoratedItem(cmStrCat(lib, this->LibLinkSuffix),
+ item.Value, out, ItemIsPath::No),
+ item.Backtrace),
+ ItemIsPath::No);
+ } else {
+ this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
+ ItemIsPath::No);
+ }
// Here we could try to find the library the linker will find and
// add a runtime information entry for it. It would probably not be
@@ -1374,10 +1711,10 @@ void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
// user.
this->CMakeInstance->IssueMessage(
MessageType::WARNING,
- cmStrCat(
- "Target \"", this->Target->GetName(),
- "\" requests linking to directory \"", item.Value,
- "\". Targets may link only to libraries. CMake is dropping the item."),
+ cmStrCat("Target \"", this->Target->GetName(),
+ "\" requests linking to directory \"", item.Value,
+ "\". Targets may link only to libraries. CMake is dropping "
+ "the item."),
item.Backtrace);
}
@@ -1804,8 +2141,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
// Add directories explicitly specified by user
std::string build_rpath;
if (this->Target->GetBuildRPATH(this->Config, build_rpath)) {
- // This will not resolve entries to use $ORIGIN, the user is expected to
- // do that if necessary.
+ // This will not resolve entries to use $ORIGIN, the user is expected
+ // to do that if necessary.
cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
}
}
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 2edcd5f..ce9f393 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
+#include <map>
#include <memory>
#include <set>
#include <string>
@@ -28,6 +29,9 @@ class cmake;
*/
class cmComputeLinkInformation
{
+private:
+ class FeatureDescriptor;
+
public:
cmComputeLinkInformation(cmGeneratorTarget const* target,
const std::string& config);
@@ -43,28 +47,33 @@ public:
Yes,
};
- enum class ItemIsObject
- {
- No,
- Yes,
- };
-
struct Item
{
- Item() = default;
Item(BT<std::string> v, ItemIsPath isPath,
- ItemIsObject isObject = ItemIsObject::No,
- cmGeneratorTarget const* target = nullptr)
+ cmGeneratorTarget const* target = nullptr,
+ FeatureDescriptor const* feature = nullptr)
: Value(std::move(v))
, IsPath(isPath)
- , IsObject(isObject)
, Target(target)
+ , Feature(feature)
{
}
BT<std::string> Value;
- ItemIsPath IsPath = ItemIsPath::Yes;
- ItemIsObject IsObject = ItemIsObject::No;
+ ItemIsPath IsPath = ItemIsPath::No;
cmGeneratorTarget const* Target = nullptr;
+
+ bool HasFeature() const { return this->Feature != nullptr; }
+
+ BT<std::string> GetFormattedItem(std::string const& path) const
+ {
+ return { (this->Feature != nullptr)
+ ? this->Feature->GetDecoratedItem(path, this->IsPath)
+ : path,
+ Value.Backtrace };
+ }
+
+ private:
+ FeatureDescriptor const* Feature = nullptr;
};
using ItemVector = std::vector<Item>;
void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
@@ -237,4 +246,36 @@ private:
void AddLibraryRuntimeInfo(std::string const& fullPath,
const cmGeneratorTarget* target);
void AddLibraryRuntimeInfo(std::string const& fullPath);
+
+ class FeatureDescriptor
+ {
+ public:
+ FeatureDescriptor() = default;
+ FeatureDescriptor(std::string name, std::string itemFormat);
+ FeatureDescriptor(std::string name, std::string itemPathFormat,
+ std::string itemNameFormat);
+ FeatureDescriptor(std::string name, std::string prefix,
+ std::string itemPathFormat, std::string itemNameFormat,
+ std::string suffix);
+
+ const std::string Name;
+ const bool Supported = false;
+ const std::string Prefix;
+ const std::string Suffix;
+ std::string GetDecoratedItem(std::string const& library,
+ ItemIsPath isPath) const;
+ std::string GetDecoratedItem(std::string const& library,
+ std::string const& linkItem,
+ std::string const& defaultValue,
+ ItemIsPath isPath) const;
+
+ private:
+ std::string ItemPathFormat;
+ std::string ItemNameFormat;
+ };
+ std::map<std::string, FeatureDescriptor> LibraryFeatureDescriptors;
+ bool AddLibraryFeature(std::string const& feature);
+ FeatureDescriptor const& GetLibraryFeature(std::string const& feature) const;
+ FeatureDescriptor const* FindLibraryFeature(
+ std::string const& feature) const;
};
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 396e9c9..b63b90b 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1198,6 +1198,68 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
}
} linkLanguageAndIdNode;
+static const struct LinkLibraryNode : public cmGeneratorExpressionNode
+{
+ LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
+
+ int NumExpectedParameters() const override { return OneOrMoreParameters; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ if (!context->HeadTarget || !dagChecker ||
+ !dagChecker->EvaluatingLinkLibraries()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> may only be used with binary targets "
+ "to specify link libraries.");
+ return std::string();
+ }
+
+ std::vector<std::string> list;
+ cmExpandLists(parameters.begin(), parameters.end(), list);
+ if (list.empty()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> expects a feature name as first argument.");
+ return std::string();
+ }
+ if (list.size() == 1) {
+ // no libraries specified, ignore this genex
+ return std::string();
+ }
+
+ auto const& feature = list.front();
+ const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
+ const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
+
+ // filter out $<LINK_LIBRARY:..> tags with same feature
+ // and raise an error for any different feature
+ cm::erase_if(list, [&](const std::string& item) -> bool {
+ return item == LL_BEGIN || item == LL_END;
+ });
+ auto it =
+ std::find_if(list.cbegin() + 1, list.cend(),
+ [&feature](const std::string& item) -> bool {
+ return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
+ item.substr(14, item.find('>', 14) - 14) != feature;
+ });
+ if (it != list.cend()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<LINK_LIBRARY:...> with different features cannot be nested.");
+ return std::string();
+ }
+
+ list.front() = LL_BEGIN;
+ list.push_back(LL_END);
+
+ return cmJoin(list, ";"_s);
+ }
+} linkLibraryNode;
+
static const struct HostLinkNode : public cmGeneratorExpressionNode
{
HostLinkNode() {} // NOLINT(modernize-use-equals-default)
@@ -2668,6 +2730,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "COMPILE_LANGUAGE", &languageNode },
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
{ "LINK_LANGUAGE", &linkLanguageNode },
+ { "LINK_LIBRARY", &linkLibraryNode },
{ "HOST_LINK", &hostLinkNode },
{ "DEVICE_LINK", &deviceLinkNode },
{ "SHELL_PATH", &shellPathNode }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9f1029e..58edefb 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -4625,7 +4625,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
}
std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
- std::vector<BT<std::string>>& result, const std::string& language) const
+ std::vector<BT<std::string>>& result, const std::string& language,
+ bool joinItems) const
{
// replace "LINKER:" prefixed elements by actual linker wrapper
const std::string wrapper(this->Makefile->GetSafeDefinition(
@@ -4684,7 +4685,14 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
std::vector<BT<std::string>> options = wrapOptions(
linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
- result.insert(entry, options.begin(), options.end());
+ if (joinItems) {
+ result.insert(entry,
+ cmJoin(cmRange<decltype(options.cbegin())>(
+ options.cbegin(), options.cend()),
+ " "_s));
+ } else {
+ result.insert(entry, options.begin(), options.end());
+ }
}
return result;
}
@@ -6377,7 +6385,8 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
std::string const& str = item.AsStr();
if (!str.empty() &&
(str[0] == '-' || str[0] == '$' || str[0] == '`' ||
- str.find_first_of("/\\") != std::string::npos)) {
+ str.find_first_of("/\\") != std::string::npos ||
+ cmHasPrefix(str, "<LINK_LIBRARY:"_s))) {
return true;
}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 45639c0..3e30913 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -513,7 +513,8 @@ public:
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>>& ResolveLinkerWrapper(
- std::vector<BT<std::string>>& result, const std::string& language) const;
+ std::vector<BT<std::string>>& result, const std::string& language,
+ bool joinItems = false) const;
void GetStaticLibraryLinkOptions(std::vector<std::string>& result,
const std::string& config,
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 203addd..489c7fb 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -3741,7 +3741,9 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
}
libPaths.Add("-framework " + this->XCodeEscapePath(fwName));
} else {
- libPaths.Add(this->XCodeEscapePath(cleanPath));
+ libPaths.Add(
+ libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
+ .Value);
}
if ((!libName.Target || libName.Target->IsImported()) &&
IsLinkPhaseLibraryExtension(libPath)) {
diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx
index 2b8f836..ed89e91 100644
--- a/Source/cmLinkLibrariesCommand.cxx
+++ b/Source/cmLinkLibrariesCommand.cxx
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
mf.AppendProperty("LINK_LIBRARIES", *i);
}
+ mf.CheckProperty("LINK_LIBRARIES");
+
return true;
}
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 5646368..290642b 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -75,14 +75,8 @@ void cmLinkLineComputer::ComputeLinkLibs(
BT<std::string> linkLib;
if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
- if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) {
- linkLib.Value += cli.GetObjLinkFileFlag();
- } else {
- linkLib.Value += cli.GetLibLinkFileFlag();
- }
- linkLib.Value += this->ConvertToOutputFormat(
- this->ConvertToLinkReference(item.Value.Value));
- linkLib.Backtrace = item.Value.Backtrace;
+ linkLib = item.GetFormattedItem(this->ConvertToOutputFormat(
+ this->ConvertToLinkReference(item.Value.Value)));
} else {
linkLib = item.Value;
}
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 43f161b..71f9f80 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -118,8 +118,10 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
// can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
cmHasLiteralSuffix(item.Value.Value, ".lib")) {
- linkLib.Value += this->ConvertToOutputFormat(
- this->ConvertToLinkReference(item.Value.Value));
+ linkLib.Value = item
+ .GetFormattedItem(this->ConvertToOutputFormat(
+ this->ConvertToLinkReference(item.Value.Value)))
+ .Value;
}
} else if (item.Value == "-framework") {
// This is the first part of '-framework Name' where the framework
@@ -127,7 +129,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
skipItemAfterFramework = true;
continue;
} else if (cmLinkItemValidForDevice(item.Value.Value)) {
- linkLib.Value += item.Value.Value;
+ linkLib.Value = item.Value.Value;
}
if (emitted.insert(linkLib.Value).second) {
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index ed7e888..f65add1 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1294,7 +1294,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries(
for (auto const& lib : libs) {
if (lib.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
std::string rel = lg->MaybeRelativeToCurBinDir(lib.Value.Value);
- fout << lg->ConvertToXMLOutputPath(rel) << " ";
+ rel = lg->ConvertToXMLOutputPath(rel);
+ fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel)
+ << " ";
} else if (!lib.Target ||
lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
fout << lib.Value.Value << " ";
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 4b1635b..6b17cc9 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3976,6 +3976,31 @@ std::vector<std::string> cmMakefile::GetPropertyKeys() const
return this->StateSnapshot.GetDirectory().GetPropertyKeys();
}
+void cmMakefile::CheckProperty(const std::string& prop) const
+{
+ // Certain properties need checking.
+ if (prop == "LINK_LIBRARIES") {
+ if (cmValue value = this->GetProperty(prop)) {
+ // Look for <LINK_LIBRARY:> internal pattern
+ static cmsys::RegularExpression linkLibrary(
+ "(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
+ if (!linkLibrary.find(value)) {
+ return;
+ }
+
+ // Report an error.
+ this->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat(
+ "Property ", prop, " contains the invalid item \"",
+ linkLibrary.match(2), "\". The ", prop,
+ " property may contain the generator-expression "
+ "\"$<LINK_LIBRARY:...>\" "
+ "which may be used to specify how the libraries are linked."));
+ }
+ }
+}
+
cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const
{
auto i = this->Targets.find(name);
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 85988b8..ecac95e 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -787,6 +787,7 @@ public:
cmValue GetProperty(const std::string& prop, bool chain) const;
bool GetPropertyAsBool(const std::string& prop) const;
std::vector<std::string> GetPropertyKeys() const;
+ void CheckProperty(const std::string& prop) const;
//! Initialize a makefile from its parent
void InitializeFromParent(cmMakefile* parent);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 87fce92..d40709f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1779,69 +1779,93 @@ void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry)
this->impl->PrecompileHeadersEntries.push_back(entry);
}
-static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
- const std::string& value,
- cmMakefile* context,
- bool imported)
+namespace {
+void CheckLinkLibraryPattern(const std::string& property,
+ const std::string& value, cmMakefile* context)
{
- // Look for link-type keywords in the value.
- static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
- if (!keys.find(value)) {
+ // Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags
+ static cmsys::RegularExpression linkLibrary(
+ "(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
+ if (!linkLibrary.find(value)) {
return;
}
+ // Report an error.
+ context->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Property ", property, " contains the invalid item \"",
+ linkLibrary.match(2), "\". The ", property,
+ " property may contain the generator-expression "
+ "\"$<LINK_LIBRARY:...>\" "
+ "which may be used to specify how the libraries are linked."));
+}
+
+void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
+ const std::string& value,
+ cmMakefile* context, bool imported)
+{
// Support imported and non-imported versions of the property.
const char* base = (imported ? "IMPORTED_LINK_INTERFACE_LIBRARIES"
: "LINK_INTERFACE_LIBRARIES");
- // Report an error.
- std::ostringstream e;
- e << "Property " << prop << " may not contain link-type keyword \""
- << keys.match(2) << "\". "
- << "The " << base << " property has a per-configuration "
- << "version called " << base << "_<CONFIG> which may be "
- << "used to specify per-configuration rules.";
- if (!imported) {
- e << " "
- << "Alternatively, an IMPORTED library may be created, configured "
- << "with a per-configuration location, and then named in the "
- << "property value. "
- << "See the add_library command's IMPORTED mode for details."
- << "\n"
- << "If you have a list of libraries that already contains the "
- << "keyword, use the target_link_libraries command with its "
- << "LINK_INTERFACE_LIBRARIES mode to set the property. "
- << "The command automatically recognizes link-type keywords and sets "
- << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
- << "properties accordingly.";
- }
- context->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
-
-static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value,
- cmMakefile* context)
-{
// Look for link-type keywords in the value.
static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
- if (!keys.find(value)) {
- return;
+ if (keys.find(value)) {
+ // Report an error.
+ std::ostringstream e;
+ e << "Property " << prop << " may not contain link-type keyword \""
+ << keys.match(2) << "\". "
+ << "The " << base << " property has a per-configuration "
+ << "version called " << base << "_<CONFIG> which may be "
+ << "used to specify per-configuration rules.";
+ if (!imported) {
+ e << " "
+ << "Alternatively, an IMPORTED library may be created, configured "
+ << "with a per-configuration location, and then named in the "
+ << "property value. "
+ << "See the add_library command's IMPORTED mode for details."
+ << "\n"
+ << "If you have a list of libraries that already contains the "
+ << "keyword, use the target_link_libraries command with its "
+ << "LINK_INTERFACE_LIBRARIES mode to set the property. "
+ << "The command automatically recognizes link-type keywords and sets "
+ << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
+ << "properties accordingly.";
+ }
+ context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
- // Report an error.
- std::ostringstream e;
+ CheckLinkLibraryPattern(base, value, context);
+}
+
+void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
+{
+ CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
+}
- e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
- "keyword \""
- << keys.match(2)
- << "\". The INTERFACE_LINK_LIBRARIES "
- "property may contain configuration-sensitive generator-expressions "
- "which may be used to specify per-configuration rules.";
+void CheckINTERFACE_LINK_LIBRARIES(const std::string& value,
+ cmMakefile* context)
+{
+ // Look for link-type keywords in the value.
+ static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
+ if (keys.find(value)) {
+ // Report an error.
+ std::ostringstream e;
+
+ e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
+ "keyword \""
+ << keys.match(2)
+ << "\". The INTERFACE_LINK_LIBRARIES "
+ "property may contain configuration-sensitive generator-expressions "
+ "which may be used to specify per-configuration rules.";
+
+ context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ }
- context->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ CheckLinkLibraryPattern("INTERFACE_LINK_LIBRARIES", value, context);
}
-static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
- cmMakefile* context)
+void CheckIMPORTED_GLOBAL(const cmTarget* target, cmMakefile* context)
{
const auto& targets = context->GetOwnedImportedTargets();
auto it =
@@ -1857,6 +1881,7 @@ static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
}
+}
void cmTarget::CheckProperty(const std::string& prop,
cmMakefile* context) const
@@ -1864,22 +1889,23 @@ void cmTarget::CheckProperty(const std::string& prop,
// Certain properties need checking.
if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
+ CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
}
- }
- if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
+ } else if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
+ CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
}
- }
- if (prop == "INTERFACE_LINK_LIBRARIES") {
+ } else if (prop == "LINK_LIBRARIES") {
if (cmValue value = this->GetProperty(prop)) {
- cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context);
+ CheckLINK_LIBRARIES(*value, context);
}
- }
- if (prop == "IMPORTED_GLOBAL") {
+ } else if (prop == "INTERFACE_LINK_LIBRARIES") {
+ if (cmValue value = this->GetProperty(prop)) {
+ CheckINTERFACE_LINK_LIBRARIES(*value, context);
+ }
+ } else if (prop == "IMPORTED_GLOBAL") {
if (this->IsImported()) {
- cmTargetCheckIMPORTED_GLOBAL(this, context);
+ CheckIMPORTED_GLOBAL(this, context);
}
}
}
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index e15c941..0b96b2d 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -318,6 +318,9 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
}
+ target->CheckProperty("LINK_LIBRARIES", &mf);
+ target->CheckProperty("INTERFACE_LINK_LIBRARIES", &mf);
+
return true;
}
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 7b197fa..92d64fe 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -3677,7 +3677,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
this->LocalGenerator->MaybeRelativeToCurBinDir(l.Value.Value);
ConvertToWindowsSlash(path);
if (!cmVS10IsTargetsFile(l.Value.Value)) {
- libVec.push_back(path);
+ libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+ : path);
}
} else {
libVec.push_back(l.Value.Value);
@@ -4315,7 +4316,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
if (cmVS10IsTargetsFile(l.Value.Value)) {
vsTargetVec.push_back(path);
} else {
- libVec.push_back(path);
+ libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
+ : path);
}
} else if (!l.Target ||
l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 1d7e632..d1a1ddd 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -312,6 +312,7 @@ add_RunCMake_test(GenEx-LINK_LANGUAGE)
add_RunCMake_test(GenEx-LINK_LANG_AND_ID)
add_RunCMake_test(GenEx-HOST_LINK)
add_RunCMake_test(GenEx-DEVICE_LINK)
+add_RunCMake_test(GenEx-LINK_LIBRARY)
add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -652,6 +653,17 @@ add_RunCMake_test(target_link_libraries)
add_RunCMake_test(target_link_libraries-ALIAS)
add_RunCMake_test(target_link_libraries-LINK_LANGUAGE)
add_RunCMake_test(target_link_libraries-LINK_LANG_AND_ID)
+add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+ -DMINGW=${MINGW}
+ -DMSYS=${MSYS}
+ -DCYGWIN=${CYGWIN}
+ -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+ -DMSVC_VERSION=${MSVC_VERSION}
+ -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
+ -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}
+ -DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX}
+ -DCMAKE_IMPORT_LIBRARY_SUFFIX=${CMAKE_IMPORT_LIBRARY_SUFFIX}
+ -DCMAKE_LINK_LIBRARY_FLAG=${CMAKE_LINK_LIBRARY_FLAG})
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}
-DCMake_TEST_CUDA=${CMake_TEST_CUDA})
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt
new file mode 100644
index 0000000..612169c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.18...3.22)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
new file mode 100644
index 0000000..ab1ac37
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -0,0 +1,25 @@
+include(RunCMake)
+
+run_cmake(add_custom_target)
+run_cmake(add_custom_command)
+run_cmake(add_link_options)
+run_cmake(link_directories)
+run_cmake(target_link_options)
+run_cmake(target_link_directories)
+run_cmake(no-arguments)
+run_cmake(empty-arguments)
+run_cmake(forbidden-arguments)
+run_cmake(bad-feature1)
+run_cmake(bad-feature2)
+run_cmake(bad-feature3)
+run_cmake(bad-feature4)
+run_cmake(bad-feature5)
+run_cmake(bad-feature6)
+run_cmake(bad-feature7)
+run_cmake(feature-not-supported)
+run_cmake(library-ignored)
+run_cmake(compatible-features)
+run_cmake(incompatible-features)
+run_cmake(nested-compatible-features)
+run_cmake(nested-incompatible-features)
+run_cmake(only-targets)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt
new file mode 100644
index 0000000..d8ff0eb
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_custom_command.cmake:[0-9]+ \(add_custom_command\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake
new file mode 100644
index 0000000..3583a67
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_command.cmake
@@ -0,0 +1,4 @@
+add_custom_target(drive)
+add_custom_command(TARGET drive PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
+)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt
new file mode 100644
index 0000000..8ca384d
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_custom_target.cmake:[0-9]+ \(add_custom_target\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake
new file mode 100644
index 0000000..ef00965
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_custom_target.cmake
@@ -0,0 +1,3 @@
+add_custom_target(drive
+ COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
+)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt
new file mode 100644
index 0000000..399a413
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at add_link_options.cmake:[0-9]+ \(add_link_options\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake
new file mode 100644
index 0000000..fdccf95
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/add_link_options.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+add_link_options("$<LINK_LIBRARY:feat>")
+
+add_library(empty SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt
new file mode 100644
index 0000000..0ff8aca
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature1.cmake:[0-9]+ \(add_library\):
+ Feature 'bad_feat', specified through generator-expression
+ '\$<LINK_LIBRARY>' to link target 'lib', is not supported for the 'C' link
+ language.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake
new file mode 100644
index 0000000..5e540cf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature1.cmake
@@ -0,0 +1,6 @@
+enable_language(C)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:bad_feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt
new file mode 100644
index 0000000..9e878cc
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at bad-feature2.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
+ link target 'lib', is not defined for the 'C' link language.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake
new file mode 100644
index 0000000..15b5ca0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature2.cmake
@@ -0,0 +1,8 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt
new file mode 100644
index 0000000..69963fe
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature3.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+ malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
+ missing\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake
new file mode 100644
index 0000000..7960465
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature3.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt
new file mode 100644
index 0000000..7385115
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature4.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+ malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
+ missing\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake
new file mode 100644
index 0000000..b40cfaf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature4.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "-opt")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt
new file mode 100644
index 0000000..9894577
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature5.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+ malformed \(wrong number of elements\) and cannot be used to link target
+ 'lib'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake
new file mode 100644
index 0000000..8ce1ecf
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature5.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "-prefix" "<LIBRARY>")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt
new file mode 100644
index 0000000..d8d0e19
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature6.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+ malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
+ for "PATH{}" alternative\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake
new file mode 100644
index 0000000..7b72ad5
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature6.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "PATH{}NAME{<LIBRARY>}")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt
new file mode 100644
index 0000000..2a498cd
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at bad-feature7.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
+ malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
+ for "NAME{}" alternative\) and cannot be used to link target 'lib'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake
new file mode 100644
index 0000000..173f86c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature7.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "NAME{}PATH{<LIBRARY>}")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
new file mode 100644
index 0000000..e79764c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/compatible-features.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt
new file mode 100644
index 0000000..1530f61
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at empty-arguments.cmake:[0-9]+ \(target_link_libraries\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:,>
+
+ \$<LINK_LIBRARY:...> expects a feature name as first argument.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake
new file mode 100644
index 0000000..c6e2260
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty-arguments.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:,>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/empty.c
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt
new file mode 100644
index 0000000..6067bce
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at feature-not-supported.cmake:[0-9]+ \(add_library\):
+ Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
+ link target 'lib', is not supported for the 'C' link language.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake
new file mode 100644
index 0000000..0666227
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/feature-not-supported.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED FALSE)
+set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt
new file mode 100644
index 0000000..5245dd8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments-stderr.txt
@@ -0,0 +1,16 @@
+CMake Error at forbidden-arguments.cmake:[0-9]+ \(link_libraries\):
+ Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
+ The LINK_LIBRARIES property may contain the generator-expression
+ "\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
+ linked.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
+
+
+CMake Error at forbidden-arguments.cmake:[0-9]+ \(target_link_libraries\):
+ Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
+ The LINK_LIBRARIES property may contain the generator-expression
+ "\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
+ linked.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake
new file mode 100644
index 0000000..1c51c44
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/forbidden-arguments.cmake
@@ -0,0 +1,6 @@
+enable_language(C)
+
+link_libraries(<LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE <LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
new file mode 100644
index 0000000..22219a2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at incompatible-features.cmake:[0-9]+ \(add_library\):
+ Impossible to link target 'lib' because the link item 'dep1', specified
+ with the feature 'feat1', has already occurred with the feature 'feat2',
+ which is not allowed.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
new file mode 100644
index 0000000..d96b214
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/incompatible-features.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
new file mode 100644
index 0000000..f9a99af
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -0,0 +1,14 @@
+CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
+ The feature 'feat', specified as part of a generator-expression
+ '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE library
+ 'front'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers. Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
+ The feature 'feat', specified as part of a generator-expression
+ '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT library 'dep'.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers. Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
new file mode 100644
index 0000000..e000b97
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
+
+add_library(dep OBJECT empty.c)
+
+add_library(lib SHARED empty.c)
+
+add_library(front INTERFACE)
+target_link_libraries(front INTERFACE lib)
+
+
+add_library(lib2 SHARED empty.c)
+target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat,front,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt
new file mode 100644
index 0000000..aeb32f2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at link_directories.cmake:[0-9]+ \(link_directories\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake
new file mode 100644
index 0000000..b6d9a36
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/link_directories.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+link_directories("$<LINK_LIBRARY:feat>")
+
+add_library(empty SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake
new file mode 100644
index 0000000..d3b04e8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-compatible-features.cmake
@@ -0,0 +1,11 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat1,dep1,$<LINK_LIBRARY:feat1,dep2>>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt
new file mode 100644
index 0000000..3f6c504
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at nested-incompatible-features.cmake:[0-9]+ \(target_link_libraries\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat2,dep1,\$<LINK_LIBRARY:feat1,dep2>>
+
+ \$<LINK_LIBRARY:...> with different features cannot be nested.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake
new file mode 100644
index 0000000..8565fa9
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/nested-incompatible-features.cmake
@@ -0,0 +1,14 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
+
+add_library(dep1 SHARED empty.c)
+
+add_library(dep2 SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,$<LINK_LIBRARY:feat1,dep2>>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt
new file mode 100644
index 0000000..af58fa0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at no-arguments.cmake:[0-9]+ \(target_link_libraries\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY>
+
+ \$<LINK_LIBRARY> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake
new file mode 100644
index 0000000..0645dc7
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/no-arguments.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt
new file mode 100644
index 0000000..6b770f0
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at only-targets.cmake:[0-9]+ \(target_link_libraries\):
+ Target "lib2" has LINK_LIBRARIES_ONLY_TARGETS enabled, but it links to:
+
+ external
+
+ which is not a target. Possible reasons include:
+
+ \* There is a typo in the target name.
+ \* A find_package call is missing for an IMPORTED target.
+ \* An ALIAS target is missing.
+
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake
new file mode 100644
index 0000000..e29ad6c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/only-targets.cmake
@@ -0,0 +1,16 @@
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
+
+set(CMAKE_LINK_LIBRARIES_ONLY_TARGETS 1)
+
+add_library(dep1 SHARED empty.c)
+
+add_library(lib1 SHARED empty.c)
+# accepted
+target_link_libraries(lib1 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
+
+add_library(lib2 SHARED empty.c)
+# invalid
+target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat1,external>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt
new file mode 100644
index 0000000..e0c60c4
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at target_link_directories.cmake:[0-9]+ \(target_link_directories\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:feat>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake
new file mode 100644
index 0000000..e8cc670
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_directories.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(empty SHARED empty.c)
+target_link_directories(empty PRIVATE "$<LINK_LIBRARY:feat>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt
new file mode 100644
index 0000000..6c9aab1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at target_link_options.cmake:[0-9]+ \(target_link_options\):
+ Error evaluating generator expression:
+
+ \$<LINK_LIBRARY:FEAT>
+
+ \$<LINK_LIBRARY:...> may only be used with binary targets to specify link
+ libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake
new file mode 100644
index 0000000..800124c
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/target_link_options.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(empty SHARED empty.c)
+target_link_options(empty PRIVATE $<LINK_LIBRARY:FEAT>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt
new file mode 100644
index 0000000..915fc41
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.1...3.22)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
new file mode 100644
index 0000000..255c9a6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}")
+ set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
new file mode 100644
index 0000000..a8e0da7
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
new file mode 100644
index 0000000..54cef2c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP <base1> <other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
new file mode 100644
index 0000000..7c38134
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
new file mode 100644
index 0000000..88b5cf6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAGother${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<base1> --LIBFLAG<other> --ITEMFLAG<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
new file mode 100644
index 0000000..c473637
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-ITEMFLAGother\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBFLAG<base1> --ITEMFLAG<other> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
new file mode 100644
index 0000000..858dcfe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
new file mode 100644
index 0000000..ab06726
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
new file mode 100644
index 0000000..62aa17c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}")
+ set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base3> --SUFFIXGROUP <other2> --PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP <other1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
new file mode 100644
index 0000000..255c9a6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}")
+ set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
new file mode 100644
index 0000000..a8e0da7
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+ set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
new file mode 100644
index 0000000..32b58fe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1")
+ set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
new file mode 100644
index 0000000..32b58fe
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1")
+ set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
new file mode 100644
index 0000000..3223a95
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
@@ -0,0 +1,81 @@
+
+enable_language(C)
+
+# ensure command line is always displayed and do not use any response file
+set(CMAKE_VERBOSE_MAKEFILE TRUE)
+set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+
+if (CMAKE_GENERATOR MATCHES "Borland|NMake")
+ 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}")
+endif()
+
+add_library(base1 SHARED base.c)
+add_library(base2 SHARED base.c)
+
+set(CMAKE_C_LINK_USING_feat1 "--LIBFLAG<LIBRARY>")
+set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat1_1 "--LIBFLAG_C<LIBRARY>")
+set(CMAKE_C_LINK_USING_feat1_1_SUPPORTED FALSE)
+set(CMAKE_LINK_USING_feat1_1 "--LIBFLAG<LIBRARY>")
+set(CMAKE_LINK_USING_feat1_1_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat2 "--PREFIXGROUP" "--LIBGROUP<LIBRARY>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat3 "--PREFIXGROUP" "<LINK_ITEM>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat3_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat4 "--PREFIXGROUP" "--LIBFLAG<LIBRARY> --ITEMFLAG<LIB_ITEM>" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat4_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat5 "--PREFIXGROUP" "PATH{--LIBFLAG<LIBRARY>}NAME{--ITEMFLAG<LIB_ITEM>}" "--SUFFIXGROUP")
+set(CMAKE_C_LINK_USING_feat5_SUPPORTED TRUE)
+
+set(CMAKE_C_LINK_USING_feat6 "<LINK_ITEM>")
+set(CMAKE_C_LINK_USING_feat6_SUPPORTED TRUE)
+
+
+add_library(LinkLibrary_simple1 SHARED lib.c)
+target_link_libraries(LinkLibrary_simple1 PRIVATE "$<LINK_LIBRARY:feat1,base1>")
+
+add_library(LinkLibrary_simple2 SHARED lib.c)
+target_link_libraries(LinkLibrary_simple2 PRIVATE "$<LINK_LIBRARY:feat1_1,base1>")
+
+add_library(LinkLibrary_group1 SHARED lib.c)
+target_link_libraries(LinkLibrary_group1 PRIVATE "$<LINK_LIBRARY:feat1,base1,base2>")
+
+add_library(LinkLibrary_group2 SHARED lib.c)
+target_link_libraries(LinkLibrary_group2 PRIVATE "$<LINK_LIBRARY:feat2,base1,base2>")
+
+add_library(LinkLibrary_nested_feature1 SHARED lib.c)
+target_link_libraries(LinkLibrary_nested_feature1 PRIVATE "$<LINK_LIBRARY:feat1,base1,$<LINK_LIBRARY:feat1,base2>>")
+
+add_library(LinkLibrary_nested_feature2 SHARED lib.c)
+target_link_libraries(LinkLibrary_nested_feature2 PRIVATE "$<LINK_LIBRARY:feat2,base1,$<LINK_LIBRARY:feat2,base2>>")
+
+add_library(LinkLibrary_link_items1 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items1 PRIVATE "$<LINK_LIBRARY:feat3,base1,other>")
+
+add_library(LinkLibrary_link_items2 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items2 PRIVATE "$<LINK_LIBRARY:feat2,base1,other>")
+
+add_library(LinkLibrary_link_items3 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items3 PRIVATE "$<LINK_LIBRARY:feat4,base1,other>")
+
+add_library(LinkLibrary_link_items4 SHARED lib.c)
+target_link_libraries(LinkLibrary_link_items4 PRIVATE "$<LINK_LIBRARY:feat5,base1,other>")
+
+add_library(base3 SHARED base.c)
+target_link_libraries(base3 PRIVATE "$<LINK_LIBRARY:feat6,base1>")
+add_library(LinkLibrary_mix_features1 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features1 PRIVATE "$<LINK_LIBRARY:feat2,base1,base3>")
+
+target_link_libraries(base3 INTERFACE "$<LINK_LIBRARY:feat2,base1>")
+add_library(LinkLibrary_mix_features2 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features2 PRIVATE "$<LINK_LIBRARY:feat2,base1,base3>")
+
+target_link_libraries(base3 INTERFACE other1)
+add_library(LinkLibrary_mix_features3 SHARED lib.c)
+target_link_libraries(LinkLibrary_mix_features3 PRIVATE base2 "$<LINK_LIBRARY:feat2,base1,base3>" other2)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
new file mode 100644
index 0000000..febada0
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -0,0 +1,73 @@
+
+include(RunCMake)
+
+cmake_policy(SET CMP0054 NEW)
+
+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} --config Release --verbose ${ARGN})
+
+ unset(RunCMake_TEST_BINARY_DIR)
+ unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+# Some environments are excluded because they are not able to honor verbose mode
+if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode"
+ OR (RunCMake_GENERATOR MATCHES "Visual Studio" AND MSVC_VERSION GREATER_EQUAL "1600"))
+ AND NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
+
+ set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+ if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+ endif()
+
+ if (CMAKE_SYSTEM_NAME STREQUAL "Windows"
+ OR CMAKE_SYSTEM_NAME STREQUAL "CYGWIN"
+ OR CMAKE_SYSTEM_NAME STREQUAL "MSYS")
+ set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_IMPORT_LIBRARY_PREFIX})
+ set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_IMPORT_LIBRARY_SUFFIX})
+ else()
+ set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
+ set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
+ endif()
+ if (MINGW OR MSYS OR CYGWIN)
+ set(LINK_EXTERN_LIBRARY_SUFFIX "")
+ else()
+ set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
+ endif()
+
+ run_cmake(LINK_LIBRARY)
+
+ run_cmake_target(LINK_LIBRARY simple1 LinkLibrary_simple1)
+ run_cmake_target(LINK_LIBRARY simple2 LinkLibrary_simple2)
+ run_cmake_target(LINK_LIBRARY group1 LinkLibrary_group1)
+ run_cmake_target(LINK_LIBRARY group2 LinkLibrary_group2)
+ run_cmake_target(LINK_LIBRARY nested-feature1 LinkLibrary_nested_feature1)
+ run_cmake_target(LINK_LIBRARY nested-feature2 LinkLibrary_nested_feature2)
+ run_cmake_target(LINK_LIBRARY link-items1 LinkLibrary_link_items1)
+ run_cmake_target(LINK_LIBRARY link-items2 LinkLibrary_link_items2)
+ run_cmake_target(LINK_LIBRARY link-items3 LinkLibrary_link_items3)
+ run_cmake_target(LINK_LIBRARY link-items4 LinkLibrary_link_items4)
+ run_cmake_target(LINK_LIBRARY mix-features1 LinkLibrary_mix_features1)
+ run_cmake_target(LINK_LIBRARY mix-features2 LinkLibrary_mix_features2)
+ run_cmake_target(LINK_LIBRARY mix-features3 LinkLibrary_mix_features3)
+
+ run_cmake(imported-target)
+
+ # tests using features as described in the documentation
+ if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
+ OR (CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER "1900")
+ OR (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
+ run_cmake(whole_archive)
+ run_cmake_target(whole_archive link-exe main)
+ endif()
+ if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ run_cmake(weak_library)
+ run_cmake_target(weak_library link-exe main)
+ endif()
+
+ unset(RunCMake_TEST_OPTIONS)
+ unset(RunCMake_TEST_OUTPUT_MERGE)
+
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c
new file mode 100644
index 0000000..a5075d4
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/base.c
@@ -0,0 +1,9 @@
+
+#if !defined(STATIC_BASE)
+# if defined(_WIN32)
+__declspec(dllexport)
+# endif
+#endif
+ void base()
+{
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
new file mode 100644
index 0000000..b3a86cc
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target-stdout.txt
@@ -0,0 +1,18 @@
+CMake Warning \(dev\) at imported-target.cmake:[0-9]+ \(add_library\):
+ The 'IMPORTED' target 'NS::lib2' uses the generator-expression
+ '\$<LINK_LIBRARY>' with the feature 'whole_archive', which is undefined or
+ unsupported.
+
+ Did you miss to define it by setting variables
+ "CMAKE_C_LINK_USING_whole_archive" and
+ "CMAKE_C_LINK_USING_whole_archive_SUPPORTED"\?
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers. Use -Wno-dev to suppress it.
+
+CMake Error at imported-target.cmake:[0-9]+ \(add_library\):
+ Feature 'whole_archive', specified through generator-expression
+ '\$<LINK_LIBRARY>' to link target 'lib', is not supported for the 'C' link
+ language.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake
new file mode 100644
index 0000000..9283054
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/imported-target.cmake
@@ -0,0 +1,18 @@
+
+enable_language(C)
+
+# Create imported target NS::lib
+add_library(NS::lib STATIC IMPORTED)
+
+# Create imported target NS::lib2
+add_library(NS::lib2 SHARED IMPORTED)
+
+set_target_properties(NS::lib2 PROPERTIES
+ IMPORTED_LOCATION "/path/to/lib"
+ IMPORTED_IMPLIB "/path/to/import.lib"
+ INTERFACE_LINK_LIBRARIES "$<LINK_LIBRARY:whole_archive,NS::lib>"
+)
+
+
+add_library(lib SHARED lib.c)
+target_link_libraries(lib PRIVATE NS::lib2)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c
new file mode 100644
index 0000000..35ab367
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/lib.c
@@ -0,0 +1,15 @@
+
+#if !defined(STATIC_BASE)
+# if defined(_WIN32)
+__declspec(dllimport)
+# endif
+#endif
+ void base();
+
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+ void lib()
+{
+ base();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c
new file mode 100644
index 0000000..601bd96
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.c
@@ -0,0 +1,18 @@
+
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+ void lib();
+
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+ void unref();
+
+int main()
+{
+ lib();
+ unref();
+
+ return 0;
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c
new file mode 100644
index 0000000..37c3206
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/unref.c
@@ -0,0 +1,8 @@
+
+
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+ void unref()
+{
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake
new file mode 100644
index 0000000..135326e
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/weak_library.cmake
@@ -0,0 +1,20 @@
+
+enable_language(C)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_LINK_USING_weak_library "PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}")
+ set(CMAKE_LINK_USING_weak_library_SUPPORTED TRUE)
+else()
+ # feature not yet supported for the other environments
+ set(CMAKE_LINK_USING_whole_library_SUPPORTED FALSE)
+endif()
+
+add_library(lib SHARED base.c lib.c unref.c)
+set_property(TARGET lib PROPERTY OUTPUT_NAME base)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE "$<LINK_LIBRARY:weak_library,lib>")
+
+add_executable(main2 main.c)
+target_link_directories(main2 PRIVATE "$<TARGET_FILE_DIR:lib>")
+target_link_libraries(main2 PRIVATE "$<LINK_LIBRARY:weak_library,base>")
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake
new file mode 100644
index 0000000..5214565
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/whole_archive.cmake
@@ -0,0 +1,34 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED TRUE)
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_C_LINK_USING_whole_archive "-force_load <LIB_ITEM>")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ execute_process(COMMAND "${CMAKE_LINKER}" --help
+ OUTPUT_VARIABLE linker_help
+ ERROR_VARIABLE linker_help)
+ if(linker_help MATCHES "--push-state" AND linker_help MATCHES "--pop-state")
+ set(CMAKE_C_LINK_USING_whole_archive "LINKER:--push-state,--whole-archive"
+ "<LINK_ITEM>"
+ "LINKER:--pop-state")
+ else()
+ set(CMAKE_C_LINK_USING_whole_archive "LINKER:--whole-archive"
+ "<LINK_ITEM>"
+ "LINKER:--no-whole-archive")
+ endif()
+elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_C_LINK_USING_whole_archive "/WHOLEARCHIVE:<LIBRARY>")
+else()
+ # feature not yet supported for the other environments
+ set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED FALSE)
+endif()
+
+add_library(base STATIC base.c unref.c)
+target_compile_definitions(base PUBLIC STATIC_BASE)
+
+add_library(lib SHARED lib.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:whole_archive,base>")
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)