Browse Source

Merge topic 'out-of-dir-link-list'

f0e67da061 target_link_libraries: Fix out-of-dir linking of a list of targets
acee629103 cmTargetLinkLibrariesCommand: Move HandleLibrary to helper struct
ba675f1ecc Tests: Enable CMP0022 in ExportImport out-of-dir linking case

Acked-by: Kitware Robot <[email protected]>
Merge-request: !4226
Brad King 6 years ago
parent
commit
a7fca06c46

+ 3 - 3
Help/prop_tgt/LINK_LIBRARIES_INDIRECTION.txt

@@ -1,9 +1,9 @@
 .. note::
 .. note::
   A call to :command:`target_link_libraries(<target> ...)` may update this
   A call to :command:`target_link_libraries(<target> ...)` may update this
   property on ``<target>``.  If ``<target>`` was not created in the same
   property on ``<target>``.  If ``<target>`` was not created in the same
-  directory as the call then :command:`target_link_libraries` will add a
-  suffix of the form ``::@(directory-id)`` to each entry, where the
-  ``::@`` is a separator and the ``(directory-id)`` is unspecified.
+  directory as the call then :command:`target_link_libraries` will wrap each
+  entry with the form ``::@(directory-id);...;::@``, where the ``::@`` is
+  literal and the ``(directory-id)`` is unspecified.
   This tells the generators that the named libraries must be looked up in
   This tells the generators that the named libraries must be looked up in
   the scope of the caller rather than in the scope in which the
   the scope of the caller rather than in the scope in which the
   ``<target>`` was created.  Valid directory ids are stripped on export
   ``<target>`` was created.  Valid directory ids are stripped on export

+ 4 - 0
Source/cmExportFileGenerator.cxx

@@ -14,6 +14,7 @@
 #include "cmComputeLinkInformation.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
 #include "cmLinkItem.h"
 #include "cmLinkItem.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
@@ -640,6 +641,9 @@ void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
   std::string sep;
   std::string sep;
   input.clear();
   input.clear();
   for (std::string& li : parts) {
   for (std::string& li : parts) {
+    if (cmHasLiteralPrefix(li, CMAKE_DIRECTORY_ID_SEP)) {
+      continue;
+    }
     if (cmGeneratorExpression::Find(li) == std::string::npos) {
     if (cmGeneratorExpression::Find(li) == std::string::npos) {
       this->AddTargetNamespace(li, target, missingTargets);
       this->AddTargetNamespace(li, target, missingTargets);
     } else {
     } else {

+ 46 - 31
Source/cmGeneratorTarget.cxx

@@ -5407,16 +5407,39 @@ void cmGeneratorTarget::ReportPropertyOrigin(
                                                          areport);
                                                          areport);
 }
 }
 
 
+bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n,
+                                          cmLocalGenerator const*& lg) const
+{
+  if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) {
+    cmDirectoryId const dirId = n.substr(sizeof(CMAKE_DIRECTORY_ID_SEP) - 1);
+    if (dirId.String.empty()) {
+      lg = this->LocalGenerator;
+      return true;
+    }
+    if (cmLocalGenerator const* otherLG =
+          this->GlobalGenerator->FindLocalGenerator(dirId)) {
+      lg = otherLG;
+      return true;
+    }
+  }
+  return false;
+}
+
 void cmGeneratorTarget::LookupLinkItems(std::vector<std::string> const& names,
 void cmGeneratorTarget::LookupLinkItems(std::vector<std::string> const& names,
                                         cmListFileBacktrace const& bt,
                                         cmListFileBacktrace const& bt,
                                         std::vector<cmLinkItem>& items) const
                                         std::vector<cmLinkItem>& items) const
 {
 {
+  cmLocalGenerator const* lg = this->LocalGenerator;
   for (std::string const& n : names) {
   for (std::string const& n : names) {
+    if (this->IsLinkLookupScope(n, lg)) {
+      continue;
+    }
+
     std::string name = this->CheckCMP0004(n);
     std::string name = this->CheckCMP0004(n);
     if (name == this->GetName() || name.empty()) {
     if (name == this->GetName() || name.empty()) {
       continue;
       continue;
     }
     }
-    items.push_back(this->ResolveLinkItem(name, bt));
+    items.push_back(this->ResolveLinkItem(name, bt, lg));
   }
   }
 }
 }
 
 
@@ -5425,6 +5448,7 @@ void cmGeneratorTarget::ExpandLinkItems(
   cmGeneratorTarget const* headTarget, bool usage_requirements_only,
   cmGeneratorTarget const* headTarget, bool usage_requirements_only,
   std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition) const
   std::vector<cmLinkItem>& items, bool& hadHeadSensitiveCondition) const
 {
 {
+  // Keep this logic in sync with ComputeLinkImplementationLibraries.
   cmGeneratorExpression ge;
   cmGeneratorExpression ge;
   cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
   cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr);
   // The $<LINK_ONLY> expression may be in a link interface to specify private
   // The $<LINK_ONLY> expression may be in a link interface to specify private
@@ -6500,6 +6524,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
   const std::string& config, cmOptionalLinkImplementation& impl,
   const std::string& config, cmOptionalLinkImplementation& impl,
   cmGeneratorTarget const* head) const
   cmGeneratorTarget const* head) const
 {
 {
+  cmLocalGenerator const* lg = this->LocalGenerator;
   cmStringRange entryRange = this->Target->GetLinkImplementationEntries();
   cmStringRange entryRange = this->Target->GetLinkImplementationEntries();
   cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces();
   cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces();
   cmBacktraceRange::const_iterator btIt = btRange.begin();
   cmBacktraceRange::const_iterator btIt = btRange.begin();
@@ -6508,6 +6533,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
                                      end = entryRange.end();
                                      end = entryRange.end();
        le != end; ++le, ++btIt) {
        le != end; ++le, ++btIt) {
     std::vector<std::string> llibs;
     std::vector<std::string> llibs;
+    // Keep this logic in sync with ExpandLinkItems.
     cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
     cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
                                                nullptr);
                                                nullptr);
     cmGeneratorExpression ge(*btIt);
     cmGeneratorExpression ge(*btIt);
@@ -6520,6 +6546,10 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
     }
     }
 
 
     for (std::string const& lib : llibs) {
     for (std::string const& lib : llibs) {
+      if (this->IsLinkLookupScope(lib, lg)) {
+        continue;
+      }
+
       // Skip entries that resolve to the target itself or are empty.
       // Skip entries that resolve to the target itself or are empty.
       std::string name = this->CheckCMP0004(lib);
       std::string name = this->CheckCMP0004(lib);
       if (name == this->GetName() || name.empty()) {
       if (name == this->GetName() || name.empty()) {
@@ -6554,7 +6584,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
       }
       }
 
 
       // The entry is meant for this configuration.
       // The entry is meant for this configuration.
-      impl.Libraries.emplace_back(this->ResolveLinkItem(name, *btIt),
+      impl.Libraries.emplace_back(this->ResolveLinkItem(name, *btIt, lg),
                                   evaluated != *le);
                                   evaluated != *le);
     }
     }
 
 
@@ -6591,38 +6621,16 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
 cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
   std::string const& name) const
   std::string const& name) const
 {
 {
-  cmLocalGenerator const* lg = this->LocalGenerator;
-  std::string const* lookupName = &name;
-
-  // When target_link_libraries() is called with a LHS target that is
-  // not created in the calling directory it adds a directory id suffix
-  // that we can use to look up the calling directory.  It is that scope
-  // in which the item name is meaningful.  This case is relatively rare
-  // so we allocate a separate string only when the directory id is present.
-  std::string::size_type pos = name.find(CMAKE_DIRECTORY_ID_SEP);
-  std::string plainName;
-  if (pos != std::string::npos) {
-    // We will look up the plain name without the directory id suffix.
-    plainName = name.substr(0, pos);
-
-    // We will look up in the scope of the directory id.
-    // If we do not recognize the id then leave the original
-    // syntax in place to produce an indicative error later.
-    cmDirectoryId const dirId =
-      name.substr(pos + sizeof(CMAKE_DIRECTORY_ID_SEP) - 1);
-    if (cmLocalGenerator const* otherLG =
-          this->GlobalGenerator->FindLocalGenerator(dirId)) {
-      lg = otherLG;
-      lookupName = &plainName;
-    }
-  }
+  return this->ResolveTargetReference(name, this->LocalGenerator);
+}
 
 
+cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
+  std::string const& name, cmLocalGenerator const* lg) const
+{
   TargetOrString resolved;
   TargetOrString resolved;
 
 
-  if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(*lookupName)) {
+  if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) {
     resolved.Target = tgt;
     resolved.Target = tgt;
-  } else if (lookupName == &plainName) {
-    resolved.String = std::move(plainName);
   } else {
   } else {
     resolved.String = name;
     resolved.String = name;
   }
   }
@@ -6633,7 +6641,14 @@ cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
   std::string const& name, cmListFileBacktrace const& bt) const
   std::string const& name, cmListFileBacktrace const& bt) const
 {
 {
-  TargetOrString resolved = this->ResolveTargetReference(name);
+  return this->ResolveLinkItem(name, bt, this->LocalGenerator);
+}
+
+cmLinkItem cmGeneratorTarget::ResolveLinkItem(std::string const& name,
+                                              cmListFileBacktrace const& bt,
+                                              cmLocalGenerator const* lg) const
+{
+  TargetOrString resolved = this->ResolveTargetReference(name, lg);
 
 
   if (!resolved.Target) {
   if (!resolved.Target) {
     return cmLinkItem(resolved.String, bt);
     return cmLinkItem(resolved.String, bt);

+ 8 - 0
Source/cmGeneratorTarget.h

@@ -378,9 +378,14 @@ public:
     cmGeneratorTarget* Target = nullptr;
     cmGeneratorTarget* Target = nullptr;
   };
   };
   TargetOrString ResolveTargetReference(std::string const& name) const;
   TargetOrString ResolveTargetReference(std::string const& name) const;
+  TargetOrString ResolveTargetReference(std::string const& name,
+                                        cmLocalGenerator const* lg) const;
 
 
   cmLinkItem ResolveLinkItem(std::string const& name,
   cmLinkItem ResolveLinkItem(std::string const& name,
                              cmListFileBacktrace const& bt) const;
                              cmListFileBacktrace const& bt) const;
+  cmLinkItem ResolveLinkItem(std::string const& name,
+                             cmListFileBacktrace const& bt,
+                             cmLocalGenerator const* lg) const;
 
 
   // Compute the set of languages compiled by the target.  This is
   // Compute the set of languages compiled by the target.  This is
   // computed every time it is called because the languages can change
   // computed every time it is called because the languages can change
@@ -919,6 +924,9 @@ private:
 
 
   std::unordered_set<std::string> UnityBatchedSourceFiles;
   std::unordered_set<std::string> UnityBatchedSourceFiles;
 
 
+  bool IsLinkLookupScope(std::string const& n,
+                         cmLocalGenerator const*& lg) const;
+
   void ExpandLinkItems(std::string const& prop, std::string const& value,
   void ExpandLinkItems(std::string const& prop, std::string const& value,
                        std::string const& config,
                        std::string const& config,
                        const cmGeneratorTarget* headTarget,
                        const cmGeneratorTarget* headTarget,

+ 3 - 10
Source/cmTarget.cxx

@@ -938,14 +938,7 @@ cmTarget::LinkLibraryVectorType const& cmTarget::GetOriginalLinkLibraries()
   return impl->OriginalLinkLibraries;
   return impl->OriginalLinkLibraries;
 }
 }
 
 
-void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib,
-                              cmTargetLinkLibraryType llt)
-{
-  this->AddLinkLibrary(mf, lib, lib, llt);
-}
-
 void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
 void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
-                              std::string const& libRef,
                               cmTargetLinkLibraryType llt)
                               cmTargetLinkLibraryType llt)
 {
 {
   cmTarget* tgt = mf.FindTargetToUse(lib);
   cmTarget* tgt = mf.FindTargetToUse(lib);
@@ -954,13 +947,13 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
 
 
     const std::string libName =
     const std::string libName =
       (isNonImportedTarget && llt != GENERAL_LibraryType)
       (isNonImportedTarget && llt != GENERAL_LibraryType)
-      ? targetNameGenex(libRef)
-      : libRef;
+      ? targetNameGenex(lib)
+      : lib;
     this->AppendProperty("LINK_LIBRARIES",
     this->AppendProperty("LINK_LIBRARIES",
                          this->GetDebugGeneratorExpressions(libName, llt));
                          this->GetDebugGeneratorExpressions(libName, llt));
   }
   }
 
 
-  if (cmGeneratorExpression::Find(lib) != std::string::npos || lib != libRef ||
+  if (cmGeneratorExpression::Find(lib) != std::string::npos ||
       (tgt &&
       (tgt &&
        (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
        (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
         tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) ||
         tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) ||

+ 1 - 3
Source/cmTarget.h

@@ -110,10 +110,8 @@ public:
   //! Clear the dependency information recorded for this target, if any.
   //! Clear the dependency information recorded for this target, if any.
   void ClearDependencyInformation(cmMakefile& mf);
   void ClearDependencyInformation(cmMakefile& mf);
 
 
-  void AddLinkLibrary(cmMakefile& mf, const std::string& lib,
-                      cmTargetLinkLibraryType llt);
   void AddLinkLibrary(cmMakefile& mf, std::string const& lib,
   void AddLinkLibrary(cmMakefile& mf, std::string const& lib,
-                      std::string const& libRef, cmTargetLinkLibraryType llt);
+                      cmTargetLinkLibraryType llt);
 
 
   enum TLLSignature
   enum TLLSignature
   {
   {

+ 118 - 82
Source/cmTargetLinkLibrariesCommand.cxx

@@ -5,6 +5,8 @@
 #include <cstring>
 #include <cstring>
 #include <memory>
 #include <memory>
 #include <sstream>
 #include <sstream>
+#include <unordered_set>
+#include <utility>
 
 
 #include "cmExecutionStatus.h"
 #include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
@@ -35,15 +37,30 @@ enum ProcessingState
 
 
 const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" };
 const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" };
 
 
+struct TLL
+{
+  cmMakefile& Makefile;
+  cmTarget* Target;
+  bool WarnRemoteInterface = false;
+  bool RejectRemoteLinking = false;
+  bool EncodeRemoteReference = false;
+  std::string DirectoryId;
+  std::unordered_set<std::string> Props;
+
+  TLL(cmMakefile& mf, cmTarget* target);
+  ~TLL();
+
+  bool HandleLibrary(ProcessingState currentProcessingState,
+                     const std::string& lib, cmTargetLinkLibraryType llt);
+  void AppendProperty(std::string const& prop, std::string const& value);
+  void AffectsProperty(std::string const& prop);
+};
+
 } // namespace
 } // namespace
 
 
 static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
 static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
                                             int right);
                                             int right);
 
 
-static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
-                          ProcessingState currentProcessingState,
-                          const std::string& lib, cmTargetLinkLibraryType llt);
-
 bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
 bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
                                   cmExecutionStatus& status)
                                   cmExecutionStatus& status)
 {
 {
@@ -148,6 +165,8 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
     return true;
     return true;
   }
   }
 
 
+  TLL tll(mf, target);
+
   // Keep track of link configuration specifiers.
   // Keep track of link configuration specifiers.
   cmTargetLinkLibraryType llt = GENERAL_LibraryType;
   cmTargetLinkLibraryType llt = GENERAL_LibraryType;
   bool haveLLT = false;
   bool haveLLT = false;
@@ -246,7 +265,7 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
     } else if (haveLLT) {
     } else if (haveLLT) {
       // The link type was specified by the previous argument.
       // The link type was specified by the previous argument.
       haveLLT = false;
       haveLLT = false;
-      if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) {
+      if (!tll.HandleLibrary(currentProcessingState, args[i], llt)) {
         return false;
         return false;
       }
       }
     } else {
     } else {
@@ -267,7 +286,7 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
           llt = OPTIMIZED_LibraryType;
           llt = OPTIMIZED_LibraryType;
         }
         }
       }
       }
-      if (!HandleLibrary(mf, target, currentProcessingState, args[i], llt)) {
+      if (!tll.HandleLibrary(currentProcessingState, args[i], llt)) {
         return false;
         return false;
       }
       }
     }
     }
@@ -310,21 +329,48 @@ static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
       "\" instead of a library name.  The first specifier will be ignored."));
       "\" instead of a library name.  The first specifier will be ignored."));
 }
 }
 
 
-static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
-                          ProcessingState currentProcessingState,
-                          const std::string& lib, cmTargetLinkLibraryType llt)
+namespace {
+
+TLL::TLL(cmMakefile& mf, cmTarget* target)
+  : Makefile(mf)
+  , Target(target)
+{
+  if (&this->Makefile != this->Target->GetMakefile()) {
+    // The LHS target was created in another directory.
+    switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0079)) {
+      case cmPolicies::WARN:
+        this->WarnRemoteInterface = true;
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        this->RejectRemoteLinking = true;
+        break;
+      case cmPolicies::REQUIRED_ALWAYS:
+      case cmPolicies::REQUIRED_IF_USED:
+      case cmPolicies::NEW:
+        this->EncodeRemoteReference = true;
+        break;
+    }
+  }
+  if (this->EncodeRemoteReference) {
+    cmDirectoryId const dirId = this->Makefile.GetDirectoryId();
+    this->DirectoryId = cmStrCat(CMAKE_DIRECTORY_ID_SEP, dirId.String);
+  }
+}
+
+bool TLL::HandleLibrary(ProcessingState currentProcessingState,
+                        const std::string& lib, cmTargetLinkLibraryType llt)
 {
 {
-  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
+  if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
       currentProcessingState != ProcessingKeywordLinkInterface) {
       currentProcessingState != ProcessingKeywordLinkInterface) {
-    mf.IssueMessage(
+    this->Makefile.IssueMessage(
       MessageType::FATAL_ERROR,
       MessageType::FATAL_ERROR,
       "INTERFACE library can only be used with the INTERFACE keyword of "
       "INTERFACE library can only be used with the INTERFACE keyword of "
       "target_link_libraries");
       "target_link_libraries");
     return false;
     return false;
   }
   }
-  if (target->IsImported() &&
+  if (this->Target->IsImported() &&
       currentProcessingState != ProcessingKeywordLinkInterface) {
       currentProcessingState != ProcessingKeywordLinkInterface) {
-    mf.IssueMessage(
+    this->Makefile.IssueMessage(
       MessageType::FATAL_ERROR,
       MessageType::FATAL_ERROR,
       "IMPORTED library can only be used with the INTERFACE keyword of "
       "IMPORTED library can only be used with the INTERFACE keyword of "
       "target_link_libraries");
       "target_link_libraries");
@@ -339,11 +385,12 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
      currentProcessingState == ProcessingKeywordLinkInterface)
      currentProcessingState == ProcessingKeywordLinkInterface)
     ? cmTarget::KeywordTLLSignature
     ? cmTarget::KeywordTLLSignature
     : cmTarget::PlainTLLSignature;
     : cmTarget::PlainTLLSignature;
-  if (!target->PushTLLCommandTrace(sig, mf.GetExecutionContext())) {
+  if (!this->Target->PushTLLCommandTrace(
+        sig, this->Makefile.GetExecutionContext())) {
     std::ostringstream e;
     std::ostringstream e;
     const char* modal = nullptr;
     const char* modal = nullptr;
     MessageType messageType = MessageType::AUTHOR_WARNING;
     MessageType messageType = MessageType::AUTHOR_WARNING;
-    switch (mf.GetPolicyStatus(cmPolicies::CMP0023)) {
+    switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0023)) {
       case cmPolicies::WARN:
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n";
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n";
         modal = "should";
         modal = "should";
@@ -364,77 +411,38 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
       e << "The " << existingSig
       e << "The " << existingSig
         << " signature for target_link_libraries has "
         << " signature for target_link_libraries has "
            "already been used with the target \""
            "already been used with the target \""
-        << target->GetName()
+        << this->Target->GetName()
         << "\".  All uses of target_link_libraries with a target " << modal
         << "\".  All uses of target_link_libraries with a target " << modal
         << " be either all-keyword or all-plain.\n";
         << " be either all-keyword or all-plain.\n";
-      target->GetTllSignatureTraces(e,
-                                    sig == cmTarget::KeywordTLLSignature
-                                      ? cmTarget::PlainTLLSignature
-                                      : cmTarget::KeywordTLLSignature);
-      mf.IssueMessage(messageType, e.str());
+      this->Target->GetTllSignatureTraces(e,
+                                          sig == cmTarget::KeywordTLLSignature
+                                            ? cmTarget::PlainTLLSignature
+                                            : cmTarget::KeywordTLLSignature);
+      this->Makefile.IssueMessage(messageType, e.str());
       if (messageType == MessageType::FATAL_ERROR) {
       if (messageType == MessageType::FATAL_ERROR) {
         return false;
         return false;
       }
       }
     }
     }
   }
   }
 
 
-  bool warnRemoteInterface = false;
-  bool rejectRemoteLinking = false;
-  bool encodeRemoteReference = false;
-  if (&mf != target->GetMakefile()) {
-    // The LHS target was created in another directory.
-    switch (mf.GetPolicyStatus(cmPolicies::CMP0079)) {
-      case cmPolicies::WARN:
-        warnRemoteInterface = true;
-        CM_FALLTHROUGH;
-      case cmPolicies::OLD:
-        rejectRemoteLinking = true;
-        break;
-      case cmPolicies::REQUIRED_ALWAYS:
-      case cmPolicies::REQUIRED_IF_USED:
-      case cmPolicies::NEW:
-        encodeRemoteReference = true;
-        break;
-    }
-  }
-
-  std::string libRef;
-  if (encodeRemoteReference && !cmSystemTools::FileIsFullPath(lib)) {
-    // This is a library name added by a caller that is not in the
-    // same directory as the target was created.  Add a suffix to
-    // the name to tell ResolveLinkItem to look up the name in the
-    // caller's directory.
-    cmDirectoryId const dirId = mf.GetDirectoryId();
-    // FIXME: The "lib" may be a genex with a list inside it.
-    // After expansion this id will only attach to the last entry,
-    // or may attach to an empty string!  We will need another way
-    // to encode this that can apply to a whole list.  See issue #20204.
-    libRef = lib + CMAKE_DIRECTORY_ID_SEP + dirId.String;
-  } else {
-    // This is an absolute path or a library name added by a caller
-    // in the same directory as the target was created.  We can use
-    // the original name directly.
-    libRef = lib;
-  }
-
   // Handle normal case where the command was called with another keyword than
   // Handle normal case where the command was called with another keyword than
   // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
   // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
   // property of the target on the LHS shall be populated.)
   // property of the target on the LHS shall be populated.)
   if (currentProcessingState != ProcessingKeywordLinkInterface &&
   if (currentProcessingState != ProcessingKeywordLinkInterface &&
       currentProcessingState != ProcessingPlainLinkInterface) {
       currentProcessingState != ProcessingPlainLinkInterface) {
 
 
-    if (rejectRemoteLinking) {
-      mf.IssueMessage(
+    if (this->RejectRemoteLinking) {
+      this->Makefile.IssueMessage(
         MessageType::FATAL_ERROR,
         MessageType::FATAL_ERROR,
         cmStrCat("Attempt to add link library \"", lib, "\" to target \"",
         cmStrCat("Attempt to add link library \"", lib, "\" to target \"",
-                 target->GetName(),
+                 this->Target->GetName(),
                  "\" which is not built in this "
                  "\" which is not built in this "
                  "directory.\nThis is allowed only when policy CMP0079 "
                  "directory.\nThis is allowed only when policy CMP0079 "
                  "is set to NEW."));
                  "is set to NEW."));
       return false;
       return false;
     }
     }
 
 
-    cmTarget* tgt = mf.GetGlobalGenerator()->FindTarget(lib);
+    cmTarget* tgt = this->Makefile.GetGlobalGenerator()->FindTarget(lib);
 
 
     if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) &&
     if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) &&
@@ -442,7 +450,7 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
         (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
         (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
         !tgt->IsExecutableWithExports()) {
         !tgt->IsExecutableWithExports()) {
-      mf.IssueMessage(
+      this->Makefile.IssueMessage(
         MessageType::FATAL_ERROR,
         MessageType::FATAL_ERROR,
         cmStrCat(
         cmStrCat(
           "Target \"", lib, "\" of type ",
           "Target \"", lib, "\" of type ",
@@ -452,15 +460,16 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
           "executables with the ENABLE_EXPORTS property set."));
           "executables with the ENABLE_EXPORTS property set."));
     }
     }
 
 
-    target->AddLinkLibrary(mf, lib, libRef, llt);
+    this->AffectsProperty("LINK_LIBRARIES");
+    this->Target->AddLinkLibrary(this->Makefile, lib, llt);
   }
   }
 
 
-  if (warnRemoteInterface) {
-    mf.IssueMessage(
+  if (this->WarnRemoteInterface) {
+    this->Makefile.IssueMessage(
       MessageType::AUTHOR_WARNING,
       MessageType::AUTHOR_WARNING,
       cmStrCat(
       cmStrCat(
         cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n  ",
         cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n  ",
-        target->GetName(),
+        this->Target->GetName(),
         "\nis not created in this "
         "\nis not created in this "
         "directory.  For compatibility with older versions of CMake, link "
         "directory.  For compatibility with older versions of CMake, link "
         "library\n  ",
         "library\n  ",
@@ -475,15 +484,15 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
   // STATIC library.)
   // STATIC library.)
   if (currentProcessingState == ProcessingKeywordPrivateInterface ||
   if (currentProcessingState == ProcessingKeywordPrivateInterface ||
       currentProcessingState == ProcessingPlainPrivateInterface) {
       currentProcessingState == ProcessingPlainPrivateInterface) {
-    if (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
-        target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+    if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
+        this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
       std::string configLib =
       std::string configLib =
-        target->GetDebugGeneratorExpressions(libRef, llt);
+        this->Target->GetDebugGeneratorExpressions(lib, llt);
       if (cmGeneratorExpression::IsValidTargetName(lib) ||
       if (cmGeneratorExpression::IsValidTargetName(lib) ||
           cmGeneratorExpression::Find(lib) != std::string::npos) {
           cmGeneratorExpression::Find(lib) != std::string::npos) {
         configLib = "$<LINK_ONLY:" + configLib + ">";
         configLib = "$<LINK_ONLY:" + configLib + ">";
       }
       }
-      target->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib);
+      this->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib);
     }
     }
     return true;
     return true;
   }
   }
@@ -491,8 +500,8 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
   // Handle general case where the command was called with another keyword than
   // Handle general case where the command was called with another keyword than
   // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES"
   // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES"
   // property of the target on the LHS shall be populated.)
   // property of the target on the LHS shall be populated.)
-  target->AppendProperty("INTERFACE_LINK_LIBRARIES",
-                         target->GetDebugGeneratorExpressions(libRef, llt));
+  this->AppendProperty("INTERFACE_LINK_LIBRARIES",
+                       this->Target->GetDebugGeneratorExpressions(lib, llt));
 
 
   // Stop processing if called without any keyword.
   // Stop processing if called without any keyword.
   if (currentProcessingState == ProcessingLinkLibraries) {
   if (currentProcessingState == ProcessingLinkLibraries) {
@@ -500,13 +509,13 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
   }
   }
   // Stop processing if policy CMP0022 is set to NEW.
   // Stop processing if policy CMP0022 is set to NEW.
   const cmPolicies::PolicyStatus policy22Status =
   const cmPolicies::PolicyStatus policy22Status =
-    target->GetPolicyStatusCMP0022();
+    this->Target->GetPolicyStatusCMP0022();
   if (policy22Status != cmPolicies::OLD &&
   if (policy22Status != cmPolicies::OLD &&
       policy22Status != cmPolicies::WARN) {
       policy22Status != cmPolicies::WARN) {
     return true;
     return true;
   }
   }
   // Stop processing if called with an INTERFACE library on the LHS.
   // Stop processing if called with an INTERFACE library on the LHS.
-  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+  if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
     return true;
     return true;
   }
   }
 
 
@@ -516,7 +525,7 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
   {
   {
     // Get the list of configurations considered to be DEBUG.
     // Get the list of configurations considered to be DEBUG.
     std::vector<std::string> debugConfigs =
     std::vector<std::string> debugConfigs =
-      mf.GetCMakeInstance()->GetDebugConfigs();
+      this->Makefile.GetCMakeInstance()->GetDebugConfigs();
     std::string prop;
     std::string prop;
 
 
     // Include this library in the link interface for the target.
     // Include this library in the link interface for the target.
@@ -524,22 +533,49 @@ static bool HandleLibrary(cmMakefile& mf, cmTarget* target,
       // Put in the DEBUG configuration interfaces.
       // Put in the DEBUG configuration interfaces.
       for (std::string const& dc : debugConfigs) {
       for (std::string const& dc : debugConfigs) {
         prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
         prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
-        target->AppendProperty(prop, libRef);
+        this->AppendProperty(prop, lib);
       }
       }
     }
     }
     if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
     if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
       // Put in the non-DEBUG configuration interfaces.
       // Put in the non-DEBUG configuration interfaces.
-      target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef);
+      this->AppendProperty("LINK_INTERFACE_LIBRARIES", lib);
 
 
       // Make sure the DEBUG configuration interfaces exist so that the
       // Make sure the DEBUG configuration interfaces exist so that the
       // general one will not be used as a fall-back.
       // general one will not be used as a fall-back.
       for (std::string const& dc : debugConfigs) {
       for (std::string const& dc : debugConfigs) {
         prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
         prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
-        if (!target->GetProperty(prop)) {
-          target->SetProperty(prop, "");
+        if (!this->Target->GetProperty(prop)) {
+          this->Target->SetProperty(prop, "");
         }
         }
       }
       }
     }
     }
   }
   }
   return true;
   return true;
 }
 }
+
+void TLL::AppendProperty(std::string const& prop, std::string const& value)
+{
+  this->AffectsProperty(prop);
+  this->Target->AppendProperty(prop, value);
+}
+
+void TLL::AffectsProperty(std::string const& prop)
+{
+  if (!this->EncodeRemoteReference) {
+    return;
+  }
+  // Add a wrapper to the expression to tell LookupLinkItems to look up
+  // names in the caller's directory.
+  if (this->Props.insert(prop).second) {
+    this->Target->AppendProperty(prop, this->DirectoryId);
+  }
+}
+
+TLL::~TLL()
+{
+  for (std::string const& prop : this->Props) {
+    this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP);
+  }
+}
+
+} // namespace

+ 3 - 2
Tests/CMakeCommands/target_link_libraries/SubDirB/CMakeLists.txt

@@ -4,8 +4,9 @@ add_executable(SubDirB SubDirB.c)
 # be visible to the directory in which TopDir is defined.
 # be visible to the directory in which TopDir is defined.
 target_link_libraries(TopDir PUBLIC debug SameNameImported optimized SameNameImported)
 target_link_libraries(TopDir PUBLIC debug SameNameImported optimized SameNameImported)
 
 
-#FIXME: Demonstrate known issue #20204.
-#target_link_libraries(TopDir PUBLIC "$<1:SameNameImported;SameNameImported>")
+# Link to a list of targets imported in this directory that would not
+# normally be visible to the directory in which TopDir is defined.
+target_link_libraries(TopDir PUBLIC "$<1:SameNameImported;SameNameImported>")
 
 
 # Link SubDirA to a target imported in this directory that has the same
 # Link SubDirA to a target imported in this directory that has the same
 # name as a target imported in SubDirA's directory.  We verify when
 # name as a target imported in SubDirA's directory.  We verify when

+ 1 - 0
Tests/ExportImport/Export/CMakeLists.txt

@@ -156,6 +156,7 @@ target_link_libraries(testLibDepends PRIVATE testStaticLibRequiredPrivate)
 cmake_policy(POP)
 cmake_policy(POP)
 
 
 cmake_policy(PUSH)
 cmake_policy(PUSH)
+cmake_policy(SET CMP0022 NEW)
 cmake_policy(SET CMP0079 NEW)
 cmake_policy(SET CMP0079 NEW)
 add_library(TopDirLib STATIC testTopDirLib.c)
 add_library(TopDirLib STATIC testTopDirLib.c)
 add_subdirectory(SubDirLinkA)
 add_subdirectory(SubDirLinkA)

+ 1 - 1
Tests/ExportImport/Export/SubDirLinkA/CMakeLists.txt

@@ -1,6 +1,6 @@
 add_library(SubDirLinkAImported IMPORTED INTERFACE)
 add_library(SubDirLinkAImported IMPORTED INTERFACE)
 target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForExport)
 target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForExport)
 
 
-target_link_libraries(TopDirLib PUBLIC SubDirLinkAImported)
+target_link_libraries(TopDirLib PUBLIC debug "$<1:SubDirLinkAImported;SubDirLinkAImported>" optimized "$<1:SubDirLinkAImported;SubDirLinkAImported>")
 
 
 add_library(SubDirLinkA STATIC SubDirLinkA.c)
 add_library(SubDirLinkA STATIC SubDirLinkA.c)

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-iface-NEW-stdout.txt

@@ -1 +1 @@
--- INTERFACE_LINK_LIBRARIES='foo::@\([Xx0-9A-Fa-f]+\)'
+-- INTERFACE_LINK_LIBRARIES='::@\([Xx0-9A-Fa-f]+\);\$<\$<CONFIG:DEBUG>:\$<1:foo;foo>>;\$<\$<NOT:\$<CONFIG:DEBUG>>:\$<1:foo;foo>>;::@'

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stdout.txt

@@ -1 +1 @@
--- INTERFACE_LINK_LIBRARIES='foo'
+-- INTERFACE_LINK_LIBRARIES='\$<\$<CONFIG:DEBUG>:\$<1:foo;foo>>;\$<\$<NOT:\$<CONFIG:DEBUG>>:\$<1:foo;foo>>'

+ 19 - 1
Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt

@@ -10,7 +10,25 @@
   is not created in this directory.  For compatibility with older versions of
   is not created in this directory.  For compatibility with older versions of
   CMake, link library
   CMake, link library
 
 
-    foo
+    \$<1:foo;foo>
+
+  will be looked up in the directory in which the target was created rather
+  than in this calling directory.
+This warning is for project developers.  Use -Wno-dev to suppress it.
++
+CMake Warning \(dev\) at CMP0079-iface/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
+  Policy CMP0079 is not set: target_link_libraries allows use with targets in
+  other directories.  Run "cmake --help-policy CMP0079" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  Target
+
+    top
+
+  is not created in this directory.  For compatibility with older versions of
+  CMake, link library
+
+    \$<1:foo;foo>
 
 
   will be looked up in the directory in which the target was created rather
   will be looked up in the directory in which the target was created rather
   than in this calling directory.
   than in this calling directory.

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stdout.txt

@@ -1 +1 @@
--- INTERFACE_LINK_LIBRARIES='foo'
+-- INTERFACE_LINK_LIBRARIES='\$<\$<CONFIG:DEBUG>:\$<1:foo;foo>>;\$<\$<NOT:\$<CONFIG:DEBUG>>:\$<1:foo;foo>>'

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-iface/CMakeLists.txt

@@ -1 +1 @@
-target_link_libraries(top INTERFACE foo)
+target_link_libraries(top INTERFACE debug "$<1:foo;foo>" optimized "$<1:foo;foo>")

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

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

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus.cmake

@@ -3,4 +3,4 @@ cmake_policy(SET CMP0079 NEW)
 enable_language(C)
 enable_language(C)
 
 
 add_executable(top empty.c)
 add_executable(top empty.c)
-set_property(TARGET top APPEND PROPERTY LINK_LIBRARIES "foo::@(0xdeadbeef)")
+set_property(TARGET top APPEND PROPERTY LINK_LIBRARIES "::@(0xdeadbeef);foo;::@")

+ 1 - 1
Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-stdout.txt

@@ -1 +1 @@
--- LINK_LIBRARIES='foo::@\([Xx0-9A-Fa-f]+\)'
+-- LINK_LIBRARIES='::@\([Xx0-9A-Fa-f]+\);foo;::@'