فهرست منبع

Merge topic 'out-of-dir-linking'

a1ad0a699b target_link_libraries: Allow use with targets in other directories
9bbae5ae28 cmTarget: Future-proof AddLinkLibrary target lookup scope
f9cb6f618a cmExportFileGenerator: Use cmGeneratorTarget::ResolveTargetReference
18441a6269 cmGeneratorTarget: Factor target name resolution out of link item resolution
2f708f5d65 Make internal TARGET_PROPERTY generator expressions more robust
94a75801c8 Android.mk: De-duplicate link libraries logic during export
8a63b23d16 cmGlobalGenerator: Remove unused FindLocalGenerator method

Acked-by: Kitware Robot <[email protected]>
Reviewed-by: Patrick Stotko <[email protected]>
Merge-request: !2370
Brad King 7 سال پیش
والد
کامیت
333804fa0e
60فایلهای تغییر یافته به همراه529 افزوده شده و 88 حذف شده
  1. 7 4
      Help/command/target_link_libraries.rst
  2. 1 0
      Help/manual/cmake-policies.7.rst
  3. 40 0
      Help/policy/CMP0079.rst
  4. 2 0
      Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst
  5. 2 0
      Help/prop_tgt/LINK_LIBRARIES.rst
  6. 10 0
      Help/prop_tgt/LINK_LIBRARIES_INDIRECTION.txt
  7. 6 0
      Help/release/dev/out-of-dir-linking.rst
  8. 5 21
      Source/cmExportBuildAndroidMKGenerator.cxx
  9. 7 2
      Source/cmExportFileGenerator.cxx
  10. 3 1
      Source/cmGeneratorExpressionNode.cxx
  11. 59 12
      Source/cmGeneratorTarget.cxx
  12. 7 0
      Source/cmGeneratorTarget.h
  13. 34 8
      Source/cmGlobalGenerator.cxx
  14. 15 2
      Source/cmGlobalGenerator.h
  15. 17 0
      Source/cmMakefile.cxx
  16. 10 0
      Source/cmMakefile.h
  17. 5 1
      Source/cmPolicies.h
  18. 11 4
      Source/cmTarget.cxx
  19. 3 0
      Source/cmTarget.h
  20. 58 22
      Source/cmTargetLinkLibrariesCommand.cxx
  21. 12 0
      Tests/CMakeCommands/target_link_libraries/CMakeLists.txt
  22. 15 0
      Tests/CMakeCommands/target_link_libraries/SubDirA/CMakeLists.txt
  23. 14 0
      Tests/CMakeCommands/target_link_libraries/SubDirA/SubDirA.c
  24. 15 0
      Tests/CMakeCommands/target_link_libraries/SubDirB/CMakeLists.txt
  25. 14 0
      Tests/CMakeCommands/target_link_libraries/SubDirB/SubDirB.c
  26. 14 0
      Tests/CMakeCommands/target_link_libraries/TopDir.c
  27. 9 0
      Tests/ExportImport/Export/CMakeLists.txt
  28. 6 0
      Tests/ExportImport/Export/SubDirLinkA/CMakeLists.txt
  29. 11 0
      Tests/ExportImport/Export/SubDirLinkA/SubDirLinkA.c
  30. 4 0
      Tests/ExportImport/Export/SubDirLinkB/CMakeLists.txt
  31. 11 0
      Tests/ExportImport/Export/testTopDirLib.c
  32. 11 0
      Tests/ExportImport/Import/A/CMakeLists.txt
  33. 14 0
      Tests/ExportImport/Import/A/SubDirLink.c
  34. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-NEW-stdout.txt
  35. 2 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-NEW.cmake
  36. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD-stdout.txt
  37. 2 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD.cmake
  38. 17 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt
  39. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stdout.txt
  40. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN.cmake
  41. 6 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface-common.cmake
  42. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-iface/CMakeLists.txt
  43. 0 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus-result.txt
  44. 6 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus-stderr.txt
  45. 6 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus.cmake
  46. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-stdout.txt
  47. 2 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-NEW.cmake
  48. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-result.txt
  49. 5 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt
  50. 2 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-OLD.cmake
  51. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-WARN-result.txt
  52. 5 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-WARN-stderr.txt
  53. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-WARN.cmake
  54. 6 0
      Tests/RunCMake/target_link_libraries/CMP0079-link-common.cmake
  55. 1 0
      Tests/RunCMake/target_link_libraries/CMP0079-link/CMakeLists.txt
  56. 1 1
      Tests/RunCMake/target_link_libraries/CMakeLists.txt
  57. 7 1
      Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
  58. 0 5
      Tests/RunCMake/target_link_libraries/SubDirTarget-stderr.txt
  59. 0 3
      Tests/RunCMake/target_link_libraries/SubDirTarget.cmake
  60. 0 1
      Tests/RunCMake/target_link_libraries/SubDirTarget/CMakeLists.txt

+ 7 - 4
Help/command/target_link_libraries.rst

@@ -18,10 +18,13 @@ All of them have the general form::
 
   target_link_libraries(<target> ... <item>... ...)
 
-The named ``<target>`` must have been created in the current directory by
-a command such as :command:`add_executable` or :command:`add_library` and
-must not be an :ref:`ALIAS target <Alias Targets>`.
-Repeated calls for the same ``<target>`` append items in the order called.
+The named ``<target>`` must have been created by a command such as
+:command:`add_executable` or :command:`add_library` and must not be an
+:ref:`ALIAS target <Alias Targets>`.  If policy :policy:`CMP0079` is not
+set to ``NEW`` then the target must have been created in the current
+directory.  Repeated calls for the same ``<target>`` append items in
+the order called.
+
 Each ``<item>`` may be:
 
 * **A library target name**: The generated link line will have the

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.13
 .. toctree::
    :maxdepth: 1
 
+   CMP0079: target_link_libraries allows use with targets in other directories. </policy/CMP0079>
    CMP0078: UseSWIG generates standard target names. </policy/CMP0078>
    CMP0077: option() honors normal variables. </policy/CMP0077>
    CMP0076: target_sources() command converts relative paths to absolute. </policy/CMP0076>

+ 40 - 0
Help/policy/CMP0079.rst

@@ -0,0 +1,40 @@
+CMP0079
+-------
+
+:command:`target_link_libraries` allows use with targets in other directories.
+
+Prior to CMake 3.13 the :command:`target_link_libraries` command did not
+accept targets not created in the calling directory as its first argument
+for calls that update the :prop_tgt:`LINK_LIBRARIES` of the target itself.
+It did accidentally accept targets from other directories on calls that
+only update the :prop_tgt:`INTERFACE_LINK_LIBRARIES`, but would simply
+add entries to the property as if the call were made in the original
+directory.  Thus link interface libraries specified this way were always
+looked up by generators in the scope of the original target rather than
+in the scope that called :command:`target_link_libraries`.
+
+CMake 3.13 now allows the :command:`target_link_libraries` command to
+be called from any directory to add link dependencies and link interface
+libraries to targets created in other directories.  The entries are added
+to :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+using a special (internal) suffix to tell the generators to look up the
+names in the calling scope rather than the scope that created the target.
+
+This policy provides compatibility with projects that already use
+:command:`target_link_libraries` with the ``INTERFACE`` keyword
+on a target in another directory to add :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+entries to be looked up in the target's directory.  Such projects should
+be updated to be aware of the new scoping rules in that case.
+
+The ``OLD`` behavior of this policy is to disallow
+:command:`target_link_libraries` calls naming targets from another directory
+except in the previously accidentally allowed case of using the ``INTERFACE``
+keyword only.  The ``NEW`` behavior of this policy is to allow all such
+calls but use the new scoping rules.
+
+This policy was introduced in CMake version 3.13.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt

+ 2 - 0
Help/prop_tgt/INTERFACE_LINK_LIBRARIES.rst

@@ -17,6 +17,8 @@ with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
 manual for more on defining buildsystem properties.
 
+.. include:: LINK_LIBRARIES_INDIRECTION.txt
+
 Creating Relocatable Packages
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

+ 2 - 0
Help/prop_tgt/LINK_LIBRARIES.rst

@@ -15,3 +15,5 @@ Contents of ``LINK_LIBRARIES`` may use "generator expressions" with the
 syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)` manual
 for available expressions.  See the :manual:`cmake-buildsystem(7)` manual
 for more on defining buildsystem properties.
+
+.. include:: LINK_LIBRARIES_INDIRECTION.txt

+ 10 - 0
Help/prop_tgt/LINK_LIBRARIES_INDIRECTION.txt

@@ -0,0 +1,10 @@
+.. note::
+  A call to :command:`target_link_libraries(<target> ...)` may update this
+  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.
+  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
+  ``<target>`` was created.  Valid directory ids are stripped on export
+  by the :command:`install(EXPORT)` and :command:`export` commands.

+ 6 - 0
Help/release/dev/out-of-dir-linking.rst

@@ -0,0 +1,6 @@
+out-of-dir-linking
+------------------
+
+* The :command:`target_link_libraries` command may now be called
+  to modify targets created outside the current directory.
+  See policy :policy:`CMP0079`.

+ 5 - 21
Source/cmExportBuildAndroidMKGenerator.cxx

@@ -8,11 +8,8 @@
 #include <utility>
 
 #include "cmAlgorithms.h"
-#include "cmGeneratorExpression.h"
-#include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorTarget.h"
 #include "cmLinkItem.h"
-#include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
@@ -104,27 +101,14 @@ void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
         os << "LOCAL_CPP_FEATURES += ";
         os << (property.second) << "\n";
       } else if (property.first == "INTERFACE_LINK_LIBRARIES") {
-        // evaluate any generator expressions with the current
-        // build type of the makefile
-        cmGeneratorExpression ge;
-        cmGeneratorExpressionDAGChecker dagChecker(
-          target, "INTERFACE_LINK_LIBRARIES", nullptr, nullptr);
-        std::unique_ptr<cmCompiledGeneratorExpression> cge =
-          ge.Parse(property.second);
-        std::string evaluated = cge->Evaluate(
-          target->GetLocalGenerator(), config, false, target, &dagChecker);
-        // need to look at list in pi->second and see if static or shared
-        // FindTargetToLink
-        // target->GetLocalGenerator()->FindGeneratorTargetToUse()
-        // then add to LOCAL_CPPFLAGS
-        std::vector<std::string> libraries;
-        cmSystemTools::ExpandListArgument(evaluated, libraries);
         std::string staticLibs;
         std::string sharedLibs;
         std::string ldlibs;
-        for (std::string const& lib : libraries) {
-          cmGeneratorTarget* gt =
-            target->GetLocalGenerator()->FindGeneratorTargetToUse(lib);
+        cmLinkInterfaceLibraries const* linkIFace =
+          target->GetLinkInterfaceLibraries(config, target, false);
+        for (cmLinkItem const& item : linkIFace->Libraries) {
+          cmGeneratorTarget const* gt = item.Target;
+          std::string const& lib = item.AsStr();
           if (gt) {
 
             if (gt->GetType() == cmStateEnums::SHARED_LIBRARY ||

+ 7 - 2
Source/cmExportFileGenerator.cxx

@@ -567,14 +567,17 @@ bool cmExportFileGenerator::AddTargetNamespace(
   std::string& input, cmGeneratorTarget* target,
   std::vector<std::string>& missingTargets)
 {
-  cmLocalGenerator* lg = target->GetLocalGenerator();
+  cmGeneratorTarget::TargetOrString resolved =
+    target->ResolveTargetReference(input);
 
-  cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(input);
+  cmGeneratorTarget* tgt = resolved.Target;
   if (!tgt) {
+    input = resolved.String;
     return false;
   }
 
   if (tgt->IsImported()) {
+    input = tgt->GetName();
     return true;
   }
   if (this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) {
@@ -584,6 +587,8 @@ bool cmExportFileGenerator::AddTargetNamespace(
     this->HandleMissingTarget(namespacedTarget, missingTargets, target, tgt);
     if (!namespacedTarget.empty()) {
       input = namespacedTarget;
+    } else {
+      input = tgt->GetName();
     }
   }
   return true;

+ 3 - 1
Source/cmGeneratorExpressionNode.cxx

@@ -1054,7 +1054,9 @@ std::string getLinkedTargetsContent(
     // Don't follow such link interface entries so as not to create a
     // self-referencing loop.
     if (l.Target && l.Target != target) {
-      depString += sep + "$<TARGET_PROPERTY:" + l.Target->GetName() + "," +
+      std::string uniqueName =
+        target->GetGlobalGenerator()->IndexGeneratorTargetUniquely(l.Target);
+      depString += sep + "$<TARGET_PROPERTY:" + std::move(uniqueName) + "," +
         interfacePropertyName + ">";
       sep = ";";
     }

+ 59 - 12
Source/cmGeneratorTarget.cxx

@@ -819,8 +819,11 @@ static void AddInterfaceEntries(
         thisTarget->GetLinkImplementationLibraries(config)) {
     for (cmLinkImplItem const& lib : impl->Libraries) {
       if (lib.Target) {
+        std::string uniqueName =
+          thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
+            lib.Target);
         std::string genex =
-          "$<TARGET_PROPERTY:" + lib.AsStr() + "," + prop + ">";
+          "$<TARGET_PROPERTY:" + std::move(uniqueName) + "," + prop + ">";
         cmGeneratorExpression ge(lib.Backtrace);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
         cge->SetEvaluateForBuildsystem(true);
@@ -840,7 +843,10 @@ static void AddObjectEntries(
     for (cmLinkImplItem const& lib : impl->Libraries) {
       if (lib.Target &&
           lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-        std::string genex = "$<TARGET_OBJECTS:" + lib.AsStr() + ">";
+        std::string uniqueName =
+          thisTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
+            lib.Target);
+        std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
         cmGeneratorExpression ge(lib.Backtrace);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
         cge->SetEvaluateForBuildsystem(true);
@@ -5639,24 +5645,65 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
   }
 }
 
+cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
+  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;
+    }
+  }
+
+  TargetOrString resolved;
+
+  if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(*lookupName)) {
+    resolved.Target = tgt;
+  } else if (lookupName == &plainName) {
+    resolved.String = std::move(plainName);
+  } else {
+    resolved.String = name;
+  }
+
+  return resolved;
+}
+
 cmLinkItem cmGeneratorTarget::ResolveLinkItem(std::string const& name) const
 {
-  cmGeneratorTarget* tgt =
-    this->LocalGenerator->FindGeneratorTargetToUse(name);
+  TargetOrString resolved = this->ResolveTargetReference(name);
+
+  if (!resolved.Target) {
+    return cmLinkItem(resolved.String);
+  }
 
   // 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 (tgt && tgt->GetType() == cmStateEnums::EXECUTABLE &&
-      !tgt->IsExecutableWithExports()) {
-    tgt = nullptr;
-  }
-
-  if (tgt) {
-    return cmLinkItem(tgt);
+  if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE &&
+      !resolved.Target->IsExecutableWithExports()) {
+    return cmLinkItem(resolved.Target->GetName());
   }
 
-  return cmLinkItem(name);
+  return cmLinkItem(resolved.Target);
 }
 
 std::string cmGeneratorTarget::GetPDBDirectory(const std::string& config) const

+ 7 - 0
Source/cmGeneratorTarget.h

@@ -357,6 +357,13 @@ public:
                                           cmOptionalLinkImplementation& impl,
                                           const cmGeneratorTarget* head) const;
 
+  struct TargetOrString
+  {
+    std::string String;
+    cmGeneratorTarget* Target = nullptr;
+  };
+  TargetOrString ResolveTargetReference(std::string const& name) const;
+
   cmLinkItem ResolveLinkItem(std::string const& name) const;
 
   // Compute the set of languages compiled by the target.  This is

+ 34 - 8
Source/cmGlobalGenerator.cxx

@@ -1139,11 +1139,14 @@ void cmGlobalGenerator::ClearEnabledLanguages()
 
 void cmGlobalGenerator::CreateLocalGenerators()
 {
+  this->LocalGeneratorSearchIndex.clear();
   cmDeleteAll(this->LocalGenerators);
   this->LocalGenerators.clear();
   this->LocalGenerators.reserve(this->Makefiles.size());
   for (cmMakefile* m : this->Makefiles) {
-    this->LocalGenerators.push_back(this->CreateLocalGenerator(m));
+    cmLocalGenerator* lg = this->CreateLocalGenerator(m);
+    this->LocalGenerators.push_back(lg);
+    this->IndexLocalGenerator(lg);
   }
 }
 
@@ -1661,6 +1664,7 @@ void cmGlobalGenerator::ClearGeneratorMembers()
   this->TargetSearchIndex.clear();
   this->GeneratorTargetSearchIndex.clear();
   this->MakefileSearchIndex.clear();
+  this->LocalGeneratorSearchIndex.clear();
   this->ProjectMap.clear();
   this->RuleHashes.clear();
   this->DirectoryContentMap.clear();
@@ -2130,15 +2134,13 @@ cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const
   return nullptr;
 }
 
-///! Find a local generator by its startdirectory
 cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(
-  const std::string& start_dir) const
+  cmDirectoryId const& id) const
 {
-  for (cmLocalGenerator* lg : this->LocalGenerators) {
-    std::string sd = lg->GetCurrentSourceDirectory();
-    if (sd == start_dir) {
-      return lg;
-    }
+  LocalGeneratorMap::const_iterator i =
+    this->LocalGeneratorSearchIndex.find(id.String);
+  if (i != this->LocalGeneratorSearchIndex.end()) {
+    return i->second;
   }
   return nullptr;
 }
@@ -2168,6 +2170,24 @@ void cmGlobalGenerator::IndexGeneratorTarget(cmGeneratorTarget* gt)
   }
 }
 
+std::string cmGlobalGenerator::IndexGeneratorTargetUniquely(
+  cmGeneratorTarget const* gt)
+{
+  // Use the pointer value to uniquely identify the target instance.
+  // Use a "T" prefix to indicate that this identifier is for a target.
+  // We must satisfy cmGeneratorExpression::IsValidTargetName so use no
+  // other special characters.
+  char buf[64];
+  sprintf(buf, "::T%p",
+          static_cast<void const*>(gt)); // cast avoids format warning
+  std::string id = gt->GetName() + buf;
+  // We internally index pointers to non-const generator targets
+  // but our callers only have pointers to const generator targets.
+  // They will give up non-const privileges when looking up anyway.
+  this->GeneratorTargetSearchIndex[id] = const_cast<cmGeneratorTarget*>(gt);
+  return id;
+}
+
 void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
 {
   // FIXME: add_subdirectory supports multiple build directories
@@ -2179,6 +2199,12 @@ void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
     MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
 }
 
+void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
+{
+  cmDirectoryId id = lg->GetMakefile()->GetDirectoryId();
+  this->LocalGeneratorSearchIndex[id.String] = lg;
+}
+
 cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const
 {
   TargetMap::const_iterator i = this->TargetSearchIndex.find(name);

+ 15 - 2
Source/cmGlobalGenerator.h

@@ -26,6 +26,9 @@
 #  include "cmFileLockPool.h"
 #endif
 
+#define CMAKE_DIRECTORY_ID_SEP "::@"
+
+class cmDirectoryId;
 class cmExportBuildFileGenerator;
 class cmExternalMakefileProjectGenerator;
 class cmGeneratorTarget;
@@ -284,8 +287,7 @@ public:
   bool NameResolvesToFramework(const std::string& libname) const;
 
   cmMakefile* FindMakefile(const std::string& start_dir) const;
-  ///! Find a local generator by its startdirectory
-  cmLocalGenerator* FindLocalGenerator(const std::string& start_dir) const;
+  cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;
 
   /** Append the subdirectory for the given configuration.  If anything is
       appended the given prefix and suffix will be appended around it, which
@@ -305,6 +307,10 @@ public:
   void IndexTarget(cmTarget* t);
   void IndexGeneratorTarget(cmGeneratorTarget* gt);
 
+  // Index the target using a name that is unique to that target
+  // even if other targets have the same name.
+  std::string IndexGeneratorTargetUniquely(cmGeneratorTarget const* gt);
+
   static bool IsReservedTarget(std::string const& name);
 
   virtual const char* GetAllTargetName() const { return "ALL_BUILD"; }
@@ -514,6 +520,7 @@ private:
   typedef std::unordered_map<std::string, cmGeneratorTarget*>
     GeneratorTargetMap;
   typedef std::unordered_map<std::string, cmMakefile*> MakefileMap;
+  typedef std::unordered_map<std::string, cmLocalGenerator*> LocalGeneratorMap;
   // Map efficiently from target name to cmTarget instance.
   // Do not use this structure for looping over all targets.
   // It contains both normal and globally visible imported targets.
@@ -525,6 +532,11 @@ private:
   // It may not contain all of them (see note in IndexMakefile method).
   MakefileMap MakefileSearchIndex;
 
+  // Map efficiently from source directory path to cmLocalGenerator instance.
+  // Do not use this structure for looping over all directories.
+  // Its order is not deterministic.
+  LocalGeneratorMap LocalGeneratorSearchIndex;
+
   cmMakefile* TryCompileOuterMakefile;
   // If you add a new map here, make sure it is copied
   // in EnableLanguagesFromGenerator
@@ -583,6 +595,7 @@ private:
                     std::string const& reason) const;
 
   void IndexMakefile(cmMakefile* mf);
+  void IndexLocalGenerator(cmLocalGenerator* lg);
 
   virtual const char* GetBuildIgnoreErrorsFlag() const { return nullptr; }
 

+ 17 - 0
Source/cmMakefile.cxx

@@ -11,6 +11,7 @@
 #include <iterator>
 #include <memory> // IWYU pragma: keep
 #include <sstream>
+#include <stdio.h>
 #include <stdlib.h>
 #include <utility>
 
@@ -48,6 +49,11 @@
 
 class cmMessenger;
 
+cmDirectoryId::cmDirectoryId(std::string s)
+  : String(std::move(s))
+{
+}
+
 // default is not to be building executables
 cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator,
                        cmStateSnapshot const& snapshot)
@@ -111,6 +117,17 @@ cmMakefile::~cmMakefile()
   cmDeleteAll(this->EvaluationFiles);
 }
 
+cmDirectoryId cmMakefile::GetDirectoryId() const
+{
+  // Use the instance pointer value to uniquely identify this directory.
+  // If we ever need to expose this to CMake language code we should
+  // add a read-only property in cmMakefile::GetProperty.
+  char buf[32];
+  sprintf(buf, "<%p>",
+          static_cast<void const*>(this)); // cast avoids format warning
+  return std::string(buf);
+}
+
 void cmMakefile::IssueMessage(cmake::MessageType t,
                               std::string const& text) const
 {

+ 10 - 0
Source/cmMakefile.h

@@ -47,6 +47,14 @@ class cmTest;
 class cmTestGenerator;
 class cmVariableWatch;
 
+/** A type-safe wrapper for a string representing a directory id.  */
+class cmDirectoryId
+{
+public:
+  cmDirectoryId(std::string s);
+  std::string String;
+};
+
 /** \class cmMakefile
  * \brief Process the input CMakeLists.txt file.
  *
@@ -75,6 +83,8 @@ public:
    */
   ~cmMakefile();
 
+  cmDirectoryId GetDirectoryId() const;
+
   bool ReadListFile(const char* filename);
 
   bool ReadDependentFile(const char* filename, bool noPolicyScope = true);

+ 5 - 1
Source/cmPolicies.h

@@ -230,7 +230,11 @@ class cmMakefile;
   SELECT(POLICY, CMP0077, "option() honors normal variables.", 3, 13, 0,      \
          cmPolicies::WARN)                                                    \
   SELECT(POLICY, CMP0078, "UseSWIG generates standard target names.", 3, 13,  \
-         0, cmPolicies::WARN)
+         0, cmPolicies::WARN)                                                 \
+  SELECT(                                                                     \
+    POLICY, CMP0079,                                                          \
+    "target_link_libraries allows use with targets in other directories.", 3, \
+    13, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 11 - 4
Source/cmTarget.cxx

@@ -745,20 +745,27 @@ void cmTarget::GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const
 void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib,
                               cmTargetLinkLibraryType llt)
 {
-  cmTarget* tgt = this->Makefile->FindTargetToUse(lib);
+  this->AddLinkLibrary(mf, lib, lib, llt);
+}
+
+void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
+                              std::string const& libRef,
+                              cmTargetLinkLibraryType llt)
+{
+  cmTarget* tgt = mf.FindTargetToUse(lib);
   {
     const bool isNonImportedTarget = tgt && !tgt->IsImported();
 
     const std::string libName =
       (isNonImportedTarget && llt != GENERAL_LibraryType)
-      ? targetNameGenex(lib)
-      : lib;
+      ? targetNameGenex(libRef)
+      : libRef;
     this->AppendProperty(
       "LINK_LIBRARIES",
       this->GetDebugGeneratorExpressions(libName, llt).c_str());
   }
 
-  if (cmGeneratorExpression::Find(lib) != std::string::npos ||
+  if (cmGeneratorExpression::Find(lib) != std::string::npos || lib != libRef ||
       (tgt &&
        (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
         tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) ||

+ 3 - 0
Source/cmTarget.h

@@ -142,6 +142,9 @@ public:
 
   void AddLinkLibrary(cmMakefile& mf, const std::string& lib,
                       cmTargetLinkLibraryType llt);
+  void AddLinkLibrary(cmMakefile& mf, std::string const& lib,
+                      std::string const& libRef, cmTargetLinkLibraryType llt);
+
   enum TLLSignature
   {
     KeywordTLLSignature,

+ 58 - 22
Source/cmTargetLinkLibrariesCommand.cxx

@@ -359,30 +359,53 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
     }
   }
 
+  bool warnRemoteInterface = false;
+  bool rejectRemoteLinking = false;
+  bool encodeRemoteReference = false;
+  if (this->Makefile != this->Target->GetMakefile()) {
+    // The LHS target was created in another directory.
+    switch (this->Makefile->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 = this->Makefile->GetDirectoryId();
+    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
   // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
   // property of the target on the LHS shall be populated.)
   if (this->CurrentProcessingState != ProcessingKeywordLinkInterface &&
       this->CurrentProcessingState != ProcessingPlainLinkInterface) {
 
-    // Assure that the target on the LHS was created in the current directory.
-    cmTarget* t =
-      this->Makefile->FindLocalNonAliasTarget(this->Target->GetName());
-    if (!t) {
-      const std::vector<cmTarget*>& importedTargets =
-        this->Makefile->GetOwnedImportedTargets();
-      for (cmTarget* importedTarget : importedTargets) {
-        if (importedTarget->GetName() == this->Target->GetName()) {
-          t = importedTarget;
-          break;
-        }
-      }
-    }
-    if (!t) {
+    if (rejectRemoteLinking) {
       std::ostringstream e;
       e << "Attempt to add link library \"" << lib << "\" to target \""
         << this->Target->GetName()
-        << "\" which is not built in this directory.";
+        << "\" which is not built in this directory.\n"
+        << "This is allowed only when policy CMP0079 is set to NEW.";
       this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
       return false;
     }
@@ -404,7 +427,20 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
       this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
     }
 
-    this->Target->AddLinkLibrary(*this->Makefile, lib, llt);
+    this->Target->AddLinkLibrary(*this->Makefile, lib, libRef, llt);
+  }
+
+  if (warnRemoteInterface) {
+    std::ostringstream w;
+    /* clang-format off */
+    w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0079) << "\n"
+      "Target\n  " << this->Target->GetName() << "\nis not created in this "
+      "directory.  For compatibility with older versions of CMake, link "
+      "library\n  " << lib << "\nwill be looked up in the directory in "
+      "which the target was created rather than in this calling "
+      "directory.";
+    /* clang-format on */
+    this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
   }
 
   // Handle (additional) case where the command was called with PRIVATE /
@@ -415,9 +451,9 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
       this->CurrentProcessingState == ProcessingPlainPrivateInterface) {
     if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) {
       std::string configLib =
-        this->Target->GetDebugGeneratorExpressions(lib, llt);
-      if (cmGeneratorExpression::IsValidTargetName(lib) ||
-          cmGeneratorExpression::Find(lib) != std::string::npos) {
+        this->Target->GetDebugGeneratorExpressions(libRef, llt);
+      if (cmGeneratorExpression::IsValidTargetName(libRef) ||
+          cmGeneratorExpression::Find(libRef) != std::string::npos) {
         configLib = "$<LINK_ONLY:" + configLib + ">";
       }
       this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
@@ -431,7 +467,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
   // property of the target on the LHS shall be populated.)
   this->Target->AppendProperty(
     "INTERFACE_LINK_LIBRARIES",
-    this->Target->GetDebugGeneratorExpressions(lib, llt).c_str());
+    this->Target->GetDebugGeneratorExpressions(libRef, llt).c_str());
 
   // Stop processing if called without any keyword.
   if (this->CurrentProcessingState == ProcessingLinkLibraries) {
@@ -464,12 +500,12 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
       for (std::string const& dc : debugConfigs) {
         prop = "LINK_INTERFACE_LIBRARIES_";
         prop += dc;
-        this->Target->AppendProperty(prop, lib.c_str());
+        this->Target->AppendProperty(prop, libRef.c_str());
       }
     }
     if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
       // Put in the non-DEBUG configuration interfaces.
-      this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", lib.c_str());
+      this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str());
 
       // Make sure the DEBUG configuration interfaces exist so that the
       // general one will not be used as a fall-back.

+ 12 - 0
Tests/CMakeCommands/target_link_libraries/CMakeLists.txt

@@ -130,3 +130,15 @@ target_link_libraries(newsignature1 PRIVATE depC INTERFACE depD PUBLIC depB PRIV
 
 assert_property(newsignature1 INTERFACE_LINK_LIBRARIES "depD;depB")
 assert_property(newsignature1 LINK_LIBRARIES "depC;depB;subdirlib")
+
+#----------------------------------------------------------------------------
+# Test cross-directory linking.
+cmake_policy(PUSH)
+cmake_policy(SET CMP0079 NEW)
+add_executable(TopDir TopDir.c)
+add_subdirectory(SubDirA)
+add_subdirectory(SubDirB)
+target_link_libraries(SubDirB TopDirImported)
+add_library(TopDirImported IMPORTED INTERFACE)
+target_compile_definitions(TopDirImported INTERFACE DEF_TopDirImported)
+cmake_policy(POP)

+ 15 - 0
Tests/CMakeCommands/target_link_libraries/SubDirA/CMakeLists.txt

@@ -0,0 +1,15 @@
+add_executable(SubDirA SubDirA.c)
+
+# Link to a target imported in this directory that would not normally
+# be visible to the directory in which TopDir is defined.
+target_link_libraries(TopDir PUBLIC SameNameImported)
+
+# Link SubDirA to a target imported in this directory that has the same
+# name as a target imported in SubDirB's directory.  SubDirB will also
+# tell us to link its copy.  At compile time we verify both are linked.
+target_link_libraries(SubDirA PRIVATE SameNameImported)
+
+# Import a target with the same name as a target imported in SubDirB.
+# Distinguish this copy by having a unique usage requirement.
+add_library(SameNameImported IMPORTED INTERFACE)
+target_compile_definitions(SameNameImported INTERFACE DEF_SameNameImportedSubDirA)

+ 14 - 0
Tests/CMakeCommands/target_link_libraries/SubDirA/SubDirA.c

@@ -0,0 +1,14 @@
+#ifndef DEF_SameNameImportedSubDirA
+#  error "DEF_SameNameImportedSubDirA is not defined but should be!"
+#endif
+#ifndef DEF_SameNameImportedSubDirB
+#  error "DEF_SameNameImportedSubDirB is not defined but should be!"
+#endif
+#ifdef DEF_TopDirImported
+#  error "DEF_TopDirImported is defined but should not be!"
+#endif
+
+int main(void)
+{
+  return 0;
+}

+ 15 - 0
Tests/CMakeCommands/target_link_libraries/SubDirB/CMakeLists.txt

@@ -0,0 +1,15 @@
+add_executable(SubDirB SubDirB.c)
+
+# Link to a target imported in this directory that would not normally
+# be visible to the directory in which TopDir is defined.
+target_link_libraries(TopDir PUBLIC SameNameImported)
+
+# 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
+# compiling SubDirA that it sees our target and its own.
+target_link_libraries(SubDirA PRIVATE SameNameImported)
+
+# Import a target with the same name as a target imported in SubDirA.
+# Distinguish this copy by having a unique usage requirement.
+add_library(SameNameImported IMPORTED INTERFACE)
+target_compile_definitions(SameNameImported INTERFACE DEF_SameNameImportedSubDirB)

+ 14 - 0
Tests/CMakeCommands/target_link_libraries/SubDirB/SubDirB.c

@@ -0,0 +1,14 @@
+#ifdef DEF_SameNameImportedSubDirA
+#  error "DEF_SameNameImportedSubDirA is defined but should not be!"
+#endif
+#ifdef DEF_SameNameImportedSubDirB
+#  error "DEF_SameNameImportedSubDirB is defined but should not be!"
+#endif
+#ifndef DEF_TopDirImported
+#  error "DEF_TopDirImported is not defined but should be!"
+#endif
+
+int main(void)
+{
+  return 0;
+}

+ 14 - 0
Tests/CMakeCommands/target_link_libraries/TopDir.c

@@ -0,0 +1,14 @@
+#ifndef DEF_SameNameImportedSubDirA
+#  error "DEF_SameNameImportedSubDirA is not defined but should be!"
+#endif
+#ifndef DEF_SameNameImportedSubDirB
+#  error "DEF_SameNameImportedSubDirB is not defined but should be!"
+#endif
+#ifdef DEF_TopDirImported
+#  error "DEF_TopDirImported is defined but should not be!"
+#endif
+
+int main(void)
+{
+  return 0;
+}

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

@@ -155,6 +155,13 @@ add_library(testStaticLibRequiredPrivate testStaticLibRequiredPrivate.c)
 target_link_libraries(testLibDepends PRIVATE testStaticLibRequiredPrivate)
 cmake_policy(POP)
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0079 NEW)
+add_library(TopDirLib STATIC testTopDirLib.c)
+add_subdirectory(SubDirLinkA)
+add_subdirectory(SubDirLinkB)
+cmake_policy(POP)
+
 macro(add_include_lib _libName)
   file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "/* no content */\n")
   add_library(${_libName} "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c")
@@ -508,6 +515,7 @@ install(
   testLibCycleA testLibCycleB
   testLibNoSONAME
   cmp0022NEW cmp0022OLD
+  TopDirLib SubDirLinkA
   systemlib
   EXPORT exp
   RUNTIME DESTINATION $<1:bin>$<0:/wrong>
@@ -566,6 +574,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3
   testSharedLibRequired testSharedLibRequiredUser testSharedLibRequiredUser2
   testSharedLibDepends renamed_on_export
   cmp0022NEW cmp0022OLD
+  TopDirLib SubDirLinkA
   systemlib
   NAMESPACE bld_
   FILE ExportBuildTree.cmake

+ 6 - 0
Tests/ExportImport/Export/SubDirLinkA/CMakeLists.txt

@@ -0,0 +1,6 @@
+add_library(SubDirLinkAImported IMPORTED INTERFACE)
+target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForExport)
+
+target_link_libraries(TopDirLib PUBLIC SubDirLinkAImported)
+
+add_library(SubDirLinkA STATIC SubDirLinkA.c)

+ 11 - 0
Tests/ExportImport/Export/SubDirLinkA/SubDirLinkA.c

@@ -0,0 +1,11 @@
+#ifdef DEF_SubDirLinkAImportedForExport
+#  error "DEF_SubDirLinkAImportedForExport is defined but should not be!"
+#endif
+#ifndef DEF_SubDirLinkBImportedForExport
+#  error "DEF_SubDirLinkBImportedForExport is not defined but should be!"
+#endif
+
+int testSubDirLinkA(void)
+{
+  return 0;
+}

+ 4 - 0
Tests/ExportImport/Export/SubDirLinkB/CMakeLists.txt

@@ -0,0 +1,4 @@
+add_library(SubDirLinkBImported IMPORTED INTERFACE)
+target_compile_definitions(SubDirLinkBImported INTERFACE DEF_SubDirLinkBImportedForExport)
+
+target_link_libraries(SubDirLinkA PUBLIC SubDirLinkBImported)

+ 11 - 0
Tests/ExportImport/Export/testTopDirLib.c

@@ -0,0 +1,11 @@
+#ifndef DEF_SubDirLinkAImportedForExport
+#  error "DEF_SubDirLinkAImportedForExport is not defined but should be!"
+#endif
+#ifdef DEF_SubDirLinkBImportedForExport
+#  error "DEF_SubDirLinkBImportedForExport is defined but should not be!"
+#endif
+
+int testTopDirLib(void)
+{
+  return 0;
+}

+ 11 - 0
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -1,3 +1,9 @@
+# Prepare imported targets that the exported project itself imported.
+add_library(SubDirLinkAImported IMPORTED INTERFACE)
+target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForImport)
+add_library(SubDirLinkBImported IMPORTED INTERFACE)
+target_compile_definitions(SubDirLinkBImported INTERFACE DEF_SubDirLinkBImportedForImport)
+
 # Import targets from the exported build tree.
 include(${Import_BINARY_DIR}/../Export/ExportBuildTree.cmake)
 
@@ -158,6 +164,11 @@ target_link_libraries(cmp0022OLD_exp_test exp_cmp0022OLD)
 add_executable(cmp0022NEW_exp_test cmp0022NEW_test_vs6_2.cpp)
 target_link_libraries(cmp0022NEW_exp_test exp_cmp0022NEW)
 
+add_executable(SubDirLink_bld SubDirLink.c)
+target_link_libraries(SubDirLink_bld PRIVATE bld_TopDirLib bld_SubDirLinkA)
+add_executable(SubDirLink_exp SubDirLink.c)
+target_link_libraries(SubDirLink_exp PRIVATE exp_TopDirLib exp_SubDirLinkA)
+
 # Try building a plugin to an executable imported from the build tree.
 add_library(imp_mod1b MODULE imp_mod1.c)
 target_link_libraries(imp_mod1b bld_testExe2)

+ 14 - 0
Tests/ExportImport/Import/A/SubDirLink.c

@@ -0,0 +1,14 @@
+#ifndef DEF_SubDirLinkAImportedForImport
+#  error "DEF_SubDirLinkAImportedForImport is not defined but should be!"
+#endif
+#ifndef DEF_SubDirLinkBImportedForImport
+#  error "DEF_SubDirLinkBImportedForImport is not defined but should be!"
+#endif
+
+extern int testTopDirLib(void);
+extern int testSubDirLinkA(void);
+
+int main(void)
+{
+  return (testTopDirLib() + testSubDirLinkA() + 0);
+}

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

@@ -0,0 +1 @@
+-- INTERFACE_LINK_LIBRARIES='foo::@<[Xx0-9A-Fa-f]+>'

+ 2 - 0
Tests/RunCMake/target_link_libraries/CMP0079-iface-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0079 NEW)
+include(CMP0079-iface-common.cmake)

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

@@ -0,0 +1 @@
+-- INTERFACE_LINK_LIBRARIES='foo'

+ 2 - 0
Tests/RunCMake/target_link_libraries/CMP0079-iface-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0079 OLD)
+include(CMP0079-iface-common.cmake)

+ 17 - 0
Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN-stderr.txt

@@ -0,0 +1,17 @@
+^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
+
+    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.$

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

@@ -0,0 +1 @@
+-- INTERFACE_LINK_LIBRARIES='foo'

+ 1 - 0
Tests/RunCMake/target_link_libraries/CMP0079-iface-WARN.cmake

@@ -0,0 +1 @@
+include(CMP0079-iface-common.cmake)

+ 6 - 0
Tests/RunCMake/target_link_libraries/CMP0079-iface-common.cmake

@@ -0,0 +1,6 @@
+enable_language(C)
+
+add_executable(top empty.c)
+add_subdirectory(CMP0079-iface)
+get_property(libs TARGET top PROPERTY INTERFACE_LINK_LIBRARIES)
+message(STATUS "INTERFACE_LINK_LIBRARIES='${libs}'")

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

@@ -0,0 +1 @@
+target_link_libraries(top INTERFACE foo)

+ 0 - 0
Tests/RunCMake/target_link_libraries/SubDirTarget-result.txt → Tests/RunCMake/target_link_libraries/CMP0079-link-NEW-bogus-result.txt


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

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

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

@@ -0,0 +1,6 @@
+cmake_policy(SET CMP0028 NEW)
+cmake_policy(SET CMP0079 NEW)
+enable_language(C)
+
+add_executable(top empty.c)
+set_property(TARGET top APPEND PROPERTY LINK_LIBRARIES "foo::@<0xdeadbeef>")

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

@@ -0,0 +1 @@
+-- LINK_LIBRARIES='foo::@<[Xx0-9A-Fa-f]+>'

+ 2 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0079 NEW)
+include(CMP0079-link-common.cmake)

+ 1 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-OLD-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at CMP0079-link/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
+  Attempt to add link library "foo" to target "top" which is not built in
+  this directory.
+
+  This is allowed only when policy CMP0079 is set to NEW.$

+ 2 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0079 OLD)
+include(CMP0079-link-common.cmake)

+ 1 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-WARN-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-WARN-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at CMP0079-link/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
+  Attempt to add link library "foo" to target "top" which is not built in
+  this directory.
+
+  This is allowed only when policy CMP0079 is set to NEW.$

+ 1 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-WARN.cmake

@@ -0,0 +1 @@
+include(CMP0079-link-common.cmake)

+ 6 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link-common.cmake

@@ -0,0 +1,6 @@
+enable_language(C)
+
+add_executable(top empty.c)
+add_subdirectory(CMP0079-link)
+get_property(libs TARGET top PROPERTY LINK_LIBRARIES)
+message(STATUS "LINK_LIBRARIES='${libs}'")

+ 1 - 0
Tests/RunCMake/target_link_libraries/CMP0079-link/CMakeLists.txt

@@ -0,0 +1 @@
+target_link_libraries(top PUBLIC foo)

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

@@ -1,3 +1,3 @@
 cmake_minimum_required(VERSION 2.8.4)
 project(${RunCMake_TEST} NONE)
-include(${RunCMake_TEST}.cmake)
+include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)

+ 7 - 1
Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake

@@ -4,11 +4,17 @@ run_cmake(CMP0023-WARN)
 run_cmake(CMP0023-NEW)
 run_cmake(CMP0023-WARN-2)
 run_cmake(CMP0023-NEW-2)
+run_cmake(CMP0079-iface-WARN)
+run_cmake(CMP0079-iface-OLD)
+run_cmake(CMP0079-iface-NEW)
+run_cmake(CMP0079-link-WARN)
+run_cmake(CMP0079-link-OLD)
+run_cmake(CMP0079-link-NEW)
+run_cmake(CMP0079-link-NEW-bogus)
 run_cmake(ImportedTarget)
 run_cmake(ImportedTargetFailure)
 run_cmake(MixedSignature)
 run_cmake(Separate-PRIVATE-LINK_PRIVATE-uses)
-run_cmake(SubDirTarget)
 run_cmake(SharedDepNotTarget)
 run_cmake(StaticPrivateDepNotExported)
 run_cmake(StaticPrivateDepNotTarget)

+ 0 - 5
Tests/RunCMake/target_link_libraries/SubDirTarget-stderr.txt

@@ -1,5 +0,0 @@
-^CMake Error at SubDirTarget.cmake:[0-9]+ \(target_link_libraries\):
-  Attempt to add link library "m" to target "subexe" which is not built in
-  this directory.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)$

+ 0 - 3
Tests/RunCMake/target_link_libraries/SubDirTarget.cmake

@@ -1,3 +0,0 @@
-enable_language(C)
-add_subdirectory(SubDirTarget)
-target_link_libraries(subexe m)

+ 0 - 1
Tests/RunCMake/target_link_libraries/SubDirTarget/CMakeLists.txt

@@ -1 +0,0 @@
-add_executable(subexe ../empty.c)