فهرست منبع

CMP0028: Report the target whose link interface has an offending item

Previously items linked via the link interface of a dependency were
reported in CMP0028 messages as if directly linked by a target.
Clarify the messages to indicate that an offending item is actually
in the link interface of a given target, regardless of its consumer.

Move the check to the end of generation and look through the final set
of link implementations and link interfaces that were used for
generation.  This avoids repeating messages on link interfaces that
have multiple consumers.
Brad King 3 سال پیش
والد
کامیت
ea050286e7

+ 78 - 36
Source/cmGeneratorTarget.cxx

@@ -2687,7 +2687,6 @@ public:
     : Config(std::move(config))
     : Config(std::move(config))
     , Languages(languages)
     , Languages(languages)
     , HeadTarget(head)
     , HeadTarget(head)
-    , Target(target)
     , SecondPass(secondPass)
     , SecondPass(secondPass)
   {
   {
     this->Visited.insert(target);
     this->Visited.insert(target);
@@ -2696,40 +2695,6 @@ public:
   void Visit(cmLinkItem const& item)
   void Visit(cmLinkItem const& item)
   {
   {
     if (!item.Target) {
     if (!item.Target) {
-      if (item.AsStr().find("::") != std::string::npos) {
-        bool noMessage = false;
-        MessageType messageType = MessageType::FATAL_ERROR;
-        std::ostringstream e;
-        switch (this->Target->GetLocalGenerator()->GetPolicyStatus(
-          cmPolicies::CMP0028)) {
-          case cmPolicies::WARN: {
-            e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0028) << "\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->Target->GetName()
-            << "\" links to target \"" << item.AsStr()
-            << "\" but the target was not found.  Perhaps a find_package() "
-               "call is missing for an IMPORTED target, or an ALIAS target is "
-               "missing?";
-          cmListFileBacktrace backtrace = item.Backtrace;
-          if (backtrace.Empty()) {
-            backtrace = this->Target->GetBacktrace();
-          }
-          this->Target->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
-            messageType, e.str(), backtrace);
-        }
-      }
       return;
       return;
     }
     }
     if (!this->Visited.insert(item.Target).second) {
     if (!this->Visited.insert(item.Target).second) {
@@ -2762,7 +2727,6 @@ private:
   std::string Config;
   std::string Config;
   std::unordered_set<std::string>& Languages;
   std::unordered_set<std::string>& Languages;
   cmGeneratorTarget const* HeadTarget;
   cmGeneratorTarget const* HeadTarget;
-  const cmGeneratorTarget* Target;
   std::set<cmGeneratorTarget const*> Visited;
   std::set<cmGeneratorTarget const*> Visited;
   bool SecondPass;
   bool SecondPass;
   bool HadLinkLanguageSensitiveCondition = false;
   bool HadLinkLanguageSensitiveCondition = false;
@@ -6281,6 +6245,84 @@ cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation(
   return i->second.get();
   return i->second.get();
 }
 }
 
 
+void cmGeneratorTarget::CheckLinkLibraries() const
+{
+  // 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;
+      }
+    }
+  }
+
+  // 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;
+        }
+      }
+    }
+  }
+}
+
+bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role,
+                                             cmLinkItem const& item) const
+{
+  if (item.Target || 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.  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");
+  cmListFileBacktrace backtrace = item.Backtrace;
+  if (backtrace.Empty()) {
+    backtrace = this->GetBacktrace();
+  }
+  this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e,
+                                                              backtrace);
+  return false;
+}
+
 void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const
 void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const
 {
 {
   int patch;
   int patch;

+ 11 - 0
Source/cmGeneratorTarget.h

@@ -84,6 +84,10 @@ public:
   cmComputeLinkInformation* GetLinkInformation(
   cmComputeLinkInformation* GetLinkInformation(
     const std::string& config) const;
     const std::string& config) const;
 
 
+  // Perform validation checks on memoized link structures.
+  // Call this after generation is complete.
+  void CheckLinkLibraries() const;
+
   cmStateEnums::TargetType GetType() const;
   cmStateEnums::TargetType GetType() const;
   const std::string& GetName() const;
   const std::string& GetName() const;
   std::string GetExportName() const;
   std::string GetExportName() const;
@@ -973,6 +977,13 @@ private:
   cmLinkImplementation const* GetLinkImplementation(const std::string& config,
   cmLinkImplementation const* GetLinkImplementation(const std::string& config,
                                                     bool secondPass) const;
                                                     bool secondPass) const;
 
 
+  enum class LinkItemRole
+  {
+    Implementation,
+    Interface,
+  };
+  bool VerifyLinkItemColons(LinkItemRole role, cmLinkItem const& item) const;
+
   // Cache import information from properties for each configuration.
   // Cache import information from properties for each configuration.
   struct ImportInfo
   struct ImportInfo
   {
   {

+ 15 - 0
Source/cmGlobalGenerator.cxx

@@ -328,6 +328,18 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const
   return failed;
   return failed;
 }
 }
 
 
+void cmGlobalGenerator::CheckTargetLinkLibraries() const
+{
+  for (const auto& generator : this->LocalGenerators) {
+    for (const auto& gt : generator->GetGeneratorTargets()) {
+      gt->CheckLinkLibraries();
+    }
+    for (const auto& gt : generator->GetOwnedImportedGeneratorTargets()) {
+      gt->CheckLinkLibraries();
+    }
+  }
+}
+
 bool cmGlobalGenerator::CheckTargetsForType() const
 bool cmGlobalGenerator::CheckTargetsForType() const
 {
 {
   if (!this->GetLanguageEnabled("Swift")) {
   if (!this->GetLanguageEnabled("Swift")) {
@@ -1606,6 +1618,9 @@ void cmGlobalGenerator::Generate()
     this->ExtraGenerator->Generate();
     this->ExtraGenerator->Generate();
   }
   }
 
 
+  // Perform validation checks on memoized link structures.
+  this->CheckTargetLinkLibraries();
+
   if (!this->CMP0042WarnTargets.empty()) {
   if (!this->CMP0042WarnTargets.empty()) {
     std::ostringstream w;
     std::ostringstream w;
     w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0042) << "\n";
     w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0042) << "\n";

+ 1 - 0
Source/cmGlobalGenerator.h

@@ -684,6 +684,7 @@ private:
 
 
   virtual void ForceLinkerLanguages();
   virtual void ForceLinkerLanguages();
 
 
+  void CheckTargetLinkLibraries() const;
   bool CheckTargetsForMissingSources() const;
   bool CheckTargetsForMissingSources() const;
   bool CheckTargetsForType() const;
   bool CheckTargetsForType() const;
   bool CheckTargetsForPchCompilePdb() const;
   bool CheckTargetsForPchCompilePdb() const;

+ 5 - 0
Source/cmLocalGenerator.h

@@ -193,6 +193,11 @@ public:
     return this->GeneratorTargets;
     return this->GeneratorTargets;
   }
   }
 
 
+  const GeneratorTargetVector& GetOwnedImportedGeneratorTargets() const
+  {
+    return this->OwnedImportedGeneratorTargets;
+  }
+
   void AddGeneratorTarget(std::unique_ptr<cmGeneratorTarget> gt);
   void AddGeneratorTarget(std::unique_ptr<cmGeneratorTarget> gt);
   void AddImportedGeneratorTarget(cmGeneratorTarget* gt);
   void AddImportedGeneratorTarget(cmGeneratorTarget* gt);
   void AddOwnedImportedGeneratorTarget(std::unique_ptr<cmGeneratorTarget> gt);
   void AddOwnedImportedGeneratorTarget(std::unique_ptr<cmGeneratorTarget> gt);

+ 9 - 3
Tests/RunCMake/CMP0028/CMP0028-NEW-iface-stderr.txt

@@ -1,6 +1,12 @@
 CMake Error at CMP0028-NEW-iface\.cmake:5 \(target_link_libraries\):
 CMake Error at CMP0028-NEW-iface\.cmake:5 \(target_link_libraries\):
-  Target "foo" links to target "External::Library" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+  The link interface of target "iface" contains:
+
+    External::Library
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)

+ 9 - 3
Tests/RunCMake/CMP0028/CMP0028-NEW-stderr.txt

@@ -1,6 +1,12 @@
 CMake Error at CMP0028-NEW\.cmake:5 \(target_link_libraries\):
 CMake Error at CMP0028-NEW\.cmake:5 \(target_link_libraries\):
-  Target "foo" links to target "External::Library" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+  Target "foo" links to:
+
+    External::Library
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)

+ 9 - 3
Tests/RunCMake/CMP0028/CMP0028-WARN-iface-stderr.txt

@@ -3,9 +3,15 @@ CMake Warning \(dev\) at CMP0028-WARN-iface\.cmake:3 \(target_link_libraries\):
   IMPORTED target.  Run "cmake --help-policy CMP0028" for policy details.
   IMPORTED target.  Run "cmake --help-policy CMP0028" for policy details.
   Use the cmake_policy command to set the policy and suppress this warning.
   Use the cmake_policy command to set the policy and suppress this warning.
 
 
-  Target "foo" links to target "External::Library" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+  The link interface of target "iface" contains:
+
+    External::Library
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.
 This warning is for project developers.  Use -Wno-dev to suppress it.

+ 9 - 3
Tests/RunCMake/CMP0028/CMP0028-WARN-stderr.txt

@@ -3,9 +3,15 @@ CMake Warning \(dev\) at CMP0028-WARN\.cmake:3 \(target_link_libraries\):
   IMPORTED target.  Run "cmake --help-policy CMP0028" for policy details.
   IMPORTED target.  Run "cmake --help-policy CMP0028" for policy details.
   Use the cmake_policy command to set the policy and suppress this warning.
   Use the cmake_policy command to set the policy and suppress this warning.
 
 
-  Target "foo" links to target "External::Library" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+  Target "foo" links to:
+
+    External::Library
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.
 This warning is for project developers.  Use -Wno-dev to suppress it.

+ 9 - 3
Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus-stderr.txt

@@ -1,6 +1,12 @@
 ^CMake Error at CMP0079-link-NEW-bogus\.cmake:6 \(set_property\):
 ^CMake Error at CMP0079-link-NEW-bogus\.cmake:6 \(set_property\):
-  Target "top" links to target "::@\(0xdeadbeef\)" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+  Target "top" links to:
+
+    ::@\(0xdeadbeef\)
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)

+ 20 - 8
Tests/RunCMake/target_link_libraries/ConfigCase-stderr.txt

@@ -1,13 +1,25 @@
-^CMake Error at ConfigCase\.cmake:6 \(target_link_libraries\):
-  Target "impl" links to target "config::impl-Debug" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+^CMake Error at ConfigCase\.cmake:4 \(target_link_libraries\):
+  The link interface of target "iface" contains:
+
+    config::iface-Debug
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)
 +
 +
-CMake Error at ConfigCase\.cmake:4 \(target_link_libraries\):
-  Target "impl" links to target "config::iface-Debug" but the target was not
-  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
-  an ALIAS target is missing\?
+CMake Error at ConfigCase\.cmake:6 \(target_link_libraries\):
+  Target "impl" links to:
+
+    config::impl-Debug
+
+  but the target was not found.  Possible reasons include:
+(
+    \*[^
+]+)*
+
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
   CMakeLists\.txt:[0-9]+ \(include\)