Merge topic 'link-deduplicate-libs'

7b99c42e57 Link step: Enable to configure deduplication of libraries
07501c1678 Link Step: Introduce EntriesProcessing class

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Acked-by: scivision <michael@scivision.dev>
Merge-request: !8946
This commit is contained in:
Brad King
2023-11-13 14:51:31 +00:00
committed by Kitware Robot
125 changed files with 763 additions and 67 deletions
+209 -43
View File
@@ -16,6 +16,8 @@
#include <cm/string_view>
#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
#include "cmComputeComponentGraph.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
@@ -26,6 +28,7 @@
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmSourceFile.h"
#include "cmStateTypes.h"
@@ -232,6 +235,204 @@ bool IsGroupFeatureSupported(cmMakefile* makefile,
cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
return makefile->GetDefinition(featureSupported).IsOn();
}
class EntriesProcessing
{
public:
using LinkEntry = cmComputeLinkDepends::LinkEntry;
using EntryVector = cmComputeLinkDepends::EntryVector;
EntriesProcessing(const cmGeneratorTarget* target,
const std::string& linkLanguage, EntryVector& entries,
EntryVector& finalEntries)
: Entries(entries)
, FinalEntries(finalEntries)
{
const auto* makefile = target->Makefile;
switch (target->GetPolicyStatusCMP0156()) {
case cmPolicies::WARN:
if (!makefile->GetCMakeInstance()->GetIsInTryCompile() &&
makefile->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0156")) {
makefile->GetCMakeInstance()->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156),
"\nSince the policy is not set, legacy libraries "
"de-duplication strategy will be applied."),
target->GetBacktrace());
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
// rely on default initialization of the class
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
makefile->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR,
cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0156),
target->GetBacktrace());
CM_FALLTHROUGH;
case cmPolicies::NEW: {
if (auto libProcessing = makefile->GetDefinition(cmStrCat(
"CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) {
cmsys::RegularExpression processingOption{
"^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$"
};
std::string errorMessage;
for (auto const& option : cmList{ libProcessing }) {
if (processingOption.find(option)) {
if (processingOption.match(1) == "ORDER") {
if (processingOption.match(2) == "FORWARD") {
this->Order = Forward;
} else if (processingOption.match(2) == "REVERSE") {
this->Order = Reverse;
} else {
errorMessage += cmStrCat(" ", option, '\n');
}
} else if (processingOption.match(1) == "UNICITY") {
if (processingOption.match(2) == "ALL") {
this->Unicity = All;
} else if (processingOption.match(2) == "NONE") {
this->Unicity = None;
} else if (processingOption.match(2) == "SHARED") {
this->Unicity = Shared;
} else {
errorMessage += cmStrCat(" ", option, '\n');
}
}
} else {
errorMessage += cmStrCat(" ", option, '\n');
}
}
if (!errorMessage.empty()) {
makefile->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Erroneous option(s) for 'CMAKE_", linkLanguage,
"_LINK_LIBRARIES_PROCESSING':\n", errorMessage),
target->GetBacktrace());
}
}
}
}
}
void AddGroups(const std::map<size_t, std::vector<size_t>>& groups)
{
if (!groups.empty()) {
this->Groups = &groups;
// record all libraries as part of groups to ensure correct
// deduplication: libraries as part of groups are always kept.
for (const auto& group : groups) {
for (auto index : group.second) {
this->Emitted.insert(index);
}
}
}
}
void AddLibraries(const std::vector<size_t>& libEntries)
{
if (this->Order == Reverse) {
// Iterate in reverse order so we can keep only the last occurrence
// of a library.
this->AddLibraries(cmReverseRange(libEntries));
} else {
this->AddLibraries(cmMakeRange(libEntries));
}
}
void AddObjects(const std::vector<size_t>& objectEntries)
{
// Place explicitly linked object files in the front. The linker will
// always use them anyway, and they may depend on symbols from libraries.
if (this->Order == Reverse) {
// Append in reverse order at the end since we reverse the final order.
for (auto index : cmReverseRange(objectEntries)) {
this->FinalEntries.emplace_back(this->Entries[index]);
}
} else {
// Append in reverse order to ensure correct final order
for (auto index : cmReverseRange(objectEntries)) {
this->FinalEntries.emplace(this->FinalEntries.begin(),
this->Entries[index]);
}
}
}
void Finalize()
{
if (this->Order == Reverse) {
// Reverse the resulting order since we iterated in reverse.
std::reverse(this->FinalEntries.begin(), this->FinalEntries.end());
}
// expand groups
if (this->Groups != nullptr) {
for (const auto& group : *this->Groups) {
const LinkEntry& groupEntry = this->Entries[group.first];
auto it = this->FinalEntries.begin();
while (true) {
it = std::find_if(it, this->FinalEntries.end(),
[&groupEntry](const LinkEntry& entry) -> bool {
return groupEntry.Item == entry.Item;
});
if (it == this->FinalEntries.end()) {
break;
}
it->Item.Value = "</LINK_GROUP>";
for (auto index = group.second.rbegin();
index != group.second.rend(); ++index) {
it = this->FinalEntries.insert(it, this->Entries[*index]);
}
it = this->FinalEntries.insert(it, groupEntry);
it->Item.Value = "<LINK_GROUP>";
}
}
}
}
private:
enum OrderKind
{
Forward,
Reverse
};
enum UnicityKind
{
None,
Shared,
All
};
bool IncludeEntry(LinkEntry const& entry) const
{
return this->Unicity == None ||
(this->Unicity == Shared &&
(entry.Target == nullptr ||
entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) ||
(this->Unicity == All && entry.Kind != LinkEntry::Library);
}
template <typename Range>
void AddLibraries(const Range& libEntries)
{
for (auto index : libEntries) {
LinkEntry const& entry = this->Entries[index];
if (this->IncludeEntry(entry) || this->Emitted.insert(index).second) {
this->FinalEntries.emplace_back(entry);
}
}
}
OrderKind Order = Reverse;
UnicityKind Unicity = Shared;
EntryVector& Entries;
EntryVector& FinalEntries;
std::set<size_t> Emitted;
const std::map<size_t, std::vector<size_t>>* Groups = nullptr;
};
}
const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
@@ -380,49 +581,14 @@ cmComputeLinkDepends::Compute()
this->OrderLinkEntries();
// Compute the final set of link entries.
// Iterate in reverse order so we can keep only the last occurrence
// of a shared library.
std::set<size_t> emitted;
for (size_t i : cmReverseRange(this->FinalLinkOrder)) {
LinkEntry const& e = this->EntryList[i];
cmGeneratorTarget const* t = e.Target;
// Entries that we know the linker will re-use do not need to be repeated.
bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY;
if (!uniquify || emitted.insert(i).second) {
this->FinalLinkEntries.push_back(e);
}
}
// Place explicitly linked object files in the front. The linker will
// always use them anyway, and they may depend on symbols from libraries.
// Append in reverse order since we reverse the final order below.
for (size_t i : cmReverseRange(this->ObjectEntries)) {
this->FinalLinkEntries.emplace_back(this->EntryList[i]);
}
// 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>";
}
}
}
EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
this->EntryList,
this->FinalLinkEntries };
// Add groups first, to ensure that libraries of the groups are always kept.
entriesProcessing.AddGroups(this->GroupItems);
entriesProcessing.AddLibraries(this->FinalLinkOrder);
entriesProcessing.AddObjects(this->ObjectEntries);
entriesProcessing.Finalize();
// Display the final set.
if (this->DebugMode) {
+7 -2
View File
@@ -473,7 +473,11 @@ class cmMakefile;
SELECT(POLICY, CMP0155, \
"C++ sources in targets with at least C++20 are scanned for " \
"imports when supported.", \
3, 28, 0, cmPolicies::WARN)
3, 28, 0, cmPolicies::WARN) \
SELECT( \
POLICY, CMP0156, \
"De-duplicate libraries on link lines based on linker capabilities.", 3, \
29, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
@@ -513,7 +517,8 @@ class cmMakefile;
F(CMP0131) \
F(CMP0142) \
F(CMP0154) \
F(CMP0155)
F(CMP0155) \
F(CMP0156)
#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
F(CMP0116) \