mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-26 04:09:35 +03:00
Genex: Add $<LINK_LIBRARY:...>
This generator expression offers the capability, for the link step, to decorate libraries with prefix/suffix flags and/or adding any specific flag for each library. Fixes: #22812, #18751, #20078, #22703
This commit is contained in:
@@ -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)`,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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`.
|
||||
@@ -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
|
||||
@@ -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``
|
||||
@@ -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``.
|
||||
+143
-34
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
|
||||
mf.AppendProperty("LINK_LIBRARIES", *i);
|
||||
}
|
||||
|
||||
mf.CheckProperty("LINK_LIBRARIES");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 << " ";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+81
-55
@@ -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.";
|
||||
// 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 " << 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());
|
||||
}
|
||||
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
||||
|
||||
CheckLinkLibraryPattern(base, value, context);
|
||||
}
|
||||
|
||||
static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value,
|
||||
cmMakefile* context)
|
||||
void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
|
||||
{
|
||||
CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
|
||||
}
|
||||
|
||||
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)) {
|
||||
return;
|
||||
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());
|
||||
}
|
||||
|
||||
// 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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.18...3.22)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.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)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,4 @@
|
||||
add_custom_target(drive)
|
||||
add_custom_command(TARGET drive PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,3 @@
|
||||
add_custom_target(drive
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,5 @@
|
||||
enable_language(C)
|
||||
|
||||
add_link_options("$<LINK_LIBRARY:feat>")
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:,>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -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.
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,5 @@
|
||||
enable_language(C)
|
||||
|
||||
link_directories("$<LINK_LIBRARY:feat>")
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
@@ -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>>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
target_link_directories(empty PRIVATE "$<LINK_LIBRARY:feat>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
target_link_options(empty PRIVATE $<LINK_LIBRARY:FEAT>)
|
||||
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.1...3.22)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
.*
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
.*
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
.*
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user