Browse Source

Merge topic 'fileapiLinkPathAndLinkDirBacktraces'

4d6334824d fileapi: add backtraces for LINK_PATH and LINK_DIRECTORIES
5bd65dff7a cmLocalGenerator: Add OutputLinkLibraries overload with backtraces
5d39e792ae cmGeneratorTarget: Store backtrace for target LINK_DIRECTORIES property
7da17ef797 cmLinkLineComputer: Add ComputeLinkLibraries overload with backtraces
d4d0dd0f6a cmLinkLineComputer: Add ComputeLinkLibs overload with backtraces
0ac9dcb807 cmLinkLineComputer: Add ComputeLinkPath overload with backtraces
0c6468178a cmComputeLinkInformation: Add GetDirectoriesWithBacktraces
a209b31d0d cmComputeLinkInformation: Add AppendValues with backtraces

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

+ 35 - 0
Source/cmComputeLinkInformation.cxx

@@ -6,6 +6,7 @@
 #include "cmComputeLinkDepends.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -406,6 +407,18 @@ cmComputeLinkInformation::~cmComputeLinkInformation()
   delete this->OrderDependentRPath;
 }
 
+void cmComputeLinkInformation::AppendValues(
+  std::string& result, std::vector<BT<std::string>>& values)
+{
+  for (BT<std::string>& p : values) {
+    if (result.empty()) {
+      result.append(" ");
+    }
+
+    result.append(p.Value);
+  }
+}
+
 cmComputeLinkInformation::ItemVector const&
 cmComputeLinkInformation::GetItems() const
 {
@@ -418,6 +431,28 @@ std::vector<std::string> const& cmComputeLinkInformation::GetDirectories()
   return this->OrderLinkerSearchPath->GetOrderedDirectories();
 }
 
+std::vector<BT<std::string>>
+cmComputeLinkInformation::GetDirectoriesWithBacktraces()
+{
+  std::vector<BT<std::string>> directoriesWithBacktraces;
+
+  std::vector<BT<std::string>> targetLinkDirectores =
+    this->Target->GetLinkDirectories(this->Config, this->LinkLanguage);
+
+  const std::vector<std::string>& orderedDirectories = this->GetDirectories();
+  for (const std::string& dir : orderedDirectories) {
+    auto result =
+      std::find(targetLinkDirectores.begin(), targetLinkDirectores.end(), dir);
+    if (result != targetLinkDirectores.end()) {
+      directoriesWithBacktraces.emplace_back(std::move(*result));
+    } else {
+      directoriesWithBacktraces.emplace_back(dir);
+    }
+  }
+
+  return directoriesWithBacktraces;
+}
+
 std::string cmComputeLinkInformation::GetRPathLinkString() const
 {
   // If there is no separate linker runtime search flag (-rpath-link)

+ 6 - 0
Source/cmComputeLinkInformation.h

@@ -17,6 +17,8 @@ class cmGlobalGenerator;
 class cmMakefile;
 class cmOrderDirectories;
 class cmake;
+template <typename T>
+class BT;
 
 /** \class cmComputeLinkInformation
  * \brief Compute link information for a target in one configuration.
@@ -43,8 +45,10 @@ public:
     cmGeneratorTarget const* Target = nullptr;
   };
   using ItemVector = std::vector<Item>;
+  void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
   ItemVector const& GetItems() const;
   std::vector<std::string> const& GetDirectories() const;
+  std::vector<BT<std::string>> GetDirectoriesWithBacktraces();
   std::vector<std::string> const& GetDepends() const;
   std::vector<std::string> const& GetFrameworkPaths() const;
   std::string GetLinkLanguage() const { return this->LinkLanguage; }
@@ -66,6 +70,8 @@ public:
 
   std::string GetConfig() const { return this->Config; }
 
+  const cmGeneratorTarget* GetTarget() { return this->Target; }
+
 private:
   void AddItem(std::string const& item, const cmGeneratorTarget* tgt);
   void AddSharedDepItem(std::string const& item, cmGeneratorTarget const* tgt);

+ 12 - 8
Source/cmFileAPICodemodel.cxx

@@ -1269,8 +1269,8 @@ Json::Value Target::DumpLinkCommandFragments()
   std::string linkLanguageFlags;
   std::vector<BT<std::string>> linkFlags;
   std::string frameworkPath;
-  std::string linkPath;
-  std::string linkLibs;
+  std::vector<BT<std::string>> linkPath;
+  std::vector<BT<std::string>> linkLibs;
   cmLocalGenerator* lg = this->GT->GetLocalGenerator();
   cmLinkLineComputer linkLineComputer(lg,
                                       lg->GetStateSnapshot().GetDirectory());
@@ -1279,8 +1279,6 @@ Json::Value Target::DumpLinkCommandFragments()
                      this->GT);
   linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags);
   frameworkPath = cmTrimWhitespace(frameworkPath);
-  linkPath = cmTrimWhitespace(linkPath);
-  linkLibs = cmTrimWhitespace(linkLibs);
 
   if (!linkLanguageFlags.empty()) {
     linkFragments.append(
@@ -1301,13 +1299,19 @@ Json::Value Target::DumpLinkCommandFragments()
   }
 
   if (!linkPath.empty()) {
-    linkFragments.append(
-      this->DumpCommandFragment(std::move(linkPath), "libraryPath"));
+    for (BT<std::string> frag : linkPath) {
+      frag.Value = cmTrimWhitespace(frag.Value);
+      linkFragments.append(
+        this->DumpCommandFragment(this->ToJBT(frag), "libraryPath"));
+    }
   }
 
   if (!linkLibs.empty()) {
-    linkFragments.append(
-      this->DumpCommandFragment(std::move(linkLibs), "libraries"));
+    for (BT<std::string> frag : linkLibs) {
+      frag.Value = cmTrimWhitespace(frag.Value);
+      linkFragments.append(
+        this->DumpCommandFragment(this->ToJBT(frag), "libraries"));
+    }
   }
 
   return linkFragments;

+ 1 - 1
Source/cmGeneratorTarget.cxx

@@ -3745,7 +3745,7 @@ void processLinkDirectories(cmGeneratorTarget const* tgt,
       // in case projects set the LINK_DIRECTORIES property directly.
       cmSystemTools::ConvertToUnixSlashes(entryDirectory);
       if (uniqueDirectories.insert(entryDirectory).second) {
-        directories.emplace_back(entryDirectory);
+        directories.emplace_back(entryDirectory, entry.Backtrace);
         if (debugDirectories) {
           usedDirectories += " * " + entryDirectory + "\n";
         }

+ 81 - 18
Source/cmLinkLineComputer.cxx

@@ -4,10 +4,13 @@
 #include "cmLinkLineComputer.h"
 
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmListFileCache.h"
 #include "cmOutputConverter.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
@@ -56,6 +59,15 @@ std::string cmLinkLineComputer::ConvertToLinkReference(
 std::string cmLinkLineComputer::ComputeLinkLibs(cmComputeLinkInformation& cli)
 {
   std::string linkLibs;
+  std::vector<BT<std::string>> linkLibsList;
+  this->ComputeLinkLibs(cli, linkLibsList);
+  cli.AppendValues(linkLibs, linkLibsList);
+  return linkLibs;
+}
+
+void cmLinkLineComputer::ComputeLinkLibs(
+  cmComputeLinkInformation& cli, std::vector<BT<std::string>>& linkLibraries)
+{
   using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& items = cli.GetItems();
   for (auto const& item : items) {
@@ -63,16 +75,33 @@ std::string cmLinkLineComputer::ComputeLinkLibs(cmComputeLinkInformation& cli)
         item.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
       continue;
     }
+
+    BT<std::string> linkLib;
     if (item.IsPath) {
-      linkLibs += cli.GetLibLinkFileFlag();
-      linkLibs +=
+      linkLib.Value += cli.GetLibLinkFileFlag();
+      linkLib.Value +=
         this->ConvertToOutputFormat(this->ConvertToLinkReference(item.Value));
     } else {
-      linkLibs += item.Value;
+      linkLib.Value += item.Value;
     }
-    linkLibs += " ";
+    linkLib.Value += " ";
+
+    const cmLinkImplementation* linkImpl =
+      cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
+
+    for (const cmLinkImplItem& iter : linkImpl->Libraries) {
+      if (iter.Target != nullptr &&
+          iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+        std::string libPath = iter.Target->GetLocation(cli.GetConfig());
+        if (item.Value == libPath) {
+          linkLib.Backtrace = iter.Backtrace;
+          break;
+        }
+      }
+    }
+
+    linkLibraries.emplace_back(linkLib);
   }
-  return linkLibs;
 }
 
 std::string cmLinkLineComputer::ConvertToOutputFormat(std::string const& input)
@@ -101,8 +130,19 @@ std::string cmLinkLineComputer::ComputeLinkPath(
   std::string const& libPathTerminator)
 {
   std::string linkPath;
+  std::vector<BT<std::string>> linkPathList;
+  this->ComputeLinkPath(cli, libPathFlag, libPathTerminator, linkPathList);
+  cli.AppendValues(linkPath, linkPathList);
+  return linkPath;
+}
 
+void cmLinkLineComputer::ComputeLinkPath(
+  cmComputeLinkInformation& cli, std::string const& libPathFlag,
+  std::string const& libPathTerminator, std::vector<BT<std::string>>& linkPath)
+{
   if (cli.GetLinkLanguage() == "Swift") {
+    std::string linkPathNoBT;
+
     for (const cmComputeLinkInformation::Item& item : cli.GetItems()) {
       const cmGeneratorTarget* target = item.Target;
       if (!target) {
@@ -116,20 +156,23 @@ std::string cmLinkLineComputer::ComputeLinkPath(
           type = cmStateEnums::ImportLibraryArtifact;
         }
 
-        linkPath += cmStrCat(" ", libPathFlag,
-                             item.Target->GetDirectory(cli.GetConfig(), type),
-                             libPathTerminator, " ");
+        linkPathNoBT += cmStrCat(
+          " ", libPathFlag, item.Target->GetDirectory(cli.GetConfig(), type),
+          libPathTerminator, " ");
       }
     }
-  }
 
-  for (std::string const& libDir : cli.GetDirectories()) {
-    linkPath +=
-      cmStrCat(" ", libPathFlag, this->ConvertToOutputForExisting(libDir),
-               libPathTerminator, " ");
+    if (!linkPathNoBT.empty()) {
+      linkPath.emplace_back(std::move(linkPathNoBT));
+    }
   }
 
-  return linkPath;
+  for (BT<std::string> libDir : cli.GetDirectoriesWithBacktraces()) {
+    libDir.Value = cmStrCat(" ", libPathFlag,
+                            this->ConvertToOutputForExisting(libDir.Value),
+                            libPathTerminator, " ");
+    linkPath.emplace_back(libDir);
+  }
 }
 
 std::string cmLinkLineComputer::ComputeRPath(cmComputeLinkInformation& cli)
@@ -179,13 +222,30 @@ std::string cmLinkLineComputer::ComputeFrameworkPath(
 std::string cmLinkLineComputer::ComputeLinkLibraries(
   cmComputeLinkInformation& cli, std::string const& stdLibString)
 {
-  std::ostringstream fout;
-  fout << this->ComputeRPath(cli);
+  std::string linkLibraries;
+  std::vector<BT<std::string>> linkLibrariesList;
+  this->ComputeLinkLibraries(cli, stdLibString, linkLibrariesList);
+  cli.AppendValues(linkLibraries, linkLibrariesList);
+  return linkLibraries;
+}
+
+void cmLinkLineComputer::ComputeLinkLibraries(
+  cmComputeLinkInformation& cli, std::string const& stdLibString,
+  std::vector<BT<std::string>>& linkLibraries)
+{
+  std::ostringstream rpathOut;
+  rpathOut << this->ComputeRPath(cli);
+
+  std::string rpath = rpathOut.str();
+  if (!rpath.empty()) {
+    linkLibraries.emplace_back(std::move(rpath));
+  }
 
   // Write the library flags to the build rule.
-  fout << this->ComputeLinkLibs(cli);
+  this->ComputeLinkLibs(cli, linkLibraries);
 
   // Add the linker runtime search path if any.
+  std::ostringstream fout;
   std::string rpath_link = cli.GetRPathLinkString();
   if (!cli.GetRPathLinkFlag().empty() && !rpath_link.empty()) {
     fout << cli.GetRPathLinkFlag();
@@ -198,7 +258,10 @@ std::string cmLinkLineComputer::ComputeLinkLibraries(
     fout << stdLibString << " ";
   }
 
-  return fout.str();
+  std::string remainingLibs = fout.str();
+  if (!remainingLibs.empty()) {
+    linkLibraries.emplace_back(remainingLibs);
+  }
 }
 
 std::string cmLinkLineComputer::GetLinkerLanguage(cmGeneratorTarget* target,

+ 16 - 2
Source/cmLinkLineComputer.h

@@ -7,12 +7,15 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <string>
+#include <vector>
 
 #include "cmStateDirectory.h"
 
 class cmComputeLinkInformation;
 class cmGeneratorTarget;
 class cmOutputConverter;
+template <typename T>
+class BT;
 
 class cmLinkLineComputer
 {
@@ -34,17 +37,28 @@ public:
                               std::string const& libPathFlag,
                               std::string const& libPathTerminator);
 
+  void ComputeLinkPath(cmComputeLinkInformation& cli,
+                       std::string const& libPathFlag,
+                       std::string const& libPathTerminator,
+                       std::vector<BT<std::string>>& linkPath);
+
   std::string ComputeFrameworkPath(cmComputeLinkInformation& cli,
                                    std::string const& fwSearchFlag);
 
-  virtual std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
-                                           std::string const& stdLibString);
+  std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
+                                   std::string const& stdLibString);
+
+  virtual void ComputeLinkLibraries(
+    cmComputeLinkInformation& cli, std::string const& stdLibString,
+    std::vector<BT<std::string>>& linkLibraries);
 
   virtual std::string GetLinkerLanguage(cmGeneratorTarget* target,
                                         std::string const& config);
 
 protected:
   std::string ComputeLinkLibs(cmComputeLinkInformation& cli);
+  void ComputeLinkLibs(cmComputeLinkInformation& cli,
+                       std::vector<BT<std::string>>& linkLibraries);
   std::string ComputeRPath(cmComputeLinkInformation& cli);
 
   std::string ConvertToOutputFormat(std::string const& input);

+ 27 - 14
Source/cmLinkLineDeviceComputer.cxx

@@ -4,13 +4,14 @@
 #include "cmLinkLineDeviceComputer.h"
 
 #include <set>
-#include <sstream>
 #include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmLinkItem.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStateDirectory.h"
@@ -67,12 +68,10 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
   return false;
 }
 
-std::string cmLinkLineDeviceComputer::ComputeLinkLibraries(
-  cmComputeLinkInformation& cli, std::string const& stdLibString)
+void cmLinkLineDeviceComputer::ComputeLinkLibraries(
+  cmComputeLinkInformation& cli, std::string const& stdLibString,
+  std::vector<BT<std::string>>& linkLibraries)
 {
-  // Write the library flags to the build rule.
-  std::ostringstream fout;
-
   // Generate the unique set of link items when device linking.
   // The nvcc device linker is designed so that each static library
   // with device symbols only needs to be listed once as it doesn't
@@ -110,7 +109,7 @@ std::string cmLinkLineDeviceComputer::ComputeLinkLibraries(
       }
     }
 
-    std::string out;
+    BT<std::string> linkLib;
     if (item.IsPath) {
       // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
       // These should be passed to nvlink.  Other extensions need to be left
@@ -118,7 +117,7 @@ std::string cmLinkLineDeviceComputer::ComputeLinkLibraries(
       // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
       if (cmHasLiteralSuffix(item.Value, ".a") ||
           cmHasLiteralSuffix(item.Value, ".lib")) {
-        out += this->ConvertToOutputFormat(
+        linkLib.Value += this->ConvertToOutputFormat(
           this->ConvertToLinkReference(item.Value));
       }
     } else if (item.Value == "-framework") {
@@ -127,19 +126,33 @@ std::string cmLinkLineDeviceComputer::ComputeLinkLibraries(
       skipItemAfterFramework = true;
       continue;
     } else if (cmLinkItemValidForDevice(item.Value)) {
-      out += item.Value;
+      linkLib.Value += item.Value;
     }
 
-    if (emitted.insert(out).second) {
-      fout << out << " ";
+    if (emitted.insert(linkLib.Value).second) {
+      linkLib.Value += " ";
+
+      const cmLinkImplementation* linkImpl =
+        cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
+
+      for (const cmLinkImplItem& iter : linkImpl->Libraries) {
+        if (iter.Target != nullptr &&
+            iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+          std::string libPath = iter.Target->GetLocation(cli.GetConfig());
+          if (item.Value == libPath) {
+            linkLib.Backtrace = iter.Backtrace;
+            break;
+          }
+        }
+      }
+
+      linkLibraries.emplace_back(linkLib);
     }
   }
 
   if (!stdLibString.empty()) {
-    fout << stdLibString << " ";
+    linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
   }
-
-  return fout.str();
 }
 
 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,

+ 6 - 2
Source/cmLinkLineDeviceComputer.h

@@ -7,6 +7,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <string>
+#include <vector>
 
 #include "cmLinkLineComputer.h"
 
@@ -15,6 +16,8 @@ class cmGeneratorTarget;
 class cmLocalGenerator;
 class cmOutputConverter;
 class cmStateDirectory;
+template <typename T>
+class BT;
 
 class cmLinkLineDeviceComputer : public cmLinkLineComputer
 {
@@ -29,8 +32,9 @@ public:
 
   bool ComputeRequiresDeviceLinking(cmComputeLinkInformation& cli);
 
-  std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
-                                   std::string const& stdLibString) override;
+  void ComputeLinkLibraries(
+    cmComputeLinkInformation& cli, std::string const& stdLibString,
+    std::vector<BT<std::string>>& linkLibraries) override;
 
   std::string GetLinkerLanguage(cmGeneratorTarget* target,
                                 std::string const& config) override;

+ 26 - 10
Source/cmLocalGenerator.cxx

@@ -1199,17 +1199,21 @@ void cmLocalGenerator::GetTargetFlags(
   std::string& linkLibs, std::string& flags, std::string& linkFlags,
   std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target)
 {
-  std::vector<BT<std::string>> tmpLinkFlags;
-  this->GetTargetFlags(linkLineComputer, config, linkLibs, flags, tmpLinkFlags,
-                       frameworkPath, linkPath, target);
-  this->AppendFlags(linkFlags, tmpLinkFlags);
+  std::vector<BT<std::string>> linkFlagsList;
+  std::vector<BT<std::string>> linkPathList;
+  std::vector<BT<std::string>> linkLibsList;
+  this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags,
+                       linkFlagsList, frameworkPath, linkPathList, target);
+  this->AppendFlags(linkFlags, linkFlagsList);
+  this->AppendFlags(linkPath, linkPathList);
+  this->AppendFlags(linkLibs, linkLibsList);
 }
 
 void cmLocalGenerator::GetTargetFlags(
   cmLinkLineComputer* linkLineComputer, const std::string& config,
-  std::string& linkLibs, std::string& flags,
+  std::vector<BT<std::string>>& linkLibs, std::string& flags,
   std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
-  std::string& linkPath, cmGeneratorTarget* target)
+  std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target)
 {
   const std::string buildType = cmSystemTools::UpperCase(config);
   cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
@@ -1526,6 +1530,19 @@ void cmLocalGenerator::OutputLinkLibraries(
   cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
   std::string& linkLibraries, std::string& frameworkPath,
   std::string& linkPath)
+{
+  std::vector<BT<std::string>> linkLibrariesList;
+  std::vector<BT<std::string>> linkPathList;
+  this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList,
+                            frameworkPath, linkPathList);
+  pcli->AppendValues(linkLibraries, linkLibrariesList);
+  pcli->AppendValues(linkPath, linkPathList);
+}
+
+void cmLocalGenerator::OutputLinkLibraries(
+  cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
+  std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath,
+  std::vector<BT<std::string>>& linkPath)
 {
   cmComputeLinkInformation& cli = *pcli;
 
@@ -1558,10 +1575,9 @@ void cmLocalGenerator::OutputLinkLibraries(
     cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG"));
 
   frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag);
-  linkPath =
-    linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator);
-
-  linkLibraries = linkLineComputer->ComputeLinkLibraries(cli, stdLibString);
+  linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator,
+                                    linkPath);
+  linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries);
 }
 
 std::string cmLocalGenerator::GetLinkLibsCMP0065(

+ 10 - 6
Source/cmLocalGenerator.h

@@ -382,12 +382,11 @@ public:
                       std::string& flags, std::string& linkFlags,
                       std::string& frameworkPath, std::string& linkPath,
                       cmGeneratorTarget* target);
-  void GetTargetFlags(cmLinkLineComputer* linkLineComputer,
-                      const std::string& config, std::string& linkLibs,
-                      std::string& flags,
-                      std::vector<BT<std::string>>& linkFlags,
-                      std::string& frameworkPath, std::string& linkPath,
-                      cmGeneratorTarget* target);
+  void GetTargetFlags(
+    cmLinkLineComputer* linkLineComputer, const std::string& config,
+    std::vector<BT<std::string>>& linkLibs, std::string& flags,
+    std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
+    std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target);
   void GetTargetDefines(cmGeneratorTarget const* target,
                         std::string const& config, std::string const& lang,
                         std::set<std::string>& defines) const;
@@ -430,6 +429,11 @@ protected:
                            cmLinkLineComputer* linkLineComputer,
                            std::string& linkLibraries,
                            std::string& frameworkPath, std::string& linkPath);
+  void OutputLinkLibraries(cmComputeLinkInformation* pcli,
+                           cmLinkLineComputer* linkLineComputer,
+                           std::vector<BT<std::string>>& linkLibraries,
+                           std::string& frameworkPath,
+                           std::vector<BT<std::string>>& linkPath);
 
   // Handle old-style install rules stored in the targets.
   void GenerateTargetInstallRules(

+ 38 - 2
Tests/RunCMake/FileAPI/codemodel-v2-check.py

@@ -249,13 +249,13 @@ def check_target(c):
 
                     if expected["backtrace"] is not None:
                         expected_keys.append("backtrace")
-                        assert actual["fragment"] == expected["fragment"]
+                        assert matches(actual["fragment"], expected["fragment"])
                         assert actual["role"] == expected["role"]
                         check_backtrace(obj, actual["backtrace"], expected["backtrace"])
 
                     assert sorted(actual.keys()) == sorted(expected_keys)
 
-                check_list_match(lambda a, e: is_string(a["fragment"], e["fragment"]),
+                check_list_match(lambda a, e: matches(a["fragment"], e["fragment"]),
                                  obj["link"]["commandFragments"], expected["link"]["commandFragments"],
                                  check=check_link_command_fragments,
                                  check_exception=lambda a, e: "Link fragment: %s" % a["fragment"],
@@ -2218,6 +2218,42 @@ def gen_check_targets(c, g, inSource):
                             },
                         ],
                     },
+                    {
+                        "fragment" : ".*TargetLinkDir\\\"?$",
+                        "role" : "libraryPath",
+                        "backtrace": [
+                            {
+                                "file": "^cxx/CMakeLists\\.txt$",
+                                "line": 19,
+                                "command": "target_link_directories",
+                                "hasParent": True,
+                            },
+                            {
+                                "file" : "^cxx/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
+                    {
+                        "fragment" : ".*cxx_lib.*",
+                        "role" : "libraries",
+                        "backtrace": [
+                            {
+                                "file": "^cxx/CMakeLists\\.txt$",
+                                "line": 6,
+                                "command": "target_link_libraries",
+                                "hasParent": True,
+                            },
+                            {
+                                "file" : "^cxx/CMakeLists\\.txt$",
+                                "line": None,
+                                "command": None,
+                                "hasParent": False,
+                            },
+                        ],
+                    },
                 ],
             },
             "archive": None,

+ 1 - 0
Tests/RunCMake/FileAPI/cxx/CMakeLists.txt

@@ -16,3 +16,4 @@ target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib)
 
 target_compile_options(cxx_exe PUBLIC TargetCompileOptions)
 target_link_options(cxx_exe PUBLIC TargetLinkOptions)
+target_link_directories(cxx_exe PUBLIC "${CMAKE_BINARY_DIR}/TargetLinkDir")