|
@@ -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);
|
|
|
+}
|