mirror of
https://github.com/Kitware/CMake.git
synced 2026-03-13 21:09:59 -05:00
Genex-LINK_GROUP: Add possibility to group libraries at link step
Fixes: #23121
This commit is contained in:
@@ -1183,13 +1183,160 @@ Output-Related Expressions
|
||||
.. 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.
|
||||
will be kept grouped. So, to manage constructs like ``start-group`` and
|
||||
``end-group``, as supported by ``GNU ld``, the :genex:`LINK_GROUP`
|
||||
generator expression can be used.
|
||||
|
||||
``CMake`` pre-defines some features of general interest:
|
||||
|
||||
.. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
|
||||
|
||||
.. genex:: $<LINK_GROUP:feature,library-list>
|
||||
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Manage the grouping of libraries during the link step.
|
||||
This expression may be used to specify how to kept groups of libraries during
|
||||
the link of a target.
|
||||
For example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1 STATIC ...)
|
||||
add_library(lib2 ...)
|
||||
target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:cross_refs,lib1,external>")
|
||||
|
||||
This specify to use the ``lib1`` target and ``external`` library with the
|
||||
group feature ``cross_refs`` for linking target ``lib2``. The feature must
|
||||
have be defined by :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>`
|
||||
variable or, if :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
|
||||
is false, by :variable:`CMAKE_LINK_GROUP_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_GROUP_USING_<FEATURE>_SUPPORTED`
|
||||
* :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>`
|
||||
* :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
|
||||
* :variable:`CMAKE_LINK_GROUP_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.
|
||||
|
||||
.. note::
|
||||
|
||||
This expression is compatible with the :genex:`LINK_LIBRARY` generator
|
||||
expression. The libraries involved in a group can be specified using the
|
||||
:genex:`LINK_LIBRARY` generator expression.
|
||||
|
||||
Each target or external library involved in the link step can be part of
|
||||
different groups as far as these groups use the same feature, so mixing
|
||||
different group features for the same target or library is forbidden. The
|
||||
different groups will be part of the link step.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1 ...)
|
||||
add_library(lib2 ...)
|
||||
|
||||
add_library(lib3 ...)
|
||||
target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
|
||||
|
||||
add_library(lib4 ...)
|
||||
target_link_libraries(lib4 PRIVATE "$<LINK_GROUP:feature1,lib1,lib3>")
|
||||
# lib4 will be linked with the groups {lib1,lib2} and {lib1,lib3}
|
||||
|
||||
add_library(lib5 ...)
|
||||
target_link_libraries(lib5 PRIVATE "$<LINK_GROUP:feature2,lib1,lib3>")
|
||||
# an error will be raised here because lib1 is part of two groups with
|
||||
# different features
|
||||
|
||||
When a target or an external library is involved in the link step as part of
|
||||
a group and also as standalone, any occurrence of the standalone link item
|
||||
will be replaced by the group or groups it belong to.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1 ...)
|
||||
add_library(lib2 ...)
|
||||
|
||||
add_library(lib3 ...)
|
||||
target_link_libraries(lib3 PUBLIC lib1)
|
||||
|
||||
add_library(lib4 ...)
|
||||
target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>")
|
||||
# lib4 will only be linked with lib3 and the group {lib1,lib2}
|
||||
|
||||
This example will be "re-written" by ``CMake`` in the following form:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1 ...)
|
||||
add_library(lib2 ...)
|
||||
|
||||
add_library(lib3 ...)
|
||||
target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
|
||||
|
||||
add_library(lib4 ...)
|
||||
target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>")
|
||||
# lib4 will only be linked with lib3 and the group {lib1,lib2}
|
||||
|
||||
Be aware that the precedence of the group over the standalone link item can
|
||||
result in some circular dependency between groups, which will raise an
|
||||
error because circular dependencies are not allowed for groups.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1A ...)
|
||||
add_library(lib1B ...)
|
||||
|
||||
add_library(lib2A ...)
|
||||
add_library(lib2B ...)
|
||||
|
||||
target_link_libraries(lib1A PUBLIC lib2A)
|
||||
target_link_libraries(lib2B PUBLIC lib1B)
|
||||
|
||||
add_library(lib ...)
|
||||
target_link_libraries(lib3 PRIVATE "$<LINK_GROUP:feat,lib1A,lib1B>"
|
||||
"$<LINK_GROUP:feat,lib2A,lib2B>")
|
||||
|
||||
This example will be "re-written" by ``CMake`` in the following form:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(lib1A ...)
|
||||
add_library(lib1B ...)
|
||||
|
||||
add_library(lib2A ...)
|
||||
add_library(lib2B ...)
|
||||
|
||||
target_link_libraries(lib1A PUBLIC "$<LINK_GROUP:feat,lib2A,lib2B>")
|
||||
target_link_libraries(lib2B PUBLIC "$<LINK_GROUP:feat,lib1A,lib1B>")
|
||||
|
||||
add_library(lib ...)
|
||||
target_link_libraries(lib3 PRIVATE "$<LINK_GROUP:feat,lib1A,lib1B>"
|
||||
"$<LINK_GROUP:feat,lib2A,lib2B>")
|
||||
|
||||
So, we have a circular dependency between groups ``{lib1A,lib1B}`` and
|
||||
``{lib2A,lib2B}``.
|
||||
|
||||
.. 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_LINK_LIBRARY_USING_FEATURE
|
||||
/variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
|
||||
/variable/CMAKE_LANG_LINKER_LAUNCHER
|
||||
/variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE
|
||||
/variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE_SUPPORTED
|
||||
/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
|
||||
/variable/CMAKE_LANG_LINK_LIBRARY_FLAG
|
||||
/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
|
||||
@@ -449,6 +451,8 @@ Variables that Control the Build
|
||||
/variable/CMAKE_LIBRARY_PATH_FLAG
|
||||
/variable/CMAKE_LINK_DEF_FILE_FLAG
|
||||
/variable/CMAKE_LINK_DEPENDS_NO_SHARED
|
||||
/variable/CMAKE_LINK_GROUP_USING_FEATURE
|
||||
/variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED
|
||||
/variable/CMAKE_LINK_INTERFACE_LIBRARIES
|
||||
/variable/CMAKE_LINK_LIBRARY_FILE_FLAG
|
||||
/variable/CMAKE_LINK_LIBRARY_FLAG
|
||||
|
||||
8
Help/release/dev/Genex-LINK_GROUP.rst
Normal file
8
Help/release/dev/Genex-LINK_GROUP.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
Genex-LINK_GROUP
|
||||
----------------
|
||||
|
||||
* The :genex:`LINK_GROUP` generator expression was added to manage the grouping
|
||||
of libraries during the link step. The variables
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` and
|
||||
:variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` are used to define features
|
||||
usable by the :genex:`LINK_GROUP` generator expression.
|
||||
20
Help/variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE.rst
Normal file
20
Help/variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
CMAKE_<LANG>_LINK_GROUP_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_GROUP` generator expression.
|
||||
|
||||
.. note::
|
||||
|
||||
* Feature names can contain Latin letters, digits and undercores.
|
||||
* Feature names defined in all uppercase are reserved to CMake.
|
||||
|
||||
See also the associated variable
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` and
|
||||
:variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` variable for the definition of
|
||||
features independent from the link language.
|
||||
|
||||
.. include:: CMAKE_LINK_GROUP_USING_FEATURE.txt
|
||||
@@ -0,0 +1,13 @@
|
||||
CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED
|
||||
-------------------------------------------------
|
||||
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>`, is supported for the
|
||||
linker language ``<LANG>``.
|
||||
|
||||
.. note::
|
||||
|
||||
This variable is evaluated before the more generic variable
|
||||
:variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED`.
|
||||
@@ -9,7 +9,8 @@ using :genex:`LINK_LIBRARY` generator expression.
|
||||
|
||||
.. note::
|
||||
|
||||
Feature names defined in all uppercase are reserved to CMake.
|
||||
* Feature names can contain Latin letters, digits and undercores.
|
||||
* Feature names defined in all uppercase are reserved to CMake.
|
||||
|
||||
See also the associated variable
|
||||
:variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` and
|
||||
|
||||
25
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE.rst
Normal file
25
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
CMAKE_LINK_GROUP_USING_<FEATURE>
|
||||
--------------------------------
|
||||
|
||||
.. versionadded:: 3.24
|
||||
|
||||
This variable defines, for the specified ``<FEATURE>``, the expression expected
|
||||
by the linker when libraries are specified using :genex:`LINK_GROUP` generator
|
||||
expression.
|
||||
|
||||
.. note::
|
||||
|
||||
* Feature names can contain Latin letters, digits and undercores.
|
||||
* Feature names defined in all uppercase are reserved to CMake.
|
||||
|
||||
See also the associated variable
|
||||
:variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` and
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` variable for the definition
|
||||
of features dependent from the link language.
|
||||
|
||||
This variable will be used by :genex:`LINK_GROUP` generator expression if,
|
||||
for the linker language, the variable
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` is false or not
|
||||
set.
|
||||
|
||||
.. include:: CMAKE_LINK_GROUP_USING_FEATURE.txt
|
||||
54
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE.txt
Normal file
54
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
It must contain two elements.
|
||||
|
||||
::
|
||||
|
||||
<PREFIX> <SUFFIX>
|
||||
|
||||
``<PREFIX>`` and ``<SUFFIX>`` will be used to encapsulate the list of
|
||||
libraries.
|
||||
|
||||
For the elements of this variable, the ``LINKER:`` prefix can be used:
|
||||
|
||||
.. include:: ../command/LINK_OPTIONS_LINKER.txt
|
||||
:start-line: 3
|
||||
|
||||
Examples
|
||||
^^^^^^^^
|
||||
|
||||
Solving cross-references between two static libraries
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
A common need is the capability to search repeatedly in a group of static
|
||||
libraries until no new undefined references are created. This capability is
|
||||
offered by different environments but with a specific syntax:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_cross_refs_SUPPORTED TRUE)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU"
|
||||
AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(CMAKE_C_LINK_GROUP_USING_cross_refs "LINKER:--start-group"
|
||||
"LINKER:--end-group")
|
||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "SunPro"
|
||||
AND CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
set(CMAKE_C_LINK_GROUP_USING_cross_refs "LINKER:-z,rescan-start"
|
||||
"LINKER:-z,rescan-end")
|
||||
else()
|
||||
# feature not yet supported for the other environments
|
||||
set(CMAKE_C_LINK_GROUP_USING_cross_refs_SUPPORTED FALSE)
|
||||
endif()
|
||||
|
||||
add_library(lib1 STATIC ...)
|
||||
|
||||
add_library(lib3 SHARED ...)
|
||||
if(CMAKE_C_LINK_GROUP_USING_cross_refs_SUPPORTED)
|
||||
target_link_libraries(lib3 PRIVATE "$<LINK_GROUP:cross_refs,lib1,external>")
|
||||
else()
|
||||
target_link_libraries(lib3 PRIVATE lib1 external)
|
||||
endif()
|
||||
|
||||
CMake will generate the following link expressions:
|
||||
|
||||
* ``GNU``: ``-Wl,--start-group /path/to/lib1.a -lexternal -Wl,--end-group``
|
||||
* ``SunPro``: ``-Wl,-z,rescan-start /path/to/lib1.a -lexternal -Wl,-z,rescan-end``
|
||||
14
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED.rst
Normal file
14
Help/variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED
|
||||
------------------------------------------
|
||||
|
||||
.. versionadded:: 3.24
|
||||
|
||||
Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
|
||||
:variable:`CMAKE_LINK_GROUP_USING_<FEATURE>`, is supported regardless the
|
||||
linker language.
|
||||
|
||||
.. note::
|
||||
|
||||
This variable is evaluated if, and only if, the variable
|
||||
:variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` evaluates to
|
||||
``FALSE``.
|
||||
@@ -9,7 +9,8 @@ using :genex:`LINK_LIBRARY` generator expression.
|
||||
|
||||
.. note::
|
||||
|
||||
Feature names defined in all uppercase are reserved to CMake.
|
||||
* Feature names can contain Latin letters, digits and undercores.
|
||||
* Feature names defined in all uppercase are reserved to CMake.
|
||||
|
||||
See also the associated variable
|
||||
:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` and
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
return 1;
|
||||
|
||||
std::filesystem::path p0(L"/a/b/c");
|
||||
|
||||
std::filesystem::path p1("/a/b/c");
|
||||
|
||||
@@ -180,6 +180,7 @@ items that we know the linker will re-use automatically (shared libs).
|
||||
*/
|
||||
|
||||
namespace {
|
||||
// LINK_LIBRARY helpers
|
||||
const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
|
||||
const auto LL_END = "</LINK_LIBRARY:"_s;
|
||||
|
||||
@@ -202,6 +203,31 @@ bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
|
||||
cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature, "_SUPPORTED");
|
||||
return makefile->GetDefinition(featureSupported).IsOn();
|
||||
}
|
||||
|
||||
// LINK_GROUP helpers
|
||||
const auto LG_BEGIN = "<LINK_GROUP:"_s;
|
||||
const auto LG_END = "</LINK_GROUP:"_s;
|
||||
|
||||
inline std::string ExtractGroupFeature(std::string const& item)
|
||||
{
|
||||
return item.substr(LG_BEGIN.length(),
|
||||
item.find(':', LG_BEGIN.length()) - LG_BEGIN.length());
|
||||
}
|
||||
|
||||
bool IsGroupFeatureSupported(cmMakefile* makefile,
|
||||
std::string const& linkLanguage,
|
||||
std::string const& feature)
|
||||
{
|
||||
auto featureSupported = cmStrCat(
|
||||
"CMAKE_", linkLanguage, "_LINK_GROUP_USING_", feature, "_SUPPORTED");
|
||||
if (makefile->GetDefinition(featureSupported).IsOn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
featureSupported =
|
||||
cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
|
||||
return makefile->GetDefinition(featureSupported).IsOn();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
|
||||
@@ -311,6 +337,11 @@ cmComputeLinkDepends::Compute()
|
||||
// Infer dependencies of targets for which they were not known.
|
||||
this->InferDependencies();
|
||||
|
||||
// finalize groups dependencies
|
||||
// All dependencies which are raw items must be replaced by the group
|
||||
// it belongs to, if any.
|
||||
this->UpdateGroupDependencies();
|
||||
|
||||
// Cleanup the constraint graph.
|
||||
this->CleanConstraintGraph();
|
||||
|
||||
@@ -325,6 +356,19 @@ cmComputeLinkDepends::Compute()
|
||||
this->DisplayConstraintGraph();
|
||||
}
|
||||
|
||||
// Compute the DAG of strongly connected components. The algorithm
|
||||
// used by cmComputeComponentGraph should identify the components in
|
||||
// the same order in which the items were originally discovered in
|
||||
// the BFS. This should preserve the original order when no
|
||||
// constraints disallow it.
|
||||
this->CCG =
|
||||
cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
|
||||
this->CCG->Compute();
|
||||
|
||||
if (!this->CheckCircularDependencies()) {
|
||||
return this->FinalLinkEntries;
|
||||
}
|
||||
|
||||
// Compute the final ordering.
|
||||
this->OrderLinkEntries();
|
||||
|
||||
@@ -350,6 +394,29 @@ cmComputeLinkDepends::Compute()
|
||||
// Reverse the resulting order since we iterated in reverse.
|
||||
std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
|
||||
|
||||
// Expand group items
|
||||
if (!this->GroupItems.empty()) {
|
||||
for (const auto& group : this->GroupItems) {
|
||||
const LinkEntry& groupEntry = this->EntryList[group.first];
|
||||
auto it = this->FinalLinkEntries.begin();
|
||||
while (true) {
|
||||
it = std::find_if(it, this->FinalLinkEntries.end(),
|
||||
[&groupEntry](const LinkEntry& entry) -> bool {
|
||||
return groupEntry.Item == entry.Item;
|
||||
});
|
||||
if (it == this->FinalLinkEntries.end()) {
|
||||
break;
|
||||
}
|
||||
it->Item.Value = "</LINK_GROUP>";
|
||||
for (auto i = group.second.rbegin(); i != group.second.rend(); ++i) {
|
||||
it = this->FinalLinkEntries.insert(it, this->EntryList[*i]);
|
||||
}
|
||||
it = this->FinalLinkEntries.insert(it, groupEntry);
|
||||
it->Item.Value = "<LINK_GROUP>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the final set.
|
||||
if (this->DebugMode) {
|
||||
this->DisplayFinalEntries();
|
||||
@@ -379,7 +446,8 @@ cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
|
||||
return lei;
|
||||
}
|
||||
|
||||
std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
|
||||
std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item,
|
||||
int groupIndex)
|
||||
{
|
||||
// Allocate a spot for the item entry.
|
||||
auto lei = this->AllocateLinkEntry(item);
|
||||
@@ -399,23 +467,28 @@ std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
|
||||
entry.Item.Value[1] != 'l' &&
|
||||
entry.Item.Value.substr(0, 10) != "-framework") {
|
||||
entry.Kind = LinkEntry::Flag;
|
||||
} else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) &&
|
||||
cmHasSuffix(entry.Item.Value, '>')) {
|
||||
entry.Kind = LinkEntry::Group;
|
||||
}
|
||||
|
||||
// If the item has dependencies queue it to follow them.
|
||||
if (entry.Target) {
|
||||
// Target dependencies are always known. Follow them.
|
||||
BFSEntry qe = { index, nullptr };
|
||||
this->BFSQueue.push(qe);
|
||||
} else {
|
||||
// Look for an old-style <item>_LIB_DEPENDS variable.
|
||||
std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
|
||||
if (cmValue val = this->Makefile->GetDefinition(var)) {
|
||||
// The item dependencies are known. Follow them.
|
||||
BFSEntry qe = { index, val->c_str() };
|
||||
if (entry.Kind != LinkEntry::Group) {
|
||||
// If the item has dependencies queue it to follow them.
|
||||
if (entry.Target) {
|
||||
// Target dependencies are always known. Follow them.
|
||||
BFSEntry qe = { index, groupIndex, nullptr };
|
||||
this->BFSQueue.push(qe);
|
||||
} else if (entry.Kind != LinkEntry::Flag) {
|
||||
// The item dependencies are not known. We need to infer them.
|
||||
this->InferredDependSets[index].Initialized = true;
|
||||
} else {
|
||||
// Look for an old-style <item>_LIB_DEPENDS variable.
|
||||
std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
|
||||
if (cmValue val = this->Makefile->GetDefinition(var)) {
|
||||
// The item dependencies are known. Follow them.
|
||||
BFSEntry qe = { index, groupIndex, val->c_str() };
|
||||
this->BFSQueue.push(qe);
|
||||
} else if (entry.Kind != LinkEntry::Flag) {
|
||||
// The item dependencies are not known. We need to infer them.
|
||||
this->InferredDependSets[index].Initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,8 +518,8 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
|
||||
void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe)
|
||||
{
|
||||
// Get this entry representation.
|
||||
int depender_index = qe.Index;
|
||||
LinkEntry const& entry = this->EntryList[depender_index];
|
||||
int depender_index = qe.GroupIndex == -1 ? qe.Index : qe.GroupIndex;
|
||||
LinkEntry const& entry = this->EntryList[qe.Index];
|
||||
|
||||
// Follow the item's dependencies.
|
||||
if (entry.Target) {
|
||||
@@ -628,6 +701,10 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
|
||||
std::map<int, DependSet> dependSets;
|
||||
std::string feature = LinkEntry::DEFAULT;
|
||||
|
||||
bool inGroup = false;
|
||||
std::pair<int, bool> groupIndex{ -1, false };
|
||||
std::vector<int> groupItems;
|
||||
|
||||
// Loop over the libraries linked directly by the depender.
|
||||
for (T const& l : libs) {
|
||||
// Skip entries that will resolve to the target getting linked or
|
||||
@@ -636,6 +713,7 @@ 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());
|
||||
@@ -666,12 +744,82 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
|
||||
cmHasSuffix(item.AsStr(), '>')) {
|
||||
groupIndex = this->AddLinkEntry(item);
|
||||
if (groupIndex.second) {
|
||||
LinkEntry& entry = this->EntryList[groupIndex.first];
|
||||
entry.Feature = ExtractGroupFeature(item.AsStr());
|
||||
}
|
||||
inGroup = true;
|
||||
if (depender_index >= 0) {
|
||||
this->EntryConstraintGraph[depender_index].emplace_back(
|
||||
groupIndex.first, false, false, cmListFileBacktrace());
|
||||
} else {
|
||||
// This is a direct dependency of the target being linked.
|
||||
this->OriginalEntries.push_back(groupIndex.first);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int dependee_index;
|
||||
|
||||
if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) {
|
||||
dependee_index = groupIndex.first;
|
||||
if (groupIndex.second) {
|
||||
this->GroupItems.emplace(groupIndex.first, groupItems);
|
||||
}
|
||||
inGroup = false;
|
||||
groupIndex = std::make_pair(-1, false);
|
||||
groupItems.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depender_index >= 0 && inGroup) {
|
||||
const auto& depender = this->EntryList[depender_index];
|
||||
const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
|
||||
if (depender.Target != nullptr && depender.Target->IsImported() &&
|
||||
!IsGroupFeatureSupported(this->Makefile, this->LinkLanguage,
|
||||
groupFeature)) {
|
||||
this->CMakeInstance->IssueMessage(
|
||||
MessageType::AUTHOR_ERROR,
|
||||
cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
|
||||
"' uses the generator-expression '$<LINK_GROUP>' with "
|
||||
"the feature '",
|
||||
groupFeature,
|
||||
"', which is undefined or unsupported.\nDid you miss to "
|
||||
"define it by setting variables \"CMAKE_",
|
||||
this->LinkLanguage, "_LINK_GROUP_USING_", groupFeature,
|
||||
"\" and \"CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_",
|
||||
groupFeature, "_SUPPORTED\"?"),
|
||||
this->Target->GetBacktrace());
|
||||
}
|
||||
}
|
||||
|
||||
// Add a link entry for this item.
|
||||
auto ale = this->AddLinkEntry(item);
|
||||
int dependee_index = ale.first;
|
||||
auto ale = this->AddLinkEntry(item, groupIndex.first);
|
||||
dependee_index = ale.first;
|
||||
LinkEntry& entry = this->EntryList[dependee_index];
|
||||
auto const& itemFeature =
|
||||
this->GetCurrentFeature(entry.Item.Value, feature);
|
||||
if (inGroup && ale.second && entry.Target != nullptr &&
|
||||
(entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
|
||||
entry.Target->GetType() ==
|
||||
cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
|
||||
const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
|
||||
this->CMakeInstance->IssueMessage(
|
||||
MessageType::AUTHOR_WARNING,
|
||||
cmStrCat(
|
||||
"The feature '", groupFeature,
|
||||
"', specified as part of a generator-expression "
|
||||
"'$",
|
||||
LG_BEGIN, groupFeature, ">', will not be applied to the ",
|
||||
(entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY
|
||||
? "OBJECT"
|
||||
: "INTERFACE"),
|
||||
" library '", entry.Item.Value, "'."),
|
||||
this->Target->GetBacktrace());
|
||||
}
|
||||
if (itemFeature != LinkEntry::DEFAULT) {
|
||||
if (ale.second) {
|
||||
// current item not yet defined
|
||||
@@ -702,50 +850,97 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
|
||||
(entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
|
||||
entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
|
||||
|
||||
if (supportedItem && entry.Feature != itemFeature) {
|
||||
// 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 ",
|
||||
(itemFeature == LinkEntry::DEFAULT
|
||||
? "without any feature or 'DEFAULT' feature"
|
||||
: cmStrCat("with the feature '", itemFeature, '\'')),
|
||||
", has already occurred ",
|
||||
(entry.Feature == LinkEntry::DEFAULT
|
||||
? "without any feature or 'DEFAULT' 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) {
|
||||
this->EntryConstraintGraph[depender_index].emplace_back(
|
||||
dependee_index, false, false, cmListFileBacktrace());
|
||||
} else {
|
||||
// This is a direct dependency of the target being linked.
|
||||
this->OriginalEntries.push_back(dependee_index);
|
||||
}
|
||||
|
||||
// Update the inferred dependencies for earlier items.
|
||||
for (auto& dependSet : dependSets) {
|
||||
// Add this item to the inferred dependencies of other items.
|
||||
// Target items are never inferred dependees because unknown
|
||||
// items are outside libraries that should not be depending on
|
||||
// targets.
|
||||
if (!this->EntryList[dependee_index].Target &&
|
||||
this->EntryList[dependee_index].Kind != LinkEntry::Flag &&
|
||||
dependee_index != dependSet.first) {
|
||||
dependSet.second.insert(dependee_index);
|
||||
if (supportedItem) {
|
||||
if (inGroup) {
|
||||
const auto& currentFeature = this->EntryList[groupIndex.first].Feature;
|
||||
for (const auto& g : this->GroupItems) {
|
||||
const auto& groupFeature = this->EntryList[g.first].Feature;
|
||||
if (groupFeature == currentFeature) {
|
||||
continue;
|
||||
}
|
||||
if (std::find(g.second.cbegin(), g.second.cend(), dependee_index) !=
|
||||
g.second.cend()) {
|
||||
this->CMakeInstance->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
cmStrCat("Impossible to link target '", this->Target->GetName(),
|
||||
"' because the link item '", entry.Item.Value,
|
||||
"', specified with the group feature '", currentFeature,
|
||||
'\'', ", has already occurred with the feature '",
|
||||
groupFeature, '\'', ", which is not allowed."),
|
||||
this->Target->GetBacktrace());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.Feature != itemFeature) {
|
||||
// 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 ",
|
||||
(itemFeature == LinkEntry::DEFAULT
|
||||
? "without any feature or 'DEFAULT' feature"
|
||||
: cmStrCat("with the feature '", itemFeature, '\'')),
|
||||
", has already occurred ",
|
||||
(entry.Feature == LinkEntry::DEFAULT
|
||||
? "without any feature or 'DEFAULT' feature"
|
||||
: cmStrCat("with the feature '", entry.Feature, '\'')),
|
||||
", which is not allowed."),
|
||||
this->Target->GetBacktrace());
|
||||
}
|
||||
}
|
||||
|
||||
// If this item needs to have dependencies inferred, do so.
|
||||
if (this->InferredDependSets[dependee_index].Initialized) {
|
||||
// Make sure an entry exists to hold the set for the item.
|
||||
dependSets[dependee_index];
|
||||
if (inGroup) {
|
||||
// store item index for dependencies handling
|
||||
groupItems.push_back(dependee_index);
|
||||
} else {
|
||||
std::vector<int> indexes;
|
||||
bool entryHandled = false;
|
||||
// search any occurrence of the library in already defined groups
|
||||
for (const auto& group : this->GroupItems) {
|
||||
for (auto index : group.second) {
|
||||
if (entry.Item.Value == this->EntryList[index].Item.Value) {
|
||||
indexes.push_back(group.first);
|
||||
entryHandled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!entryHandled) {
|
||||
indexes.push_back(dependee_index);
|
||||
}
|
||||
|
||||
for (auto index : indexes) {
|
||||
// The dependee must come after the depender.
|
||||
if (depender_index >= 0) {
|
||||
this->EntryConstraintGraph[depender_index].emplace_back(
|
||||
index, false, false, cmListFileBacktrace());
|
||||
} else {
|
||||
// This is a direct dependency of the target being linked.
|
||||
this->OriginalEntries.push_back(index);
|
||||
}
|
||||
|
||||
// Update the inferred dependencies for earlier items.
|
||||
for (auto& dependSet : dependSets) {
|
||||
// Add this item to the inferred dependencies of other items.
|
||||
// Target items are never inferred dependees because unknown
|
||||
// items are outside libraries that should not be depending on
|
||||
// targets.
|
||||
if (!this->EntryList[index].Target &&
|
||||
this->EntryList[index].Kind != LinkEntry::Flag &&
|
||||
this->EntryList[index].Kind != LinkEntry::Group &&
|
||||
dependee_index != dependSet.first) {
|
||||
dependSet.second.insert(index);
|
||||
}
|
||||
}
|
||||
|
||||
// If this item needs to have dependencies inferred, do so.
|
||||
if (this->InferredDependSets[index].Initialized) {
|
||||
// Make sure an entry exists to hold the set for the item.
|
||||
dependSets[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,6 +1003,36 @@ void cmComputeLinkDepends::InferDependencies()
|
||||
}
|
||||
}
|
||||
|
||||
void cmComputeLinkDepends::UpdateGroupDependencies()
|
||||
{
|
||||
if (this->GroupItems.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walks through all entries of the constraint graph to replace dependencies
|
||||
// over raw items by the group it belongs to, if any.
|
||||
for (auto& edgeList : this->EntryConstraintGraph) {
|
||||
for (auto& edge : edgeList) {
|
||||
int index = edge;
|
||||
if (this->EntryList[index].Kind == LinkEntry::Group ||
|
||||
this->EntryList[index].Kind == LinkEntry::Flag ||
|
||||
this->EntryList[index].Kind == LinkEntry::Object) {
|
||||
continue;
|
||||
}
|
||||
// search the item in the defined groups
|
||||
for (const auto& groupItems : this->GroupItems) {
|
||||
auto pos = std::find(groupItems.second.cbegin(),
|
||||
groupItems.second.cend(), index);
|
||||
if (pos != groupItems.second.cend()) {
|
||||
// replace lib dependency by the group it belongs to
|
||||
edge = cmGraphEdge{ groupItems.first, false, false,
|
||||
cmListFileBacktrace() };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmComputeLinkDepends::CleanConstraintGraph()
|
||||
{
|
||||
for (cmGraphEdgeList& edgeList : this->EntryConstraintGraph) {
|
||||
@@ -821,6 +1046,76 @@ void cmComputeLinkDepends::CleanConstraintGraph()
|
||||
}
|
||||
}
|
||||
|
||||
bool cmComputeLinkDepends::CheckCircularDependencies() const
|
||||
{
|
||||
std::vector<NodeList> const& components = this->CCG->GetComponents();
|
||||
int nc = static_cast<int>(components.size());
|
||||
for (int c = 0; c < nc; ++c) {
|
||||
// Get the current component.
|
||||
NodeList const& nl = components[c];
|
||||
|
||||
// Skip trivial components.
|
||||
if (nl.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// no group must be evolved
|
||||
bool cycleDetected = false;
|
||||
for (int ni : nl) {
|
||||
if (this->EntryList[ni].Kind == LinkEntry::Group) {
|
||||
cycleDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cycleDetected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Construct the error message.
|
||||
auto formatItem = [](LinkEntry const& entry) -> std::string {
|
||||
if (entry.Kind == LinkEntry::Group) {
|
||||
auto items =
|
||||
entry.Item.Value.substr(entry.Item.Value.find(':', 12) + 1);
|
||||
items.pop_back();
|
||||
std::replace(items.begin(), items.end(), '|', ',');
|
||||
return cmStrCat("group \"", ExtractGroupFeature(entry.Item.Value),
|
||||
":{", items, "}\"");
|
||||
}
|
||||
return cmStrCat('"', entry.Item.Value, '"');
|
||||
};
|
||||
|
||||
std::ostringstream e;
|
||||
e << "The inter-target dependency graph, for the target \""
|
||||
<< this->Target->GetName()
|
||||
<< "\", contains the following strongly connected component "
|
||||
"(cycle):\n";
|
||||
std::vector<int> const& cmap = this->CCG->GetComponentMap();
|
||||
for (int i : nl) {
|
||||
// Get the depender.
|
||||
LinkEntry const& depender = this->EntryList[i];
|
||||
|
||||
// Describe the depender.
|
||||
e << " " << formatItem(depender) << "\n";
|
||||
|
||||
// List its dependencies that are inside the component.
|
||||
EdgeList const& el = this->EntryConstraintGraph[i];
|
||||
for (cmGraphEdge const& ni : el) {
|
||||
int j = ni;
|
||||
if (cmap[j] == c) {
|
||||
LinkEntry const& dependee = this->EntryList[j];
|
||||
e << " depends on " << formatItem(dependee) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
|
||||
this->Target->GetBacktrace());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cmComputeLinkDepends::DisplayConstraintGraph()
|
||||
{
|
||||
// Display the graph nodes and their edges.
|
||||
@@ -835,15 +1130,6 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
|
||||
|
||||
void cmComputeLinkDepends::OrderLinkEntries()
|
||||
{
|
||||
// Compute the DAG of strongly connected components. The algorithm
|
||||
// used by cmComputeComponentGraph should identify the components in
|
||||
// the same order in which the items were originally discovered in
|
||||
// the BFS. This should preserve the original order when no
|
||||
// constraints disallow it.
|
||||
this->CCG =
|
||||
cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph);
|
||||
this->CCG->Compute();
|
||||
|
||||
// The component graph is guaranteed to be acyclic. Start a DFS
|
||||
// from every entry to compute a topological order for the
|
||||
// components.
|
||||
@@ -1033,11 +1319,18 @@ int cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
|
||||
void cmComputeLinkDepends::DisplayFinalEntries()
|
||||
{
|
||||
fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
|
||||
char space[] = " ";
|
||||
int count = 2;
|
||||
for (LinkEntry const& lei : this->FinalLinkEntries) {
|
||||
if (lei.Target) {
|
||||
fprintf(stderr, " target [%s]", lei.Target->GetName().c_str());
|
||||
if (lei.Kind == LinkEntry::Group) {
|
||||
fprintf(stderr, " %s group",
|
||||
lei.Item.Value == "<LINK_GROUP>" ? "start" : "end");
|
||||
count = lei.Item.Value == "<LINK_GROUP>" ? 4 : 2;
|
||||
} else if (lei.Target) {
|
||||
fprintf(stderr, "%*starget [%s]", count, space,
|
||||
lei.Target->GetName().c_str());
|
||||
} else {
|
||||
fprintf(stderr, " item [%s]", lei.Item.Value.c_str());
|
||||
fprintf(stderr, "%*sitem [%s]", count, space, lei.Item.Value.c_str());
|
||||
}
|
||||
if (lei.Feature != LinkEntry::DEFAULT) {
|
||||
fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
|
||||
|
||||
@@ -54,7 +54,10 @@ public:
|
||||
Library,
|
||||
Object,
|
||||
SharedDep,
|
||||
Flag
|
||||
Flag,
|
||||
// The following member is for the management of items specified
|
||||
// through genex $<LINK_GROUP:...>
|
||||
Group
|
||||
};
|
||||
|
||||
BT<std::string> Item;
|
||||
@@ -90,7 +93,8 @@ private:
|
||||
|
||||
std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
|
||||
cmLinkItem const& item);
|
||||
std::pair<int, bool> AddLinkEntry(cmLinkItem const& item);
|
||||
std::pair<int, bool> AddLinkEntry(cmLinkItem const& item,
|
||||
int groupIndex = -1);
|
||||
void AddLinkObject(cmLinkItem const& item);
|
||||
void AddVarLinkEntries(int depender_index, const char* value);
|
||||
void AddDirectLinkEntries();
|
||||
@@ -103,10 +107,14 @@ private:
|
||||
std::vector<LinkEntry> EntryList;
|
||||
std::map<cmLinkItem, int> LinkEntryIndex;
|
||||
|
||||
// map storing, for each group, the list of items
|
||||
std::map<int, std::vector<int>> GroupItems;
|
||||
|
||||
// BFS of initial dependencies.
|
||||
struct BFSEntry
|
||||
{
|
||||
int Index;
|
||||
int GroupIndex;
|
||||
const char* LibDepends;
|
||||
};
|
||||
std::queue<BFSEntry> BFSQueue;
|
||||
@@ -139,12 +147,16 @@ private:
|
||||
std::vector<DependSetList> InferredDependSets;
|
||||
void InferDependencies();
|
||||
|
||||
// To finalize dependencies over groups in place of raw items
|
||||
void UpdateGroupDependencies();
|
||||
|
||||
// Ordering constraint graph adjacency list.
|
||||
using NodeList = cmGraphNodeList;
|
||||
using EdgeList = cmGraphEdgeList;
|
||||
using Graph = cmGraphAdjacencyList;
|
||||
Graph EntryConstraintGraph;
|
||||
void CleanConstraintGraph();
|
||||
bool CheckCircularDependencies() const;
|
||||
void DisplayConstraintGraph();
|
||||
|
||||
// Ordering algorithm.
|
||||
|
||||
@@ -350,21 +350,23 @@ cmComputeLinkInformation::cmComputeLinkInformation(
|
||||
if (!this->GetLibLinkFileFlag().empty()) {
|
||||
this->LibraryFeatureDescriptors.emplace(
|
||||
"__CMAKE_LINK_LIBRARY",
|
||||
FeatureDescriptor{ "__CMAKE_LINK_LIBRARY",
|
||||
cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") });
|
||||
LibraryFeatureDescriptor{
|
||||
"__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>") });
|
||||
LibraryFeatureDescriptor{
|
||||
"__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>") });
|
||||
LibraryFeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE",
|
||||
cmStrCat(this->LoaderFlag, "<LIBRARY>") });
|
||||
}
|
||||
|
||||
// Check the platform policy for missing soname case.
|
||||
@@ -544,6 +546,19 @@ bool cmComputeLinkInformation::Compute()
|
||||
|
||||
// Add the link line items.
|
||||
for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
|
||||
if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::Group) {
|
||||
const auto& groupFeature = this->GetGroupFeature(linkEntry.Feature);
|
||||
if (groupFeature.Supported) {
|
||||
this->Items.emplace_back(
|
||||
BT<std::string>{ linkEntry.Item.Value == "<LINK_GROUP>"
|
||||
? groupFeature.Prefix
|
||||
: groupFeature.Suffix,
|
||||
linkEntry.Item.Backtrace },
|
||||
ItemIsPath::No);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentFeature != nullptr &&
|
||||
linkEntry.Feature != currentFeature->Name) {
|
||||
// emit feature suffix, if any
|
||||
@@ -664,6 +679,117 @@ bool IsValidFeatureFormat(const std::string& format)
|
||||
format.find("<LIB_ITEM>") != std::string::npos ||
|
||||
format.find("<LINK_ITEM>") != std::string::npos;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
}
|
||||
cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor(
|
||||
std::string name, std::string prefix, std::string suffix, bool)
|
||||
: Name(std::move(name))
|
||||
, Supported(true)
|
||||
, Prefix(std::move(prefix))
|
||||
, Suffix(std::move(suffix))
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
|
||||
std::string name, std::string itemFormat)
|
||||
: FeatureDescriptor(std::move(name), std::move(itemFormat))
|
||||
{
|
||||
}
|
||||
cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
|
||||
std::string name, std::string itemPathFormat, std::string itemNameFormat)
|
||||
: FeatureDescriptor(std::move(name), std::move(itemPathFormat),
|
||||
std::move(itemNameFormat))
|
||||
{
|
||||
}
|
||||
cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor(
|
||||
std::string name, std::string prefix, std::string itemPathFormat,
|
||||
std::string itemNameFormat, std::string suffix)
|
||||
: FeatureDescriptor(std::move(name), std::move(prefix),
|
||||
std::move(itemPathFormat), std::move(itemNameFormat),
|
||||
std::move(suffix))
|
||||
{
|
||||
}
|
||||
|
||||
bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature)
|
||||
@@ -792,12 +918,13 @@ bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature)
|
||||
|
||||
if (items.size() == 2) {
|
||||
this->LibraryFeatureDescriptors.emplace(
|
||||
feature, FeatureDescriptor{ feature, items[0].Value, items[1].Value });
|
||||
feature,
|
||||
LibraryFeatureDescriptor{ 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 });
|
||||
LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value,
|
||||
items[2].Value, items[3].Value });
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -819,89 +946,80 @@ cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class FeaturePlaceHolderExpander : public cmPlaceholderExpander
|
||||
cmComputeLinkInformation::GroupFeatureDescriptor::GroupFeatureDescriptor(
|
||||
std::string name, std::string prefix, std::string suffix)
|
||||
: FeatureDescriptor(std::move(name), std::move(prefix), std::move(suffix),
|
||||
true)
|
||||
{
|
||||
public:
|
||||
FeaturePlaceHolderExpander(const std::string* library,
|
||||
const std::string* libItem = nullptr,
|
||||
const std::string* linkItem = nullptr)
|
||||
: Library(library)
|
||||
, LibItem(libItem)
|
||||
, LinkItem(linkItem)
|
||||
{
|
||||
}
|
||||
|
||||
cmComputeLinkInformation::FeatureDescriptor const&
|
||||
cmComputeLinkInformation::GetGroupFeature(std::string const& feature)
|
||||
{
|
||||
auto it = this->GroupFeatureDescriptors.find(feature);
|
||||
if (it != this->GroupFeatureDescriptors.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
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;
|
||||
auto featureName =
|
||||
cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_", feature);
|
||||
cmValue featureSupported =
|
||||
this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
|
||||
if (!featureSupported.IsOn()) {
|
||||
featureName = cmStrCat("CMAKE_LINK_GROUP_USING_", feature);
|
||||
featureSupported =
|
||||
this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
|
||||
}
|
||||
if (!featureSupported.IsOn()) {
|
||||
this->CMakeInstance->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
cmStrCat("Feature '", feature,
|
||||
"', specified through generator-expression '$<LINK_GROUP>' to "
|
||||
"link target '",
|
||||
this->Target->GetName(), "', is not supported for the '",
|
||||
this->LinkLanguage, "' link language."),
|
||||
this->Target->GetBacktrace());
|
||||
return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
const std::string* Library = nullptr;
|
||||
const std::string* LibItem = nullptr;
|
||||
const std::string* LinkItem = nullptr;
|
||||
};
|
||||
}
|
||||
cmValue langFeature = this->Makefile->GetDefinition(featureName);
|
||||
if (!langFeature) {
|
||||
this->CMakeInstance->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
cmStrCat("Feature '", feature,
|
||||
"', specified through generator-expression '$<LINK_GROUP>' to "
|
||||
"link target '",
|
||||
this->Target->GetName(), "', is not defined for the '",
|
||||
this->LinkLanguage, "' link language."),
|
||||
this->Target->GetBacktrace());
|
||||
return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
}
|
||||
auto items =
|
||||
cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true);
|
||||
|
||||
std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem(
|
||||
std::string const& library, ItemIsPath isPath) const
|
||||
{
|
||||
auto format =
|
||||
isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat;
|
||||
// replace LINKER: pattern
|
||||
this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true);
|
||||
|
||||
// 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;
|
||||
if (items.size() == 2) {
|
||||
return this->GroupFeatureDescriptors
|
||||
.emplace(
|
||||
feature,
|
||||
GroupFeatureDescriptor{ feature, items[0].Value, items[1].Value })
|
||||
.first->second;
|
||||
}
|
||||
|
||||
// replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns
|
||||
FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem);
|
||||
return expander.ExpandVariables(format);
|
||||
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 this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
void cmComputeLinkInformation::AddImplicitLinkInfo()
|
||||
|
||||
@@ -255,12 +255,6 @@ private:
|
||||
{
|
||||
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;
|
||||
@@ -273,13 +267,44 @@ private:
|
||||
std::string const& defaultValue,
|
||||
ItemIsPath isPath) const;
|
||||
|
||||
protected:
|
||||
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);
|
||||
|
||||
FeatureDescriptor(std::string name, std::string prefix, std::string suffix,
|
||||
bool isGroup);
|
||||
|
||||
private:
|
||||
std::string ItemPathFormat;
|
||||
std::string ItemNameFormat;
|
||||
};
|
||||
|
||||
class LibraryFeatureDescriptor : public FeatureDescriptor
|
||||
{
|
||||
public:
|
||||
LibraryFeatureDescriptor(std::string name, std::string itemFormat);
|
||||
LibraryFeatureDescriptor(std::string name, std::string itemPathFormat,
|
||||
std::string itemNameFormat);
|
||||
LibraryFeatureDescriptor(std::string name, std::string prefix,
|
||||
std::string itemPathFormat,
|
||||
std::string itemNameFormat, std::string suffix);
|
||||
};
|
||||
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;
|
||||
|
||||
class GroupFeatureDescriptor : public FeatureDescriptor
|
||||
{
|
||||
public:
|
||||
GroupFeatureDescriptor(std::string name, std::string prefix,
|
||||
std::string suffix);
|
||||
};
|
||||
std::map<std::string, FeatureDescriptor> GroupFeatureDescriptors;
|
||||
FeatureDescriptor const& GetGroupFeature(std::string const& feature);
|
||||
};
|
||||
|
||||
@@ -1231,7 +1231,15 @@ static const struct LinkLibraryNode : public cmGeneratorExpressionNode
|
||||
return std::string();
|
||||
}
|
||||
|
||||
static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
|
||||
auto const& feature = list.front();
|
||||
if (!featureNameValidator.find(feature)) {
|
||||
reportError(context, content->GetOriginalExpression(),
|
||||
cmStrCat("The feature name '", feature,
|
||||
"' contains invalid characters."));
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
|
||||
const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
|
||||
|
||||
@@ -1252,6 +1260,17 @@ static const struct LinkLibraryNode : public cmGeneratorExpressionNode
|
||||
"$<LINK_LIBRARY:...> with different features cannot be nested.");
|
||||
return std::string();
|
||||
}
|
||||
// $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...>
|
||||
it = std::find_if(list.cbegin() + 1, list.cend(),
|
||||
[](const std::string& item) -> bool {
|
||||
return cmHasPrefix(item, "<LINK_GROUP:"_s);
|
||||
});
|
||||
if (it != list.cend()) {
|
||||
reportError(context, content->GetOriginalExpression(),
|
||||
"$<LINK_GROUP:...> cannot be nested inside a "
|
||||
"$<LINK_LIBRARY:...> expression.");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
list.front() = LL_BEGIN;
|
||||
list.push_back(LL_END);
|
||||
@@ -1260,6 +1279,71 @@ static const struct LinkLibraryNode : public cmGeneratorExpressionNode
|
||||
}
|
||||
} linkLibraryNode;
|
||||
|
||||
static const struct LinkGroupNode : public cmGeneratorExpressionNode
|
||||
{
|
||||
LinkGroupNode() {} // 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_GROUP:...> may only be used with binary targets "
|
||||
"to specify group of link libraries.");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> list;
|
||||
cmExpandLists(parameters.begin(), parameters.end(), list);
|
||||
if (list.empty()) {
|
||||
reportError(
|
||||
context, content->GetOriginalExpression(),
|
||||
"$<LINK_GROUP:...> expects a feature name as first argument.");
|
||||
return std::string();
|
||||
}
|
||||
// $<LINK_GROUP:..> cannot be nested
|
||||
if (std::find_if(list.cbegin(), list.cend(),
|
||||
[](const std::string& item) -> bool {
|
||||
return cmHasPrefix(item, "<LINK_GROUP"_s);
|
||||
}) != list.cend()) {
|
||||
reportError(context, content->GetOriginalExpression(),
|
||||
"$<LINK_GROUP:...> cannot be nested.");
|
||||
return std::string();
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
// no libraries specified, ignore this genex
|
||||
return std::string();
|
||||
}
|
||||
|
||||
static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
|
||||
auto const& feature = list.front();
|
||||
if (!featureNameValidator.find(feature)) {
|
||||
reportError(context, content->GetOriginalExpression(),
|
||||
cmStrCat("The feature name '", feature,
|
||||
"' contains invalid characters."));
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const auto LG_BEGIN = cmStrCat(
|
||||
"<LINK_GROUP:", feature, ':',
|
||||
cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()),
|
||||
"|"_s),
|
||||
'>');
|
||||
const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>');
|
||||
|
||||
list.front() = LG_BEGIN;
|
||||
list.push_back(LG_END);
|
||||
|
||||
return cmJoin(list, ";"_s);
|
||||
}
|
||||
} linkGroupNode;
|
||||
|
||||
static const struct HostLinkNode : public cmGeneratorExpressionNode
|
||||
{
|
||||
HostLinkNode() {} // NOLINT(modernize-use-equals-default)
|
||||
@@ -2731,6 +2815,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
|
||||
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
|
||||
{ "LINK_LANGUAGE", &linkLanguageNode },
|
||||
{ "LINK_LIBRARY", &linkLibraryNode },
|
||||
{ "LINK_GROUP", &linkGroupNode },
|
||||
{ "HOST_LINK", &hostLinkNode },
|
||||
{ "DEVICE_LINK", &deviceLinkNode },
|
||||
{ "SHELL_PATH", &shellPathNode }
|
||||
|
||||
@@ -6341,7 +6341,8 @@ cm::string_view missingTargetPossibleReasons =
|
||||
bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role,
|
||||
cmLinkItem const& item) const
|
||||
{
|
||||
if (item.Target || item.AsStr().find("::") == std::string::npos) {
|
||||
if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) ||
|
||||
item.AsStr().find("::") == std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
MessageType messageType = MessageType::FATAL_ERROR;
|
||||
@@ -6388,7 +6389,8 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
|
||||
if (!str.empty() &&
|
||||
(str[0] == '-' || str[0] == '$' || str[0] == '`' ||
|
||||
str.find_first_of("/\\") != std::string::npos ||
|
||||
cmHasPrefix(str, "<LINK_LIBRARY:"_s))) {
|
||||
cmHasPrefix(str, "<LINK_LIBRARY:"_s) ||
|
||||
cmHasPrefix(str, "<LINK_GROUP:"_s))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3988,21 +3988,21 @@ void cmMakefile::CheckProperty(const std::string& prop) const
|
||||
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)) {
|
||||
static cmsys::RegularExpression linkPattern(
|
||||
"(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)");
|
||||
if (!linkPattern.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."));
|
||||
cmStrCat("Property ", prop, " contains the invalid item \"",
|
||||
linkPattern.match(2), "\". The ", prop,
|
||||
" property may contain the generator-expression \"$<LINK_",
|
||||
linkPattern.match(3),
|
||||
":...>\" which may be used to specify how the libraries are "
|
||||
"linked."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1788,20 +1788,21 @@ void CheckLinkLibraryPattern(const std::string& property,
|
||||
const std::string& value, cmMakefile* context)
|
||||
{
|
||||
// Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags
|
||||
static cmsys::RegularExpression linkLibrary(
|
||||
"(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
|
||||
if (!linkLibrary.find(value)) {
|
||||
static cmsys::RegularExpression linkPattern(
|
||||
"(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)");
|
||||
if (!linkPattern.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."));
|
||||
cmStrCat(
|
||||
"Property ", property, " contains the invalid item \"",
|
||||
linkPattern.match(2), "\". The ", property,
|
||||
" property may contain the generator-expression \"$<LINK_",
|
||||
linkPattern.match(3),
|
||||
":...>\" which may be used to specify how the libraries are linked."));
|
||||
}
|
||||
|
||||
void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
|
||||
|
||||
@@ -314,6 +314,7 @@ 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-LINK_GROUP)
|
||||
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)
|
||||
@@ -670,6 +671,18 @@ add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE
|
||||
-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(target_link_libraries-LINK_GROUP -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
|
||||
-DMINGW=${MINGW}
|
||||
-DMSYS=${MSYS}
|
||||
-DCYGWIN=${CYGWIN}
|
||||
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
|
||||
-DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
|
||||
-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})
|
||||
|
||||
3
Tests/RunCMake/GenEx-LINK_GROUP/CMakeLists.txt
Normal file
3
Tests/RunCMake/GenEx-LINK_GROUP/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.18...3.22)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
35
Tests/RunCMake/GenEx-LINK_GROUP/RunCMakeTest.cmake
Normal file
35
Tests/RunCMake/GenEx-LINK_GROUP/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
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(nested-incompatible-genex)
|
||||
run_cmake(invalid-feature)
|
||||
run_cmake(bad-feature1)
|
||||
run_cmake(bad-feature2)
|
||||
run_cmake(bad-feature3)
|
||||
run_cmake(bad-feature4)
|
||||
run_cmake(bad-feature5)
|
||||
run_cmake(feature-not-supported)
|
||||
run_cmake(library-ignored)
|
||||
run_cmake(compatible-features1)
|
||||
run_cmake(compatible-features2)
|
||||
run_cmake(compatible-features3)
|
||||
run_cmake(incompatible-features1)
|
||||
run_cmake(nested-incompatible-features1)
|
||||
run_cmake(nested-incompatible-features2)
|
||||
run_cmake(circular-dependencies1)
|
||||
run_cmake(circular-dependencies2)
|
||||
run_cmake(only-targets)
|
||||
|
||||
# usage of LINK_LIBRARY with LINK_GROUP
|
||||
run_cmake(incompatible-library-features1)
|
||||
run_cmake(incompatible-library-features2)
|
||||
run_cmake(override-library-features1)
|
||||
run_cmake(override-library-features2)
|
||||
@@ -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_GROUP:feat>
|
||||
|
||||
\$<LINK_GROUP:...> may only be used with binary targets to specify group of
|
||||
link libraries.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
4
Tests/RunCMake/GenEx-LINK_GROUP/add_custom_command.cmake
Normal file
4
Tests/RunCMake/GenEx-LINK_GROUP/add_custom_command.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
add_custom_target(drive)
|
||||
add_custom_command(TARGET drive PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_GROUP: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_GROUP:feat>
|
||||
|
||||
\$<LINK_GROUP:...> may only be used with binary targets to specify group of
|
||||
link libraries.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
3
Tests/RunCMake/GenEx-LINK_GROUP/add_custom_target.cmake
Normal file
3
Tests/RunCMake/GenEx-LINK_GROUP/add_custom_target.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
add_custom_target(drive
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_GROUP: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_GROUP:feat>
|
||||
|
||||
\$<LINK_GROUP:...> may only be used with binary targets to specify group of
|
||||
link libraries.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
5
Tests/RunCMake/GenEx-LINK_GROUP/add_link_options.cmake
Normal file
5
Tests/RunCMake/GenEx-LINK_GROUP/add_link_options.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
enable_language(C)
|
||||
|
||||
add_link_options("$<LINK_GROUP:feat>")
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1-stderr.txt
Normal file
5
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
CMake Error at bad-feature1.cmake:[0-9]+ \(add_library\):
|
||||
Feature 'bad_feat', specified through generator-expression '\$<LINK_GROUP>'
|
||||
to link target 'lib', is not supported for the 'C' link language.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1.cmake
Normal file
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature1.cmake
Normal file
@@ -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_GROUP:bad_feat,dep>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2-stderr.txt
Normal file
5
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
CMake Error at bad-feature2.cmake:[0-9]+ \(add_library\):
|
||||
Feature 'feat', specified through generator-expression '\$<LINK_GROUP>' to
|
||||
link target 'lib', is not defined for the 'C' link language.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
8
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2.cmake
Normal file
8
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature2.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
|
||||
add_library(dep SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat,dep>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3-stderr.txt
Normal file
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3-stderr.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
CMake Error at bad-feature3.cmake:[0-9]+ \(add_library\):
|
||||
Feature 'feat', specified by variable 'CMAKE_C_LINK_GROUP_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\)
|
||||
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3.cmake
Normal file
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature3.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "")
|
||||
|
||||
add_library(dep SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat,dep>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4-stderr.txt
Normal file
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4-stderr.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
CMake Error at bad-feature4.cmake:[0-9]+ \(add_library\):
|
||||
Feature 'feat', specified by variable 'CMAKE_C_LINK_GROUP_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\)
|
||||
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4.cmake
Normal file
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature4.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "-opt")
|
||||
|
||||
add_library(dep SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat,dep>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5-stderr.txt
Normal file
6
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5-stderr.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
CMake Error at bad-feature5.cmake:[0-9]+ \(add_library\):
|
||||
Feature 'feat', specified by variable 'CMAKE_C_LINK_GROUP_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\)
|
||||
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5.cmake
Normal file
9
Tests/RunCMake/GenEx-LINK_GROUP/bad-feature5.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "-start" "<LIBRARY>" "-stop")
|
||||
|
||||
add_library(dep SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat,dep>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,11 @@
|
||||
CMake Error at circular-dependencies1.cmake:[0-9]+ \(add_library\):
|
||||
The inter-target dependency graph, for the target "lib1", contains the
|
||||
following strongly connected component \(cycle\):
|
||||
|
||||
group "feat:{dep1.1,dep1.2}"
|
||||
depends on group "feat:{dep2.1,dep2.2}"
|
||||
group "feat:{dep2.1,dep2.2}"
|
||||
depends on group "feat:{dep1.1,dep1.2}"
|
||||
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
17
Tests/RunCMake/GenEx-LINK_GROUP/circular-dependencies1.cmake
Normal file
17
Tests/RunCMake/GenEx-LINK_GROUP/circular-dependencies1.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--stop")
|
||||
|
||||
add_library(dep1.1 SHARED empty.c)
|
||||
add_library(dep1.2 SHARED empty.c)
|
||||
|
||||
add_library(dep2.1 SHARED empty.c)
|
||||
add_library(dep2.2 SHARED empty.c)
|
||||
|
||||
target_link_libraries(dep1.1 PUBLIC dep2.1)
|
||||
target_link_libraries(dep2.2 PUBLIC dep1.2)
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PRIVATE "$<LINK_GROUP:feat,dep1.1,dep1.2>"
|
||||
"$<LINK_GROUP:feat,dep2.1,dep2.2>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,11 @@
|
||||
CMake Error at circular-dependencies2.cmake:[0-9]+ \(add_library\):
|
||||
The inter-target dependency graph, for the target "lib2", contains the
|
||||
following strongly connected component \(cycle\):
|
||||
|
||||
group "feat:{base3,base4}"
|
||||
depends on group "feat:{base1,base2}"
|
||||
group "feat:{base1,base2}"
|
||||
depends on group "feat:{base3,base4}"
|
||||
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
18
Tests/RunCMake/GenEx-LINK_GROUP/circular-dependencies2.cmake
Normal file
18
Tests/RunCMake/GenEx-LINK_GROUP/circular-dependencies2.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--stop")
|
||||
|
||||
add_library(base1 SHARED empty.c)
|
||||
add_library(base2 SHARED empty.c)
|
||||
add_library(base3 SHARED empty.c)
|
||||
add_library(base4 SHARED empty.c)
|
||||
|
||||
target_link_libraries(base1 PUBLIC base3)
|
||||
target_link_libraries(base4 PUBLIC base2)
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PUBLIC "$<LINK_GROUP:feat,base1,base2>")
|
||||
|
||||
add_library(lib2 SHARED empty.c)
|
||||
target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:feat,base3,base4>" lib1)
|
||||
18
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features1.cmake
Normal file
18
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features1.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1 "--start" "--stop")
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2 "--start" "--stop")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
|
||||
add_library(dep2 SHARED empty.c)
|
||||
target_link_libraries(dep2 PRIVATE "$<LINK_GROUP:feat1,dep1>")
|
||||
|
||||
add_library(dep3 SHARED empty.c)
|
||||
target_link_libraries(dep3 PUBLIC dep2)
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PRIVATE $<LINK_GROUP:feat2,dep1,dep2>)
|
||||
13
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features2.cmake
Normal file
13
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features2.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--stop")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
add_library(dep2 SHARED empty.c)
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PUBLIC "$<LINK_GROUP:feat,dep1,dep2>")
|
||||
|
||||
add_library(lib2 SHARED empty.c)
|
||||
target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:feat,dep2,lib1>")
|
||||
13
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features3.cmake
Normal file
13
Tests/RunCMake/GenEx-LINK_GROUP/compatible-features3.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--stop")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
add_library(dep2 SHARED empty.c)
|
||||
target_link_libraries(dep2 PUBLIC dep1)
|
||||
add_library(dep3 SHARED empty.c)
|
||||
target_link_libraries(dep3 PUBLIC dep1)
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PUBLIC "$<LINK_GROUP:feat,dep1,dep2>" dep3)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,8 @@
|
||||
CMake Error at empty-arguments.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_GROUP:,>
|
||||
|
||||
\$<LINK_GROUP:...> expects a feature name as first argument.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
4
Tests/RunCMake/GenEx-LINK_GROUP/empty-arguments.cmake
Normal file
4
Tests/RunCMake/GenEx-LINK_GROUP/empty-arguments.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:,>")
|
||||
0
Tests/RunCMake/GenEx-LINK_GROUP/empty.c
Normal file
0
Tests/RunCMake/GenEx-LINK_GROUP/empty.c
Normal file
@@ -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_GROUP>' 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_GROUP_USING_feat_SUPPORTED FALSE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--end")
|
||||
|
||||
add_library(dep SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP: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_GROUP:feat>". The
|
||||
LINK_LIBRARIES property may contain the generator-expression
|
||||
"\$<LINK_GROUP:...>" 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_GROUP:feat>". The
|
||||
LINK_LIBRARIES property may contain the generator-expression
|
||||
"\$<LINK_GROUP:...>" 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_GROUP:feat> foo </LINK_GROUP:feat>)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE <LINK_GROUP:feat> foo </LINK_GROUP:feat>)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
CMake Error at incompatible-features1.cmake:[0-9]+ \(add_library\):
|
||||
Impossible to link target 'lib1' because the link item 'dep2', specified
|
||||
with the group feature 'feat1', has already occurred with the feature
|
||||
'feat2', which is not allowed.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
15
Tests/RunCMake/GenEx-LINK_GROUP/incompatible-features1.cmake
Normal file
15
Tests/RunCMake/GenEx-LINK_GROUP/incompatible-features1.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1 "--start" "--stop")
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2 "--start" "--stop")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
add_library(dep2 SHARED empty.c)
|
||||
add_library(dep3 SHARED empty.c)
|
||||
target_link_libraries(dep3 PUBLIC "$<LINK_GROUP:feat1,dep1,dep2>")
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PRIVATE "$<LINK_GROUP:feat2,dep2,dep3>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
CMake Error at incompatible-library-features1.cmake:[0-9]+ \(add_library\):
|
||||
Impossible to link target 'lib1' because the link item 'dep1', specified
|
||||
with the feature 'feat1', has already occurred without any feature or
|
||||
'DEFAULT' feature, which is not allowed.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1,17 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1 "--start" "--stop")
|
||||
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat1 "--libflag1<LIBRARY>")
|
||||
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat2_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat2 "--libflag2<LIBRARY>")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
add_library(dep2 SHARED empty.c)
|
||||
target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PRIVATE "$<LINK_GROUP:feat1,$<LINK_LIBRARY:feat2,dep2>,dep1>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
CMake Error at incompatible-library-features2.cmake:[0-9]+ \(add_library\):
|
||||
Impossible to link target 'lib1' 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,17 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1 "--start" "--stop")
|
||||
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat1 "--libflag1<LIBRARY>")
|
||||
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat2_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_LIBRARY_USING_feat2 "--libflag2<LIBRARY>")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
add_library(dep2 SHARED empty.c)
|
||||
target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
|
||||
|
||||
add_library(lib1 SHARED empty.c)
|
||||
target_link_libraries(lib1 PRIVATE "$<LINK_GROUP:feat1,$<LINK_LIBRARY:feat2,dep2,dep1>>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,8 @@
|
||||
CMake Error at invalid-feature.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_GROUP:feat:invalid,dep>
|
||||
|
||||
The feature name 'feat:invalid' contains invalid characters.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
6
Tests/RunCMake/GenEx-LINK_GROUP/invalid-feature.cmake
Normal file
6
Tests/RunCMake/GenEx-LINK_GROUP/invalid-feature.cmake
Normal file
@@ -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_GROUP:feat:invalid,dep>")
|
||||
13
Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt
Normal file
13
Tests/RunCMake/GenEx-LINK_GROUP/library-ignored-stderr.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
|
||||
The feature 'feat', specified as part of a generator-expression
|
||||
'\$<LINK_GROUP: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_GROUP: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.
|
||||
15
Tests/RunCMake/GenEx-LINK_GROUP/library-ignored.cmake
Normal file
15
Tests/RunCMake/GenEx-LINK_GROUP/library-ignored.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--end")
|
||||
|
||||
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_GROUP: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_GROUP:feat>
|
||||
|
||||
\$<LINK_GROUP:...> may only be used with binary targets to specify group of
|
||||
link libraries.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
5
Tests/RunCMake/GenEx-LINK_GROUP/link_directories.cmake
Normal file
5
Tests/RunCMake/GenEx-LINK_GROUP/link_directories.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
enable_language(C)
|
||||
|
||||
link_directories("$<LINK_GROUP:feat>")
|
||||
|
||||
add_library(empty SHARED empty.c)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,8 @@
|
||||
CMake Error at nested-incompatible-features1.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_GROUP:feat,dep1,\$<LINK_GROUP:feat,dep2>>
|
||||
|
||||
\$<LINK_GROUP:...> cannot be nested.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
||||
@@ -0,0 +1,11 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--end")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
|
||||
add_library(dep2 SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat,dep1,$<LINK_GROUP:feat,dep2>>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,8 @@
|
||||
CMake Error at nested-incompatible-features2.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_GROUP:feat1,dep1,\$<LINK_GROUP:feat2,dep2>>
|
||||
|
||||
\$<LINK_GROUP:...> 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_GROUP_USING_feat1_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat1 "--start" "--end")
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat2 "--start" "--end")
|
||||
|
||||
add_library(dep1 SHARED empty.c)
|
||||
|
||||
add_library(dep2 SHARED empty.c)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP:feat1,dep1,$<LINK_GROUP:feat2,dep2>>")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,18 @@
|
||||
CMake Error at nested-incompatible-genex.cmake:[0-9]+ \(add_library\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_LIBRARY:feat,\$<LINK_GROUP:feat,foo>>
|
||||
|
||||
\$<LINK_GROUP:...> cannot be nested inside a \$<LINK_LIBRARY:...> expression.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Error at nested-incompatible-genex.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_LIBRARY:feat,\$<LINK_GROUP:feat,foo>>
|
||||
|
||||
\$<LINK_GROUP:...> cannot be nested inside a \$<LINK_LIBRARY:...> expression.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1,6 @@
|
||||
enable_language(C)
|
||||
|
||||
link_libraries("$<LINK_LIBRARY:feat,$<LINK_GROUP:feat,foo>>")
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,$<LINK_GROUP:feat,foo>>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
8
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments-stderr.txt
Normal file
8
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments-stderr.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
CMake Error at no-arguments.cmake:[0-9]+ \(target_link_libraries\):
|
||||
Error evaluating generator expression:
|
||||
|
||||
\$<LINK_GROUP>
|
||||
|
||||
\$<LINK_GROUP> expression requires at least one parameter.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:[0-9]+ \(include\)
|
||||
4
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments.cmake
Normal file
4
Tests/RunCMake/GenEx-LINK_GROUP/no-arguments.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(lib SHARED empty.c)
|
||||
target_link_libraries(lib PRIVATE "$<LINK_GROUP>")
|
||||
1
Tests/RunCMake/GenEx-LINK_GROUP/only-targets-result.txt
Normal file
1
Tests/RunCMake/GenEx-LINK_GROUP/only-targets-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
13
Tests/RunCMake/GenEx-LINK_GROUP/only-targets-stderr.txt
Normal file
13
Tests/RunCMake/GenEx-LINK_GROUP/only-targets-stderr.txt
Normal file
@@ -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\)
|
||||
16
Tests/RunCMake/GenEx-LINK_GROUP/only-targets.cmake
Normal file
16
Tests/RunCMake/GenEx-LINK_GROUP/only-targets.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
enable_language(C)
|
||||
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat_SUPPORTED TRUE)
|
||||
set(CMAKE_C_LINK_GROUP_USING_feat "--start" "--end")
|
||||
|
||||
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_GROUP:feat,dep1>")
|
||||
|
||||
add_library(lib2 SHARED empty.c)
|
||||
# invalid
|
||||
target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:feat,external>")
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
include(incompatible-library-features1.cmake)
|
||||
|
||||
set_property(TARGET lib1 PROPERTY LINK_LIBRARY_OVERRIDE "feat1,dep1")
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
include(incompatible-library-features2.cmake)
|
||||
|
||||
set_property(TARGET lib1 PROPERTY LINK_LIBRARY_OVERRIDE_dep1 "feat1")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user