diff options
author | Brad King <brad.king@kitware.com> | 2022-01-31 15:36:42 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-01-31 15:36:55 (GMT) |
commit | 5305d5aa1a6900c64a5833176b43a21acb13fb30 (patch) | |
tree | 8af9de023d9e278904d849709c78535f257eea0b /Source | |
parent | e7c300e3567a394bcc8e90ddccf46faa68d3afae (diff) | |
parent | f3ad061858d03cc2a0569f903df3560152106050 (diff) | |
download | CMake-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.cxx | 27 | ||||
-rw-r--r-- | Source/cmExportTryCompileFileGenerator.cxx | 2 | ||||
-rw-r--r-- | Source/cmGeneratorExpressionDAGChecker.cxx | 8 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 350 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 23 | ||||
-rw-r--r-- | Source/cmLinkItem.cxx | 4 | ||||
-rw-r--r-- | Source/cmLinkItem.h | 10 | ||||
-rw-r--r-- | Source/cmLocalGenerator.cxx | 5 | ||||
-rw-r--r-- | Source/cmTarget.cxx | 59 | ||||
-rw-r--r-- | Source/cmTarget.h | 2 |
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; |