summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorMarc Chevrier <marc.chevrier@gmail.com>2022-01-31 15:43:41 (GMT)
committerMarc Chevrier <marc.chevrier@gmail.com>2022-02-28 09:26:26 (GMT)
commit0a81ea1f12cbaf60ec60b8e4a27c5ea476a655de (patch)
tree8491b1ae05b5c252b1165244c6976d81c87397ee /Source
parenta9928eb4a54431780d589f70460e5715258f1d27 (diff)
downloadCMake-0a81ea1f12cbaf60ec60b8e4a27c5ea476a655de.zip
CMake-0a81ea1f12cbaf60ec60b8e4a27c5ea476a655de.tar.gz
CMake-0a81ea1f12cbaf60ec60b8e4a27c5ea476a655de.tar.bz2
Genex-LINK_GROUP: Add possibility to group libraries at link step
Fixes: #23121
Diffstat (limited to 'Source')
-rw-r--r--Source/Checks/cm_cxx_filesystem.cxx2
-rw-r--r--Source/cmComputeLinkDepends.cxx433
-rw-r--r--Source/cmComputeLinkDepends.h16
-rw-r--r--Source/cmComputeLinkInformation.cxx284
-rw-r--r--Source/cmComputeLinkInformation.h37
-rw-r--r--Source/cmGeneratorExpressionNode.cxx85
-rw-r--r--Source/cmGeneratorTarget.cxx6
-rw-r--r--Source/cmMakefile.cxx18
-rw-r--r--Source/cmTarget.cxx17
9 files changed, 718 insertions, 180 deletions
diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx
index ae8acc5..b7d5be5 100644
--- a/Source/Checks/cm_cxx_filesystem.cxx
+++ b/Source/Checks/cm_cxx_filesystem.cxx
@@ -3,6 +3,8 @@
int main()
{
+ return 1;
+
std::filesystem::path p0(L"/a/b/c");
std::filesystem::path p1("/a/b/c");
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 7c8de9e..a4dc01b 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -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());
+ 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());
+ }
}
- // The dependee must come after the depender.
- if (depender_index >= 0) {
- this->EntryConstraintGraph[depender_index].emplace_back(
- dependee_index, false, false, cmListFileBacktrace());
+ if (inGroup) {
+ // store item index for dependencies handling
+ groupItems.push_back(dependee_index);
} 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);
+ 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[dependee_index].Initialized) {
- // Make sure an entry exists to hold the set for the item.
- dependSets[dependee_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());
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 3c83f5a..8cc916a 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -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.
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index fe4491d..67214f1 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -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)
- {
- }
+}
-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;
- }
+cmComputeLinkInformation::FeatureDescriptor const&
+cmComputeLinkInformation::GetGroupFeature(std::string const& feature)
+{
+ auto it = this->GroupFeatureDescriptors.find(feature);
+ if (it != this->GroupFeatureDescriptors.end()) {
+ return it->second;
+ }
- 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()
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 4b7fb1a..a4ada1f 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -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);
};
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index b63b90b..db043ec 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -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 }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index a246c12..e893b44 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -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;
}
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 6a5d518..be189a6 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -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."));
}
}
}
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 92a0ac4..4ca1b9b 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -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,