Genex-LINK_GROUP: Add possibility to group libraries at link step

Fixes: #23121
This commit is contained in:
Marc Chevrier
2022-01-31 16:43:41 +01:00
parent a9928eb4a5
commit 0a81ea1f12
134 changed files with 1883 additions and 186 deletions

View File

@@ -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)`,

View File

@@ -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

View 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.

View 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

View File

@@ -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`.

View File

@@ -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

View 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

View 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``

View 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``.

View File

@@ -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

View File

@@ -3,6 +3,8 @@
int main()
{
return 1;
std::filesystem::path p0(L"/a/b/c");
std::filesystem::path p1("/a/b/c");

View File

@@ -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());

View File

@@ -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.

View File

@@ -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()

View File

@@ -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);
};

View File

@@ -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 }

View File

@@ -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;
}

View File

@@ -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."));
}
}
}

View File

@@ -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,

View File

@@ -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})

View File

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

View 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)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -0,0 +1,4 @@
add_custom_target(drive)
add_custom_command(TARGET drive PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_GROUP:feat>"
)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -0,0 +1,3 @@
add_custom_target(drive
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_GROUP:feat>"
)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -0,0 +1,5 @@
enable_language(C)
add_link_options("$<LINK_GROUP:feat>")
add_library(empty SHARED empty.c)

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -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\)

View 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>")

View File

@@ -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\)

View 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)

View 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>)

View 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>")

View 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)

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -0,0 +1,4 @@
enable_language(C)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_GROUP:,>")

View File

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -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>")

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -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>)

View File

@@ -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\)

View 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>")

View File

@@ -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\)

View File

@@ -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>")

View File

@@ -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\)

View File

@@ -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>>")

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View 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>")

View 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.

View 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>")

View File

@@ -0,0 +1 @@
1

View File

@@ -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\)

View File

@@ -0,0 +1,5 @@
enable_language(C)
link_directories("$<LINK_GROUP:feat>")
add_library(empty SHARED empty.c)

View File

@@ -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\)

View File

@@ -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>>")

View File

@@ -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\)

View File

@@ -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>>")

View File

@@ -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\)

View File

@@ -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>>")

View File

@@ -0,0 +1 @@
1

View 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\)

View File

@@ -0,0 +1,4 @@
enable_language(C)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_GROUP>")

View File

@@ -0,0 +1 @@
1

View 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\)

View 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>")

View File

@@ -0,0 +1,4 @@
include(incompatible-library-features1.cmake)
set_property(TARGET lib1 PROPERTY LINK_LIBRARY_OVERRIDE "feat1,dep1")

View File

@@ -0,0 +1,4 @@
include(incompatible-library-features2.cmake)
set_property(TARGET lib1 PROPERTY LINK_LIBRARY_OVERRIDE_dep1 "feat1")

Some files were not shown because too many files have changed in this diff Show More