From 5ca130e22394978814a9e59418529a7a3e1a61bd Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Sat, 22 Sep 2018 14:25:21 +0200
Subject: Refactoring: introduce function to check if a string is a generator
 expression

---
 Source/cmGeneratorExpression.cxx     | 6 +++---
 Source/cmGeneratorExpression.h       | 9 +++++++++
 Source/cmIncludeDirectoryCommand.cxx | 8 ++------
 Source/cmTarget.cxx                  | 4 +---
 4 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 658e9a7..2727d9a 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -168,7 +168,7 @@ static std::string stripAllGeneratorExpressions(const std::string& input)
     const char* c = input.c_str() + pos;
     const char* const cStart = c;
     for (; *c; ++c) {
-      if (c[0] == '$' && c[1] == '<') {
+      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
         ++nestingLevel;
         ++c;
         continue;
@@ -243,7 +243,7 @@ static std::string stripExportInterface(
     const char* c = input.c_str() + pos;
     const char* const cStart = c;
     for (; *c; ++c) {
-      if (c[0] == '$' && c[1] == '<') {
+      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
         ++nestingLevel;
         ++c;
         continue;
@@ -310,7 +310,7 @@ void cmGeneratorExpression::Split(const std::string& input,
     const char* c = input.c_str() + pos;
     const char* const cStart = c;
     for (; *c; ++c) {
-      if (c[0] == '$' && c[1] == '<') {
+      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
         ++nestingLevel;
         ++c;
         continue;
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index 8176d5c..9c05f60 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -63,6 +63,15 @@ public:
 
   static std::string StripEmptyListElements(const std::string& input);
 
+  static inline bool StartsWithGeneratorExpression(const std::string& input)
+  {
+    return input.length() >= 2 && input[0] == '$' && input[1] == '<';
+  }
+  static inline bool StartsWithGeneratorExpression(const char* input)
+  {
+    return input != nullptr && input[0] == '$' && input[1] == '<';
+  }
+
 private:
   cmListFileBacktrace Backtrace;
 };
diff --git a/Source/cmIncludeDirectoryCommand.cxx b/Source/cmIncludeDirectoryCommand.cxx
index eaaf64d..caec67d 100644
--- a/Source/cmIncludeDirectoryCommand.cxx
+++ b/Source/cmIncludeDirectoryCommand.cxx
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <set>
 
+#include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
 
@@ -69,11 +70,6 @@ bool cmIncludeDirectoryCommand::InitialPass(
   return true;
 }
 
-static bool StartsWithGeneratorExpression(const std::string& input)
-{
-  return input[0] == '$' && input[1] == '<';
-}
-
 // do a lot of cleanup on the arguments because this is one place where folks
 // sometimes take the output of a program and pass it directly into this
 // command not thinking that a single argument could be filled with spaces
@@ -124,7 +120,7 @@ void cmIncludeDirectoryCommand::NormalizeInclude(std::string& inc)
     cmSystemTools::ConvertToUnixSlashes(inc);
 
     if (!cmSystemTools::FileIsFullPath(inc)) {
-      if (!StartsWithGeneratorExpression(inc)) {
+      if (!cmGeneratorExpression::StartsWithGeneratorExpression(inc)) {
         std::string tmp = this->Makefile->GetCurrentSourceDirectory();
         tmp += "/";
         tmp += inc;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 4e353c7..6cf7c42 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -514,9 +514,7 @@ void cmTarget::AddSources(std::vector<std::string> const& srcs)
   std::string srcFiles;
   const char* sep = "";
   for (auto filename : srcs) {
-    const char* src = filename.c_str();
-
-    if (!(src[0] == '$' && src[1] == '<')) {
+    if (!cmGeneratorExpression::StartsWithGeneratorExpression(filename)) {
       if (!filename.empty()) {
         filename = this->ProcessSourceItemCMP0049(filename);
         if (filename.empty()) {
-- 
cgit v0.12


From a71caab46b205c2b0367c2b11c12a9b55b09bcca Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Fri, 14 Sep 2018 17:48:20 +0200
Subject: LINK_DIRECTORIES: Add new properties and commands

These new capabilities enable to manage link directories

Two new properties:
* target properties: LINK_DIRECTORIES and INTERFACE_LINK_DIRECTORIES

One new command
* target_link_directories(): to populate target properties

Fixes: #17215
---
 Help/command/link_directories.rst                  |  44 +++++++--
 Help/command/target_link_directories.rst           |  55 +++++++++++
 Help/manual/cmake-commands.7.rst                   |   1 +
 Help/manual/cmake-properties.7.rst                 |   2 +
 Help/prop_dir/LINK_DIRECTORIES.rst                 |  15 ++-
 Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst       |   9 ++
 Help/prop_tgt/LINK_DIRECTORIES.rst                 |  18 ++++
 Help/release/dev/LINK_DIRECTORIES.rst              |   9 ++
 Source/CMakeLists.txt                              |   2 +
 Source/cmCPluginAPI.cxx                            |   2 +-
 Source/cmCommands.cxx                              |   3 +
 Source/cmComputeLinkInformation.cxx                |  11 +--
 Source/cmExportBuildFileGenerator.cxx              |   3 +
 Source/cmExportFileGenerator.cxx                   |  31 ++++++
 Source/cmExportFileGenerator.h                     |   4 +
 Source/cmExportInstallFileGenerator.cxx            |   2 +
 Source/cmGeneratorExpressionDAGChecker.h           |   1 +
 Source/cmGeneratorTarget.cxx                       | 107 ++++++++++++++++++++-
 Source/cmGeneratorTarget.h                         |   8 +-
 Source/cmLinkDirectoriesCommand.cxx                |   7 +-
 Source/cmMakefile.cxx                              |  31 +++---
 Source/cmMakefile.h                                |   3 +
 Source/cmState.cxx                                 |   5 +
 Source/cmStateDirectory.cxx                        |  53 ++++++++++
 Source/cmStateDirectory.h                          |   8 ++
 Source/cmStatePrivate.h                            |   4 +
 Source/cmStateSnapshot.cxx                         |   7 ++
 Source/cmTarget.cxx                                |  79 ++++++++++++---
 Source/cmTarget.h                                  |  11 +--
 Source/cmTargetLinkDirectoriesCommand.cxx          |  61 ++++++++++++
 Source/cmTargetLinkDirectoriesCommand.h            |  41 ++++++++
 .../target_link_directories/CMakeLists.txt         |  40 ++++++++
 .../target_link_directories/LinkDirectoriesLib.c   |   7 ++
 .../target_link_directories/subdir/CMakeLists.txt  |   2 +
 Tests/CMakeLists.txt                               |   1 +
 Tests/ExportImport/Export/CMakeLists.txt           |  12 +++
 Tests/ExportImport/Import/A/CMakeLists.txt         |   5 +
 Tests/LinkDirectory/External/CMakeLists.txt        |  14 +++
 .../set_property/LINK_DIRECTORIES-stdout.txt       |   2 +
 Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake |   3 +
 Tests/RunCMake/set_property/RunCMakeTest.cmake     |   1 +
 41 files changed, 662 insertions(+), 62 deletions(-)
 create mode 100644 Help/command/target_link_directories.rst
 create mode 100644 Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst
 create mode 100644 Help/prop_tgt/LINK_DIRECTORIES.rst
 create mode 100644 Help/release/dev/LINK_DIRECTORIES.rst
 create mode 100644 Source/cmTargetLinkDirectoriesCommand.cxx
 create mode 100644 Source/cmTargetLinkDirectoriesCommand.h
 create mode 100644 Tests/CMakeCommands/target_link_directories/CMakeLists.txt
 create mode 100644 Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c
 create mode 100644 Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt
 create mode 100644 Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt
 create mode 100644 Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake

diff --git a/Help/command/link_directories.rst b/Help/command/link_directories.rst
index 5c64bc6..3efa8e5 100644
--- a/Help/command/link_directories.rst
+++ b/Help/command/link_directories.rst
@@ -1,19 +1,45 @@
 link_directories
 ----------------
 
-Specify directories in which the linker will look for libraries.
+Add directories in which the linker will look for libraries.
 
 ::
 
-  link_directories(directory1 directory2 ...)
+  link_directories(directory1 [directory2 ...])
 
-Specify the paths in which the linker should search for libraries.
-The command will apply only to targets created after it is called.
+Add the paths in which the linker should search for libraries.
 Relative paths given to this command are interpreted as relative to
 the current source directory, see :policy:`CMP0015`.
 
-Note that this command is rarely necessary.  Library locations
-returned by :command:`find_package` and :command:`find_library` are
-absolute paths. Pass these absolute library file paths directly to the
-:command:`target_link_libraries` command.  CMake will ensure the linker finds
-them.
+The directories are added to the :prop_dir:`LINK_DIRECTORIES` directory
+property for the current ``CMakeLists.txt`` file, converting relative
+paths to absolute as needed.
+The command will apply only to targets created after it is called.
+
+Arguments to ``link_directories`` 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.
+
+.. note::
+
+  This command is rarely necessary and should be avoided where there are
+  other choices.  Prefer to pass full absolute paths to libraries where
+  possible, since this ensures the correct library will always be linked.
+  The :command:`find_library` command provides the full path, which can
+  generally be used directly in calls to :command:`target_link_libraries`.
+  Situations where a library search path may be needed include:
+
+  - Project generators like Xcode where the user can switch target
+    architecture at build time, but a full path to a library cannot
+    be used because it only provides one architecture (i.e. it is not
+    a universal binary).
+  - Libraries may themselves have other private library dependencies
+    that expect to be found via ``RPATH`` mechanisms, but some linkers
+    are not able to fully decode those paths (e.g. due to the presence
+    of things like ``$ORIGIN``).
+
+  If a library search path must be provided, prefer to localize the effect
+  where possible by using the :command:`target_link_directories` command
+  rather than ``link_directories()``.  The target-specific command can also
+  control how the search directories propagate to other dependent targets.
diff --git a/Help/command/target_link_directories.rst b/Help/command/target_link_directories.rst
new file mode 100644
index 0000000..b46aac0
--- /dev/null
+++ b/Help/command/target_link_directories.rst
@@ -0,0 +1,55 @@
+target_link_directories
+-----------------------
+
+Add link directories to a target.
+
+::
+
+  target_link_directories(<target> [BEFORE]
+    <INTERFACE|PUBLIC|PRIVATE> [items1...]
+    [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
+
+Specify the paths in which the linker should search for libraries when
+linking a given target.  Each item can be an absolute or relative path,
+with the latter being interpreted as relative to the current source
+directory.  These items will be added to the link command.
+
+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>`.
+
+The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
+specify the scope of the items that follow them.  ``PRIVATE`` and
+``PUBLIC`` items will populate the :prop_tgt:`LINK_DIRECTORIES` property
+of ``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+:prop_tgt:`INTERFACE_LINK_DIRECTORIES` property of ``<target>``
+(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items).
+Each item specifies a link directory and will be converted to an absolute
+path if necessary before adding it to the relevant property.  Repeated
+calls for the same ``<target>`` append items in the order called.
+
+If ``BEFORE`` is specified, the content will be prepended to the relevant
+property instead of being appended.
+
+Arguments to ``target_link_directories`` 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.
+
+.. note::
+
+  This command is rarely necessary and should be avoided where there are
+  other choices.  Prefer to pass full absolute paths to libraries where
+  possible, since this ensures the correct library will always be linked.
+  The :command:`find_library` command provides the full path, which can
+  generally be used directly in calls to :command:`target_link_libraries`.
+  Situations where a library search path may be needed include:
+
+  - Project generators like Xcode where the user can switch target
+    architecture at build time, but a full path to a library cannot
+    be used because it only provides one architecture (i.e. it is not
+    a universal binary).
+  - Libraries may themselves have other private library dependencies
+    that expect to be found via ``RPATH`` mechanisms, but some linkers
+    are not able to fully decode those paths (e.g. due to the presence
+    of things like ``$ORIGIN``).
diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst
index 753647d..0cc5fca 100644
--- a/Help/manual/cmake-commands.7.rst
+++ b/Help/manual/cmake-commands.7.rst
@@ -111,6 +111,7 @@ These commands are available only in CMake projects.
    /command/target_compile_features
    /command/target_compile_options
    /command/target_include_directories
+   /command/target_link_directories
    /command/target_link_libraries
    /command/target_link_options
    /command/target_sources
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 8ccd7f6..5c3eb81 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -228,6 +228,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_DEPENDS
+   /prop_tgt/INTERFACE_LINK_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_LINK_OPTIONS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
@@ -252,6 +253,7 @@ Properties on Targets
    /prop_tgt/LINK_DEPENDS_NO_SHARED
    /prop_tgt/LINK_DEPENDS
    /prop_tgt/LINKER_LANGUAGE
+   /prop_tgt/LINK_DIRECTORIES
    /prop_tgt/LINK_FLAGS_CONFIG
    /prop_tgt/LINK_FLAGS
    /prop_tgt/LINK_INTERFACE_LIBRARIES_CONFIG
diff --git a/Help/prop_dir/LINK_DIRECTORIES.rst b/Help/prop_dir/LINK_DIRECTORIES.rst
index fa37576..f9fb815 100644
--- a/Help/prop_dir/LINK_DIRECTORIES.rst
+++ b/Help/prop_dir/LINK_DIRECTORIES.rst
@@ -3,6 +3,15 @@ LINK_DIRECTORIES
 
 List of linker search directories.
 
-This read-only property specifies the list of directories given so far
-to the link_directories command.  It is intended for debugging
-purposes.
+This property holds a :ref:`;-list <CMake Language Lists>` of directories
+and is typically populated using the :command:`link_directories` command.
+It gets its initial value from its parent directory, if it has one.
+
+The directory property is used to initialize the :prop_tgt:`LINK_DIRECTORIES`
+target property when a target is created.  That target property is used
+by the generators to set the library search directories for the linker.
+
+Contents of ``LINK_DIRECTORIES`` 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.
diff --git a/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst
new file mode 100644
index 0000000..56a4ec0
--- /dev/null
+++ b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst
@@ -0,0 +1,9 @@
+INTERFACE_LINK_DIRECTORIES
+--------------------------
+
+.. |property_name| replace:: link directories
+.. |command_name| replace:: :command:`target_link_directories`
+.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_LINK_DIRECTORIES``
+.. |PROPERTY_LINK| replace:: :prop_tgt:`LINK_DIRECTORIES`
+.. |PROPERTY_GENEX| replace:: ``$<TARGET_PROPERTY:foo,INTERFACE_LINK_DIRECTORIES>``
+.. include:: INTERFACE_BUILD_PROPERTY.txt
diff --git a/Help/prop_tgt/LINK_DIRECTORIES.rst b/Help/prop_tgt/LINK_DIRECTORIES.rst
new file mode 100644
index 0000000..085a701
--- /dev/null
+++ b/Help/prop_tgt/LINK_DIRECTORIES.rst
@@ -0,0 +1,18 @@
+LINK_DIRECTORIES
+----------------
+
+List of directories to use for the link step of shared library, module
+and executable targets.
+
+This property holds a :ref:`;-list <CMake Language Lists>` of directories
+specified so far for its target.  Use the :command:`target_link_directories`
+command to append more search directories.
+
+This property is initialized by the :prop_dir:`LINK_DIRECTORIES` directory
+property when a target is created, and is used by the generators to set
+the search directories for the linker.
+
+Contents of ``LINK_DIRECTORIES`` 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.
diff --git a/Help/release/dev/LINK_DIRECTORIES.rst b/Help/release/dev/LINK_DIRECTORIES.rst
new file mode 100644
index 0000000..dc7d609
--- /dev/null
+++ b/Help/release/dev/LINK_DIRECTORIES.rst
@@ -0,0 +1,9 @@
+LINK_DIRECTORIES
+----------------
+
+* CMake gained new capabilities to manage link directories:
+
+  * :prop_tgt:`LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DIRECTORIES`
+    target properties.
+  * :command:`target_link_directories` command to add link directories to
+    targets.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index bfddbc6..3cf6c8f 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -579,6 +579,8 @@ set(SRCS
   cmTargetIncludeDirectoriesCommand.h
   cmTargetLinkOptionsCommand.cxx
   cmTargetLinkOptionsCommand.h
+  cmTargetLinkDirectoriesCommand.cxx
+  cmTargetLinkDirectoriesCommand.h
   cmTargetLinkLibrariesCommand.cxx
   cmTargetLinkLibrariesCommand.h
   cmTargetPropCommandBase.cxx
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index 3aa59d6..22ae340 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -171,7 +171,7 @@ void CCONV cmAddLinkDirectoryForTarget(void* arg, const char* tgt,
       " for directory ", d);
     return;
   }
-  t->AddLinkDirectory(d);
+  t->InsertLinkDirectory(d, mf->GetBacktrace());
 }
 
 void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs,
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index 15fbd40..873372f 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -101,6 +101,7 @@
 #  include "cmRemoveDefinitionsCommand.h"
 #  include "cmSourceGroupCommand.h"
 #  include "cmSubdirDependsCommand.h"
+#  include "cmTargetLinkDirectoriesCommand.h"
 #  include "cmTargetLinkOptionsCommand.h"
 #  include "cmUseMangledMesaCommand.h"
 #  include "cmUtilitySourceCommand.h"
@@ -278,6 +279,8 @@ void GetProjectCommands(cmState* state)
   state->AddBuiltinCommand("link_libraries", new cmLinkLibrariesCommand);
   state->AddBuiltinCommand("target_link_options",
                            new cmTargetLinkOptionsCommand);
+  state->AddBuiltinCommand("target_link_directories",
+                           new cmTargetLinkDirectoriesCommand);
   state->AddBuiltinCommand("load_cache", new cmLoadCacheCommand);
   state->AddBuiltinCommand("qt_wrap_cpp", new cmQTWrapCPPCommand);
   state->AddBuiltinCommand("qt_wrap_ui", new cmQTWrapUICommand);
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index a3e135f..0e48ca8 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -357,10 +357,10 @@ cmComputeLinkInformation::cmComputeLinkInformation(
   }
 
   // Add the search path entries requested by the user to path ordering.
-  this->OrderLinkerSearchPath->AddUserDirectories(
-    this->Target->GetLinkDirectories());
-  this->OrderRuntimeSearchPath->AddUserDirectories(
-    this->Target->GetLinkDirectories());
+  std::vector<std::string> directories;
+  this->Target->GetLinkDirectories(directories, config, this->LinkLanguage);
+  this->OrderLinkerSearchPath->AddUserDirectories(directories);
+  this->OrderRuntimeSearchPath->AddUserDirectories(directories);
 
   // Set up the implicit link directories.
   this->LoadImplicitLinkInfo();
@@ -387,8 +387,7 @@ cmComputeLinkInformation::cmComputeLinkInformation(
   if (this->OldLinkDirMode) {
     // Construct a mask to not bother with this behavior for link
     // directories already specified by the user.
-    std::vector<std::string> const& dirs = this->Target->GetLinkDirectories();
-    this->OldLinkDirMask.insert(dirs.begin(), dirs.end());
+    this->OldLinkDirMask.insert(directories.begin(), directories.end());
   }
 
   this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled(
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 7f42035..024e641 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -98,6 +98,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index d6573b8..4cf9dd7 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -451,6 +451,37 @@ void cmExportFileGenerator::PopulateLinkDependsInterface(
   }
 }
 
+void cmExportFileGenerator::PopulateLinkDirectoriesInterface(
+  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
+{
+  cmGeneratorTarget* gt = tei->Target;
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
+
+  const char* propName = "INTERFACE_LINK_DIRECTORIES";
+  const char* input = gt->GetProperty(propName);
+
+  if (!input) {
+    return;
+  }
+
+  if (!*input) {
+    properties[propName].clear();
+    return;
+  }
+
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(input, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, gt, missingTargets);
+
+    if (!checkInterfaceDirs(prepro, gt, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
+  }
+}
+
 void cmExportFileGenerator::PopulateInterfaceProperty(
   const std::string& propName, cmGeneratorTarget* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 6ca2e07..41c6538 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -147,6 +147,10 @@ protected:
     cmTargetExport* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
+  void PopulateLinkDirectoriesInterface(
+    cmTargetExport* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
   void PopulateLinkDependsInterface(
     cmTargetExport* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index bfb7a05..e444087 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -106,6 +106,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
+    this->PopulateLinkDirectoriesInterface(
+      te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
     this->PopulateLinkDependsInterface(
       te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
 
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index 8b1697b..a5134c3 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -28,6 +28,7 @@ class cmGeneratorTarget;
   SELECT(F, EvaluatingSources, SOURCES)                                       \
   SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
   SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
+  SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES)                      \
   SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index a58d3cb..012d77a 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -103,6 +103,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   , DebugCompileFeaturesDone(false)
   , DebugCompileDefinitionsDone(false)
   , DebugLinkOptionsDone(false)
+  , DebugLinkDirectoriesDone(false)
   , DebugSourcesDone(false)
   , LinkImplementationLanguageIsContextDependent(true)
   , UtilityItemsDone(false)
@@ -133,6 +134,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
                                      t->GetLinkOptionsBacktraces(),
                                      this->LinkOptionsEntries);
 
+  CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(),
+                                     t->GetLinkDirectoriesBacktraces(),
+                                     this->LinkDirectoriesEntries);
+
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
                                      t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
@@ -150,6 +155,7 @@ cmGeneratorTarget::~cmGeneratorTarget()
   cmDeleteAll(this->CompileFeaturesEntries);
   cmDeleteAll(this->CompileDefinitionsEntries);
   cmDeleteAll(this->LinkOptionsEntries);
+  cmDeleteAll(this->LinkDirectoriesEntries);
   cmDeleteAll(this->SourceEntries);
   cmDeleteAll(this->LinkInformation);
 }
@@ -1704,11 +1710,6 @@ cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const
   return this->Target->GetBacktrace();
 }
 
-const std::vector<std::string>& cmGeneratorTarget::GetLinkDirectories() const
-{
-  return this->Target->GetLinkDirectories();
-}
-
 const std::set<std::string>& cmGeneratorTarget::GetUtilities() const
 {
   return this->Target->GetUtilities();
@@ -3068,6 +3069,102 @@ void cmGeneratorTarget::GetStaticLibraryLinkOptions(
 }
 
 namespace {
+void processLinkDirectories(
+  cmGeneratorTarget const* tgt,
+  const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
+  std::vector<std::string>& directories,
+  std::unordered_set<std::string>& uniqueDirectories,
+  cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
+  bool debugDirectories, std::string const& language)
+{
+  for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) {
+    cmLinkImplItem const& item = entry->LinkImplItem;
+    std::string const& targetName = item.AsStr();
+
+    std::vector<std::string> entryDirectories;
+    cmSystemTools::ExpandListArgument(
+      entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt,
+                          dagChecker, language),
+      entryDirectories);
+
+    std::string usedDirectories;
+    for (std::string& entryDirectory : entryDirectories) {
+      if (!cmSystemTools::FileIsFullPath(entryDirectory)) {
+        std::ostringstream e;
+        if (!targetName.empty()) {
+          /* clang-format off */
+          e << "Target \"" << targetName << "\" contains relative "
+            "path in its INTERFACE_LINK_DIRECTORIES:\n"
+            "  \"" << entryDirectory << "\"";
+          /* clang-format on */
+          tgt->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, e.str());
+          return;
+        }
+      }
+
+      // Sanitize the path the same way the link_directories command does
+      // in case projects set the LINK_DIRECTORIES property directly.
+      cmSystemTools::ConvertToUnixSlashes(entryDirectory);
+      if (uniqueDirectories.insert(entryDirectory).second) {
+        directories.push_back(entryDirectory);
+        if (debugDirectories) {
+          usedDirectories += " * " + entryDirectory + "\n";
+        }
+      }
+    }
+    if (!usedDirectories.empty()) {
+      tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
+        cmake::LOG,
+        std::string("Used link directories for target ") + tgt->GetName() +
+          ":\n" + usedDirectories,
+        entry->ge->GetBacktrace());
+    }
+  }
+}
+}
+
+void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result,
+                                           const std::string& config,
+                                           const std::string& language) const
+{
+  std::unordered_set<std::string> uniqueDirectories;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr,
+                                             nullptr);
+
+  std::vector<std::string> debugProperties;
+  const char* debugProp =
+    this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
+  if (debugProp) {
+    cmSystemTools::ExpandListArgument(debugProp, debugProperties);
+  }
+
+  bool debugDirectories = !this->DebugLinkDirectoriesDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "LINK_DIRECTORIES") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugLinkDirectoriesDone = true;
+  }
+
+  processLinkDirectories(this, this->LinkDirectoriesEntries, result,
+                         uniqueDirectories, &dagChecker, config,
+                         debugDirectories, language);
+
+  std::vector<cmGeneratorTarget::TargetPropertyEntry*>
+    linkInterfaceLinkDirectoriesEntries;
+
+  AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES",
+                      linkInterfaceLinkDirectoriesEntries);
+
+  processLinkDirectories(this, linkInterfaceLinkDirectoriesEntries, result,
+                         uniqueDirectories, &dagChecker, config,
+                         debugDirectories, language);
+
+  cmDeleteAll(linkInterfaceLinkDirectoriesEntries);
+}
+
+namespace {
 void processLinkDepends(
   cmGeneratorTarget const* tgt,
   const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 9d8c9f5..bfd95ac 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -273,8 +273,6 @@ public:
 
   cmListFileBacktrace GetBacktrace() const;
 
-  const std::vector<std::string>& GetLinkDirectories() const;
-
   std::set<std::string> const& GetUtilities() const;
   cmListFileBacktrace const* GetUtilityBacktrace(const std::string& u) const;
 
@@ -435,6 +433,10 @@ public:
                                    const std::string& config,
                                    const std::string& language) const;
 
+  void GetLinkDirectories(std::vector<std::string>& result,
+                          const std::string& config,
+                          const std::string& language) const;
+
   void GetLinkDepends(std::vector<std::string>& result,
                       const std::string& config,
                       const std::string& language) const;
@@ -825,6 +827,7 @@ private:
   std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
   std::vector<TargetPropertyEntry*> LinkOptionsEntries;
+  std::vector<TargetPropertyEntry*> LinkDirectoriesEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   mutable std::set<std::string> LinkImplicitNullProperties;
 
@@ -874,6 +877,7 @@ private:
   mutable bool DebugCompileFeaturesDone;
   mutable bool DebugCompileDefinitionsDone;
   mutable bool DebugLinkOptionsDone;
+  mutable bool DebugLinkDirectoriesDone;
   mutable bool DebugSourcesDone;
   mutable bool LinkImplementationLanguageIsContextDependent;
   mutable bool UtilityItemsDone;
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 1371c53..5c52c76 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <sstream>
 
+#include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
 #include "cmSystemTools.h"
@@ -29,7 +30,8 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir)
 {
   std::string unixPath = dir;
   cmSystemTools::ConvertToUnixSlashes(unixPath);
-  if (!cmSystemTools::FileIsFullPath(unixPath)) {
+  if (!cmSystemTools::FileIsFullPath(unixPath) &&
+      !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) {
     bool convertToAbsolute = false;
     std::ostringstream e;
     /* clang-format off */
@@ -41,6 +43,7 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir)
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015);
         this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str());
+        break;
       case cmPolicies::OLD:
         // OLD behavior does not convert
         break;
@@ -61,5 +64,5 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir)
       unixPath = tmp;
     }
   }
-  this->Makefile->AppendProperty("LINK_DIRECTORIES", unixPath.c_str());
+  this->Makefile->AddLinkDirectory(unixPath);
 }
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 354da4e..963bb44 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -243,6 +243,17 @@ cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const
   return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces();
 }
 
+cmStringRange cmMakefile::GetLinkDirectoriesEntries() const
+{
+  return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries();
+}
+
+cmBacktraceRange cmMakefile::GetLinkDirectoriesBacktraces() const
+{
+  return this->StateSnapshot.GetDirectory()
+    .GetLinkDirectoriesEntryBacktraces();
+}
+
 cmListFileBacktrace cmMakefile::GetBacktrace() const
 {
   return this->Backtrace;
@@ -1237,6 +1248,11 @@ void cmMakefile::AddLinkOption(std::string const& option)
   this->AppendProperty("LINK_OPTIONS", option.c_str());
 }
 
+void cmMakefile::AddLinkDirectory(std::string const& directory)
+{
+  this->AppendProperty("LINK_DIRECTORIES", directory.c_str());
+}
+
 bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove)
 {
   // Create a regular expression to match valid definitions.
@@ -1335,10 +1351,6 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent)
   // link libraries
   this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES"));
 
-  // link directories
-  this->SetProperty("LINK_DIRECTORIES",
-                    parent->GetProperty("LINK_DIRECTORIES"));
-
   // the initial project name
   this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName());
 
@@ -1872,17 +1884,6 @@ void cmMakefile::AddGlobalLinkInformation(cmTarget& target)
       return;
     default:;
   }
-  if (const char* linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) {
-    std::vector<std::string> linkDirs;
-    cmSystemTools::ExpandListArgument(linkDirsProp, linkDirs);
-
-    for (std::string& linkDir : linkDirs) {
-      // Sanitize the path the same way the link_directories command does
-      // in case projects set the LINK_DIRECTORIES property directly.
-      cmSystemTools::ConvertToUnixSlashes(linkDir);
-      target.AddLinkDirectory(linkDir);
-    }
-  }
 
   if (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
     std::vector<std::string> linkLibs;
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index bb01c0b..cde661b 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -182,6 +182,7 @@ public:
   void AddCompileDefinition(std::string const& definition);
   void AddCompileOption(std::string const& option);
   void AddLinkOption(std::string const& option);
+  void AddLinkDirectory(std::string const& directory);
 
   /** Create a new imported target with the name and type given.  */
   cmTarget* AddImportedTarget(const std::string& name,
@@ -802,6 +803,8 @@ public:
   cmBacktraceRange GetCompileDefinitionsBacktraces() const;
   cmStringRange GetLinkOptionsEntries() const;
   cmBacktraceRange GetLinkOptionsBacktraces() const;
+  cmStringRange GetLinkDirectoriesEntries() const;
+  cmBacktraceRange GetLinkDirectoriesBacktraces() const;
 
   std::set<std::string> const& GetSystemIncludeDirectories() const
   {
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index c6667f6..4b65cf1 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -284,6 +284,8 @@ cmStateSnapshot cmState::Reset()
     it->CompileOptionsBacktraces.clear();
     it->LinkOptions.clear();
     it->LinkOptionsBacktraces.clear();
+    it->LinkDirectories.clear();
+    it->LinkDirectoriesBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
     it->Properties.clear();
@@ -660,6 +662,7 @@ cmStateSnapshot cmState::CreateBaseSnapshot()
   pos->CompileDefinitionsPosition = 0;
   pos->CompileOptionsPosition = 0;
   pos->LinkOptionsPosition = 0;
+  pos->LinkDirectoriesPosition = 0;
   pos->BuildSystemDirectory->DirectoryEnd = pos;
   pos->Policies = this->PolicyStack.Root();
   pos->PolicyRoot = this->PolicyStack.Root();
@@ -813,6 +816,8 @@ cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot)
     prevPos->BuildSystemDirectory->CompileOptions.size();
   prevPos->LinkOptionsPosition =
     prevPos->BuildSystemDirectory->LinkOptions.size();
+  prevPos->LinkDirectoriesPosition =
+    prevPos->BuildSystemDirectory->LinkDirectories.size();
   prevPos->BuildSystemDirectory->DirectoryEnd = prevPos;
 
   if (!pos->Keep && this->SnapshotData.IsLast(pos)) {
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 925b161..5160f51 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -396,6 +396,43 @@ void cmStateDirectory::ClearLinkOptions()
                this->Snapshot_.Position->LinkOptionsPosition);
 }
 
+cmStringRange cmStateDirectory::GetLinkDirectoriesEntries() const
+{
+  return GetPropertyContent(this->DirectoryState->LinkDirectories,
+                            this->Snapshot_.Position->LinkDirectoriesPosition);
+}
+
+cmBacktraceRange cmStateDirectory::GetLinkDirectoriesEntryBacktraces() const
+{
+  return GetPropertyBacktraces(
+    this->DirectoryState->LinkDirectories,
+    this->DirectoryState->LinkDirectoriesBacktraces,
+    this->Snapshot_.Position->LinkDirectoriesPosition);
+}
+
+void cmStateDirectory::AppendLinkDirectoriesEntry(
+  const std::string& vec, const cmListFileBacktrace& lfbt)
+{
+  AppendEntry(this->DirectoryState->LinkDirectories,
+              this->DirectoryState->LinkDirectoriesBacktraces,
+              this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt);
+}
+
+void cmStateDirectory::SetLinkDirectories(const std::string& vec,
+                                          const cmListFileBacktrace& lfbt)
+{
+  SetContent(this->DirectoryState->LinkDirectories,
+             this->DirectoryState->LinkDirectoriesBacktraces,
+             this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt);
+}
+
+void cmStateDirectory::ClearLinkDirectories()
+{
+  ClearContent(this->DirectoryState->LinkDirectories,
+               this->DirectoryState->LinkDirectoriesBacktraces,
+               this->Snapshot_.Position->LinkDirectoriesPosition);
+}
+
 void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
                                    cmListFileBacktrace const& lfbt)
 {
@@ -431,6 +468,14 @@ void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
     this->SetLinkOptions(value, lfbt);
     return;
   }
+  if (prop == "LINK_DIRECTORIES") {
+    if (!value) {
+      this->ClearLinkDirectories();
+      return;
+    }
+    this->SetLinkDirectories(value, lfbt);
+    return;
+  }
 
   this->DirectoryState->Properties.SetProperty(prop, value);
 }
@@ -455,6 +500,10 @@ void cmStateDirectory::AppendProperty(const std::string& prop,
     this->AppendLinkOptionsEntry(value, lfbt);
     return;
   }
+  if (prop == "LINK_DIRECTORIES") {
+    this->AppendLinkDirectoriesEntry(value, lfbt);
+    return;
+  }
 
   this->DirectoryState->Properties.AppendProperty(prop, value, asString);
 }
@@ -542,6 +591,10 @@ const char* cmStateDirectory::GetProperty(const std::string& prop,
     output = cmJoin(this->GetLinkOptionsEntries(), ";");
     return output.c_str();
   }
+  if (prop == "LINK_DIRECTORIES") {
+    output = cmJoin(this->GetLinkDirectoriesEntries(), ";");
+    return output.c_str();
+  }
 
   const char* retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
   if (!retVal && chain) {
diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h
index 412664f..06345e2 100644
--- a/Source/cmStateDirectory.h
+++ b/Source/cmStateDirectory.h
@@ -65,6 +65,14 @@ public:
   void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt);
   void ClearLinkOptions();
 
+  cmStringRange GetLinkDirectoriesEntries() const;
+  cmBacktraceRange GetLinkDirectoriesEntryBacktraces() const;
+  void AppendLinkDirectoriesEntry(std::string const& vec,
+                                  cmListFileBacktrace const& lfbt);
+  void SetLinkDirectories(std::string const& vec,
+                          cmListFileBacktrace const& lfbt);
+  void ClearLinkDirectories();
+
   void SetProperty(const std::string& prop, const char* value,
                    cmListFileBacktrace const& lfbt);
   void AppendProperty(const std::string& prop, const char* value,
diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h
index 7177221..e76f2af 100644
--- a/Source/cmStatePrivate.h
+++ b/Source/cmStatePrivate.h
@@ -43,6 +43,7 @@ struct cmStateDetail::SnapshotDataType
   std::vector<std::string>::size_type CompileDefinitionsPosition;
   std::vector<std::string>::size_type CompileOptionsPosition;
   std::vector<std::string>::size_type LinkOptionsPosition;
+  std::vector<std::string>::size_type LinkDirectoriesPosition;
 };
 
 struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap
@@ -88,6 +89,9 @@ struct cmStateDetail::BuildsystemDirectoryStateType
   std::vector<std::string> LinkOptions;
   std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
 
+  std::vector<std::string> LinkDirectories;
+  std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces;
+
   std::vector<std::string> NormalTargetNames;
 
   std::string ProjectName;
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index 0379e7e..c2510f3 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -398,6 +398,13 @@ void cmStateSnapshot::InitializeFromParent()
     this->Position->BuildSystemDirectory->LinkOptionsBacktraces,
     this->Position->LinkOptionsPosition);
 
+  InitializeContentFromParent(
+    parent->BuildSystemDirectory->LinkDirectories,
+    this->Position->BuildSystemDirectory->LinkDirectories,
+    parent->BuildSystemDirectory->LinkDirectoriesBacktraces,
+    this->Position->BuildSystemDirectory->LinkDirectoriesBacktraces,
+    this->Position->LinkDirectoriesPosition);
+
   const char* include_regex =
     parent->BuildSystemDirectory->Properties.GetPropertyValue(
       "INCLUDE_REGULAR_EXPRESSION");
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 6cf7c42..c5295f2 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -168,6 +168,8 @@ public:
   std::vector<cmListFileBacktrace> SourceBacktraces;
   std::vector<std::string> LinkOptionsEntries;
   std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
+  std::vector<std::string> LinkDirectoriesEntries;
+  std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces;
   std::vector<std::string> LinkImplementationPropertyEntries;
   std::vector<cmListFileBacktrace> LinkImplementationPropertyBacktraces;
 };
@@ -391,6 +393,18 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     this->Internal->LinkOptionsBacktraces.insert(
       this->Internal->LinkOptionsBacktraces.end(),
       parentLinkOptionsBts.begin(), parentLinkOptionsBts.end());
+
+    const cmStringRange parentLinkDirectories =
+      this->Makefile->GetLinkDirectoriesEntries();
+    const cmBacktraceRange parentLinkDirectoriesBts =
+      this->Makefile->GetLinkDirectoriesBacktraces();
+
+    this->Internal->LinkDirectoriesEntries.insert(
+      this->Internal->LinkDirectoriesEntries.end(),
+      parentLinkDirectories.begin(), parentLinkDirectories.end());
+    this->Internal->LinkDirectoriesBacktraces.insert(
+      this->Internal->LinkDirectoriesBacktraces.end(),
+      parentLinkDirectoriesBts.begin(), parentLinkDirectoriesBts.end());
   }
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
@@ -654,19 +668,6 @@ cmSourceFile* cmTarget::AddSource(const std::string& src)
                                            cmSourceFileLocationKind::Known);
 }
 
-void cmTarget::AddLinkDirectory(const std::string& d)
-{
-  // Make sure we don't add unnecessary search directories.
-  if (this->LinkDirectoriesEmmitted.insert(d).second) {
-    this->LinkDirectories.push_back(d);
-  }
-}
-
-const std::vector<std::string>& cmTarget::GetLinkDirectories() const
-{
-  return this->LinkDirectories;
-}
-
 void cmTarget::ClearDependencyInformation(cmMakefile& mf)
 {
   std::string depname = this->GetName();
@@ -874,6 +875,16 @@ cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const
   return cmMakeRange(this->Internal->LinkOptionsBacktraces);
 }
 
+cmStringRange cmTarget::GetLinkDirectoriesEntries() const
+{
+  return cmMakeRange(this->Internal->LinkDirectoriesEntries);
+}
+
+cmBacktraceRange cmTarget::GetLinkDirectoriesBacktraces() const
+{
+  return cmMakeRange(this->Internal->LinkDirectoriesBacktraces);
+}
+
 cmStringRange cmTarget::GetLinkImplementationEntries() const
 {
   return cmMakeRange(this->Internal->LinkImplementationPropertyEntries);
@@ -900,6 +911,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(INCLUDE_DIRECTORIES);
   MAKE_STATIC_PROP(LINK_OPTIONS);
+  MAKE_STATIC_PROP(LINK_DIRECTORIES);
   MAKE_STATIC_PROP(LINK_LIBRARIES);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
   MAKE_STATIC_PROP(NAME);
@@ -986,6 +998,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->LinkOptionsBacktraces.push_back(lfbt);
     }
+  } else if (prop == propLINK_DIRECTORIES) {
+    this->Internal->LinkDirectoriesEntries.clear();
+    this->Internal->LinkDirectoriesBacktraces.clear();
+    if (value) {
+      this->Internal->LinkDirectoriesEntries.push_back(value);
+      cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+      this->Internal->LinkDirectoriesBacktraces.push_back(lfbt);
+    }
   } else if (prop == propLINK_LIBRARIES) {
     this->Internal->LinkImplementationPropertyEntries.clear();
     this->Internal->LinkImplementationPropertyBacktraces.clear();
@@ -1097,6 +1117,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
       this->Internal->LinkOptionsBacktraces.push_back(lfbt);
     }
+  } else if (prop == "LINK_DIRECTORIES") {
+    if (value && *value) {
+      this->Internal->LinkDirectoriesEntries.push_back(value);
+      cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+      this->Internal->LinkDirectoriesBacktraces.push_back(lfbt);
+    }
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
@@ -1194,6 +1220,21 @@ void cmTarget::InsertLinkOption(std::string const& entry,
   this->Internal->LinkOptionsBacktraces.insert(btPosition, bt);
 }
 
+void cmTarget::InsertLinkDirectory(std::string const& entry,
+                                   cmListFileBacktrace const& bt, bool before)
+{
+  std::vector<std::string>::iterator position = before
+    ? this->Internal->LinkDirectoriesEntries.begin()
+    : this->Internal->LinkDirectoriesEntries.end();
+
+  std::vector<cmListFileBacktrace>::iterator btPosition = before
+    ? this->Internal->LinkDirectoriesBacktraces.begin()
+    : this->Internal->LinkDirectoriesBacktraces.end();
+
+  this->Internal->LinkDirectoriesEntries.insert(position, entry);
+  this->Internal->LinkDirectoriesBacktraces.insert(btPosition, bt);
+}
+
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
                                                   const char* value,
                                                   cmMakefile* context,
@@ -1314,6 +1355,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(LINK_OPTIONS);
+  MAKE_STATIC_PROP(LINK_DIRECTORIES);
   MAKE_STATIC_PROP(IMPORTED);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1330,6 +1372,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
     specialProps.insert(propCOMPILE_OPTIONS);
     specialProps.insert(propCOMPILE_DEFINITIONS);
     specialProps.insert(propLINK_OPTIONS);
+    specialProps.insert(propLINK_DIRECTORIES);
     specialProps.insert(propIMPORTED);
     specialProps.insert(propIMPORTED_GLOBAL);
     specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES);
@@ -1397,6 +1440,16 @@ const char* cmTarget::GetProperty(const std::string& prop) const
       output = cmJoin(this->Internal->LinkOptionsEntries, ";");
       return output.c_str();
     }
+    if (prop == propLINK_DIRECTORIES) {
+      if (this->Internal->LinkDirectoriesEntries.empty()) {
+        return nullptr;
+      }
+
+      static std::string output;
+      output = cmJoin(this->Internal->LinkDirectoriesEntries, ";");
+
+      return output.c_str();
+    }
     if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
       if (this->Utilities.empty()) {
         return nullptr;
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 1f380df..694de1c 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -154,10 +154,6 @@ public:
                            cmListFileContext const& lfc);
   void GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const;
 
-  const std::vector<std::string>& GetLinkDirectories() const;
-
-  void AddLinkDirectory(const std::string& d);
-
   /**
    * Set the path where this target should be installed. This is relative to
    * INSTALL_PREFIX
@@ -247,6 +243,8 @@ public:
                                cmListFileBacktrace const& bt);
   void InsertLinkOption(std::string const& entry,
                         cmListFileBacktrace const& bt, bool before = false);
+  void InsertLinkDirectory(std::string const& entry,
+                           cmListFileBacktrace const& bt, bool before = false);
 
   void AppendBuildInterfaceIncludes();
 
@@ -277,6 +275,9 @@ public:
   cmStringRange GetLinkOptionsEntries() const;
   cmBacktraceRange GetLinkOptionsBacktraces() const;
 
+  cmStringRange GetLinkDirectoriesEntries() const;
+  cmBacktraceRange GetLinkDirectoriesBacktraces() const;
+
   cmStringRange GetLinkImplementationEntries() const;
   cmBacktraceRange GetLinkImplementationBacktraces() const;
 
@@ -306,14 +307,12 @@ private:
   bool IsGeneratorProvided;
   cmPropertyMap Properties;
   std::set<std::string> SystemIncludeDirectories;
-  std::set<std::string> LinkDirectoriesEmmitted;
   std::set<std::string> Utilities;
   std::map<std::string, cmListFileBacktrace> UtilityBacktraces;
   cmPolicies::PolicyMap PolicyMap;
   std::string Name;
   std::string InstallPath;
   std::string RuntimeInstallPath;
-  std::vector<std::string> LinkDirectories;
   std::vector<cmCustomCommand> PreBuildCommands;
   std::vector<cmCustomCommand> PreLinkCommands;
   std::vector<cmCustomCommand> PostBuildCommands;
diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx
new file mode 100644
index 0000000..bca3e45
--- /dev/null
+++ b/Source/cmTargetLinkDirectoriesCommand.cxx
@@ -0,0 +1,61 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTargetLinkDirectoriesCommand.h"
+
+#include <sstream>
+
+#include "cmAlgorithms.h"
+#include "cmGeneratorExpression.h"
+#include "cmListFileCache.h"
+#include "cmMakefile.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+class cmExecutionStatus;
+
+bool cmTargetLinkDirectoriesCommand::InitialPass(
+  std::vector<std::string> const& args, cmExecutionStatus&)
+{
+  return this->HandleArguments(args, "LINK_DIRECTORIES", PROCESS_BEFORE);
+}
+
+void cmTargetLinkDirectoriesCommand::HandleMissingTarget(
+  const std::string& name)
+{
+  std::ostringstream e;
+  e << "Cannot specify link directories for target \"" << name
+    << "\" which is not built by this project.";
+  this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
+}
+
+std::string cmTargetLinkDirectoriesCommand::Join(
+  const std::vector<std::string>& content)
+{
+  std::vector<std::string> directories;
+
+  for (const auto& dir : content) {
+    auto unixPath = dir;
+    cmSystemTools::ConvertToUnixSlashes(unixPath);
+    if (!cmSystemTools::FileIsFullPath(unixPath) &&
+        !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) {
+      auto tmp = this->Makefile->GetCurrentSourceDirectory();
+      tmp += "/";
+      tmp += unixPath;
+      unixPath = tmp;
+    }
+    directories.push_back(unixPath);
+  }
+
+  return cmJoin(directories, ";");
+}
+
+bool cmTargetLinkDirectoriesCommand::HandleDirectContent(
+  cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool)
+{
+  cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
+
+  tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend);
+
+  return true; // Successfully handled.
+}
diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h
new file mode 100644
index 0000000..52c75a0
--- /dev/null
+++ b/Source/cmTargetLinkDirectoriesCommand.h
@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetLinkDirectoriesCommand_h
+#define cmTargetLinkDirectoriesCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include "cmTargetPropCommandBase.h"
+
+class cmCommand;
+class cmExecutionStatus;
+class cmTarget;
+
+class cmTargetLinkDirectoriesCommand : public cmTargetPropCommandBase
+{
+public:
+  /**
+   * This is a virtual constructor for the command.
+   */
+  cmCommand* Clone() override { return new cmTargetLinkDirectoriesCommand; }
+
+  /**
+   * This is called when the command is first encountered in
+   * the CMakeLists.txt file.
+   */
+  bool InitialPass(std::vector<std::string> const& args,
+                   cmExecutionStatus& status) override;
+
+private:
+  void HandleMissingTarget(const std::string& name) override;
+
+  std::string Join(const std::vector<std::string>& content) override;
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool system) override;
+};
+
+#endif
diff --git a/Tests/CMakeCommands/target_link_directories/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt
new file mode 100644
index 0000000..bc7b9b2
--- /dev/null
+++ b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt
@@ -0,0 +1,40 @@
+
+cmake_minimum_required(VERSION 3.12)
+
+project(target_link_directories LANGUAGES C)
+
+add_library(target_link_directories SHARED LinkDirectoriesLib.c)
+# Test no items
+target_link_directories(target_link_directories PRIVATE)
+
+add_library(target_link_directories_2 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c)
+target_link_directories(target_link_directories_2 PRIVATE /private/dir INTERFACE /interface/dir)
+get_target_property(result target_link_directories_2 LINK_DIRECTORIES)
+if (NOT result MATCHES "/private/dir")
+  message(SEND_ERROR "${result} target_link_directories not populated the LINK_DIRECTORIES target property")
+endif()
+get_target_property(result target_link_directories_2 INTERFACE_LINK_DIRECTORIES)
+if (NOT result MATCHES "/interface/dir")
+  message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of shared library")
+endif()
+
+add_library(target_link_directories_3 STATIC EXCLUDE_FROM_ALL LinkDirectoriesLib.c)
+target_link_directories(target_link_directories_3 INTERFACE /interface/dir)
+get_target_property(result target_link_directories_3 INTERFACE_LINK_DIRECTORIES)
+if (NOT result MATCHES "/interface/dir")
+  message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of static library")
+endif()
+
+add_library(target_link_directories_4 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c)
+target_link_directories(target_link_directories_4 PRIVATE relative/dir)
+get_target_property(result target_link_directories_4 LINK_DIRECTORIES)
+if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir")
+  message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path")
+endif()
+
+add_subdirectory(subdir)
+target_link_directories(target_link_directories_5 PRIVATE relative/dir)
+get_target_property(result target_link_directories_5 LINK_DIRECTORIES)
+if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir")
+  message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path")
+endif()
diff --git a/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c
new file mode 100644
index 0000000..9bbd24c
--- /dev/null
+++ b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c
@@ -0,0 +1,7 @@
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  int flags_lib(void)
+{
+  return 0;
+}
diff --git a/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt
new file mode 100644
index 0000000..7e7ad2a
--- /dev/null
+++ b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt
@@ -0,0 +1,2 @@
+
+add_library(target_link_directories_5 SHARED EXCLUDE_FROM_ALL ../LinkDirectoriesLib.c)
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index fb44077..4753aac 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -2823,6 +2823,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
   ADD_TEST_MACRO(CMakeCommands.add_compile_definitions add_compile_definitions)
   ADD_TEST_MACRO(CMakeCommands.add_compile_options add_compile_options)
   ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries)
+  ADD_TEST_MACRO(CMakeCommands.target_link_directories)
   ADD_TEST_MACRO(CMakeCommands.target_include_directories target_include_directories)
   ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
   ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt
index cb048be..c6b7dbc 100644
--- a/Tests/ExportImport/Export/CMakeLists.txt
+++ b/Tests/ExportImport/Export/CMakeLists.txt
@@ -619,6 +619,18 @@ export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
 
 
 #------------------------------------------------------------------------------
+# test export of INTERFACE_LINK_DIRECTORIES
+add_library(testLinkDirectories INTERFACE)
+target_link_directories(testLinkDirectories INTERFACE
+  $<BUILD_INTERFACE:/interface/build>
+  $<INSTALL_INTERFACE:interface/install>)
+
+install(TARGETS testLinkDirectories
+        EXPORT RequiredExp DESTINATION lib)
+export(TARGETS testLinkDirectories NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
+
+
+#------------------------------------------------------------------------------
 # test export of INTERFACE_LINK_DEPENDS
 if(CMAKE_GENERATOR MATCHES "Make|Ninja")
   add_library(testLinkDepends INTERFACE)
diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt
index 8791a19..67fcc02 100644
--- a/Tests/ExportImport/Import/A/CMakeLists.txt
+++ b/Tests/ExportImport/Import/A/CMakeLists.txt
@@ -490,6 +490,11 @@ checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
 checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
 
 #---------------------------------------------------------------------------------
+# check that imported libraries have the expected INTERFACE_LINK_DIRECTORIES property
+checkForProperty(bld_testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "/interface/build")
+checkForProperty(Req::testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "${CMAKE_INSTALL_PREFIX}/interface/install")
+
+#---------------------------------------------------------------------------------
 # check that imported libraries have the expected INTERFACE_LINK_DEPENDS property
 if(CMAKE_GENERATOR MATCHES "Make|Ninja")
   checkForProperty(bld_testLinkDepends "INTERFACE_LINK_DEPENDS" "BUILD_LINK_DEPENDS")
diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt
index f7c840f..d2a1f9f 100644
--- a/Tests/LinkDirectory/External/CMakeLists.txt
+++ b/Tests/LinkDirectory/External/CMakeLists.txt
@@ -1,6 +1,20 @@
 cmake_minimum_required(VERSION 2.8)
 project(LinkDirectoryExternal C)
 
+
+add_executable(myexe2 myexe.c)
+set_property(TARGET myexe2 PROPERTY OUTPUT_NAME LinkDirectory2)
+target_link_directories(myexe2 PRIVATE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib")
+target_link_libraries(myexe2 PRIVATE mylibA mylibB)
+
+add_library (mylibs INTERFACE)
+target_link_directories(mylibs INTERFACE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib")
+target_link_libraries(mylibs INTERFACE mylibA mylibB)
+add_executable(myexe3 myexe.c)
+set_property(TARGET myexe3 PROPERTY OUTPUT_NAME LinkDirectory3)
+target_link_libraries(myexe3 PRIVATE mylibs)
+
+
 # Test CMP0015 OLD behavior: -L../lib
 cmake_policy(SET CMP0015 OLD)
 link_directories(../lib)
diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt
new file mode 100644
index 0000000..580c373
--- /dev/null
+++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt
@@ -0,0 +1,2 @@
+-- Target LINK_DIRECTORIES is 'a;b;c;d;;e'
+-- Directory LINK_DIRECTORIES is 'a;b;c;d;;e'
diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake
new file mode 100644
index 0000000..8529ef5
--- /dev/null
+++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake
@@ -0,0 +1,3 @@
+include(Common.cmake)
+test_target_property(LINK_DIRECTORIES)
+test_directory_property(LINK_DIRECTORIES)
diff --git a/Tests/RunCMake/set_property/RunCMakeTest.cmake b/Tests/RunCMake/set_property/RunCMakeTest.cmake
index 77da703..8d4614c 100644
--- a/Tests/RunCMake/set_property/RunCMakeTest.cmake
+++ b/Tests/RunCMake/set_property/RunCMakeTest.cmake
@@ -6,6 +6,7 @@ run_cmake(COMPILE_OPTIONS)
 run_cmake(IMPORTED_GLOBAL)
 run_cmake(INCLUDE_DIRECTORIES)
 run_cmake(LINK_OPTIONS)
+run_cmake(LINK_DIRECTORIES)
 run_cmake(LINK_LIBRARIES)
 run_cmake(SOURCES)
 run_cmake(TYPE)
-- 
cgit v0.12


From b5915744ebccd086891f1fab0ae91af54deb3a86 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Tue, 18 Sep 2018 17:23:07 +0200
Subject: LINK_DIRECTORIES target property: add policy for absolute paths
 check.

---
 Help/manual/cmake-policies.7.rst                   |  1 +
 Help/policy/CMP0081.rst                            | 22 +++++++++++++++++
 Help/release/dev/LINK_DIRECTORIES-policy.rst       |  5 ++++
 Source/cmGeneratorTarget.cxx                       | 28 ++++++++++++++++++++--
 Source/cmPolicies.h                                |  8 +++++--
 Tests/RunCMake/CMP0081/CMP0081-Common.cmake        |  5 ++++
 Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt      |  1 +
 Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt      |  4 ++++
 Tests/RunCMake/CMP0081/CMP0081-NEW.cmake           |  4 ++++
 Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt      |  1 +
 Tests/RunCMake/CMP0081/CMP0081-OLD.cmake           |  4 ++++
 Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt     |  1 +
 Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt     | 10 ++++++++
 Tests/RunCMake/CMP0081/CMP0081-WARN.cmake          |  2 ++
 Tests/RunCMake/CMP0081/CMakeLists.txt              |  3 +++
 Tests/RunCMake/CMP0081/RunCMakeTest.cmake          |  5 ++++
 Tests/RunCMake/CMP0081/empty.cpp                   |  7 ++++++
 Tests/RunCMake/CMakeLists.txt                      |  1 +
 .../RunCMake/TargetPolicies/PolicyList-stderr.txt  |  1 +
 19 files changed, 109 insertions(+), 4 deletions(-)
 create mode 100644 Help/policy/CMP0081.rst
 create mode 100644 Help/release/dev/LINK_DIRECTORIES-policy.rst
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-Common.cmake
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-NEW.cmake
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-OLD.cmake
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt
 create mode 100644 Tests/RunCMake/CMP0081/CMP0081-WARN.cmake
 create mode 100644 Tests/RunCMake/CMP0081/CMakeLists.txt
 create mode 100644 Tests/RunCMake/CMP0081/RunCMakeTest.cmake
 create mode 100644 Tests/RunCMake/CMP0081/empty.cpp

diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 904ebee..2cc52fe 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.13
 .. toctree::
    :maxdepth: 1
 
+   CMP0081: Relative paths not allowed in LINK_DIRECTORIES target property. </policy/CMP0081>
    CMP0080: BundleUtilities cannot be included at configure time. </policy/CMP0080>
    CMP0079: target_link_libraries allows use with targets in other directories. </policy/CMP0079>
    CMP0078: UseSWIG generates standard target names. </policy/CMP0078>
diff --git a/Help/policy/CMP0081.rst b/Help/policy/CMP0081.rst
new file mode 100644
index 0000000..d3b2872
--- /dev/null
+++ b/Help/policy/CMP0081.rst
@@ -0,0 +1,22 @@
+CMP0081
+-------
+
+Relative paths not allowed in :prop_tgt:`LINK_DIRECTORIES` target property.
+
+CMake 3.12 and lower allowed the :prop_dir:`LINK_DIRECTORIES` directory
+property to contain relative paths.  The base path for such relative
+entries is not well defined.  CMake 3.13 and later will issue a
+``FATAL_ERROR`` if the :prop_tgt:`LINK_DIRECTORIES` target property
+(which is initialized by the :prop_dir:`LINK_DIRECTORIES` directory property)
+contains a relative path.
+
+The ``OLD`` behavior for this policy is not to warn about relative paths
+in the :prop_tgt:`LINK_DIRECTORIES` target property.  The ``NEW`` behavior for
+this policy is to issue a ``FATAL_ERROR`` if :prop_tgt:`LINK_DIRECTORIES`
+contains a relative path.
+
+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
diff --git a/Help/release/dev/LINK_DIRECTORIES-policy.rst b/Help/release/dev/LINK_DIRECTORIES-policy.rst
new file mode 100644
index 0000000..5bbfa51
--- /dev/null
+++ b/Help/release/dev/LINK_DIRECTORIES-policy.rst
@@ -0,0 +1,5 @@
+LINK_DIRECTORIES-policy
+-----------------------
+
+* The :prop_tgt:`LINK_DIRECTORIES` target property expects absolute paths.
+  See policy :policy:`CMP0081`.
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 012d77a..29c6058 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3091,14 +3091,38 @@ void processLinkDirectories(
     for (std::string& entryDirectory : entryDirectories) {
       if (!cmSystemTools::FileIsFullPath(entryDirectory)) {
         std::ostringstream e;
+        bool noMessage = false;
+        cmake::MessageType messageType = cmake::FATAL_ERROR;
         if (!targetName.empty()) {
           /* clang-format off */
           e << "Target \"" << targetName << "\" contains relative "
             "path in its INTERFACE_LINK_DIRECTORIES:\n"
             "  \"" << entryDirectory << "\"";
           /* clang-format on */
-          tgt->GetLocalGenerator()->IssueMessage(cmake::FATAL_ERROR, e.str());
-          return;
+        } else {
+          switch (tgt->GetPolicyStatusCMP0081()) {
+            case cmPolicies::WARN: {
+              e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n";
+              messageType = cmake::AUTHOR_WARNING;
+            } break;
+            case cmPolicies::OLD:
+              noMessage = true;
+              break;
+            case cmPolicies::REQUIRED_IF_USED:
+            case cmPolicies::REQUIRED_ALWAYS:
+            case cmPolicies::NEW:
+              // Issue the fatal message.
+              break;
+          }
+          e << "Found relative path while evaluating link directories of "
+               "\""
+            << tgt->GetName() << "\":\n  \"" << entryDirectory << "\"\n";
+        }
+        if (!noMessage) {
+          tgt->GetLocalGenerator()->IssueMessage(messageType, e.str());
+          if (messageType == cmake::FATAL_ERROR) {
+            return;
+          }
         }
       }
 
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index f99cc0f..a367e47 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -237,7 +237,10 @@ class cmMakefile;
     13, 0, cmPolicies::WARN)                                                  \
   SELECT(POLICY, CMP0080,                                                     \
          "BundleUtilities cannot be included at configure time", 3, 13, 0,    \
-         cmPolicies::WARN)
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0081,                                                     \
+         "Relative paths not allowed in LINK_DIRECTORIES target property.",   \
+         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)                                         \
@@ -263,7 +266,8 @@ class cmMakefile;
   F(CMP0068)                                                                  \
   F(CMP0069)                                                                  \
   F(CMP0073)                                                                  \
-  F(CMP0076)
+  F(CMP0076)                                                                  \
+  F(CMP0081)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies
diff --git a/Tests/RunCMake/CMP0081/CMP0081-Common.cmake b/Tests/RunCMake/CMP0081/CMP0081-Common.cmake
new file mode 100644
index 0000000..3ea5277
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-Common.cmake
@@ -0,0 +1,5 @@
+
+enable_language(CXX)
+
+add_library(foo SHARED empty.cpp)
+set_target_properties(foo PROPERTIES LINK_DIRECTORIES "../lib")
diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt b/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt
new file mode 100644
index 0000000..d31c149
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error in CMakeLists.txt:
+  Found relative path while evaluating link directories of "foo":
+
+    "../lib"
diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake b/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake
new file mode 100644
index 0000000..9b927a2
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0081 NEW)
+
+include (CMP0081-Common.cmake)
diff --git a/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt b/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake b/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake
new file mode 100644
index 0000000..2e91bf6
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0081 OLD)
+
+include (CMP0081-Common.cmake)
diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt b/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt
new file mode 100644
index 0000000..eac0648
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt
@@ -0,0 +1,10 @@
+CMake Warning \(dev\) in CMakeLists.txt:
+  Policy CMP0081 is not set: Relative paths not allowed in LINK_DIRECTORIES
+  target property.  Run "cmake --help-policy CMP0081" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  Found relative path while evaluating link directories of "foo":
+
+    "../lib"
+
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake b/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake
new file mode 100644
index 0000000..33bb21d
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake
@@ -0,0 +1,2 @@
+
+include (CMP0081-Common.cmake)
diff --git a/Tests/RunCMake/CMP0081/CMakeLists.txt b/Tests/RunCMake/CMP0081/CMakeLists.txt
new file mode 100644
index 0000000..ef2163c
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.1)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0081/RunCMakeTest.cmake b/Tests/RunCMake/CMP0081/RunCMakeTest.cmake
new file mode 100644
index 0000000..335d8c5
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0081-OLD)
+run_cmake(CMP0081-NEW)
+run_cmake(CMP0081-WARN)
diff --git a/Tests/RunCMake/CMP0081/empty.cpp b/Tests/RunCMake/CMP0081/empty.cpp
new file mode 100644
index 0000000..11ec041
--- /dev/null
+++ b/Tests/RunCMake/CMP0081/empty.cpp
@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int empty()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 69cb5b7..080d0d0 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -106,6 +106,7 @@ if(CMAKE_SYSTEM_NAME MATCHES Darwin AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG)
   add_RunCMake_test(CMP0068)
 endif()
 add_RunCMake_test(CMP0069)
+add_RunCMake_test(CMP0081)
 
 # The test for Policy 65 requires the use of the
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index 6c861fa..2441a9c 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -25,6 +25,7 @@
    \* CMP0069
    \* CMP0073
    \* CMP0076
+   \* CMP0081
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
-- 
cgit v0.12


From f9717725f9d4c7f4a1da52b0184365cd757bc076 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Wed, 19 Sep 2018 14:12:35 +0200
Subject: link_directories(): enhance capabilities

---
 Help/command/link_directories.rst                  |  8 +++++-
 Help/manual/cmake-variables.7.rst                  |  1 +
 Help/release/dev/link_directories-enhancements.rst |  5 ++++
 Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst    |  9 +++++++
 Source/cmLinkDirectoriesCommand.cxx                | 25 +++++++++++++++---
 Source/cmLinkDirectoriesCommand.h                  |  3 ++-
 Source/cmMakefile.cxx                              | 11 ++++++--
 Source/cmMakefile.h                                |  2 +-
 Source/cmStateDirectory.cxx                        | 27 +++++++++++++++++++
 Source/cmStateDirectory.h                          |  2 ++
 .../CMakeCommands/link_directories/CMakeLists.txt  | 30 ++++++++++++++++++++++
 .../link_directories/LinkDirectoriesExe.c          |  4 +++
 Tests/CMakeLists.txt                               |  3 ++-
 13 files changed, 120 insertions(+), 10 deletions(-)
 create mode 100644 Help/release/dev/link_directories-enhancements.rst
 create mode 100644 Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst
 create mode 100644 Tests/CMakeCommands/link_directories/CMakeLists.txt
 create mode 100644 Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c

diff --git a/Help/command/link_directories.rst b/Help/command/link_directories.rst
index 3efa8e5..1dce9a0 100644
--- a/Help/command/link_directories.rst
+++ b/Help/command/link_directories.rst
@@ -5,7 +5,7 @@ Add directories in which the linker will look for libraries.
 
 ::
 
-  link_directories(directory1 [directory2 ...])
+  link_directories([AFTER|BEFORE] directory1 [directory2 ...])
 
 Add the paths in which the linker should search for libraries.
 Relative paths given to this command are interpreted as relative to
@@ -16,6 +16,12 @@ property for the current ``CMakeLists.txt`` file, converting relative
 paths to absolute as needed.
 The command will apply only to targets created after it is called.
 
+By default the directories specified are appended onto the current list of
+directories.  This default behavior can be changed by setting
+:variable:`CMAKE_LINK_DIRECTORIES_BEFORE` to ``ON``.  By using
+``AFTER`` or ``BEFORE`` explicitly, you can select between appending and
+prepending, independent of the default.
+
 Arguments to ``link_directories`` may use "generator expressions" with
 the syntax "$<...>".  See the :manual:`cmake-generator-expressions(7)`
 manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 6071999..78353fb 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -179,6 +179,7 @@ Variables that Change Behavior
    /variable/CMAKE_INSTALL_PREFIX
    /variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT
    /variable/CMAKE_LIBRARY_PATH
+   /variable/CMAKE_LINK_DIRECTORIES_BEFORE
    /variable/CMAKE_MFC_FLAG
    /variable/CMAKE_MODULE_PATH
    /variable/CMAKE_NOT_USING_CONFIG_FLAGS
diff --git a/Help/release/dev/link_directories-enhancements.rst b/Help/release/dev/link_directories-enhancements.rst
new file mode 100644
index 0000000..2521fa1
--- /dev/null
+++ b/Help/release/dev/link_directories-enhancements.rst
@@ -0,0 +1,5 @@
+link_directories-enhancements
+-----------------------------
+
+* :command:`link_directories` command gains capability to control directories
+  insertion position.
diff --git a/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst b/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst
new file mode 100644
index 0000000..026ca35
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst
@@ -0,0 +1,9 @@
+CMAKE_LINK_DIRECTORIES_BEFORE
+-----------------------------
+
+Whether to append or prepend directories by default in
+:command:`link_directories`.
+
+This variable affects the default behavior of the :command:`link_directories`
+command.  Setting this variable to ``ON`` is equivalent to using the ``BEFORE``
+option in all uses of that command.
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 5c52c76..10425fd 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <sstream>
 
+#include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
@@ -20,13 +21,29 @@ bool cmLinkDirectoriesCommand::InitialPass(
     return true;
   }
 
-  for (std::string const& i : args) {
-    this->AddLinkDir(i);
+  bool before = this->Makefile->IsOn("CMAKE_LINK_DIRECTORIES_BEFORE");
+
+  auto i = args.cbegin();
+  if ((*i) == "BEFORE") {
+    before = true;
+    ++i;
+  } else if ((*i) == "AFTER") {
+    before = false;
+    ++i;
+  }
+
+  std::vector<std::string> directories;
+  for (; i != args.cend(); ++i) {
+    this->AddLinkDir(*i, directories);
   }
+
+  this->Makefile->AddLinkDirectory(cmJoin(directories, ";"), before);
+
   return true;
 }
 
-void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir)
+void cmLinkDirectoriesCommand::AddLinkDir(
+  std::string const& dir, std::vector<std::string>& directories)
 {
   std::string unixPath = dir;
   cmSystemTools::ConvertToUnixSlashes(unixPath);
@@ -64,5 +81,5 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir)
       unixPath = tmp;
     }
   }
-  this->Makefile->AddLinkDirectory(unixPath);
+  directories.push_back(unixPath);
 }
diff --git a/Source/cmLinkDirectoriesCommand.h b/Source/cmLinkDirectoriesCommand.h
index 3fd4e50..ae4fb7f 100644
--- a/Source/cmLinkDirectoriesCommand.h
+++ b/Source/cmLinkDirectoriesCommand.h
@@ -36,7 +36,8 @@ public:
                    cmExecutionStatus& status) override;
 
 private:
-  void AddLinkDir(std::string const& dir);
+  void AddLinkDir(std::string const& dir,
+                  std::vector<std::string>& directories);
 };
 
 #endif
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 963bb44..8d163b7 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -1248,9 +1248,16 @@ void cmMakefile::AddLinkOption(std::string const& option)
   this->AppendProperty("LINK_OPTIONS", option.c_str());
 }
 
-void cmMakefile::AddLinkDirectory(std::string const& directory)
+void cmMakefile::AddLinkDirectory(std::string const& directory, bool before)
 {
-  this->AppendProperty("LINK_DIRECTORIES", directory.c_str());
+  cmListFileBacktrace lfbt = this->GetBacktrace();
+  if (before) {
+    this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry(directory,
+                                                                   lfbt);
+  } else {
+    this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry(directory,
+                                                                  lfbt);
+  }
 }
 
 bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove)
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index cde661b..b30f281 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -182,7 +182,7 @@ public:
   void AddCompileDefinition(std::string const& definition);
   void AddCompileOption(std::string const& option);
   void AddLinkOption(std::string const& option);
-  void AddLinkDirectory(std::string const& directory);
+  void AddLinkDirectory(std::string const& directory, bool before = false);
 
   /** Create a new imported target with the name and type given.  */
   cmTarget* AddImportedTarget(const std::string& name,
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 5160f51..f94e714 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -417,6 +417,33 @@ void cmStateDirectory::AppendLinkDirectoriesEntry(
               this->DirectoryState->LinkDirectoriesBacktraces,
               this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt);
 }
+void cmStateDirectory::PrependLinkDirectoriesEntry(
+  const std::string& vec, const cmListFileBacktrace& lfbt)
+{
+  std::vector<std::string>::iterator entryEnd =
+    this->DirectoryState->LinkDirectories.begin() +
+    this->Snapshot_.Position->LinkDirectoriesPosition;
+
+  std::vector<std::string>::reverse_iterator rend =
+    this->DirectoryState->LinkDirectories.rend();
+  std::vector<std::string>::reverse_iterator rbegin =
+    cmMakeReverseIterator(entryEnd);
+  rbegin = std::find(rbegin, rend, cmPropertySentinal);
+
+  std::vector<std::string>::iterator entryIt = rbegin.base();
+  std::vector<std::string>::iterator entryBegin =
+    this->DirectoryState->LinkDirectories.begin();
+
+  std::vector<cmListFileBacktrace>::iterator btIt =
+    this->DirectoryState->LinkDirectoriesBacktraces.begin() +
+    std::distance(entryBegin, entryIt);
+
+  this->DirectoryState->LinkDirectories.insert(entryIt, vec);
+  this->DirectoryState->LinkDirectoriesBacktraces.insert(btIt, lfbt);
+
+  this->Snapshot_.Position->LinkDirectoriesPosition =
+    this->DirectoryState->LinkDirectories.size();
+}
 
 void cmStateDirectory::SetLinkDirectories(const std::string& vec,
                                           const cmListFileBacktrace& lfbt)
diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h
index 06345e2..e5f4d05 100644
--- a/Source/cmStateDirectory.h
+++ b/Source/cmStateDirectory.h
@@ -62,6 +62,8 @@ public:
   cmBacktraceRange GetLinkOptionsEntryBacktraces() const;
   void AppendLinkOptionsEntry(std::string const& vec,
                               cmListFileBacktrace const& lfbt);
+  void PrependLinkDirectoriesEntry(std::string const& vec,
+                                   cmListFileBacktrace const& lfbt);
   void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt);
   void ClearLinkOptions();
 
diff --git a/Tests/CMakeCommands/link_directories/CMakeLists.txt b/Tests/CMakeCommands/link_directories/CMakeLists.txt
new file mode 100644
index 0000000..60c07b6
--- /dev/null
+++ b/Tests/CMakeCommands/link_directories/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.12)
+
+project(link_directories LANGUAGES C)
+
+
+link_directories(/A)
+link_directories(BEFORE /B)
+
+set(CMAKE_LINK_DIRECTORIES_BEFORE ON)
+link_directories(/C)
+
+get_directory_property(result LINK_DIRECTORIES)
+if (NOT result MATCHES "/C;/B;/A")
+  message(SEND_ERROR "link_directories not populated the LINK_DIRECTORIES directory property")
+endif()
+
+
+add_executable(link_directories EXCLUDE_FROM_ALL LinkDirectoriesExe.c)
+
+get_target_property(result link_directories LINK_DIRECTORIES)
+if (NOT result MATCHES "/C;/B;/A")
+  message(SEND_ERROR "link_directories not populated the LINK_DIRECTORIES target property")
+endif()
+
+
+add_library(imp UNKNOWN IMPORTED)
+get_target_property(result imp LINK_DIRECTORIES)
+if (result)
+  message(FATAL_ERROR "link_directories populated the LINK_DIRECTORIES target property")
+endif()
diff --git a/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c b/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 4753aac..83349e3 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -2823,7 +2823,6 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
   ADD_TEST_MACRO(CMakeCommands.add_compile_definitions add_compile_definitions)
   ADD_TEST_MACRO(CMakeCommands.add_compile_options add_compile_options)
   ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries)
-  ADD_TEST_MACRO(CMakeCommands.target_link_directories)
   ADD_TEST_MACRO(CMakeCommands.target_include_directories target_include_directories)
   ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
   ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
@@ -2831,6 +2830,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
 
   ADD_TEST_MACRO(CMakeCommands.add_link_options)
   ADD_TEST_MACRO(CMakeCommands.target_link_options)
+  ADD_TEST_MACRO(CMakeCommands.link_directories)
+  ADD_TEST_MACRO(CMakeCommands.target_link_directories)
 
   # The cmake server-mode test requires python for a simple client.
   find_package(PythonInterp QUIET)
-- 
cgit v0.12