diff options
author | Brad King <brad.king@kitware.com> | 2024-05-17 16:13:53 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2024-05-20 23:28:06 (GMT) |
commit | feaca409311dd05b5b9c9702b21a6b6d151deb0d (patch) | |
tree | a998f7adb8411f1d0b232d2f4d2a14e72f44a08f | |
parent | b4924c562a9197b84b1f511043a5b2775cb865b3 (diff) | |
download | CMake-feaca409311dd05b5b9c9702b21a6b6d151deb0d.zip CMake-feaca409311dd05b5b9c9702b21a6b6d151deb0d.tar.gz CMake-feaca409311dd05b5b9c9702b21a6b6d151deb0d.tar.bz2 |
cmGeneratorTarget: Factor link interface/impl methods into own source
-rw-r--r-- | Source/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 1441 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 2 | ||||
-rw-r--r-- | Source/cmGeneratorTarget_Link.cxx | 1489 | ||||
-rwxr-xr-x | bootstrap | 1 |
5 files changed, 1493 insertions, 1441 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5962719..8582b39 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -287,6 +287,7 @@ add_library( cmGeneratorTarget.h cmGeneratorTarget_CompatibleInterface.cxx cmGeneratorTarget_IncludeDirectories.cxx + cmGeneratorTarget_Link.cxx cmGeneratorTarget_LinkDirectories.cxx cmGeneratorTarget_Options.cxx cmGeneratorTarget_Sources.cxx diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 6238ed2..13f6feb 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -21,7 +21,7 @@ #include <cmext/string_view> #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" +#include "cmComputeLinkInformation.h" // IWYU pragma: keep #include "cmCryptoHash.h" #include "cmCxxModuleUsageEffects.h" #include "cmExperimental.h" @@ -39,7 +39,6 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPropertyMap.h" -#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" @@ -58,12 +57,6 @@ namespace { using UseTo = cmGeneratorTarget::UseTo; using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; - -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"; } const std::map<cm::string_view, TransitiveProperty> @@ -1964,214 +1957,6 @@ const std::string* cmGeneratorTarget::GetExportMacro() const return nullptr; } -class cmTargetCollectLinkLanguages -{ -public: - cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, - std::string config, - std::unordered_set<std::string>& languages, - cmGeneratorTarget const* head, bool secondPass) - : Config(std::move(config)) - , Languages(languages) - , HeadTarget(head) - , SecondPass(secondPass) - { - this->Visited.insert(target); - } - - void Visit(cmLinkItem const& item) - { - if (!item.Target) { - return; - } - if (!this->Visited.insert(item.Target).second) { - return; - } - cmLinkInterface const* iface = item.Target->GetLinkInterface( - this->Config, this->HeadTarget, this->SecondPass); - if (!iface) { - return; - } - if (iface->HadLinkLanguageSensitiveCondition) { - this->HadLinkLanguageSensitiveCondition = true; - } - - for (std::string const& language : iface->Languages) { - this->Languages.insert(language); - } - - for (cmLinkItem const& lib : iface->Libraries) { - this->Visit(lib); - } - } - - bool GetHadLinkLanguageSensitiveCondition() const - { - return this->HadLinkLanguageSensitiveCondition; - } - -private: - std::string Config; - std::unordered_set<std::string>& Languages; - cmGeneratorTarget const* HeadTarget; - std::set<cmGeneratorTarget const*> Visited; - bool SecondPass; - bool HadLinkLanguageSensitiveCondition = false; -}; - -cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static LinkClosure const empty = { {}, {} }; - return ∅ - } - - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkClosureMap.find(key); - if (i == this->LinkClosureMap.end()) { - LinkClosure lc; - this->ComputeLinkClosure(config, lc); - LinkClosureMapType::value_type entry(key, lc); - i = this->LinkClosureMap.insert(entry).first; - } - return &i->second; -} - -class cmTargetSelectLinker -{ - int Preference = 0; - cmGeneratorTarget const* Target; - cmGlobalGenerator* GG; - std::set<std::string> Preferred; - -public: - cmTargetSelectLinker(cmGeneratorTarget const* target) - : Target(target) - { - this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); - } - void Consider(const std::string& lang) - { - int preference = this->GG->GetLinkerPreference(lang); - if (preference > this->Preference) { - this->Preference = preference; - this->Preferred.clear(); - } - if (preference == this->Preference) { - this->Preferred.insert(lang); - } - } - std::string Choose() - { - if (this->Preferred.empty()) { - return ""; - } - if (this->Preferred.size() > 1) { - std::ostringstream e; - e << "Target " << this->Target->GetName() - << " contains multiple languages with the highest linker preference" - << " (" << this->Preference << "):\n"; - for (std::string const& li : this->Preferred) { - e << " " << li << "\n"; - } - e << "Set the LINKER_LANGUAGE property for this target."; - cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); - cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Target->GetBacktrace()); - } - return *this->Preferred.begin(); - } -}; - -bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc, - bool secondPass) const -{ - // Get languages built in this target. - std::unordered_set<std::string> languages; - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass); - assert(impl); - languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); - - // Add interface languages from linked targets. - // cmTargetCollectLinkLanguages cll(this, config, languages, this, - // secondPass); - cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - cll.Visit(lib); - } - - // Store the transitive closure of languages. - cm::append(lc.Languages, languages); - - // Choose the language whose linker should be used. - if (secondPass || lc.LinkerLanguage.empty()) { - // Find the language with the highest preference value. - cmTargetSelectLinker tsl(this); - - // First select from the languages compiled directly in this target. - for (std::string const& l : impl->Languages) { - tsl.Consider(l); - } - - // Now consider languages that propagate from linked targets. - for (std::string const& lang : languages) { - std::string propagates = - "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; - if (this->Makefile->IsOn(propagates)) { - tsl.Consider(lang); - } - } - - lc.LinkerLanguage = tsl.Choose(); - } - - return impl->HadLinkLanguageSensitiveCondition || - cll.GetHadLinkLanguageSensitiveCondition(); -} - -void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc) const -{ - bool secondPass = false; - - { - LinkClosure linkClosure; - linkClosure.LinkerLanguage = this->LinkerLanguage; - - bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || - !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); - - // Get languages built in this target. - secondPass = this->ComputeLinkClosure(config, linkClosure, false) && - !hasHardCodedLinkerLanguage; - this->LinkerLanguage = linkClosure.LinkerLanguage; - if (!secondPass) { - lc = std::move(linkClosure); - } - } - - if (secondPass) { - LinkClosure linkClosure; - - this->ComputeLinkClosure(config, linkClosure, secondPass); - lc = std::move(linkClosure); - - // linker language must not be changed between the two passes - if (this->LinkerLanguage != lc.LinkerLanguage) { - std::ostringstream e; - e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " - "changes\nthe linker language for target \"" - << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" - << lc.LinkerLanguage << "') which is invalid."; - cmSystemTools::Error(e.str()); - } - } -} - cmGeneratorTarget::NameComponents const& cmGeneratorTarget::GetFullNameComponents( std::string const& config, cmStateEnums::ArtifactType artifact) const @@ -2336,51 +2121,6 @@ void cmGeneratorTarget::GetAutoUicOptions(std::vector<std::string>& result, result); } -static void processILibs(const std::string& config, - cmGeneratorTarget const* headTarget, - cmLinkItem const& item, cmGlobalGenerator* gg, - std::vector<cmGeneratorTarget const*>& tgts, - std::set<cmGeneratorTarget const*>& emitted) -{ - if (item.Target && emitted.insert(item.Target).second) { - tgts.push_back(item.Target); - if (cmLinkInterfaceLibraries const* iface = - item.Target->GetLinkInterfaceLibraries(config, headTarget, - UseTo::Compile)) { - for (cmLinkItem const& lib : iface->Libraries) { - processILibs(config, headTarget, lib, gg, tgts, emitted); - } - } - } -} - -const std::vector<const cmGeneratorTarget*>& -cmGeneratorTarget::GetLinkImplementationClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static std::vector<const cmGeneratorTarget*> const empty; - return empty; - } - - LinkImplClosure& tgts = this->LinkImplClosureMap[config]; - if (!tgts.Done) { - tgts.Done = true; - std::set<cmGeneratorTarget const*> emitted; - - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, UseTo::Compile); - assert(impl); - - for (cmLinkImplItem const& lib : impl->Libraries) { - processILibs(config, this, lib, - this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); - } - } - return tgts; -} - void cmGeneratorTarget::TraceDependencies() { // CMake-generated targets have no dependencies to trace. Normally tracing @@ -3924,165 +3664,6 @@ bool cmGeneratorTarget::SetDeviceLink(bool deviceLink) return previous; } -cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( - const std::string& config) const -{ - // Lookup any existing information for this configuration. - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkInformation.find(key); - if (i == this->LinkInformation.end()) { - // Compute information for this configuration. - auto info = cm::make_unique<cmComputeLinkInformation>(this, config); - if (info && !info->Compute()) { - info.reset(); - } - - // Store the information for this configuration. - i = this->LinkInformation.emplace(key, std::move(info)).first; - - if (i->second) { - this->CheckPropertyCompatibility(*i->second, config); - } - } - return i->second.get(); -} - -void cmGeneratorTarget::CheckLinkLibraries() const -{ - bool linkLibrariesOnlyTargets = - this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); - - // Evaluate the link interface of this target if needed for extra checks. - if (linkLibrariesOnlyTargets) { - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& config : configs) { - this->GetLinkInterfaceLibraries(config, this, UseTo::Link); - } - } - - // Check link the implementation for each generated configuration. - for (auto const& hmp : this->LinkImplMap) { - HeadToLinkImplementationMap const& hm = hmp.second; - // There could be several entries used when computing the pre-CMP0022 - // default link interface. Check only the entry for our own link impl. - auto const hmi = hm.find(this); - if (hmi == hm.end() || !hmi->second.LibrariesDone) { - continue; - } - for (cmLinkImplItem const& item : hmi->second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { - return; - } - } - } - - // Check link the interface for each generated combination of - // configuration and consuming head target. We should not need to - // consider LinkInterfaceUsageRequirementsOnlyMap because its entries - // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). - for (auto const& hmp : this->LinkInterfaceMap) { - for (auto const& hmi : hmp.second) { - if (!hmi.second.LibrariesDone) { - continue; - } - for (cmLinkItem const& item : hmi.second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { - return; - } - } - } - } -} - -namespace { -cm::string_view missingTargetPossibleReasons = - "Possible reasons include:\n" - " * There is a typo in the target name.\n" - " * A find_package call is missing for an IMPORTED target.\n" - " * An ALIAS target is missing.\n"_s; -} - -bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || - item.AsStr().find("::") == std::string::npos) { - return true; - } - MessageType messageType = MessageType::FATAL_ERROR; - std::string e; - switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { - case cmPolicies::WARN: { - e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - return true; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (role == LinkItemRole::Implementation) { - e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); - } else { - e = cmStrCat(e, "The link interface of target \"", this->GetName(), - "\" contains"); - } - e = - cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, - backtrace); - return false; -} - -bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target) { - return true; - } - std::string const& str = item.AsStr(); - if (!str.empty() && - (str[0] == '-' || str[0] == '$' || str[0] == '`' || - str.find_first_of("/\\") != std::string::npos || - cmHasPrefix(str, "<LINK_LIBRARY:"_s) || - cmHasPrefix(str, "<LINK_GROUP:"_s))) { - return true; - } - - std::string e = cmStrCat("Target \"", this->GetName(), - "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", - role == LinkItemRole::Implementation - ? "it links to" - : "its link interface contains", - ":\n ", item.AsStr(), "\nwhich is not a target. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e, backtrace); - return false; -} - void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const { int patch; @@ -4321,291 +3902,6 @@ void cmGeneratorTarget::ReportPropertyOrigin( areport); } -bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, - cmLocalGenerator const*& lg) const -{ - if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { - cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); - if (dirId.String.empty()) { - lg = this->LocalGenerator; - return true; - } - if (cmLocalGenerator const* otherLG = - this->GlobalGenerator->FindLocalGenerator(dirId)) { - lg = otherLG; - return true; - } - } - return false; -} - -cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( - std::string const& n, cmListFileBacktrace const& bt, - std::string const& linkFeature, LookupLinkItemScope* scope, - LookupSelf lookupSelf) const -{ - cm::optional<cmLinkItem> maybeItem; - if (this->IsLinkLookupScope(n, scope->LG)) { - return maybeItem; - } - - std::string name = this->CheckCMP0004(n); - if (name.empty() || - (lookupSelf == LookupSelf::No && name == this->GetName())) { - return maybeItem; - } - maybeItem = - this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); - return maybeItem; -} - -void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, - cmBTStringRange entries, - std::string const& config, - cmGeneratorTarget const* headTarget, - UseTo usage, LinkInterfaceField field, - cmLinkInterface& iface) const -{ - if (entries.empty()) { - return; - } - // Keep this logic in sync with ComputeLinkImplementationLibraries. - cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr, - this->LocalGenerator); - // The $<LINK_ONLY> expression may be in a link interface to specify - // private link dependencies that are otherwise excluded from usage - // requirements. - if (usage == UseTo::Compile) { - dagChecker.SetTransitivePropertiesOnly(); - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - } - cmMakefile const* mf = this->LocalGenerator->GetMakefile(); - LookupLinkItemScope scope{ this->LocalGenerator }; - for (BT<std::string> const& entry : entries) { - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, - &dagChecker, this, - headTarget->LinkerLanguage) }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : libs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - lib, cge->GetBacktrace(), linkFeature, &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(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - iface.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - iface.Libraries.emplace_back(std::move(item)); - } - } - if (cge->GetHadHeadSensitiveCondition()) { - iface.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - iface.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - iface.HadLinkLanguageSensitiveCondition = true; - } - } -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head) const -{ - return this->GetLinkInterface(config, head, false); -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head, - bool secondPass) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, UseTo::Link, secondPass); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (secondPass) { - iface = cmOptionalLinkInterface(); - } - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, UseTo::Link); - } - if (!iface.AllDone) { - iface.AllDone = true; - if (iface.Exists) { - this->ComputeLinkInterface(config, iface, head, secondPass); - this->ComputeLinkInterfaceRuntimeLibraries(config, iface); - } - } - - return iface.Exists ? &iface : nullptr; -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget) const -{ - this->ComputeLinkInterface(config, iface, headTarget, false); -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, bool secondPass) const -{ - if (iface.Explicit) { - if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->GetType() == cmStateEnums::STATIC_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - // Shared libraries may have runtime implementation dependencies - // on other shared libraries that are not in the interface. - std::set<cmLinkItem> emitted; - for (cmLinkItem const& lib : iface.Libraries) { - emitted.insert(lib); - } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - if (emitted.insert(lib).second) { - if (lib.Target) { - // This is a runtime dependency on another shared library. - if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { - iface.SharedDeps.push_back(lib); - } - } else { - // TODO: Recognize shared library file names. Perhaps this - // should be moved to cmComputeLinkInformation, but that - // creates a chicken-and-egg problem since this list is needed - // for its construction. - } - } - } - } - } - } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || - this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { - // The link implementation is the default link interface. - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - UseTo::Link); - iface.ImplementationIsInterface = true; - iface.WrongConfigLibraries = impl->WrongConfigLibraries; - } - - if (this->LinkLanguagePropagatesToDependents()) { - // Targets using this archive need its language runtime libraries. - if (cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Link, secondPass)) { - iface.Languages = impl->Languages; - } - } - - if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // How many repetitions are needed if this library has cyclic - // dependencies? - std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); - if (cmValue config_reps = this->GetProperty(propName)) { - sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); - } else if (cmValue reps = - this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { - sscanf(reps->c_str(), "%u", &iface.Multiplicity); - } - } -} - -const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( - const std::string& config, cmGeneratorTarget const* head, UseTo usage) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, usage); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, usage); - } - - return iface.Exists ? &iface : nullptr; -} - std::string cmGeneratorTarget::GetDirectory( const std::string& config, cmStateEnums::ArtifactType artifact) const { @@ -4861,303 +4157,6 @@ bool cmGeneratorTarget::GetRPATH(const std::string& config, return true; } -void cmGeneratorTarget::ComputeLinkInterfaceLibraries( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, UseTo usage) const -{ - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // An explicit list of interface libraries may be set for shared - // libraries and executables that export symbols. - 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. - 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. - 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 (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 (!haveExplicitLibraries && - (this->GetType() == cmStateEnums::EXECUTABLE || - (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { - return; - } - iface.Exists = true; - - // 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 || 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, usage, LinkInterfaceField::Libraries, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, - this->Target->GetLinkInterfaceDirectEntries(), - config, headTarget, usage, - LinkInterfaceField::HeadInclude, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - this->Target->GetLinkInterfaceDirectExcludeEntries(), - config, headTarget, usage, - 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, usage, - LinkInterfaceField::Libraries, iface); - } - - // If the link interface is explicit, do not fall back to the link impl. - if (iface.Explicit) { - return; - } - - // The link implementation is the default link interface. - if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - usage)) { - iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), - impl->Libraries.end()); - if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022 && usage == UseTo::Link) { - // Compare the link implementation fallback link interface to the - // preferred new link interface property and warn if different. - cmLinkInterface ifaceNew; - this->ExpandLinkItems( - kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), - config, headTarget, usage, LinkInterfaceField::Libraries, ifaceNew); - if (ifaceNew.Libraries != iface.Libraries) { - std::string oldLibraries = cmJoin(impl->Libraries, ";"); - std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); - if (oldLibraries.empty()) { - oldLibraries = "(empty)"; - } - if (newLibraries.empty()) { - newLibraries = "(empty)"; - } - - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property. " - "This should be preferred as the source of the link interface " - "for this library but because CMP0022 is not set CMake is " - "ignoring the property and using the link implementation " - "as the link interface instead." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << newLibraries << "\n" - "Link implementation:\n" - " " << oldLibraries << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); - this->PolicyWarnedCMP0022 = true; - } - } - } -} - -namespace { - -template <typename ReturnType> -ReturnType constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt); - -template <> -inline cmLinkImplItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkImplItem(cmLinkItem(target, false, bt), false); -} - -template <> -inline cmLinkItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkItem(target, false, bt); -} - -template <typename ValueType> -std::vector<ValueType> computeImplicitLanguageTargets( - std::string const& lang, std::string const& config, - cmGeneratorTarget const* currentTarget) -{ - cmListFileBacktrace bt; - std::vector<ValueType> result; - cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); - - std::string const& runtimeLibrary = - currentTarget->GetRuntimeLinkLibrary(lang, config); - if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( - "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { - cmList libsList{ *runtimeLinkOptions }; - result.reserve(libsList.size()); - - for (auto const& i : libsList) { - cmGeneratorTarget::TargetOrString resolved = - currentTarget->ResolveTargetReference(i, lg); - if (resolved.Target) { - result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); - } - } - } - - return result; -} -} - -void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( - const std::string& config, cmOptionalLinkInterface& iface) const -{ - for (std::string const& lang : iface.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - iface.LanguageRuntimeLibraries.find(lang) == - iface.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); - iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( - const std::string& config, cmOptionalLinkImplementation& impl) const -{ - for (std::string const& lang : impl.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - impl.LanguageRuntimeLibraries.find(lang) == - impl.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); - impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( - const std::string& config, cmGeneratorTarget const* headTarget, UseTo usage, - bool secondPass) const -{ - cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); - if (!info) { - return nullptr; - } - - cmHeadToLinkInterfaceMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - 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, usage, LinkInterfaceField::HeadInclude, - iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - cmMakeRange(info->LibrariesHeadExclude), config, - headTarget, usage, LinkInterfaceField::HeadExclude, - iface); - this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), - config, headTarget, usage, - LinkInterfaceField::Libraries, iface); - cmList deps{ info->SharedDeps }; - LookupLinkItemScope scope{ this->LocalGenerator }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& dep : deps) { - if (auto maybeLinkFeature = ParseLinkFeature(dep)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { - iface.SharedDeps.emplace_back(std::move(*maybeItem)); - } - } - } - - return &iface; -} - cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo( const std::string& config) const { @@ -5347,69 +4346,6 @@ void cmGeneratorTarget::ComputeImportInfo(std::string const& desired_config, } } -cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( - const std::string& config) const -{ - return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; -} - -cmHeadToLinkInterfaceMap& -cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( - const std::string& config) const -{ - return this - ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, UseTo usage) const -{ - return this->GetLinkImplementation(config, usage, false); -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, UseTo usage, bool secondPass) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - HeadToLinkImplementationMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - cmOptionalLinkImplementation& impl = hm[this]; - if (secondPass) { - impl = cmOptionalLinkImplementation(); - } - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, this, usage); - } - if (!impl.LanguagesDone) { - impl.LanguagesDone = true; - this->ComputeLinkImplementationLanguages(config, impl); - this->ComputeLinkImplementationRuntimeLibraries(config, impl); - } - return &impl; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationMap( - std::string const& config) const -{ - return this->LinkImplMap[cmSystemTools::UpperCase(config)]; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( - std::string const& config) const -{ - return this - ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode( std::vector<cmSourceFile*>& files) const { @@ -5659,156 +4595,12 @@ bool cmGeneratorTarget::HaveBuildTreeRPATH(const std::string& config) const return false; } -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibraries(const std::string& config, - UseTo usage) const -{ - return this->GetLinkImplementationLibrariesInternal(config, this, usage); -} - -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibrariesInternal( - const std::string& config, cmGeneratorTarget const* head, UseTo usage) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - // Populate the link implementation libraries for this configuration. - HeadToLinkImplementationMap& hm = - (usage == UseTo::Compile - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - - // If the link implementation does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkImplementation& impl = hm[head]; - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, head, usage); - } - return &impl; -} - bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( const std::string& p) const { return cm::contains(this->LinkImplicitNullProperties, p); } -namespace { -class TransitiveLinkImpl -{ - cmGeneratorTarget const* Self; - std::string const& Config; - UseTo ImplFor; - 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, - UseTo usage, cmLinkImplementation& impl) - : Self(self) - , Config(config) - , ImplFor(usage) - , 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, this->ImplFor); - 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, UseTo usage, - cmLinkImplementation& impl) -{ - TransitiveLinkImpl transitiveLinkImpl(self, config, usage, impl); - transitiveLinkImpl.Compute(); -} -} - bool cmGeneratorTarget::ApplyCXXStdTargets() { cmStandardLevelResolver standardResolver(this->Makefile); @@ -6034,237 +4826,6 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, return true; } -void cmGeneratorTarget::ComputeLinkImplementationLibraries( - const std::string& config, cmOptionalLinkImplementation& impl, - cmGeneratorTarget const* head, UseTo usage) const -{ - cmLocalGenerator const* lg = this->LocalGenerator; - cmMakefile const* mf = lg->GetMakefile(); - cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); - auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; - // Collect libraries directly linked in this configuration. - for (auto const& entry : entryRange) { - // Keep this logic in sync with ExpandLinkItems. - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, - nullptr, this->LocalGenerator); - // The $<LINK_ONLY> expression may be used to specify link dependencies - // that are otherwise excluded from usage requirements. - if (usage == UseTo::Compile) { - dagChecker.SetTransitivePropertiesOnly(); - switch (this->GetPolicyStatusCMP0131()) { - case cmPolicies::WARN: - case cmPolicies::OLD: - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - break; - } - } - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> const cge = - ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - std::string const& evaluated = - cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, - this->LinkerLanguage); - bool const checkCMP0027 = evaluated != entry.Value; - cmList llibs(evaluated); - if (cge->GetHadHeadSensitiveCondition()) { - impl.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - impl.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - impl.HadLinkLanguageSensitiveCondition = true; - } - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : llibs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (this->IsLinkLookupScope(lib, lg)) { - continue; - } - - // Skip entries that resolve to the target itself or are empty. - std::string name = this->CheckCMP0004(lib); - if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { - // resolve alias name - auto* target = this->Makefile->FindTargetToUse(name); - if (target) { - name = target->GetName(); - } - } - if (name == this->GetName() || name.empty()) { - if (name == this->GetName()) { - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; - std::ostringstream e; - switch (this->GetPolicyStatusCMP0038()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (!noMessage) { - e << "Target \"" << this->GetName() << "\" links to itself."; - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - messageType, e.str(), this->GetBacktrace()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - continue; - } - - // The entry is meant for this configuration. - cmLinkItem item = this->ResolveLinkItem( - BT<std::string>(name, entry.Backtrace), lg, linkFeature); - if (item.Target) { - auto depsForTarget = synthTargetsForConfig.find(item.Target); - if (depsForTarget != synthTargetsForConfig.end()) { - for (auto const* depForTarget : depsForTarget->second) { - cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); - impl.Libraries.emplace_back(std::move(synthItem), false); - } - } - } else { - // Report explicitly linked object files separately. - std::string const& maybeObj = item.AsStr(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - impl.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - impl.Libraries.emplace_back(std::move(item), checkCMP0027); - } - - std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); - for (std::string const& sp : seenProps) { - if (!this->GetProperty(sp)) { - this->LinkImplicitNullProperties.insert(sp); - } - } - cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); - } - - // Update the list of direct link dependencies from usage requirements. - if (head == this) { - ComputeLinkImplTransitive(this, config, usage, impl); - } - - // Get the list of configurations considered to be DEBUG. - std::vector<std::string> debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); - - cmTargetLinkLibraryType linkType = - CMP0003_ComputeLinkType(config, debugConfigs); - cmTarget::LinkLibraryVectorType const& oldllibs = - this->Target->GetOriginalLinkLibraries(); - - auto linkFeature = cmLinkItem::DEFAULT; - for (cmTarget::LibraryID const& oldllib : oldllibs) { - if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { - std::string name = this->CheckCMP0004(oldllib.first); - if (name == this->GetName() || name.empty()) { - continue; - } - // Support OLD behavior for CMP0003. - impl.WrongConfigLibraries.push_back( - this->ResolveLinkItem(BT<std::string>(name), linkFeature)); - } - } -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name) const -{ - return this->ResolveTargetReference(name, this->LocalGenerator); -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name, cmLocalGenerator const* lg) const -{ - TargetOrString resolved; - - if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { - resolved.Target = tgt; - } else { - resolved.String = name; - } - - return resolved; -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, std::string const& linkFeature) const -{ - return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, cmLocalGenerator const* lg, - std::string const& linkFeature) const -{ - auto bt = name.Backtrace; - TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); - - if (!resolved.Target) { - return cmLinkItem(resolved.String, false, bt, linkFeature); - } - - // Check deprecation, issue message with `bt` backtrace. - if (resolved.Target->IsDeprecated()) { - std::ostringstream w; - /* clang-format off */ - w << - "The library that is being linked to, " << resolved.Target->GetName() << - ", is marked as being deprecated by the owner. The message provided by " - "the developer is: \n" << resolved.Target->GetDeprecation() << "\n"; - /* clang-format on */ - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::AUTHOR_WARNING, w.str(), bt); - } - - // Skip targets that will not really be linked. This is probably a - // name conflict between an external library and an executable - // within the project. - if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && - !resolved.Target->IsExecutableWithExports()) { - return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); - } - - return cmLinkItem(resolved.Target, false, bt, linkFeature); -} - bool cmGeneratorTarget::HasPackageReferences() const { return this->IsInBuildSystem() && diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 259c83f..51f112e 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -18,7 +18,6 @@ #include <cm/string_view> #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmPolicies.h" @@ -29,6 +28,7 @@ class cmake; enum class cmBuildStep; class cmCompiledGeneratorExpression; +class cmComputeLinkInformation; class cmCustomCommand; class cmFileSet; class cmGlobalGenerator; diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx new file mode 100644 index 0000000..3ab36b6 --- /dev/null +++ b/Source/cmGeneratorTarget_Link.cxx @@ -0,0 +1,1489 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <cassert> +#include <cstdio> +#include <map> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmAlgorithms.h" +#include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmRange.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +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"; +} + +class cmTargetCollectLinkLanguages +{ +public: + cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, + std::string config, + std::unordered_set<std::string>& languages, + cmGeneratorTarget const* head, bool secondPass) + : Config(std::move(config)) + , Languages(languages) + , HeadTarget(head) + , SecondPass(secondPass) + { + this->Visited.insert(target); + } + + void Visit(cmLinkItem const& item) + { + if (!item.Target) { + return; + } + if (!this->Visited.insert(item.Target).second) { + return; + } + cmLinkInterface const* iface = item.Target->GetLinkInterface( + this->Config, this->HeadTarget, this->SecondPass); + if (!iface) { + return; + } + if (iface->HadLinkLanguageSensitiveCondition) { + this->HadLinkLanguageSensitiveCondition = true; + } + + for (std::string const& language : iface->Languages) { + this->Languages.insert(language); + } + + for (cmLinkItem const& lib : iface->Libraries) { + this->Visit(lib); + } + } + + bool GetHadLinkLanguageSensitiveCondition() const + { + return this->HadLinkLanguageSensitiveCondition; + } + +private: + std::string Config; + std::unordered_set<std::string>& Languages; + cmGeneratorTarget const* HeadTarget; + std::set<cmGeneratorTarget const*> Visited; + bool SecondPass; + bool HadLinkLanguageSensitiveCondition = false; +}; + +cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( + const std::string& config) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static LinkClosure const empty = { {}, {} }; + return ∅ + } + + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkClosureMap.find(key); + if (i == this->LinkClosureMap.end()) { + LinkClosure lc; + this->ComputeLinkClosure(config, lc); + LinkClosureMapType::value_type entry(key, lc); + i = this->LinkClosureMap.insert(entry).first; + } + return &i->second; +} + +class cmTargetSelectLinker +{ + int Preference = 0; + cmGeneratorTarget const* Target; + cmGlobalGenerator* GG; + std::set<std::string> Preferred; + +public: + cmTargetSelectLinker(cmGeneratorTarget const* target) + : Target(target) + { + this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); + } + void Consider(const std::string& lang) + { + int preference = this->GG->GetLinkerPreference(lang); + if (preference > this->Preference) { + this->Preference = preference; + this->Preferred.clear(); + } + if (preference == this->Preference) { + this->Preferred.insert(lang); + } + } + std::string Choose() + { + if (this->Preferred.empty()) { + return ""; + } + if (this->Preferred.size() > 1) { + std::ostringstream e; + e << "Target " << this->Target->GetName() + << " contains multiple languages with the highest linker preference" + << " (" << this->Preference << "):\n"; + for (std::string const& li : this->Preferred) { + e << " " << li << "\n"; + } + e << "Set the LINKER_LANGUAGE property for this target."; + cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); + cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + } + return *this->Preferred.begin(); + } +}; + +bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc, + bool secondPass) const +{ + // Get languages built in this target. + std::unordered_set<std::string> languages; + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + assert(impl); + languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); + + // Add interface languages from linked targets. + // cmTargetCollectLinkLanguages cll(this, config, languages, this, + // secondPass); + cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + cll.Visit(lib); + } + + // Store the transitive closure of languages. + cm::append(lc.Languages, languages); + + // Choose the language whose linker should be used. + if (secondPass || lc.LinkerLanguage.empty()) { + // Find the language with the highest preference value. + cmTargetSelectLinker tsl(this); + + // First select from the languages compiled directly in this target. + for (std::string const& l : impl->Languages) { + tsl.Consider(l); + } + + // Now consider languages that propagate from linked targets. + for (std::string const& lang : languages) { + std::string propagates = + "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; + if (this->Makefile->IsOn(propagates)) { + tsl.Consider(lang); + } + } + + lc.LinkerLanguage = tsl.Choose(); + } + + return impl->HadLinkLanguageSensitiveCondition || + cll.GetHadLinkLanguageSensitiveCondition(); +} + +void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc) const +{ + bool secondPass = false; + + { + LinkClosure linkClosure; + linkClosure.LinkerLanguage = this->LinkerLanguage; + + bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || + !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); + + // Get languages built in this target. + secondPass = this->ComputeLinkClosure(config, linkClosure, false) && + !hasHardCodedLinkerLanguage; + this->LinkerLanguage = linkClosure.LinkerLanguage; + if (!secondPass) { + lc = std::move(linkClosure); + } + } + + if (secondPass) { + LinkClosure linkClosure; + + this->ComputeLinkClosure(config, linkClosure, secondPass); + lc = std::move(linkClosure); + + // linker language must not be changed between the two passes + if (this->LinkerLanguage != lc.LinkerLanguage) { + std::ostringstream e; + e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " + "changes\nthe linker language for target \"" + << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" + << lc.LinkerLanguage << "') which is invalid."; + cmSystemTools::Error(e.str()); + } + } +} + +static void processILibs(const std::string& config, + cmGeneratorTarget const* headTarget, + cmLinkItem const& item, cmGlobalGenerator* gg, + std::vector<cmGeneratorTarget const*>& tgts, + std::set<cmGeneratorTarget const*>& emitted) +{ + if (item.Target && emitted.insert(item.Target).second) { + tgts.push_back(item.Target); + if (cmLinkInterfaceLibraries const* iface = + item.Target->GetLinkInterfaceLibraries(config, headTarget, + UseTo::Compile)) { + for (cmLinkItem const& lib : iface->Libraries) { + processILibs(config, headTarget, lib, gg, tgts, emitted); + } + } + } +} + +const std::vector<const cmGeneratorTarget*>& +cmGeneratorTarget::GetLinkImplementationClosure( + const std::string& config) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static std::vector<const cmGeneratorTarget*> const empty; + return empty; + } + + LinkImplClosure& tgts = this->LinkImplClosureMap[config]; + if (!tgts.Done) { + tgts.Done = true; + std::set<cmGeneratorTarget const*> emitted; + + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config, UseTo::Compile); + assert(impl); + + for (cmLinkImplItem const& lib : impl->Libraries) { + processILibs(config, this, lib, + this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); + } + } + return tgts; +} + +cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( + const std::string& config) const +{ + // Lookup any existing information for this configuration. + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkInformation.find(key); + if (i == this->LinkInformation.end()) { + // Compute information for this configuration. + auto info = cm::make_unique<cmComputeLinkInformation>(this, config); + if (info && !info->Compute()) { + info.reset(); + } + + // Store the information for this configuration. + i = this->LinkInformation.emplace(key, std::move(info)).first; + + if (i->second) { + this->CheckPropertyCompatibility(*i->second, config); + } + } + return i->second.get(); +} + +void cmGeneratorTarget::CheckLinkLibraries() const +{ + bool linkLibrariesOnlyTargets = + this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); + + // Evaluate the link interface of this target if needed for extra checks. + if (linkLibrariesOnlyTargets) { + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configs) { + this->GetLinkInterfaceLibraries(config, this, UseTo::Link); + } + } + + // Check link the implementation for each generated configuration. + for (auto const& hmp : this->LinkImplMap) { + HeadToLinkImplementationMap const& hm = hmp.second; + // There could be several entries used when computing the pre-CMP0022 + // default link interface. Check only the entry for our own link impl. + auto const hmi = hm.find(this); + if (hmi == hm.end() || !hmi->second.LibrariesDone) { + continue; + } + for (cmLinkImplItem const& item : hmi->second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { + return; + } + } + } + + // Check link the interface for each generated combination of + // configuration and consuming head target. We should not need to + // consider LinkInterfaceUsageRequirementsOnlyMap because its entries + // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). + for (auto const& hmp : this->LinkInterfaceMap) { + for (auto const& hmi : hmp.second) { + if (!hmi.second.LibrariesDone) { + continue; + } + for (cmLinkItem const& item : hmi.second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { + return; + } + } + } + } +} + +namespace { +cm::string_view missingTargetPossibleReasons = + "Possible reasons include:\n" + " * There is a typo in the target name.\n" + " * A find_package call is missing for an IMPORTED target.\n" + " * An ALIAS target is missing.\n"_s; +} + +bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || + item.AsStr().find("::") == std::string::npos) { + return true; + } + MessageType messageType = MessageType::FATAL_ERROR; + std::string e; + switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { + case cmPolicies::WARN: { + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (role == LinkItemRole::Implementation) { + e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); + } else { + e = cmStrCat(e, "The link interface of target \"", this->GetName(), + "\" contains"); + } + e = + cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, + backtrace); + return false; +} + +bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target) { + return true; + } + std::string const& str = item.AsStr(); + if (!str.empty() && + (str[0] == '-' || str[0] == '$' || str[0] == '`' || + str.find_first_of("/\\") != std::string::npos || + cmHasPrefix(str, "<LINK_LIBRARY:"_s) || + cmHasPrefix(str, "<LINK_GROUP:"_s))) { + return true; + } + + std::string e = cmStrCat("Target \"", this->GetName(), + "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", + role == LinkItemRole::Implementation + ? "it links to" + : "its link interface contains", + ":\n ", item.AsStr(), "\nwhich is not a target. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e, backtrace); + return false; +} + +bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, + cmLocalGenerator const*& lg) const +{ + if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { + cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); + if (dirId.String.empty()) { + lg = this->LocalGenerator; + return true; + } + if (cmLocalGenerator const* otherLG = + this->GlobalGenerator->FindLocalGenerator(dirId)) { + lg = otherLG; + return true; + } + } + return false; +} + +cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( + std::string const& n, cmListFileBacktrace const& bt, + std::string const& linkFeature, LookupLinkItemScope* scope, + LookupSelf lookupSelf) const +{ + cm::optional<cmLinkItem> maybeItem; + if (this->IsLinkLookupScope(n, scope->LG)) { + return maybeItem; + } + + std::string name = this->CheckCMP0004(n); + if (name.empty() || + (lookupSelf == LookupSelf::No && name == this->GetName())) { + return maybeItem; + } + maybeItem = + this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); + return maybeItem; +} + +void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, + cmBTStringRange entries, + std::string const& config, + cmGeneratorTarget const* headTarget, + UseTo usage, LinkInterfaceField field, + cmLinkInterface& iface) const +{ + if (entries.empty()) { + return; + } + // Keep this logic in sync with ComputeLinkImplementationLibraries. + cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr, + this->LocalGenerator); + // The $<LINK_ONLY> expression may be in a link interface to specify + // private link dependencies that are otherwise excluded from usage + // requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + } + cmMakefile const* mf = this->LocalGenerator->GetMakefile(); + LookupLinkItemScope scope{ this->LocalGenerator }; + for (BT<std::string> const& entry : entries) { + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, + &dagChecker, this, + headTarget->LinkerLanguage) }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : libs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + lib, cge->GetBacktrace(), linkFeature, &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(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + iface.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + iface.Libraries.emplace_back(std::move(item)); + } + } + if (cge->GetHadHeadSensitiveCondition()) { + iface.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + iface.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + iface.HadLinkLanguageSensitiveCondition = true; + } + } +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head) const +{ + return this->GetLinkInterface(config, head, false); +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head, + bool secondPass) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, UseTo::Link, secondPass); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, UseTo::Link); + } + if (!iface.AllDone) { + iface.AllDone = true; + if (iface.Exists) { + this->ComputeLinkInterface(config, iface, head, secondPass); + this->ComputeLinkInterfaceRuntimeLibraries(config, iface); + } + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget) const +{ + this->ComputeLinkInterface(config, iface, headTarget, false); +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, bool secondPass) const +{ + if (iface.Explicit) { + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->GetType() == cmStateEnums::STATIC_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + // Shared libraries may have runtime implementation dependencies + // on other shared libraries that are not in the interface. + std::set<cmLinkItem> emitted; + for (cmLinkItem const& lib : iface.Libraries) { + emitted.insert(lib); + } + if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + if (emitted.insert(lib).second) { + if (lib.Target) { + // This is a runtime dependency on another shared library. + if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { + iface.SharedDeps.push_back(lib); + } + } else { + // TODO: Recognize shared library file names. Perhaps this + // should be moved to cmComputeLinkInformation, but that + // creates a chicken-and-egg problem since this list is needed + // for its construction. + } + } + } + } + } + } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || + this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { + // The link implementation is the default link interface. + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + UseTo::Link); + iface.ImplementationIsInterface = true; + iface.WrongConfigLibraries = impl->WrongConfigLibraries; + } + + if (this->LinkLanguagePropagatesToDependents()) { + // Targets using this archive need its language runtime libraries. + if (cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass)) { + iface.Languages = impl->Languages; + } + } + + if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // How many repetitions are needed if this library has cyclic + // dependencies? + std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); + if (cmValue config_reps = this->GetProperty(propName)) { + sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); + } else if (cmValue reps = + this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { + sscanf(reps->c_str(), "%u", &iface.Multiplicity); + } + } +} + +const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, usage); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, usage); + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterfaceLibraries( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, UseTo usage) const +{ + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // An explicit list of interface libraries may be set for shared + // libraries and executables that export symbols. + 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. + 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. + 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 (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 (!haveExplicitLibraries && + (this->GetType() == cmStateEnums::EXECUTABLE || + (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { + return; + } + iface.Exists = true; + + // 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 || 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, usage, LinkInterfaceField::Libraries, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + this->Target->GetLinkInterfaceDirectEntries(), + config, headTarget, usage, + LinkInterfaceField::HeadInclude, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + this->Target->GetLinkInterfaceDirectExcludeEntries(), + config, headTarget, usage, + 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, usage, + LinkInterfaceField::Libraries, iface); + } + + // If the link interface is explicit, do not fall back to the link impl. + if (iface.Explicit) { + return; + } + + // The link implementation is the default link interface. + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + usage)) { + iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), + impl->Libraries.end()); + if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022 && usage == UseTo::Link) { + // Compare the link implementation fallback link interface to the + // preferred new link interface property and warn if different. + cmLinkInterface ifaceNew; + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, usage, LinkInterfaceField::Libraries, ifaceNew); + if (ifaceNew.Libraries != iface.Libraries) { + std::string oldLibraries = cmJoin(impl->Libraries, ";"); + std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); + if (oldLibraries.empty()) { + oldLibraries = "(empty)"; + } + if (newLibraries.empty()) { + newLibraries = "(empty)"; + } + + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property. " + "This should be preferred as the source of the link interface " + "for this library but because CMP0022 is not set CMake is " + "ignoring the property and using the link implementation " + "as the link interface instead." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << newLibraries << "\n" + "Link implementation:\n" + " " << oldLibraries << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } + } + } +} + +namespace { + +template <typename ReturnType> +ReturnType constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt); + +template <> +inline cmLinkImplItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkImplItem(cmLinkItem(target, false, bt), false); +} + +template <> +inline cmLinkItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkItem(target, false, bt); +} + +template <typename ValueType> +std::vector<ValueType> computeImplicitLanguageTargets( + std::string const& lang, std::string const& config, + cmGeneratorTarget const* currentTarget) +{ + cmListFileBacktrace bt; + std::vector<ValueType> result; + cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); + + std::string const& runtimeLibrary = + currentTarget->GetRuntimeLinkLibrary(lang, config); + if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( + "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { + cmList libsList{ *runtimeLinkOptions }; + result.reserve(libsList.size()); + + for (auto const& i : libsList) { + cmGeneratorTarget::TargetOrString resolved = + currentTarget->ResolveTargetReference(i, lg); + if (resolved.Target) { + result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); + } + } + } + + return result; +} +} + +void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( + const std::string& config, cmOptionalLinkInterface& iface) const +{ + for (std::string const& lang : iface.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + iface.LanguageRuntimeLibraries.find(lang) == + iface.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); + iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( + const std::string& config, cmOptionalLinkImplementation& impl) const +{ + for (std::string const& lang : impl.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + impl.LanguageRuntimeLibraries.find(lang) == + impl.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); + impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( + const std::string& config, cmGeneratorTarget const* headTarget, UseTo usage, + bool secondPass) const +{ + cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); + if (!info) { + return nullptr; + } + + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + 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, usage, LinkInterfaceField::HeadInclude, + iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + cmMakeRange(info->LibrariesHeadExclude), config, + headTarget, usage, LinkInterfaceField::HeadExclude, + iface); + this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), + config, headTarget, usage, + LinkInterfaceField::Libraries, iface); + cmList deps{ info->SharedDeps }; + LookupLinkItemScope scope{ this->LocalGenerator }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& dep : deps) { + if (auto maybeLinkFeature = ParseLinkFeature(dep)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { + iface.SharedDeps.emplace_back(std::move(*maybeItem)); + } + } + } + + return &iface; +} + +cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( + const std::string& config) const +{ + return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; +} + +cmHeadToLinkInterfaceMap& +cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( + const std::string& config) const +{ + return this + ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage) const +{ + return this->GetLinkImplementation(config, usage, false); +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage, bool secondPass) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + cmOptionalLinkImplementation& impl = hm[this]; + if (secondPass) { + impl = cmOptionalLinkImplementation(); + } + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, this, usage); + } + if (!impl.LanguagesDone) { + impl.LanguagesDone = true; + this->ComputeLinkImplementationLanguages(config, impl); + this->ComputeLinkImplementationRuntimeLibraries(config, impl); + } + return &impl; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationMap( + std::string const& config) const +{ + return this->LinkImplMap[cmSystemTools::UpperCase(config)]; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( + std::string const& config) const +{ + return this + ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibraries(const std::string& config, + UseTo usage) const +{ + return this->GetLinkImplementationLibrariesInternal(config, this, usage); +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibrariesInternal( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + // Populate the link implementation libraries for this configuration. + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + + // If the link implementation does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkImplementation& impl = hm[head]; + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, head, usage); + } + return &impl; +} + +namespace { +class TransitiveLinkImpl +{ + cmGeneratorTarget const* Self; + std::string const& Config; + UseTo ImplFor; + 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, + UseTo usage, cmLinkImplementation& impl) + : Self(self) + , Config(config) + , ImplFor(usage) + , 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, this->ImplFor); + 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, UseTo usage, + cmLinkImplementation& impl) +{ + TransitiveLinkImpl transitiveLinkImpl(self, config, usage, impl); + transitiveLinkImpl.Compute(); +} +} + +void cmGeneratorTarget::ComputeLinkImplementationLibraries( + const std::string& config, cmOptionalLinkImplementation& impl, + cmGeneratorTarget const* head, UseTo usage) const +{ + cmLocalGenerator const* lg = this->LocalGenerator; + cmMakefile const* mf = lg->GetMakefile(); + cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); + auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; + // Collect libraries directly linked in this configuration. + for (auto const& entry : entryRange) { + // Keep this logic in sync with ExpandLinkItems. + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, + nullptr, this->LocalGenerator); + // The $<LINK_ONLY> expression may be used to specify link dependencies + // that are otherwise excluded from usage requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + switch (this->GetPolicyStatusCMP0131()) { + case cmPolicies::WARN: + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + break; + } + } + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> const cge = + ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + std::string const& evaluated = + cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, + this->LinkerLanguage); + bool const checkCMP0027 = evaluated != entry.Value; + cmList llibs(evaluated); + if (cge->GetHadHeadSensitiveCondition()) { + impl.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + impl.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + impl.HadLinkLanguageSensitiveCondition = true; + } + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : llibs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (this->IsLinkLookupScope(lib, lg)) { + continue; + } + + // Skip entries that resolve to the target itself or are empty. + std::string name = this->CheckCMP0004(lib); + if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { + // resolve alias name + auto* target = this->Makefile->FindTargetToUse(name); + if (target) { + name = target->GetName(); + } + } + if (name == this->GetName() || name.empty()) { + if (name == this->GetName()) { + bool noMessage = false; + MessageType messageType = MessageType::FATAL_ERROR; + std::ostringstream e; + switch (this->GetPolicyStatusCMP0038()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (!noMessage) { + e << "Target \"" << this->GetName() << "\" links to itself."; + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + messageType, e.str(), this->GetBacktrace()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + continue; + } + + // The entry is meant for this configuration. + cmLinkItem item = this->ResolveLinkItem( + BT<std::string>(name, entry.Backtrace), lg, linkFeature); + if (item.Target) { + auto depsForTarget = synthTargetsForConfig.find(item.Target); + if (depsForTarget != synthTargetsForConfig.end()) { + for (auto const* depForTarget : depsForTarget->second) { + cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); + impl.Libraries.emplace_back(std::move(synthItem), false); + } + } + } else { + // Report explicitly linked object files separately. + std::string const& maybeObj = item.AsStr(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + impl.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + impl.Libraries.emplace_back(std::move(item), checkCMP0027); + } + + std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); + for (std::string const& sp : seenProps) { + if (!this->GetProperty(sp)) { + this->LinkImplicitNullProperties.insert(sp); + } + } + cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); + } + + // Update the list of direct link dependencies from usage requirements. + if (head == this) { + ComputeLinkImplTransitive(this, config, usage, impl); + } + + // Get the list of configurations considered to be DEBUG. + std::vector<std::string> debugConfigs = + this->Makefile->GetCMakeInstance()->GetDebugConfigs(); + + cmTargetLinkLibraryType linkType = + CMP0003_ComputeLinkType(config, debugConfigs); + cmTarget::LinkLibraryVectorType const& oldllibs = + this->Target->GetOriginalLinkLibraries(); + + auto linkFeature = cmLinkItem::DEFAULT; + for (cmTarget::LibraryID const& oldllib : oldllibs) { + if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { + std::string name = this->CheckCMP0004(oldllib.first); + if (name == this->GetName() || name.empty()) { + continue; + } + // Support OLD behavior for CMP0003. + impl.WrongConfigLibraries.push_back( + this->ResolveLinkItem(BT<std::string>(name), linkFeature)); + } + } +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name) const +{ + return this->ResolveTargetReference(name, this->LocalGenerator); +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name, cmLocalGenerator const* lg) const +{ + TargetOrString resolved; + + if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { + resolved.Target = tgt; + } else { + resolved.String = name; + } + + return resolved; +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, std::string const& linkFeature) const +{ + return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, cmLocalGenerator const* lg, + std::string const& linkFeature) const +{ + auto bt = name.Backtrace; + TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); + + if (!resolved.Target) { + return cmLinkItem(resolved.String, false, bt, linkFeature); + } + + // Check deprecation, issue message with `bt` backtrace. + if (resolved.Target->IsDeprecated()) { + std::ostringstream w; + /* clang-format off */ + w << + "The library that is being linked to, " << resolved.Target->GetName() << + ", is marked as being deprecated by the owner. The message provided by " + "the developer is: \n" << resolved.Target->GetDeprecation() << "\n"; + /* clang-format on */ + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, w.str(), bt); + } + + // Skip targets that will not really be linked. This is probably a + // name conflict between an external library and an executable + // within the project. + if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && + !resolved.Target->IsExecutableWithExports()) { + return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); + } + + return cmLinkItem(resolved.Target, false, bt, linkFeature); +} @@ -383,6 +383,7 @@ CMAKE_CXX_SOURCES="\ cmGeneratorTarget \ cmGeneratorTarget_CompatibleInterface \ cmGeneratorTarget_IncludeDirectories \ + cmGeneratorTarget_Link \ cmGeneratorTarget_LinkDirectories \ cmGeneratorTarget_Options \ cmGeneratorTarget_Sources \ |