summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-01-31 15:36:42 (GMT)
committerKitware Robot <kwrobot@kitware.com>2022-01-31 15:36:55 (GMT)
commit5305d5aa1a6900c64a5833176b43a21acb13fb30 (patch)
tree8af9de023d9e278904d849709c78535f257eea0b /Source
parente7c300e3567a394bcc8e90ddccf46faa68d3afae (diff)
parentf3ad061858d03cc2a0569f903df3560152106050 (diff)
downloadCMake-5305d5aa1a6900c64a5833176b43a21acb13fb30.zip
CMake-5305d5aa1a6900c64a5833176b43a21acb13fb30.tar.gz
CMake-5305d5aa1a6900c64a5833176b43a21acb13fb30.tar.bz2
Merge topic 'link-interface-direct'
f3ad061858 Add usage requirements to update direct link dependencies 193a999cd5 cmTarget: Add INTERFACE_LINK_LIBRARIES_DIRECT{,_EXCLUDE} backtrace storage 22d5427aa6 cmGeneratorTarget: Add LookupLinkItem option to consider own target name f3d2eab36a cmGeneratorTarget: Fix link interface caching of partial results d75ab9d066 cmGeneratorTarget: Clarify CMP0022 logic in ComputeLinkInterfaceLibraries f3e9e03fe0 cmGeneratorTarget: Simplify CMP0022 warning check 216aa14997 cmGeneratorTarget: Return early from ExpandLinkItems with no items 1bc98371d1 Tests: Remove unnecessary policy setting from ObjectLibrary test ... Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !6886
Diffstat (limited to 'Source')
-rw-r--r--Source/cmExportFileGenerator.cxx27
-rw-r--r--Source/cmExportTryCompileFileGenerator.cxx2
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx8
-rw-r--r--Source/cmGeneratorTarget.cxx350
-rw-r--r--Source/cmGeneratorTarget.h23
-rw-r--r--Source/cmLinkItem.cxx4
-rw-r--r--Source/cmLinkItem.h10
-rw-r--r--Source/cmLocalGenerator.cxx5
-rw-r--r--Source/cmTarget.cxx59
-rw-r--r--Source/cmTarget.h2
10 files changed, 375 insertions, 115 deletions
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 896240c..412d104 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmExportFileGenerator.h"
+#include <array>
#include <cassert>
#include <cstring>
#include <sstream>
@@ -175,18 +176,24 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
if (!target->IsLinkable()) {
return false;
}
- cmValue input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
- if (input) {
- std::string prepro =
- cmGeneratorExpression::Preprocess(*input, preprocessRule);
- if (!prepro.empty()) {
- this->ResolveTargetsInGeneratorExpressions(
- prepro, target, missingTargets, ReplaceFreeTargets);
- properties["INTERFACE_LINK_LIBRARIES"] = prepro;
- return true;
+ static const std::array<std::string, 3> linkIfaceProps = {
+ { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT",
+ "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" }
+ };
+ bool hadINTERFACE_LINK_LIBRARIES = false;
+ for (std::string const& linkIfaceProp : linkIfaceProps) {
+ if (cmValue input = target->GetProperty(linkIfaceProp)) {
+ std::string prepro =
+ cmGeneratorExpression::Preprocess(*input, preprocessRule);
+ if (!prepro.empty()) {
+ this->ResolveTargetsInGeneratorExpressions(
+ prepro, target, missingTargets, ReplaceFreeTargets);
+ properties[linkIfaceProp] = prepro;
+ hadINTERFACE_LINK_LIBRARIES = true;
+ }
}
}
- return false;
+ return hadINTERFACE_LINK_LIBRARIES;
}
static bool isSubDirectory(std::string const& a, std::string const& b)
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index db9b05b..e98aa05 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -111,6 +111,8 @@ void cmExportTryCompileFileGenerator::PopulateProperties(
std::vector<std::string> props = target->GetPropertyKeys();
// Include special properties that might be relevant here.
props.emplace_back("INTERFACE_LINK_LIBRARIES");
+ props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT");
+ props.emplace_back("INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE");
for (std::string const& p : props) {
cmValue v = target->GetProperty(p);
if (!v) {
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index a1fce55..d4b02a5 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -188,11 +188,13 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
return top->Target == tgt && prop == "LINK_LIBRARIES"_s;
}
- return prop == "LINK_LIBRARIES"_s || prop == "LINK_INTERFACE_LIBRARIES"_s ||
+ return prop == "LINK_LIBRARIES"_s || prop == "INTERFACE_LINK_LIBRARIES"_s ||
+ prop == "INTERFACE_LINK_LIBRARIES_DIRECT"_s ||
+ prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s ||
+ prop == "LINK_INTERFACE_LIBRARIES"_s ||
prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
- cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_") ||
- prop == "INTERFACE_LINK_LIBRARIES"_s;
+ cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_");
}
cmGeneratorExpressionDAGChecker const* cmGeneratorExpressionDAGChecker::Top()
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 45e3b64..c4f1a13 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -57,6 +57,11 @@ using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor;
const cmsys::RegularExpression FrameworkRegularExpression(
"^(.*/)?([^/]*)\\.framework/(.*)$");
+const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES";
+const std::string kINTERFACE_LINK_LIBRARIES_DIRECT =
+ "INTERFACE_LINK_LIBRARIES_DIRECT";
+const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE =
+ "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE";
}
template <>
@@ -749,6 +754,12 @@ void cmGeneratorTarget::ClearSourcesCache()
this->LinkImplMap.clear();
}
+void cmGeneratorTarget::ClearLinkInterfaceCache()
+{
+ this->LinkInterfaceMap.clear();
+ this->LinkInterfaceUsageRequirementsOnlyMap.clear();
+}
+
void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before)
{
this->SourceEntries.insert(
@@ -3645,7 +3656,7 @@ void processIncludeDirectories(cmGeneratorTarget const* tgt,
cmLinkImplItem const& item = entry.LinkImplItem;
std::string const& targetName = item.AsStr();
bool const fromImported = item.Target && item.Target->IsImported();
- bool const checkCMP0027 = item.FromGenex;
+ bool const checkCMP0027 = item.CheckCMP0027;
std::string usedIncludes;
for (std::string& entryInclude : entry.Values) {
@@ -6638,7 +6649,7 @@ bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n,
cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
std::string const& n, cmListFileBacktrace const& bt,
- LookupLinkItemScope* scope) const
+ LookupLinkItemScope* scope, LookupSelf lookupSelf) const
{
cm::optional<cmLinkItem> maybeItem;
if (this->IsLinkLookupScope(n, scope->LG)) {
@@ -6646,20 +6657,22 @@ cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
}
std::string name = this->CheckCMP0004(n);
- if (name == this->GetName() || name.empty()) {
+ if (name.empty() ||
+ (lookupSelf == LookupSelf::No && name == this->GetName())) {
return maybeItem;
}
maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG);
return maybeItem;
}
-void cmGeneratorTarget::ExpandLinkItems(std::string const& prop,
- cmBTStringRange entries,
- std::string const& config,
- cmGeneratorTarget const* headTarget,
- LinkInterfaceFor interfaceFor,
- cmLinkInterface& iface) const
+void cmGeneratorTarget::ExpandLinkItems(
+ std::string const& prop, cmBTStringRange entries, std::string const& config,
+ cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor,
+ LinkInterfaceField field, cmLinkInterface& iface) const
{
+ if (entries.empty()) {
+ return;
+ }
// Keep this logic in sync with ComputeLinkImplementationLibraries.
cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
// The $<LINK_ONLY> expression may be in a link interface to specify
@@ -6678,10 +6691,20 @@ void cmGeneratorTarget::ExpandLinkItems(std::string const& prop,
cge->Evaluate(this->LocalGenerator, config, headTarget, &dagChecker,
this, headTarget->LinkerLanguage));
for (std::string const& lib : libs) {
- if (cm::optional<cmLinkItem> maybeItem =
- this->LookupLinkItem(lib, cge->GetBacktrace(), &scope)) {
+ if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
+ lib, cge->GetBacktrace(), &scope,
+ field == LinkInterfaceField::Libraries ? LookupSelf::No
+ : LookupSelf::Yes)) {
cmLinkItem item = std::move(*maybeItem);
+ if (field == LinkInterfaceField::HeadInclude) {
+ iface.HeadInclude.emplace_back(std::move(item));
+ continue;
+ }
+ if (field == LinkInterfaceField::HeadExclude) {
+ iface.HeadExclude.emplace_back(std::move(item));
+ continue;
+ }
if (!item.Target) {
// Report explicitly linked object files separately.
std::string const& maybeObj = item.AsStr();
@@ -6736,17 +6759,16 @@ cmLinkInterface const* cmGeneratorTarget::GetLinkInterface(
// Lookup any existing link interface for this configuration.
cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config);
- if (secondPass) {
- hm.erase(head);
- }
-
// If the link interface does not depend on the head target
- // then return the one we computed first.
+ // then re-use the one from the head we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
- return &hm.begin()->second;
+ head = hm.begin()->first;
}
cmOptionalLinkInterface& iface = hm[head];
+ if (secondPass) {
+ iface = cmOptionalLinkInterface();
+ }
if (!iface.LibrariesDone) {
iface.LibrariesDone = true;
this->ComputeLinkInterfaceLibraries(config, iface, head,
@@ -6865,9 +6887,9 @@ const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries(
: this->GetHeadToLinkInterfaceMap(config));
// If the link interface does not depend on the head target
- // then return the one we computed first.
+ // then re-use the one from the head we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
- return &hm.begin()->second;
+ head = hm.begin()->first;
}
cmOptionalLinkInterface& iface = hm[head];
@@ -7146,59 +7168,66 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// An explicit list of interface libraries may be set for shared
// libraries and executables that export symbols.
- cmValue explicitLibraries = nullptr;
- std::string linkIfaceProp;
+ bool haveExplicitLibraries = false;
+ cmValue explicitLibrariesCMP0022OLD;
+ std::string linkIfacePropCMP0022OLD;
bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
if (cmp0022NEW) {
// CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
- linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
- explicitLibraries = this->GetProperty(linkIfaceProp);
- } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
- this->IsExecutableWithExports()) {
+ haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() ||
+ !this->Target->GetLinkInterfaceDirectEntries().empty() ||
+ !this->Target->GetLinkInterfaceDirectExcludeEntries().empty();
+ } else {
// CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a
// shared lib or executable.
-
- // Lookup the per-configuration property.
- linkIfaceProp = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix);
- explicitLibraries = this->GetProperty(linkIfaceProp);
-
- // If not set, try the generic property.
- if (!explicitLibraries) {
- linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
- explicitLibraries = this->GetProperty(linkIfaceProp);
+ if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+ this->IsExecutableWithExports()) {
+ // Lookup the per-configuration property.
+ linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix);
+ explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD);
+
+ // If not set, try the generic property.
+ if (!explicitLibrariesCMP0022OLD) {
+ linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES";
+ explicitLibrariesCMP0022OLD =
+ this->GetProperty(linkIfacePropCMP0022OLD);
+ }
}
- }
- if (explicitLibraries &&
- this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
- !this->PolicyWarnedCMP0022) {
- // Compare the explicitly set old link interface properties to the
- // preferred new link interface property one and warn if different.
- cmValue newExplicitLibraries =
- this->GetProperty("INTERFACE_LINK_LIBRARIES");
- if (newExplicitLibraries &&
- (*newExplicitLibraries != *explicitLibraries)) {
- std::ostringstream w;
- /* clang-format off */
- w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
- "Target \"" << this->GetName() << "\" has an "
- "INTERFACE_LINK_LIBRARIES property which differs from its " <<
- linkIfaceProp << " properties."
- "\n"
- "INTERFACE_LINK_LIBRARIES:\n"
- " " << *newExplicitLibraries << "\n" <<
- linkIfaceProp << ":\n"
- " " << *explicitLibraries << "\n";
- /* clang-format on */
- this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
- this->PolicyWarnedCMP0022 = true;
+ if (explicitLibrariesCMP0022OLD &&
+ this->GetPolicyStatusCMP0022() == cmPolicies::WARN &&
+ !this->PolicyWarnedCMP0022) {
+ // Compare the explicitly set old link interface properties to the
+ // preferred new link interface property one and warn if different.
+ cmValue newExplicitLibraries =
+ this->GetProperty("INTERFACE_LINK_LIBRARIES");
+ if (newExplicitLibraries &&
+ (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) {
+ std::ostringstream w;
+ /* clang-format off */
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n"
+ "Target \"" << this->GetName() << "\" has an "
+ "INTERFACE_LINK_LIBRARIES property which differs from its " <<
+ linkIfacePropCMP0022OLD << " properties."
+ "\n"
+ "INTERFACE_LINK_LIBRARIES:\n"
+ " " << *newExplicitLibraries << "\n" <<
+ linkIfacePropCMP0022OLD << ":\n"
+ " " << *explicitLibrariesCMP0022OLD << "\n";
+ /* clang-format on */
+ this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING,
+ w.str());
+ this->PolicyWarnedCMP0022 = true;
+ }
}
+
+ haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD);
}
// There is no implicit link interface for executables or modules
// so if none was explicitly set then there is no link interface.
- if (!explicitLibraries &&
+ if (!haveExplicitLibraries &&
(this->GetType() == cmStateEnums::EXECUTABLE ||
(this->GetType() == cmStateEnums::MODULE_LIBRARY))) {
return;
@@ -7208,22 +7237,29 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// If CMP0022 is NEW then the plain tll signature sets the
// INTERFACE_LINK_LIBRARIES property. Even if the project
// clears it, the link interface is still explicit.
- iface.Explicit = cmp0022NEW || explicitLibraries;
-
- if (explicitLibraries) {
- // The interface libraries have been explicitly set.
- if (cmp0022NEW) {
- // The explicitLibraries came from INTERFACE_LINK_LIBRARIES.
- // Use its special representation directly to get backtraces.
- this->ExpandLinkItems(linkIfaceProp,
- this->Target->GetLinkInterfaceEntries(), config,
- headTarget, interfaceFor, iface);
- } else {
- std::vector<BT<std::string>> entries;
- entries.emplace_back(*explicitLibraries);
- this->ExpandLinkItems(linkIfaceProp, cmMakeRange(entries), config,
- headTarget, interfaceFor, iface);
- }
+ iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD;
+
+ if (cmp0022NEW) {
+ // The interface libraries are specified by INTERFACE_LINK_LIBRARIES.
+ // Use its special representation directly to get backtraces.
+ this->ExpandLinkItems(
+ kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(),
+ config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
+ this->Target->GetLinkInterfaceDirectEntries(),
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::HeadInclude, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
+ this->Target->GetLinkInterfaceDirectExcludeEntries(),
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::HeadExclude, iface);
+ } else if (explicitLibrariesCMP0022OLD) {
+ // The interface libraries have been explicitly set in pre-CMP0022 style.
+ std::vector<BT<std::string>> entries;
+ entries.emplace_back(*explicitLibrariesCMP0022OLD);
+ this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries),
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, iface);
}
// If the link interface is explicit, do not fall back to the link impl.
@@ -7241,13 +7277,10 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries(
// Compare the link implementation fallback link interface to the
// preferred new link interface property and warn if different.
cmLinkInterface ifaceNew;
- static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
- if (cmValue newExplicitLibraries = this->GetProperty(newProp)) {
- std::vector<BT<std::string>> entries;
- entries.emplace_back(*newExplicitLibraries);
- this->ExpandLinkItems(linkIfaceProp, cmMakeRange(entries), config,
- headTarget, interfaceFor, ifaceNew);
- }
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES,
+ this->Target->GetLinkInterfaceEntries(), config,
+ headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, ifaceNew);
if (ifaceNew.Libraries != iface.Libraries) {
std::string oldLibraries = cmJoin(impl->Libraries, ";");
std::string newLibraries = cmJoin(ifaceNew.Libraries, ";");
@@ -7372,29 +7405,37 @@ const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface(
? this->GetHeadToLinkInterfaceUsageRequirementsMap(config)
: this->GetHeadToLinkInterfaceMap(config));
- if (secondPass) {
- hm.erase(headTarget);
- }
-
// If the link interface does not depend on the head target
- // then return the one we computed first.
+ // then re-use the one from the head we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
- return &hm.begin()->second;
+ headTarget = hm.begin()->first;
}
cmOptionalLinkInterface& iface = hm[headTarget];
+ if (secondPass) {
+ iface = cmOptionalLinkInterface();
+ }
if (!iface.AllDone) {
iface.AllDone = true;
iface.LibrariesDone = true;
iface.Multiplicity = info->Multiplicity;
cmExpandList(info->Languages, iface.Languages);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT,
+ cmMakeRange(info->LibrariesHeadInclude), config,
+ headTarget, interfaceFor,
+ LinkInterfaceField::HeadInclude, iface);
+ this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
+ cmMakeRange(info->LibrariesHeadExclude), config,
+ headTarget, interfaceFor,
+ LinkInterfaceField::HeadExclude, iface);
this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries),
- config, headTarget, interfaceFor, iface);
+ config, headTarget, interfaceFor,
+ LinkInterfaceField::Libraries, iface);
std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
LookupLinkItemScope scope{ this->LocalGenerator };
for (std::string const& dep : deps) {
- if (cm::optional<cmLinkItem> maybeItem =
- this->LookupLinkItem(dep, cmListFileBacktrace(), &scope)) {
+ if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
+ dep, cmListFileBacktrace(), &scope, LookupSelf::No)) {
iface.SharedDeps.emplace_back(std::move(*maybeItem));
}
}
@@ -7482,6 +7523,14 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config,
}
}
}
+ for (BT<std::string> const& entry :
+ this->Target->GetLinkInterfaceDirectEntries()) {
+ info.LibrariesHeadInclude.emplace_back(entry);
+ }
+ for (BT<std::string> const& entry :
+ this->Target->GetLinkInterfaceDirectExcludeEntries()) {
+ info.LibrariesHeadExclude.emplace_back(entry);
+ }
if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
if (loc) {
info.LibName = *loc;
@@ -7896,9 +7945,9 @@ cmGeneratorTarget::GetLinkImplementationLibrariesInternal(
this->LinkImplMap[cmSystemTools::UpperCase(config)];
// If the link implementation does not depend on the head target
- // then return the one we computed first.
+ // then re-use the one from the head we computed first.
if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) {
- return &hm.begin()->second;
+ head = hm.begin()->first;
}
cmOptionalLinkImplementation& impl = hm[head];
@@ -7915,6 +7964,112 @@ bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
return cm::contains(this->LinkImplicitNullProperties, p);
}
+namespace {
+class TransitiveLinkImpl
+{
+ cmGeneratorTarget const* Self;
+ std::string const& Config;
+ cmLinkImplementation& Impl;
+
+ std::set<cmLinkItem> Emitted;
+ std::set<cmLinkItem> Excluded;
+ std::unordered_set<cmGeneratorTarget const*> Followed;
+
+ void Follow(cmGeneratorTarget const* target);
+
+public:
+ TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config,
+ cmLinkImplementation& impl)
+ : Self(self)
+ , Config(config)
+ , Impl(impl)
+ {
+ }
+
+ void Compute();
+};
+
+void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target)
+{
+ if (!target || !this->Followed.insert(target).second ||
+ target->GetPolicyStatusCMP0022() == cmPolicies::OLD ||
+ target->GetPolicyStatusCMP0022() == cmPolicies::WARN) {
+ return;
+ }
+
+ // Get this target's usage requirements.
+ cmLinkInterfaceLibraries const* iface = target->GetLinkInterfaceLibraries(
+ this->Config, this->Self, LinkInterfaceFor::Usage);
+ if (!iface) {
+ return;
+ }
+ if (iface->HadContextSensitiveCondition) {
+ this->Impl.HadContextSensitiveCondition = true;
+ }
+
+ // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements.
+ for (cmLinkItem const& item : iface->HeadInclude) {
+ // Inject direct dependencies from the item's usage requirements
+ // before the item itself.
+ this->Follow(item.Target);
+
+ // Add the item itself, but at most once.
+ if (this->Emitted.insert(item).second) {
+ this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false);
+ }
+ }
+
+ // Follow transitive dependencies.
+ for (cmLinkItem const& item : iface->Libraries) {
+ this->Follow(item.Target);
+ }
+
+ // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
+ // usage requirements.
+ for (cmLinkItem const& item : iface->HeadExclude) {
+ this->Excluded.insert(item);
+ }
+}
+
+void TransitiveLinkImpl::Compute()
+{
+ // Save the original items and start with an empty list.
+ std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries);
+
+ // Avoid injecting any original items as usage requirements.
+ // This gives LINK_LIBRARIES final control over the order
+ // if it explicitly lists everything.
+ this->Emitted.insert(original.cbegin(), original.cend());
+
+ // Process each original item.
+ for (cmLinkImplItem& item : original) {
+ // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT'
+ // usage requirements before the item itself.
+ this->Follow(item.Target);
+
+ // Add the item itself.
+ this->Impl.Libraries.emplace_back(std::move(item));
+ }
+
+ // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE'
+ // usage requirements found through any dependency above.
+ this->Impl.Libraries.erase(
+ std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(),
+ [this](cmLinkImplItem const& item) {
+ return this->Excluded.find(item) != this->Excluded.end();
+ }),
+ this->Impl.Libraries.end());
+}
+
+void ComputeLinkImplTransitive(cmGeneratorTarget const* self,
+ std::string const& config,
+ cmLinkImplementation& impl)
+{
+ TransitiveLinkImpl transitiveLinkImpl(self, config, impl);
+ transitiveLinkImpl.Compute();
+}
+}
+
void cmGeneratorTarget::ComputeLinkImplementationLibraries(
const std::string& config, cmOptionalLinkImplementation& impl,
cmGeneratorTarget const* head) const
@@ -7935,7 +8090,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
std::string const& evaluated =
cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
this->LinkerLanguage);
- bool const fromGenex = evaluated != entry.Value;
+ bool const checkCMP0027 = evaluated != entry.Value;
cmExpandList(evaluated, llibs);
if (cge->GetHadHeadSensitiveCondition()) {
impl.HadHeadSensitiveCondition = true;
@@ -8009,7 +8164,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
}
}
- impl.Libraries.emplace_back(std::move(item), fromGenex);
+ impl.Libraries.emplace_back(std::move(item), checkCMP0027);
}
std::set<std::string> const& seenProps = cge->GetSeenTargetProperties();
@@ -8021,6 +8176,11 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards);
}
+ // Update the list of direct link dependencies from usage requirements.
+ if (head == this) {
+ ComputeLinkImplTransitive(this, config, impl);
+ }
+
// Get the list of configurations considered to be DEBUG.
std::vector<std::string> debugConfigs =
this->Makefile->GetCMakeInstance()->GetDebugConfigs();
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index a58dc93..45639c0 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -667,6 +667,9 @@ public:
*/
void ClearSourcesCache();
+ // Do not use. This is only for a specific call site with a FIXME comment.
+ void ClearLinkInterfaceCache();
+
void AddSource(const std::string& src, bool before = false);
void AddTracedSources(std::vector<std::string> const& srcs);
@@ -1001,8 +1004,10 @@ private:
std::string ImportLibrary;
std::string LibName;
std::string Languages;
- std::vector<BT<std::string>> Libraries;
std::string LibrariesProp;
+ std::vector<BT<std::string>> Libraries;
+ std::vector<BT<std::string>> LibrariesHeadInclude;
+ std::vector<BT<std::string>> LibrariesHeadExclude;
std::string SharedDeps;
};
@@ -1063,19 +1068,31 @@ private:
bool IsLinkLookupScope(std::string const& n,
cmLocalGenerator const*& lg) const;
+ enum class LinkInterfaceField
+ {
+ Libraries,
+ HeadExclude,
+ HeadInclude,
+ };
void ExpandLinkItems(std::string const& prop, cmBTStringRange entries,
std::string const& config,
const cmGeneratorTarget* headTarget,
- LinkInterfaceFor interfaceFor,
+ LinkInterfaceFor interfaceFor, LinkInterfaceField field,
cmLinkInterface& iface) const;
struct LookupLinkItemScope
{
cmLocalGenerator const* LG;
};
+ enum class LookupSelf
+ {
+ No,
+ Yes,
+ };
cm::optional<cmLinkItem> LookupLinkItem(std::string const& n,
cmListFileBacktrace const& bt,
- LookupLinkItemScope* scope) const;
+ LookupLinkItemScope* scope,
+ LookupSelf lookupSelf) const;
std::vector<BT<std::string>> GetSourceFilePaths(
std::string const& config) const;
diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx
index 62e7ef4..2dc40ff 100644
--- a/Source/cmLinkItem.cxx
+++ b/Source/cmLinkItem.cxx
@@ -68,8 +68,8 @@ cmLinkImplItem::cmLinkImplItem()
{
}
-cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool fromGenex)
+cmLinkImplItem::cmLinkImplItem(cmLinkItem item, bool checkCMP0027)
: cmLinkItem(std::move(item))
- , FromGenex(fromGenex)
+ , CheckCMP0027(checkCMP0027)
{
}
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index 0863edd..262728b 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -40,8 +40,8 @@ class cmLinkImplItem : public cmLinkItem
{
public:
cmLinkImplItem();
- cmLinkImplItem(cmLinkItem item, bool fromGenex);
- bool FromGenex = false;
+ cmLinkImplItem(cmLinkItem item, bool checkCMP0027);
+ bool CheckCMP0027 = false;
};
/** The link implementation specifies the direct library
@@ -70,6 +70,12 @@ struct cmLinkInterfaceLibraries
// Object files listed in the interface.
std::vector<cmLinkItem> Objects;
+ // Items to be included as if directly linked by the head target.
+ std::vector<cmLinkItem> HeadInclude;
+
+ // Items to be excluded from direct linking by the head target.
+ std::vector<cmLinkItem> HeadExclude;
+
// Whether the list depends on a genex referencing the head target.
bool HadHeadSensitiveCondition = false;
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 50e6cdc..2adb232 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -2615,10 +2615,15 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
true);
} else if (reuseTarget->GetType() ==
cmStateEnums::OBJECT_LIBRARY) {
+ // FIXME: This can propagate more than one level, unlike
+ // the rest of the object files in an object library.
+ // Find another way to do this.
target->Target->AppendProperty(
"INTERFACE_LINK_LIBRARIES",
cmStrCat("$<$<CONFIG:", config,
">:$<LINK_ONLY:", pchSourceObj, ">>"));
+ // We updated the link interface, so ensure it is recomputed.
+ target->ClearLinkInterfaceCache();
}
}
} else {
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index ad19e03..87fce92 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -202,6 +202,8 @@ public:
std::vector<BT<std::string>> LinkDirectoriesEntries;
std::vector<BT<std::string>> LinkImplementationPropertyEntries;
std::vector<BT<std::string>> LinkInterfacePropertyEntries;
+ std::vector<BT<std::string>> LinkInterfaceDirectPropertyEntries;
+ std::vector<BT<std::string>> LinkInterfaceDirectExcludePropertyEntries;
std::vector<BT<std::string>> HeaderSetsEntries;
std::vector<BT<std::string>> InterfaceHeaderSetsEntries;
std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>>
@@ -1138,6 +1140,16 @@ cmBTStringRange cmTarget::GetLinkInterfaceEntries() const
return cmMakeRange(this->impl->LinkInterfacePropertyEntries);
}
+cmBTStringRange cmTarget::GetLinkInterfaceDirectEntries() const
+{
+ return cmMakeRange(this->impl->LinkInterfaceDirectPropertyEntries);
+}
+
+cmBTStringRange cmTarget::GetLinkInterfaceDirectExcludeEntries() const
+{
+ return cmMakeRange(this->impl->LinkInterfaceDirectExcludePropertyEntries);
+}
+
cmBTStringRange cmTarget::GetHeaderSetsEntries() const
{
return cmMakeRange(this->impl->HeaderSetsEntries);
@@ -1182,6 +1194,8 @@ MAKE_PROP(HEADER_SET);
MAKE_PROP(HEADER_SETS);
MAKE_PROP(INTERFACE_HEADER_SETS);
MAKE_PROP(INTERFACE_LINK_LIBRARIES);
+MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT);
+MAKE_PROP(INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE);
#undef MAKE_PROP
}
@@ -1313,6 +1327,19 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt);
}
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ this->impl->LinkInterfaceDirectPropertyEntries.clear();
+ if (value) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt);
+ }
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.clear();
+ if (value) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value,
+ lfbt);
+ }
} else if (prop == propSOURCES) {
this->impl->SourceEntries.clear();
if (value) {
@@ -1571,6 +1598,17 @@ void cmTarget::AppendProperty(const std::string& prop,
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt);
}
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ if (!value.empty()) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt);
+ }
+ } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ if (!value.empty()) {
+ cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+ this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value,
+ lfbt);
+ }
} else if (prop == "SOURCES") {
cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
this->impl->SourceEntries.emplace_back(value, lfbt);
@@ -1881,6 +1919,8 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
propHEADER_SETS,
propINTERFACE_HEADER_SETS,
propINTERFACE_LINK_LIBRARIES,
+ propINTERFACE_LINK_LIBRARIES_DIRECT,
+ propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
};
if (specialProps.count(prop)) {
if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
@@ -1910,6 +1950,25 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
output = cmJoin(this->impl->LinkInterfacePropertyEntries, ";");
return cmValue(output);
}
+ if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
+ if (this->impl->LinkInterfaceDirectPropertyEntries.empty()) {
+ return nullptr;
+ }
+
+ static std::string output;
+ output = cmJoin(this->impl->LinkInterfaceDirectPropertyEntries, ";");
+ return cmValue(output);
+ }
+ if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
+ if (this->impl->LinkInterfaceDirectExcludePropertyEntries.empty()) {
+ return nullptr;
+ }
+
+ static std::string output;
+ output =
+ cmJoin(this->impl->LinkInterfaceDirectExcludePropertyEntries, ";");
+ return cmValue(output);
+ }
// the type property returns what type the target is
if (prop == propTYPE) {
return cmValue(cmState::GetTargetTypeName(this->GetType()));
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 1173f49..18e39c7 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -266,6 +266,8 @@ public:
cmBTStringRange GetLinkImplementationEntries() const;
cmBTStringRange GetLinkInterfaceEntries() const;
+ cmBTStringRange GetLinkInterfaceDirectEntries() const;
+ cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
cmBTStringRange GetHeaderSetsEntries() const;